-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Change is volatile #1051
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change is volatile #1051
Changes from all commits
633e2eb
41f0567
1cc4d90
1b7745e
7ccd02c
322bcfe
44c14b3
9a6f82b
5fd2028
0480cb2
e87dee2
a0fb068
3637e08
defba2a
1441622
f44a1ed
20fc6bd
d34256c
ec4a3a0
eaa1578
187c241
f6a5802
1511cb4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package dotty.tools | ||
package dotc | ||
package core | ||
|
||
import Contexts._, Types._, Symbols._, Names._, Flags._, Scopes._ | ||
import SymDenotations._, Denotations.SingleDenotation | ||
import config.Printers._ | ||
import util.Positions._ | ||
import Decorators._ | ||
import StdNames._ | ||
import Annotations._ | ||
import collection.mutable | ||
import ast.tpd._ | ||
|
||
/** Realizability status */ | ||
object CheckRealizable { | ||
|
||
abstract class Realizability(val msg: String) { | ||
def andAlso(other: => Realizability) = | ||
if (this == Realizable) other else this | ||
def mapError(f: Realizability => Realizability) = | ||
if (this == Realizable) this else f(this) | ||
} | ||
|
||
object Realizable extends Realizability("") | ||
|
||
object NotConcrete extends Realizability(" is not a concrete type") | ||
|
||
object NotStable extends Realizability(" is not a stable reference") | ||
|
||
class NotFinal(sym: Symbol)(implicit ctx: Context) | ||
extends Realizability(i" refers to nonfinal $sym") | ||
|
||
class HasProblemBounds(typ: SingleDenotation)(implicit ctx: Context) | ||
extends Realizability(i" has a member $typ with possibly conflicting bounds ${typ.info.bounds.lo} <: ... <: ${typ.info.bounds.hi}") | ||
|
||
class HasProblemField(fld: SingleDenotation, problem: Realizability)(implicit ctx: Context) | ||
extends Realizability(i" has a member $fld which is not a legal path\n since ${fld.symbol.name}: ${fld.info}${problem.msg}") | ||
|
||
class ProblemInUnderlying(tp: Type, problem: Realizability)(implicit ctx: Context) | ||
extends Realizability(i"s underlying type ${tp}${problem.msg}") { | ||
assert(problem != Realizable) | ||
} | ||
|
||
def realizability(tp: Type)(implicit ctx: Context) = | ||
new CheckRealizable().realizability(tp) | ||
|
||
def boundsRealizability(tp: Type)(implicit ctx: Context) = | ||
new CheckRealizable().boundsRealizability(tp) | ||
} | ||
|
||
/** Compute realizability status */ | ||
class CheckRealizable(implicit ctx: Context) { | ||
import CheckRealizable._ | ||
|
||
/** A set of all fields that have already been checked. Used | ||
* to avoid infinite recursions when analyzing recursive types. | ||
*/ | ||
private val checkedFields: mutable.Set[Symbol] = mutable.LinkedHashSet[Symbol]() | ||
|
||
/** Is symbol's definitition a lazy val? | ||
* (note we exclude modules here, because their realizability is ensured separately) | ||
*/ | ||
private def isLateInitialized(sym: Symbol) = sym.is(Lazy, butNot = Module) | ||
|
||
/** Is this type a path with some part that is initialized on use? | ||
*/ | ||
private def isLateInitialized(tp: Type): Boolean = tp.dealias match { | ||
case tp: TermRef => | ||
isLateInitialized(tp.symbol) || isLateInitialized(tp.prefix) | ||
case _: SingletonType | NoPrefix => | ||
false | ||
case tp: TypeRef => | ||
true | ||
case tp: TypeProxy => | ||
isLateInitialized(tp.underlying) | ||
case tp: AndOrType => | ||
isLateInitialized(tp.tp1) || isLateInitialized(tp.tp2) | ||
case _ => | ||
true | ||
} | ||
|
||
/** The realizability status of given type `tp`*/ | ||
def realizability(tp: Type): Realizability = tp.dealias match { | ||
case tp: TermRef => | ||
val sym = tp.symbol | ||
if (sym.is(Stable)) realizability(tp.prefix) | ||
else { | ||
val r = | ||
if (!sym.isStable) NotStable | ||
else if (!isLateInitialized(sym)) realizability(tp.prefix) | ||
else if (!sym.isEffectivelyFinal) new NotFinal(sym) | ||
else realizability(tp.info).mapError(r => new ProblemInUnderlying(tp.info, r)) | ||
if (r == Realizable) sym.setFlag(Stable) | ||
r | ||
} | ||
case _: SingletonType | NoPrefix => | ||
Realizable | ||
case tp => | ||
def isConcrete(tp: Type): Boolean = tp.dealias match { | ||
case tp: TypeRef => tp.symbol.isClass | ||
case tp: TypeProxy => isConcrete(tp.underlying) | ||
case tp: AndOrType => isConcrete(tp.tp1) && isConcrete(tp.tp2) | ||
case _ => false | ||
} | ||
if (!isConcrete(tp)) NotConcrete | ||
else boundsRealizability(tp).andAlso(memberRealizability(tp)) | ||
} | ||
|
||
/** `Realizable` if `tp` has good bounds, a `HasProblemBounds` instance | ||
* pointing to a bad bounds member otherwise. | ||
*/ | ||
private def boundsRealizability(tp: Type) = { | ||
def hasBadBounds(mbr: SingleDenotation) = { | ||
val bounds = mbr.info.bounds | ||
!(bounds.lo <:< bounds.hi) | ||
} | ||
tp.nonClassTypeMembers.find(hasBadBounds) match { | ||
case Some(mbr) => new HasProblemBounds(mbr) | ||
case _ => Realizable | ||
} | ||
} | ||
|
||
/** `Realizable` if `tp` all of `tp`'s non-struct fields have realizable types, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the first |
||
* a `HasProblemField` instance pointing to a bad field otherwise. | ||
*/ | ||
private def memberRealizability(tp: Type) = { | ||
def checkField(sofar: Realizability, fld: SingleDenotation): Realizability = | ||
sofar andAlso { | ||
if (checkedFields.contains(fld.symbol) || fld.symbol.is(Private | Mutable | Lazy)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you bypass the realizability checks if a field is private or mutable or lazy? |
||
Realizable | ||
else { | ||
checkedFields += fld.symbol | ||
realizability(fld.info).mapError(r => new HasProblemField(fld, r)) | ||
} | ||
} | ||
if (ctx.settings.strict.value) | ||
// check fields only under strict mode for now. | ||
// Reason: An embedded field could well be nullable, which means it | ||
// should not be part of a path and need not be checked; but we cannot recognize | ||
// this situation until we have a typesystem that tracks nullability. | ||
((Realizable: Realizability) /: tp.fields)(checkField) | ||
else | ||
Realizable | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is never used.