diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 8161b6d11a6a..3583ac4058dc 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -54,6 +54,12 @@ object Flags { def (x: FlagSet) ^ (y: FlagSet) = FlagSet((x.bits | y.bits) & KINDFLAGS | (x.bits ^ y.bits) & ~KINDFLAGS) + def (x: FlagSet) partitionByIntersection(y: FlagSet): (FlagSet, FlagSet) = + val intersection = x & y + val complement = x &~ y + (intersection, complement) + + /** Does the given flag set contain the given flag? * This means that both the kind flags and the carrier bits have non-empty intersection. */ @@ -439,10 +445,9 @@ object Flags { val FromStartFlags: FlagSet = commonFlags( Module, Package, Deferred, Method, Case, HigherKinded, Param, ParamAccessor, - Scala2ExistentialCommon, Mutable, Opaque, Touched, JavaStatic, + Scala2ExistentialCommon, Mutable, Opaque, JavaStatic, OuterOrCovariant, LabelOrContravariant, CaseAccessor, - Extension, NonMember, Implicit, Given, Permanent, Synthetic, - SuperAccessorOrScala2x, Inline, Macro) + Extension, NonMember, Implicit, Given, Permanent, 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 @@ -450,7 +455,8 @@ object Flags { * is completed) */ val AfterLoadFlags: FlagSet = commonFlags( - FromStartFlags, AccessFlags, Final, AccessorOrSealed, LazyOrTrait, SelfName, JavaDefined) + FromStartFlags, AccessFlags, AccessorOrSealed, LazyOrTrait, SelfName, JavaDefined, + SuperAccessorOrScala2x, Synthetic) /** A value that's unstable unless complemented with a Stable flag */ diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 54912ded4e28..171c9c0d991e 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -167,13 +167,13 @@ object SymDenotations { /** 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 @@ -184,43 +184,68 @@ object SymDenotations { if (myFlags.is(Trait)) NoInitsInterface & bodyFlags // no parents are initialized from a trait else NoInits & bodyFlags & parentFlags) + /** Flags that are immutable at the current stage of + * this denotation's lifecycle. + */ + final def immutableFlags: FlagSet = + var result: FlagSet = AfterLoadFlags + if (myInfo.isInstanceOf[SymbolLoaders.LoadingCompleter]) result = FromStartFlags + result + + private def setMyFlags(fs: FlagSet) = + val changedImmutableFlags = (myFlags ^ fs) & immutableFlags + assert(changedImmutableFlags.isEmpty, s"Illegal mutation of flags ${changedImmutableFlags.flagsString} on denotation $this") + myFlags = fs + private def isCurrent(fs: FlagSet) = - fs <= ( - if (myInfo.isInstanceOf[SymbolLoader]) FromStartFlags - else AfterLoadFlags) + fs <= immutableFlags final def relevantFlagsFor(fs: FlagSet)(implicit ctx: Context) = if (isCurrent(fs)) myFlags else flags /** Has this denotation one of given flag set? */ - final def is(flag: Flag)(implicit ctx: Context): Boolean = - (if (isCurrent(flag)) myFlags else flags).is(flag) - - /** Has this denotation one of the flags in `fs` set? */ - final def isOneOf(fs: FlagSet)(implicit ctx: Context): Boolean = - (if (isCurrent(fs)) myFlags else flags).isOneOf(fs) + final inline def is(flag: Flag)(implicit ctx: Context): Boolean = + is(flag, EmptyFlags) /** Has this denotation the given flag set, whereas none of the flags * in `butNot` are set? */ - final def is(flag: Flag, butNot: FlagSet)(implicit ctx: Context): Boolean = - (if (isCurrent(flag) && isCurrent(butNot)) myFlags else flags).is(flag, butNot) + final inline def is(flag: Flag, butNot: FlagSet)(implicit ctx: Context): Boolean = + isAllOf(flag, butNot) + + /** Has this denotation one of the flags in `fs` set? */ + final inline def isOneOf(fs: FlagSet)(implicit ctx: Context): Boolean = + isOneOf(fs, EmptyFlags) /** Has this denotation one of the flags in `fs` set, whereas none of the flags * in `butNot` are set? */ final def isOneOf(fs: FlagSet, butNot: FlagSet)(implicit ctx: Context): Boolean = - (if (isCurrent(fs) && isCurrent(butNot)) myFlags else flags).isOneOf(fs, butNot) + val immutable = immutableFlags + val (fsImmut, fsMut) = fs.partitionByIntersection(immutable) + val (butNotImmut, butNotMut) = butNot.partitionByIntersection(immutable) + + (butNotImmut & myFlags).isEmpty && + (!(fsImmut & myFlags).isEmpty || (!fsMut.isEmpty && !(fsMut & flags).isEmpty)) && + (butNotMut.isEmpty || (butNotMut & flags).isEmpty) /** Has this denotation all of the flags in `fs` set? */ - final def isAllOf(fs: FlagSet)(implicit ctx: Context): Boolean = - (if (isCurrent(fs)) myFlags else flags).isAllOf(fs) + final inline def isAllOf(fs: FlagSet)(implicit ctx: Context): Boolean = + isAllOf(fs, EmptyFlags) /** Has this denotation all of the flags in `fs` set, whereas none of the flags * in `butNot` are set? */ final def isAllOf(fs: FlagSet, butNot: FlagSet)(implicit ctx: Context): Boolean = - (if (isCurrent(fs) && isCurrent(butNot)) myFlags else flags).isAllOf(fs, butNot) + val immutable = immutableFlags + val (fsImmut, fsMut) = fs.partitionByIntersection(immutable) + val (butNotImmut, butNotMut) = butNot.partitionByIntersection(immutable) + + fsImmut <= myFlags && + (butNotImmut & myFlags).isEmpty && + (fsMut.isEmpty || fsMut <= flags) && + (butNotMut.isEmpty || (butNotMut & flags).isEmpty) + /** The type info, or, if symbol is not yet completed, the completer */ final def infoOrCompleter: Type = myInfo diff --git a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala index 5fc455f1d868..e6c86af11e7c 100644 --- a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -27,6 +27,11 @@ object SymbolLoaders { */ trait SecondCompleter + /** A marker trait for a completer that loads information from + * an external source such as a TASTy or a class file. + */ + trait LoadingCompleter + private def enterNew( owner: Symbol, member: Symbol, completer: SymbolLoader, scope: Scope = EmptyScope)(implicit ctx: Context): Symbol = { @@ -302,7 +307,7 @@ object SymbolLoaders { /** A lazy type that completes itself by calling parameter doComplete. * Any linked modules/classes or module classes are also initialized. */ -abstract class SymbolLoader extends LazyType { self => +abstract class SymbolLoader extends LazyType with SymbolLoaders.LoadingCompleter { self => /** Load source or class file for `root`, return */ def doComplete(root: SymDenotation)(implicit ctx: Context): Unit diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index c8391e959cad..ee4ebc6254a9 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -106,7 +106,7 @@ class TreeUnpickler(reader: TastyReader, } } - class Completer(reader: TastyReader)(implicit @constructorOnly ctx: Context) extends LazyType { + class Completer(reader: TastyReader)(implicit @constructorOnly ctx: Context) extends LazyType with SymbolLoaders.LoadingCompleter { import reader._ val owner = ctx.owner val source = ctx.source diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 4bfcd1b5236d..333fc3e12151 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -160,7 +160,13 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas import Scala2Unpickler._ - val moduleRoot: SymDenotation = moduleClassRoot.sourceModule(ictx).denot(ictx) + val (moduleRoot: SymDenotation, moduleRootCompleter: ModuleCompleter) = { + val denot = moduleClassRoot.sourceModule(ictx).denot(ictx) + val completer = denot.completer + denot.info = new NoLoader // This denotation is still loading, mark it as such with this completer + (denot, completer) + } + assert(moduleRoot.isTerm) checkVersion(ictx) @@ -548,6 +554,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas case MODULEsym => if (isModuleRoot) { moduleRoot setFlag flags + moduleRoot.info = moduleRootCompleter moduleRoot.symbol } else ctx.newSymbol(owner, name.asTermName, flags, new LocalUnpickler() withModuleClass(implicit ctx => @@ -559,7 +566,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas }) } - class LocalUnpickler extends LazyType { + class LocalUnpickler extends LazyType with SymbolLoaders.LoadingCompleter { def startCoord(denot: SymDenotation): Coord = denot.symbol.coord def complete(denot: SymDenotation)(implicit ctx: Context): Unit = try { def parseToCompletion(denot: SymDenotation)(implicit ctx: Context) = { diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index cc2c7bcb5b28..6a73dd8a346a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -762,10 +762,8 @@ class Namer { typer: Typer => else bound } - def missingType(sym: Symbol, modifier: String)(implicit ctx: Context): Unit = { + def missingType(sym: Symbol, modifier: String)(implicit ctx: Context): Unit = ctx.error(s"${modifier}type of implicit definition needs to be given explicitly", sym.sourcePos) - sym.resetFlag(GivenOrImplicit) - } /** The completer of a symbol defined by a member def or import (except ClassSymbols) */ class Completer(val original: Tree)(implicit ctx: Context) extends LazyType with SymbolLoaders.SecondCompleter { diff --git a/tests/neg/i3067.scala b/tests/neg/i3067.scala index d7822c91a9f0..4bee02a4ad1c 100644 --- a/tests/neg/i3067.scala +++ b/tests/neg/i3067.scala @@ -10,6 +10,6 @@ object o { implicit def y = "abc" // error - implicit object a extends Test(_ map identity) // error // error: no implicit argument found + implicit object a extends Test(_ map identity) // error implicit object b extends Test(_ map identity) // error // error: cyclic reference } diff --git a/tests/neg/i7407.scala b/tests/neg/i7407.scala index 03823eb22e68..178692ab9568 100644 --- a/tests/neg/i7407.scala +++ b/tests/neg/i7407.scala @@ -1,2 +1,2 @@ def qc(given ctx: scala.quoted.QuoteContext) = println(ctx) -inline def g = qc // error: no implicit argument +inline def g = qc // error: cyclic reference