Skip to content

Cosmetic changes to the completions framework #7451

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
7e66e65
Drop the workaround -Yno-inline flag from semanticdb
anatoliykmetyuk Oct 24, 2019
5e7ba22
Check flags that are not supposed to be mutated by completion
anatoliykmetyuk Oct 15, 2019
6a69358
Better error message
anatoliykmetyuk Oct 15, 2019
163c83e
Flag mutation check case added for loaded classes
anatoliykmetyuk Oct 16, 2019
d8e1085
Fix loading of the module classes
anatoliykmetyuk Oct 16, 2019
54cedb7
Fix neg completeFromSource test
anatoliykmetyuk Oct 16, 2019
c1de679
Mark Scala2Unpickler-processed denotations as loading
anatoliykmetyuk Oct 17, 2019
4d46b59
Do not reset the GivenOrImplicit flags on implicits without explicit …
anatoliykmetyuk Oct 18, 2019
e99d073
Drop `forceInline` annotation
anatoliykmetyuk Oct 18, 2019
f098055
Count symbol redefinition on late compilation as symbol loading
anatoliykmetyuk Oct 18, 2019
46bab0c
Remove Macro from FromStartFlags
anatoliykmetyuk Oct 18, 2019
d635240
Fix i7407 test error message
anatoliykmetyuk Oct 18, 2019
a11d995
Tighten flag checking rules on completion
anatoliykmetyuk Oct 21, 2019
87217db
Introduce conditionally mutable flags wrt completion
anatoliykmetyuk Oct 23, 2019
bd4719e
Opaque flag is immutable wrt completion only after load
anatoliykmetyuk Oct 23, 2019
d8a51f2
Process Deferred flag last on unpickling
anatoliykmetyuk Oct 24, 2019
aebd4c5
isLoading variable replaced with a flag for performance reasons
anatoliykmetyuk Oct 25, 2019
f7c1bb0
Rename Touched flag to Completing
anatoliykmetyuk Oct 25, 2019
910098b
Rename isCompleted and isCompleting methods of SymDenotation
anatoliykmetyuk Oct 25, 2019
052ff94
Add isCompleting predicate to symbol denotations
anatoliykmetyuk Oct 25, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion community-build/community-projects/semanticdb
Submodule semanticdb updated 1 files
+1 −1 build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ trait ConstraintHandling[AbstractContext] {
else
isSubType(tp1, tp2)

@forceInline final def inFrozenConstraint[T](op: => T): T = {
inline final def inFrozenConstraint[T](op: => T): T = {
val savedFrozen = frozenConstraint
val savedLambda = caseLambda
frozenConstraint = true
Expand Down
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ class Definitions {

@tu lazy val ObjectClass: ClassSymbol = {
val cls = ctx.requiredClass("java.lang.Object")
assert(!cls.isCompleted, "race for completing java.lang.Object")
assert(!cls.isCompletedOrStubbed, "race for completing java.lang.Object")
cls.info = ClassInfo(cls.owner.thisType, cls, AnyClass.typeRef :: Nil, newScope)
cls.setFlag(NoInits)

Expand Down Expand Up @@ -750,7 +750,6 @@ class Definitions {
@tu lazy val DeprecatedAnnot: ClassSymbol = ctx.requiredClass("scala.deprecated")
@tu lazy val ImplicitAmbiguousAnnot: ClassSymbol = ctx.requiredClass("scala.annotation.implicitAmbiguous")
@tu lazy val ImplicitNotFoundAnnot: ClassSymbol = ctx.requiredClass("scala.annotation.implicitNotFound")
@tu lazy val ForceInlineAnnot: ClassSymbol = ctx.requiredClass("scala.forceInline")
@tu lazy val InlineParamAnnot: ClassSymbol = ctx.requiredClass("scala.annotation.internal.InlineParam")
@tu lazy val InvariantBetweenAnnot: ClassSymbol = ctx.requiredClass("scala.annotation.internal.InvariantBetween")
@tu lazy val MainAnnot: ClassSymbol = ctx.requiredClass("scala.main")
Expand Down
30 changes: 23 additions & 7 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -356,8 +356,8 @@ object Flags {
/** Symbol is not a member of its owner */
val (NonMember @ _, _, _) = newFlags(45, "<non-member>")

/** Denotation is in train of being loaded and completed, used to catch cyclic dependencies */
val (Touched @ _, _, _) = newFlags(48, "<touched>")
/** Denotation is in train of being completed, used to catch cyclic dependencies */
val (Completing @ _, _, _) = newFlags(48, "<completing>")

/** Class has been lifted out to package level, local value has been lifted out to class level */
val (Lifted @ _, _, _) = newFlags(51, "<lifted>")
Expand Down Expand Up @@ -406,6 +406,11 @@ object Flags {
/** A denotation that is valid in all run-ids */
val (Permanent @ _, _, _) = newFlags(61, "<permanent>")

/** Denotation is being loaded. If `Completing` is not set,
* this means this denotation was loaded from an external file.
*/
val (Loading @ _, _, _) = newFlags(62, "<loading>")

// --------- Combined Flag Sets and Conjunctions ----------------------

/** All possible flags */
Expand Down Expand Up @@ -434,23 +439,34 @@ object Flags {
commonFlags(Module, Param, Synthetic, Package, Local, Mutable, Trait)

/** Flags that are not (re)set when completing the denotation
* TODO: Should check that FromStartFlags do not change in completion
*/
val FromStartFlags: FlagSet = commonFlags(
Module, Package, Deferred, Method, Case,
Module, Package, Method, Case,
HigherKinded, Param, ParamAccessor,
Scala2ExistentialCommon, Mutable, Opaque, Touched, JavaStatic,
Scala2ExistentialCommon, Mutable, Completing, JavaStatic,
OuterOrCovariant, LabelOrContravariant, CaseAccessor,
Extension, NonMember, Implicit, Given, Permanent, Synthetic,
SuperAccessorOrScala2x, Inline, Macro)
SuperAccessorOrScala2x, Inline)

/** Flags that are not (re)set when completing the denotation, or, if symbol is
* a top-level class or object, when completing the denotation once the class
* file defining the symbol is loaded (which is generally before the denotation
* is completed)
*/
val AfterLoadFlags: FlagSet = commonFlags(
FromStartFlags, AccessFlags, Final, AccessorOrSealed, LazyOrTrait, SelfName, JavaDefined)
FromStartFlags, Opaque, AccessFlags, Final, AccessorOrSealed, LazyOrTrait, SelfName, JavaDefined)

/** Flags that are not mutated during completion when a certain
* condition is satisfied
*/
object ConditionallyImmutableFlags {
val flagsAndConditions: List[(Flag, (given Contexts.Context) => SymDenotations.SymDenotation => Boolean)] = List(
Deferred -> { denot => denot.is(Opaque) && denot.isType }
)

val flags: FlagSet = flagsAndConditions.map(_._1)
.foldLeft(EmptyFlags) { (accum, next) => accum | next }
}


/** A value that's unstable unless complemented with a Stable flag */
Expand Down
83 changes: 54 additions & 29 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -160,20 +160,34 @@ object SymDenotations {
*/
private[dotc] final def flagsUNSAFE: FlagSet = myFlags

private def setMyFlags(flags: FlagSet) =
lazy val immutableFlags = if myInfo.isInstanceOf[SymbolLoader] then AfterLoadFlags else FromStartFlags
lazy val changedImmutableFlags = (myFlags ^ flags) & immutableFlags
if myFlags.is(Completing, butNot = Loading) then assert(changedImmutableFlags.isEmpty,
s"Illegal mutation of flags ${changedImmutableFlags.flagsString} on completion of $this")
myFlags = flags

final def startedLoading() =
myFlags |= Loading

final def finishedLoading() =
myFlags &~= Loading


final def flagsString(implicit ctx: Context): String = flags.flagsString

/** Adapt flag set to this denotation's term or type nature */
private def adaptFlags(flags: FlagSet) = if (isType) flags.toTypeFlags else flags.toTermFlags

/** Update the flag set */
final def flags_=(flags: FlagSet): Unit =
myFlags = adaptFlags(flags)
setMyFlags(adaptFlags(flags))

/** Set given flags(s) of this denotation */
final def setFlag(flags: FlagSet): Unit = { myFlags |= flags }
final def setFlag(flags: FlagSet): Unit = { setMyFlags(myFlags | flags) }

/** Unset given flags(s) of this denotation */
final def resetFlag(flags: FlagSet): Unit = { myFlags &~= flags }
final def resetFlag(flags: FlagSet): Unit = { setMyFlags(myFlags &~ flags) }

/** Set applicable flags in {NoInits, PureInterface}
* @param parentFlags The flags that match the class or trait's parents
Expand All @@ -184,10 +198,19 @@ object SymDenotations {
if (myFlags.is(Trait)) NoInitsInterface & bodyFlags // no parents are initialized from a trait
else NoInits & bodyFlags & parentFlags)

private def isCurrent(fs: FlagSet) =
fs <= (
private def isCurrent(fs: FlagSet)(implicit ctx: Context): Boolean =
val immutableFlags =
if (myInfo.isInstanceOf[SymbolLoader]) FromStartFlags
else AfterLoadFlags)
else AfterLoadFlags
val mutableFlagsBeingQueried = fs &~ immutableFlags

mutableFlagsBeingQueried.isEmpty || ( // All flags are immutable wrt completion
mutableFlagsBeingQueried <= ConditionallyImmutableFlags.flags && // All the mutable flags being queried are not mutated during completion under certain conditions
ConditionallyImmutableFlags.flagsAndConditions.forall { case (flag, condition) =>
(flag & mutableFlagsBeingQueried).isEmpty || !condition(this)
}
)


final def relevantFlagsFor(fs: FlagSet)(implicit ctx: Context) =
if (isCurrent(fs)) myFlags else flags
Expand Down Expand Up @@ -236,8 +259,8 @@ object SymDenotations {
println(i"${" " * indent}completing ${if (isType) "type" else "val"} $name")
indent += 1

if (myFlags.is(Touched)) throw CyclicReference(this)
myFlags |= Touched
if (isCompleting) throw CyclicReference(this)
myFlags |= Completing

// completions.println(s"completing ${this.debugString}")
try completer.complete(this)(ctx.withPhase(validFor.firstPhaseId))
Expand All @@ -252,10 +275,11 @@ object SymDenotations {
}
}
else {
if (myFlags.is(Touched)) throw CyclicReference(this)
myFlags |= Touched
if (isCompleting) throw CyclicReference(this)
myFlags |= Completing
completer.complete(this)(ctx.withPhase(validFor.firstPhaseId))
}
myFlags &~= Completing

protected[dotc] def info_=(tp: Type): Unit = {
/* // DEBUG
Expand Down Expand Up @@ -300,10 +324,10 @@ object SymDenotations {

/** Set privateWithin, prefer setting it at symbol-creation time instead if
* possible.
* @pre `isCompleting` is false, or this is a ModuleCompleter or SymbolLoader
* @pre `isCompletingAndNotStubbed` is false, or this is a ModuleCompleter or SymbolLoader
*/
protected[dotc] final def setPrivateWithin(pw: Symbol)(implicit ctx: Context): Unit = {
if (isCompleting)
if (isCompletingAndNotStubbed)
assert(myInfo.isInstanceOf[ModuleCompleter | SymbolLoader],
s"Illegal call to `setPrivateWithin($pw)` while completing $this using completer $myInfo")
myPrivateWithin = pw
Expand Down Expand Up @@ -370,11 +394,19 @@ object SymDenotations {
case Nil => Nil
}

/** The denotation is completed: info is not a lazy type and attributes have defined values */
final def isCompleted: Boolean = !myInfo.isInstanceOf[LazyType]
/** The denotation's info is not set to a LazyType. This means one of
* two things: either it is completed or it is stubbed with a temporary
* info so that to avoid cyclic references.
*/
final def isCompletedOrStubbed: Boolean = !myInfo.isInstanceOf[LazyType]

/** The denotation is in train of being completed and its info is not stubbed
* with a non-lazy type.
*/
final def isCompletingAndNotStubbed: Boolean = isCompleting && !isCompletedOrStubbed

/** The denotation is in train of being completed */
final def isCompleting: Boolean = myFlags.is(Touched) && !isCompleted
final def isCompleting: Boolean = myFlags.is(Completing)

/** The completer of this denotation. @pre: Denotation is not yet completed */
final def completer: LazyType = myInfo.asInstanceOf[LazyType]
Expand Down Expand Up @@ -546,10 +578,10 @@ object SymDenotations {
def isError: Boolean = false

/** Make denotation not exist.
* @pre `isCompleting` is false, or this is a ModuleCompleter or SymbolLoader
* @pre `isCompletingAndNotStubbed` is false, or this is a ModuleCompleter or SymbolLoader
*/
final def markAbsent()(implicit ctx: Context): Unit = {
if (isCompleting)
if (isCompletingAndNotStubbed)
assert(myInfo.isInstanceOf[ModuleCompleter | SymbolLoader],
s"Illegal call to `markAbsent()` while completing $this using completer $myInfo")
myInfo = NoType
Expand Down Expand Up @@ -741,7 +773,7 @@ object SymDenotations {
final def isSetter(implicit ctx: Context): Boolean =
this.is(Accessor) &&
originalName.isSetterName &&
(!isCompleted || info.firstParamTypes.nonEmpty) // to avoid being fooled by var x_= : Unit = ...
(!isCompletedOrStubbed || info.firstParamTypes.nonEmpty) // to avoid being fooled by var x_= : Unit = ...

/** is this a symbol representing an import? */
final def isImport: Boolean = name == nme.IMPORT
Expand Down Expand Up @@ -918,17 +950,10 @@ object SymDenotations {
/** Is this a Scala 2 macro */
final def isScala2Macro(implicit ctx: Context): Boolean = is(Macro) && symbol.owner.is(Scala2x)

/** An erased value or an inline method, excluding @forceInline annotated methods.
* The latter have to be kept around to get to parity with Scala.
* This is necessary at least until we have full bootstrap. Right now
* dotty-bootstrapped involves running the Dotty compiler compiled with Scala 2 with
* a Dotty runtime library compiled with Dotty. If we erase @forceInline annotated
* methods, this means that the support methods in dotty.runtime.LazyVals vanish.
* But they are needed for running the lazy val implementations in the Scala-2 compiled compiler.
/** An erased value or an inline method.
*/
def isEffectivelyErased(implicit ctx: Context): Boolean =
is(Erased) ||
isInlineMethod && unforcedAnnotation(defn.ForceInlineAnnot).isEmpty
is(Erased) || isInlineMethod

/** ()T and => T types should be treated as equivalent for this symbol.
* Note: For the moment, we treat Scala-2 compiled symbols as loose matching,
Expand Down Expand Up @@ -2121,7 +2146,7 @@ object SymDenotations {
override def computeNPMembersNamed(name: Name)(implicit ctx: Context): PreDenotation = {
def recur(pobjs: List[ClassDenotation], acc: PreDenotation): PreDenotation = pobjs match {
case pcls :: pobjs1 =>
if (pcls.isCompleting) recur(pobjs1, acc)
if (pcls.isCompletingAndNotStubbed) recur(pobjs1, acc)
else {
// A package object inherits members from `Any` and `Object` which
// should not be accessible from the package prefix.
Expand Down Expand Up @@ -2228,7 +2253,7 @@ object SymDenotations {

/** The type parameters computed by the completer before completion has finished */
def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[TypeParamInfo] =
if (sym.is(Touched)) Nil // return `Nil` instead of throwing a cyclic reference
if (sym.is(Completing)) Nil // return `Nil` instead of throwing a cyclic reference
else sym.info.typeParams

def decls: Scope = myDecls
Expand Down
8 changes: 6 additions & 2 deletions compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ object SymbolLoaders {
assert(root is PackageClass, root)
val pre = root.owner.thisType
root.info = ClassInfo(pre, root.symbol.asClass, Nil, currentDecls, pre select sourceModule)
if (!sourceModule.isCompleted)
if (!sourceModule.isCompletedOrStubbed)
sourceModule.completer.complete(sourceModule)

val packageName = if (root.isEffectiveRoot) "" else root.fullName.mangledString
Expand Down Expand Up @@ -351,7 +351,7 @@ abstract class SymbolLoader extends LazyType { self =>
}
finally {
def postProcess(denot: SymDenotation) =
if (!denot.isCompleted &&
if (!denot.isCompletedOrStubbed &&
!denot.completer.isInstanceOf[SymbolLoaders.SecondCompleter])
denot.markAbsent()
postProcess(root)
Expand Down Expand Up @@ -394,6 +394,7 @@ class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader {
load(root)

def load(root: SymDenotation)(implicit ctx: Context): Unit = {
root.startedLoading()
val (classRoot, moduleRoot) = rootDenots(root.asClass)
val classfileParser = new ClassfileParser(classfile, classRoot, moduleRoot)(ctx)
val result = classfileParser.run()
Expand All @@ -404,6 +405,7 @@ class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader {
moduleRoot.classSymbol.rootTreeOrProvider = unpickler
case _ =>
}
root.finishedLoading()
}

private def mayLoadTreesFromTasty(implicit ctx: Context): Boolean =
Expand All @@ -414,7 +416,9 @@ class SourcefileLoader(val srcfile: AbstractFile) extends SymbolLoader {
def description(implicit ctx: Context): String = "source file " + srcfile.toString
override def sourceFileOrNull: AbstractFile = srcfile
def doComplete(root: SymDenotation)(implicit ctx: Context): Unit =
root.startedLoading()
ctx.run.lateCompile(srcfile, typeCheck = ctx.settings.YretainTrees.value)
root.finishedLoading()
}

/** A NoCompleter which is also a SymbolLoader. */
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ trait Symbols { this: Context =>
infoFn(module, modcls), privateWithin)
val mdenot = SymDenotation(
module, owner, name, modFlags | ModuleValCreationFlags,
if (cdenot.isCompleted) TypeRef(owner.thisType, modcls)
if (cdenot.isCompletedOrStubbed) TypeRef(owner.thisType, modcls)
else new ModuleCompleter(modcls))
module.denot = mdenot
modcls.denot = cdenot
Expand Down Expand Up @@ -345,7 +345,7 @@ trait Symbols { this: Context =>
copy.denot = odenot.copySymDenotation(
symbol = copy,
owner = ttmap1.mapOwner(odenot.owner),
initFlags = odenot.flags &~ Touched,
initFlags = odenot.flags &~ Completing,
info = completer,
privateWithin = ttmap1.mapOwner(odenot.privateWithin), // since this refers to outer symbols, need not include copies (from->to) in ownermap here.
annotations = odenot.annotations)
Expand Down Expand Up @@ -547,7 +547,7 @@ object Symbols {

/** The symbol's signature if it is completed or a method, NotAMethod otherwise. */
final def signature(implicit ctx: Context): Signature =
if (lastDenot != null && (lastDenot.isCompleted || lastDenot.is(Method)))
if (lastDenot != null && (lastDenot.isCompletedOrStubbed || lastDenot.is(Method)))
denot.signature
else
Signature.NotAMethod
Expand Down Expand Up @@ -908,6 +908,6 @@ object Symbols {
override def toString: String = value.asScala.toString()
}

@forceInline def newMutableSymbolMap[T]: MutableSymbolMap[T] =
inline def newMutableSymbolMap[T]: MutableSymbolMap[T] =
new MutableSymbolMap(new java.util.IdentityHashMap[Symbol, T]())
}
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/TypeApplications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ class TypeApplications(val self: Type) extends AnyVal {
* Scala2 files such as `scala.collection.generic.Mapfactory`.
*/
final def safeAppliedTo(args: List[Type])(implicit ctx: Context): Type = self match {
case self: TypeRef if !self.symbol.isClass && self.symbol.isCompleting =>
case self: TypeRef if !self.symbol.isClass && self.symbol.isCompletingAndNotStubbed =>
AppliedType(self, args)
case _ =>
appliedTo(args)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4432,7 +4432,7 @@ object Types {
abstract class VariantTraversal {
protected[core] var variance: Int = 1

@forceInline protected def atVariance[T](v: Int)(op: => T): T = {
inline protected def atVariance[T](v: Int)(op: => T): T = {
val saved = variance
variance = v
val res = op
Expand Down
6 changes: 4 additions & 2 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -541,13 +541,15 @@ class TreeUnpickler(reader: TastyReader,
val sym =
roots.find(root => (root.owner eq ctx.owner) && root.name == name) match {
case Some(rootd) =>
rootd.startedLoading()
pickling.println(i"overwriting ${rootd.symbol} # ${rootd.hashCode}")
rootd.symbol.coord = coord
rootd.info = adjustIfModule(
new Completer(subReader(start, end)) with SymbolLoaders.SecondCompleter)
rootd.flags = flags &~ Touched // allow one more completion
rootd.flags = flags &~ Completing // allow one more completion
rootd.setPrivateWithin(privateWithin)
seenRoots += rootd.symbol
rootd.finishedLoading()
rootd.symbol
case _ =>
val completer = adjustIfModule(new Completer(subReader(start, end)))
Expand Down Expand Up @@ -675,7 +677,7 @@ class TreeUnpickler(reader: TastyReader,
if (sym.isTerm && !sym.isOneOf(DeferredOrLazyOrMethod))
initsFlags = EmptyFlags
else if (sym.isClass ||
sym.is(Method, butNot = Deferred) && !sym.isConstructor)
sym.is(Method) && !sym.is(Deferred) && !sym.isConstructor)
initsFlags &= NoInits
case IMPORT =>
skipTree()
Expand Down
Loading