Skip to content

Commit 6101ffa

Browse files
committed
Merge pull request #1051 from dotty-staging/change-isVolatile
Change is volatile
2 parents 6e7bb3a + 1511cb4 commit 6101ffa

36 files changed

+633
-166
lines changed

Diff for: src/dotty/tools/backend/jvm/DottyBackendInterface.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ class DottyBackendInterface()(implicit ctx: Context) extends BackendInterface{
131131

132132
val hashMethodSym: Symbol = NoSymbol // used to dispatch ## on primitives to ScalaRuntime.hash. Should be implemented by a miniphase
133133
val externalEqualsNumNum: Symbol = defn.BoxesRunTimeModule.requiredMethod(nme.equalsNumNum)
134-
lazy val externalEqualsNumChar: Symbol = ??? // ctx.requiredMethod(BoxesRunTimeTypeRef, nme.equalsNumChar) // this method is private
134+
val externalEqualsNumChar: Symbol = NoSymbol // ctx.requiredMethod(BoxesRunTimeTypeRef, nme.equalsNumChar) // this method is private
135135
val externalEqualsNumObject: Symbol = defn.BoxesRunTimeModule.requiredMethod(nme.equalsNumObject)
136136
val externalEquals: Symbol = defn.BoxesRunTimeClass.info.decl(nme.equals_).suchThat(toDenot(_).info.firstParamTypes.size == 2).symbol
137137
val MaxFunctionArity: Int = Definitions.MaxFunctionArity

Diff for: src/dotty/tools/dotc/core/CheckRealizable.scala

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package dotty.tools
2+
package dotc
3+
package core
4+
5+
import Contexts._, Types._, Symbols._, Names._, Flags._, Scopes._
6+
import SymDenotations._, Denotations.SingleDenotation
7+
import config.Printers._
8+
import util.Positions._
9+
import Decorators._
10+
import StdNames._
11+
import Annotations._
12+
import collection.mutable
13+
import ast.tpd._
14+
15+
/** Realizability status */
16+
object CheckRealizable {
17+
18+
abstract class Realizability(val msg: String) {
19+
def andAlso(other: => Realizability) =
20+
if (this == Realizable) other else this
21+
def mapError(f: Realizability => Realizability) =
22+
if (this == Realizable) this else f(this)
23+
}
24+
25+
object Realizable extends Realizability("")
26+
27+
object NotConcrete extends Realizability(" is not a concrete type")
28+
29+
object NotStable extends Realizability(" is not a stable reference")
30+
31+
class NotFinal(sym: Symbol)(implicit ctx: Context)
32+
extends Realizability(i" refers to nonfinal $sym")
33+
34+
class HasProblemBounds(typ: SingleDenotation)(implicit ctx: Context)
35+
extends Realizability(i" has a member $typ with possibly conflicting bounds ${typ.info.bounds.lo} <: ... <: ${typ.info.bounds.hi}")
36+
37+
class HasProblemField(fld: SingleDenotation, problem: Realizability)(implicit ctx: Context)
38+
extends Realizability(i" has a member $fld which is not a legal path\n since ${fld.symbol.name}: ${fld.info}${problem.msg}")
39+
40+
class ProblemInUnderlying(tp: Type, problem: Realizability)(implicit ctx: Context)
41+
extends Realizability(i"s underlying type ${tp}${problem.msg}") {
42+
assert(problem != Realizable)
43+
}
44+
45+
def realizability(tp: Type)(implicit ctx: Context) =
46+
new CheckRealizable().realizability(tp)
47+
48+
def boundsRealizability(tp: Type)(implicit ctx: Context) =
49+
new CheckRealizable().boundsRealizability(tp)
50+
}
51+
52+
/** Compute realizability status */
53+
class CheckRealizable(implicit ctx: Context) {
54+
import CheckRealizable._
55+
56+
/** A set of all fields that have already been checked. Used
57+
* to avoid infinite recursions when analyzing recursive types.
58+
*/
59+
private val checkedFields: mutable.Set[Symbol] = mutable.LinkedHashSet[Symbol]()
60+
61+
/** Is symbol's definitition a lazy val?
62+
* (note we exclude modules here, because their realizability is ensured separately)
63+
*/
64+
private def isLateInitialized(sym: Symbol) = sym.is(Lazy, butNot = Module)
65+
66+
/** Is this type a path with some part that is initialized on use?
67+
*/
68+
private def isLateInitialized(tp: Type): Boolean = tp.dealias match {
69+
case tp: TermRef =>
70+
isLateInitialized(tp.symbol) || isLateInitialized(tp.prefix)
71+
case _: SingletonType | NoPrefix =>
72+
false
73+
case tp: TypeRef =>
74+
true
75+
case tp: TypeProxy =>
76+
isLateInitialized(tp.underlying)
77+
case tp: AndOrType =>
78+
isLateInitialized(tp.tp1) || isLateInitialized(tp.tp2)
79+
case _ =>
80+
true
81+
}
82+
83+
/** The realizability status of given type `tp`*/
84+
def realizability(tp: Type): Realizability = tp.dealias match {
85+
case tp: TermRef =>
86+
val sym = tp.symbol
87+
if (sym.is(Stable)) realizability(tp.prefix)
88+
else {
89+
val r =
90+
if (!sym.isStable) NotStable
91+
else if (!isLateInitialized(sym)) realizability(tp.prefix)
92+
else if (!sym.isEffectivelyFinal) new NotFinal(sym)
93+
else realizability(tp.info).mapError(r => new ProblemInUnderlying(tp.info, r))
94+
if (r == Realizable) sym.setFlag(Stable)
95+
r
96+
}
97+
case _: SingletonType | NoPrefix =>
98+
Realizable
99+
case tp =>
100+
def isConcrete(tp: Type): Boolean = tp.dealias match {
101+
case tp: TypeRef => tp.symbol.isClass
102+
case tp: TypeProxy => isConcrete(tp.underlying)
103+
case tp: AndOrType => isConcrete(tp.tp1) && isConcrete(tp.tp2)
104+
case _ => false
105+
}
106+
if (!isConcrete(tp)) NotConcrete
107+
else boundsRealizability(tp).andAlso(memberRealizability(tp))
108+
}
109+
110+
/** `Realizable` if `tp` has good bounds, a `HasProblemBounds` instance
111+
* pointing to a bad bounds member otherwise.
112+
*/
113+
private def boundsRealizability(tp: Type) = {
114+
def hasBadBounds(mbr: SingleDenotation) = {
115+
val bounds = mbr.info.bounds
116+
!(bounds.lo <:< bounds.hi)
117+
}
118+
tp.nonClassTypeMembers.find(hasBadBounds) match {
119+
case Some(mbr) => new HasProblemBounds(mbr)
120+
case _ => Realizable
121+
}
122+
}
123+
124+
/** `Realizable` if `tp` all of `tp`'s non-struct fields have realizable types,
125+
* a `HasProblemField` instance pointing to a bad field otherwise.
126+
*/
127+
private def memberRealizability(tp: Type) = {
128+
def checkField(sofar: Realizability, fld: SingleDenotation): Realizability =
129+
sofar andAlso {
130+
if (checkedFields.contains(fld.symbol) || fld.symbol.is(Private | Mutable | Lazy))
131+
Realizable
132+
else {
133+
checkedFields += fld.symbol
134+
realizability(fld.info).mapError(r => new HasProblemField(fld, r))
135+
}
136+
}
137+
if (ctx.settings.strict.value)
138+
// check fields only under strict mode for now.
139+
// Reason: An embedded field could well be nullable, which means it
140+
// should not be part of a path and need not be checked; but we cannot recognize
141+
// this situation until we have a typesystem that tracks nullability.
142+
((Realizable: Realizability) /: tp.fields)(checkField)
143+
else
144+
Realizable
145+
}
146+
}

Diff for: src/dotty/tools/dotc/core/Contexts.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ object Contexts {
493493
}
494494

495495
@sharable object NoContext extends Context {
496-
lazy val base = unsupported("base")
496+
val base = null
497497
override val implicits: ContextualImplicits = new ContextualImplicits(Nil, null)(this)
498498
}
499499

Diff for: src/dotty/tools/dotc/core/Flags.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ object Flags {
300300
*/
301301
final val Abstract = commonFlag(23, "abstract")
302302

303-
/** Method is assumed to be stable */
303+
/** Lazy val or method is known or assumed to be stable and realizable */
304304
final val Stable = termFlag(24, "<stable>")
305305

306306
/** A case parameter accessor */

Diff for: src/dotty/tools/dotc/core/SymDenotations.scala

+7-16
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import scala.reflect.io.AbstractFile
1212
import Decorators.SymbolIteratorDecorator
1313
import ast._
1414
import annotation.tailrec
15+
import CheckRealizable._
1516
import typer.Mode
1617
import util.SimpleMap
1718
import util.Stats
@@ -519,15 +520,8 @@ object SymDenotations {
519520
)
520521

521522
/** Is this a denotation of a stable term (or an arbitrary type)? */
522-
final def isStable(implicit ctx: Context) = {
523-
val isUnstable =
524-
(this is UnstableValue) ||
525-
ctx.isVolatile(info) && !hasAnnotation(defn.UncheckedStableAnnot)
526-
(this is Stable) || isType || {
527-
if (isUnstable) false
528-
else { setFlag(Stable); true }
529-
}
530-
}
523+
final def isStable(implicit ctx: Context) =
524+
isType || is(Stable) || !(is(UnstableValue) || info.isInstanceOf[ExprType])
531525

532526
/** Is this a "real" method? A real method is a method which is:
533527
* - not an accessor
@@ -816,14 +810,11 @@ object SymDenotations {
816810
enclClass(symbol, false)
817811
}
818812

819-
final def isEffectivelyFinal(implicit ctx: Context): Boolean = {
820-
(this.flags is Flags.PrivateOrFinal) || (!this.owner.isClass) ||
821-
((this.owner.flags is (Flags.ModuleOrFinal)) && (!this.flags.is(Flags.MutableOrLazy))) ||
822-
(this.owner.isAnonymousClass)
823-
}
813+
/** A symbol is effectively final if it cannot be overridden in a subclass */
814+
final def isEffectivelyFinal(implicit ctx: Context): Boolean =
815+
is(PrivateOrFinal) || !owner.isClass || owner.is(ModuleOrFinal) || owner.isAnonymousClass
824816

825-
/** The class containing this denotation which has the given effective name.
826-
*/
817+
/** The class containing this denotation which has the given effective name. */
827818
final def enclosingClassNamed(name: Name)(implicit ctx: Context): Symbol = {
828819
val cls = enclosingClass
829820
if (cls.effectiveName == name || !cls.exists) cls else cls.owner.enclosingClassNamed(name)

Diff for: src/dotty/tools/dotc/core/TypeOps.scala

+1-93
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package dotc
33
package core
44

55
import Contexts._, Types._, Symbols._, Names._, Flags._, Scopes._
6-
import SymDenotations._, Denotations.Denotation
6+
import SymDenotations._, Denotations.SingleDenotation
77
import config.Printers._
88
import util.Positions._
99
import Decorators._
@@ -341,96 +341,6 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
341341
}
342342
}
343343

344-
/** A type is volatile if its DNF contains an alternative of the form
345-
* {P1, ..., Pn}, {N1, ..., Nk}, where the Pi are parent typerefs and the
346-
* Nj are refinement names, and one the 4 following conditions is met:
347-
*
348-
* 1. At least two of the parents Pi are abstract types.
349-
* 2. One of the parents Pi is an abstract type, and one other type Pj,
350-
* j != i has an abstract member which has the same name as an
351-
* abstract member of the whole type.
352-
* 3. One of the parents Pi is an abstract type, and one of the refinement
353-
* names Nj refers to an abstract member of the whole type.
354-
* 4. One of the parents Pi is an an alias type with a volatile alias
355-
* or an abstract type with a volatile upper bound.
356-
*
357-
* Lazy values are not allowed to have volatile type, as otherwise
358-
* unsoundness can result.
359-
*/
360-
final def isVolatile(tp: Type): Boolean = {
361-
362-
/** Pre-filter to avoid expensive DNF computation
363-
* If needsChecking returns false it is guaranteed that
364-
* DNF does not contain intersections, or abstract types with upper
365-
* bounds that themselves need checking.
366-
*/
367-
def needsChecking(tp: Type, isPart: Boolean): Boolean = tp match {
368-
case tp: TypeRef =>
369-
tp.info match {
370-
case TypeAlias(alias) =>
371-
needsChecking(alias, isPart)
372-
case TypeBounds(lo, hi) =>
373-
isPart || tp.controlled(isVolatile(hi))
374-
case _ => false
375-
}
376-
case tp: RefinedType =>
377-
needsChecking(tp.parent, true)
378-
case tp: TypeProxy =>
379-
needsChecking(tp.underlying, isPart)
380-
case tp: AndType =>
381-
true
382-
case tp: OrType =>
383-
isPart || needsChecking(tp.tp1, isPart) && needsChecking(tp.tp2, isPart)
384-
case _ =>
385-
false
386-
}
387-
388-
needsChecking(tp, false) && {
389-
DNF(tp) forall { case (parents, refinedNames) =>
390-
val absParents = parents filter (_.symbol is Deferred)
391-
absParents.nonEmpty && {
392-
absParents.lengthCompare(2) >= 0 || {
393-
val ap = absParents.head
394-
((parents exists (p =>
395-
(p ne ap)
396-
|| p.memberNames(abstractTypeNameFilter, tp).nonEmpty
397-
|| p.memberNames(abstractTermNameFilter, tp).nonEmpty))
398-
|| (refinedNames & tp.memberNames(abstractTypeNameFilter, tp)).nonEmpty
399-
|| (refinedNames & tp.memberNames(abstractTermNameFilter, tp)).nonEmpty
400-
|| isVolatile(ap))
401-
}
402-
}
403-
}
404-
}
405-
}
406-
407-
/** The disjunctive normal form of this type.
408-
* This collects a set of alternatives, each alternative consisting
409-
* of a set of typerefs and a set of refinement names. Both sets are represented
410-
* as lists, to obtain a deterministic order. Collected are
411-
* all type refs reachable by following aliases and type proxies, and
412-
* collecting the elements of conjunctions (&) and disjunctions (|).
413-
* The set of refinement names in each alternative
414-
* are the set of names in refinement types encountered during the collection.
415-
*/
416-
final def DNF(tp: Type): List[(List[TypeRef], Set[Name])] = ctx.traceIndented(s"DNF($this)", checks) {
417-
tp.dealias match {
418-
case tp: TypeRef =>
419-
(tp :: Nil, Set[Name]()) :: Nil
420-
case RefinedType(parent, name) =>
421-
for ((ps, rs) <- DNF(parent)) yield (ps, rs + name)
422-
case tp: TypeProxy =>
423-
DNF(tp.underlying)
424-
case AndType(l, r) =>
425-
for ((lps, lrs) <- DNF(l); (rps, rrs) <- DNF(r))
426-
yield (lps | rps, lrs | rrs)
427-
case OrType(l, r) =>
428-
DNF(l) | DNF(r)
429-
case tp =>
430-
TypeOps.emptyDNF
431-
}
432-
}
433-
434344
private def enterArgBinding(formal: Symbol, info: Type, cls: ClassSymbol, decls: Scope) = {
435345
val lazyInfo = new LazyType { // needed so we do not force `formal`.
436346
def complete(denot: SymDenotation)(implicit ctx: Context): Unit = {
@@ -644,10 +554,8 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
644554
if (scala2Mode) migrationWarning(msg, pos)
645555
scala2Mode
646556
}
647-
648557
}
649558

650559
object TypeOps {
651-
val emptyDNF = (Nil, Set[Name]()) :: Nil
652560
@sharable var track = false // !!!DEBUG
653561
}

Diff for: src/dotty/tools/dotc/core/Types.scala

+15
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,12 @@ object Types {
573573
(name, buf) => buf += member(name).asSingleDenotation)
574574
}
575575

576+
/** The set of abstract type members of this type. */
577+
final def nonClassTypeMembers(implicit ctx: Context): Seq[SingleDenotation] = track("nonClassTypeMembers") {
578+
memberDenots(nonClassTypeNameFilter,
579+
(name, buf) => buf += member(name).asSingleDenotation)
580+
}
581+
576582
/** The set of type members of this type */
577583
final def typeMembers(implicit ctx: Context): Seq[SingleDenotation] = track("typeMembers") {
578584
memberDenots(typeNameFilter,
@@ -3368,6 +3374,15 @@ object Types {
33683374
}
33693375
}
33703376

3377+
/** A filter for names of abstract types of a given type */
3378+
object nonClassTypeNameFilter extends NameFilter {
3379+
def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean =
3380+
name.isTypeName && {
3381+
val mbr = pre.member(name)
3382+
mbr.symbol.isType && !mbr.symbol.isClass
3383+
}
3384+
}
3385+
33713386
/** A filter for names of deferred term definitions of a given type */
33723387
object abstractTermNameFilter extends NameFilter {
33733388
def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean =

Diff for: src/dotty/tools/dotc/transform/PostTyper.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran
101101
case tree: TypeTree => tree
102102
case _ =>
103103
if (tree.isType) {
104-
Checking.boundsChecker.traverse(tree)
104+
Checking.typeChecker.traverse(tree)
105105
TypeTree(tree.tpe).withPos(tree.pos)
106106
}
107107
else tree.tpe.widenTermRefExpr match {
@@ -180,7 +180,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran
180180
val tree1 =
181181
if (sym.isClass) tree
182182
else {
183-
Checking.boundsChecker.traverse(tree.rhs)
183+
Checking.typeChecker.traverse(tree.rhs)
184184
cpy.TypeDef(tree)(rhs = TypeTree(tree.symbol.info))
185185
}
186186
super.transform(tree1)

0 commit comments

Comments
 (0)