@@ -4,8 +4,16 @@ package typer
44
55import core ._
66import ast ._
7- import Contexts ._ , Types ._ , Flags ._ , Denotations ._ , Names ._ , StdNames ._ , NameOps ._ , Symbols ._
8- import Trees ._ , ProtoTypes ._
7+ import Contexts ._
8+ import Types ._
9+ import Flags ._
10+ import Denotations ._
11+ import Names ._
12+ import StdNames ._
13+ import NameOps ._
14+ import Symbols ._
15+ import Trees ._
16+ import ProtoTypes ._
917import Constants ._
1018import Scopes ._
1119import annotation .unchecked
@@ -14,13 +22,125 @@ import util.{Stats, SimpleMap}
1422import util .common ._
1523import Decorators ._
1624import Uniques ._
17- import ErrorReporting .{errorType , DiagnosticString }
25+ import ErrorReporting .{err , errorType , DiagnosticString }
1826import config .Printers ._
1927import collection .mutable
28+ import SymDenotations .NoCompleter
29+
30+ object Checking {
31+ import tpd ._
32+
33+ /** A general checkBounds method that can be used for TypeApply nodes as
34+ * well as for AppliedTypeTree nodes.
35+ */
36+ def checkBounds (args : List [tpd.Tree ], bounds : List [TypeBounds ], instantiate : (Type , List [Type ]) => Type )(implicit ctx : Context ) = {
37+ val argTypes = args.tpes
38+ for ((arg, bounds) <- args zip bounds) {
39+ def notConforms (which : String , bound : Type ) = {
40+ ctx.error(
41+ d " Type argument ${arg.tpe} does not conform to $which bound $bound ${err.whyNoMatchStr(arg.tpe, bound)}" ,
42+ 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+ }
57+
58+ /** A type map which checks that the only cycles in a type are F-bounds
59+ * and that protects all F-bounded references by LazyRefs.
60+ */
61+ class CheckNonCyclicMap (implicit ctx : Context ) extends TypeMap {
62+
63+ /** Are cycles allowed within nested refinedInfos of currently checked type? */
64+ private var nestedCycleOK = false
65+
66+ /** Are cycles allwoed within currently checked type? */
67+ private var cycleOK = false
68+
69+ /** A diagnostic output string that indicates the position of the last
70+ * part of a type bounds checked by checkInfo. Possible choices:
71+ * alias, lower bound, upper bound.
72+ */
73+ var where : String = " "
74+
75+ /** Check info `tp` for cycles. Throw CyclicReference for illegal cycles,
76+ * break direct cycle with a LazyRef for legal, F-bounded cycles.
77+ */
78+ def checkInfo (tp : Type ): Type = tp match {
79+ case tp @ TypeBounds (lo, hi) =>
80+ if (lo eq hi)
81+ try tp.derivedTypeAlias(apply(lo))
82+ finally where = " alias"
83+ else {
84+ val lo1 = try apply(lo) finally where = " lower bound"
85+ val saved = nestedCycleOK
86+ nestedCycleOK = true
87+ try tp.derivedTypeBounds(lo1, apply(hi))
88+ finally {
89+ nestedCycleOK = saved
90+ where = " upper bound"
91+ }
92+ }
93+ case _ =>
94+ tp
95+ }
96+
97+ def apply (tp : Type ) = tp match {
98+ case tp @ RefinedType (parent, name) =>
99+ val parent1 = this (parent)
100+ val saved = cycleOK
101+ cycleOK = nestedCycleOK
102+ try tp.derivedRefinedType(parent1, name, this (tp.refinedInfo))
103+ finally cycleOK = saved
104+ case tp @ TypeRef (pre, name) =>
105+ try {
106+ // Check info of typeref recursively, marking the referred symbol
107+ // with NoCompleter. This provokes a CyclicReference when the symbol
108+ // is hit again. Without this precaution we could stackoverflow here.
109+ val info = tp.info
110+ val symInfo = tp.symbol.info
111+ if (tp.symbol.exists) tp.symbol.info = SymDenotations .NoCompleter
112+ try checkInfo(info)
113+ finally if (tp.symbol.exists) tp.symbol.info = symInfo
114+ tp
115+ } catch {
116+ case ex : CyclicReference =>
117+ ctx.debuglog(i " cycle detected for $tp, $nestedCycleOK, $cycleOK" )
118+ if (cycleOK) LazyRef (() => tp) else throw ex
119+ }
120+ case _ => mapOver(tp)
121+ }
122+ }
123+ }
20124
21125trait Checking {
22126
23127 import tpd ._
128+ import Checking ._
129+
130+ /** Check that `info` of symbol `sym` is not cyclic.
131+ * @pre sym is not yet initialized (i.e. its type is a Completer).
132+ * @return `info` where every legal F-bounded reference is proctected
133+ * by a `LazyRef`, or `ErrorType` if a cycle was detected and reported.
134+ */
135+ def checkNonCyclic (sym : Symbol , info : TypeBounds )(implicit ctx : Context ): Type = {
136+ val checker = new CheckNonCyclicMap
137+ try checker.checkInfo(info)
138+ catch {
139+ case ex : CyclicReference =>
140+ ctx.error(i " illegal cyclic reference: ${checker.where} $info of $sym refers back to the type itself " , sym.pos)
141+ ErrorType
142+ }
143+ }
24144
25145 /** Check that Java statics and packages can only be used in selections.
26146 */
@@ -32,17 +152,13 @@ trait Checking {
32152 tree
33153 }
34154
35- /** Check that type arguments `args` conform to corresponding bounds in `poly` */
36- def checkBounds (args : List [tpd.Tree ], poly : PolyType , pos : Position )(implicit ctx : Context ): Unit = {
37- val argTypes = args.tpes
38- def substituted (tp : Type ) = tp.substParams(poly, argTypes)
39- for ((arg, bounds) <- args zip poly.paramBounds) {
40- def notConforms (which : String , bound : Type ) =
41- ctx.error(d " Type argument ${arg.tpe} does not conform to $which bound $bound" , arg.pos)
42- if (! (arg.tpe <:< substituted(bounds.hi))) notConforms(" upper" , bounds.hi)
43- if (! (bounds.lo <:< arg.tpe)) notConforms(" lower" , bounds.lo)
44- }
45- }
155+ /** Check that type arguments `args` conform to corresponding bounds in `poly`
156+ * Note: This does not check the bounds of AppliedTypeTrees. These
157+ * are handled by method checkBounds in FirstTransform
158+ * TODO: remove pos parameter
159+ */
160+ def checkBounds (args : List [tpd.Tree ], poly : PolyType , pos : Position )(implicit ctx : Context ): Unit = Checking .checkBounds(
161+ args, poly.paramBounds, (tp, argTypes) => tp.substParams(poly, argTypes))
46162
47163 /** Check that type `tp` is stable. */
48164 def checkStable (tp : Type , pos : Position )(implicit ctx : Context ): Unit =
0 commit comments