Skip to content

Commit c6fb9cf

Browse files
authored
Merge pull request #2830 from dotty-staging/fix-complete-scala
Fix #2809: Invalidate and unlink symbols for artifact classfiles
2 parents 2407a24 + 77b247d commit c6fb9cf

File tree

7 files changed

+57
-9
lines changed

7 files changed

+57
-9
lines changed

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -441,10 +441,17 @@ object SymDenotations {
441441
final def markAbsent(): Unit =
442442
myInfo = NoType
443443

444+
/** Is symbol known to not exist, or potentially not completed yet? */
445+
final def unforcedIsAbsent(implicit ctx: Context): Boolean =
446+
myInfo == NoType ||
447+
(this is (ModuleVal, butNot = Package)) && moduleClass.unforcedIsAbsent
448+
444449
/** Is symbol known to not exist? */
445-
final def isAbsent(implicit ctx: Context): Boolean =
450+
final def isAbsent(implicit ctx: Context): Boolean = {
451+
ensureCompleted()
446452
myInfo == NoType ||
447453
(this is (ModuleVal, butNot = Package)) && moduleClass.isAbsent
454+
}
448455

449456
/** Is this symbol the root class or its companion object? */
450457
final def isRoot: Boolean =
@@ -563,7 +570,7 @@ object SymDenotations {
563570
final def isCoDefinedWith(that: Symbol)(implicit ctx: Context) =
564571
(this.effectiveOwner == that.effectiveOwner) &&
565572
( !(this.effectiveOwner is PackageClass)
566-
|| this.isAbsent || that.isAbsent
573+
|| this.unforcedIsAbsent || that.unforcedIsAbsent
567574
|| { // check if they are defined in the same file(or a jar)
568575
val thisFile = this.symbol.associatedFile
569576
val thatFile = that.symbol.associatedFile

compiler/src/dotty/tools/dotc/core/Symbols.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ trait Symbols { this: Context =>
185185
val companionMethodFlags = Flags.Synthetic | Flags.Private | Flags.Method
186186

187187
def synthesizeCompanionMethod(name: Name, target: SymDenotation, owner: SymDenotation)(implicit ctx: Context) =
188-
if (owner.exists && target.exists && !owner.isAbsent && !target.isAbsent) {
188+
if (owner.exists && target.exists && !owner.unforcedIsAbsent && !target.unforcedIsAbsent) {
189189
val existing = owner.unforcedDecls.lookup(name)
190190

191191
existing.orElse{

compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ import scala.util.control.NonFatal
1919
object ClassfileParser {
2020
/** Marker trait for unpicklers that can be embedded in classfiles. */
2121
trait Embedded
22+
23+
/** Indicate that there is nothing to unpickle and the corresponding symbols can
24+
* be invalidated. */
25+
object NoEmbedded extends Embedded
2226
}
2327

2428
class ClassfileParser(
@@ -147,6 +151,15 @@ class ClassfileParser(
147151

148152
setClassInfo(classRoot, classInfo)
149153
setClassInfo(moduleRoot, staticInfo)
154+
} else if (result == Some(NoEmbedded)) {
155+
for (sym <- List(moduleRoot.sourceModule.symbol, moduleRoot.symbol, classRoot.symbol)) {
156+
classRoot.owner.asClass.delete(sym)
157+
if (classRoot.owner == defn.ScalaShadowingPackageClass) {
158+
// Symbols in scalaShadowing are also added to scala
159+
defn.ScalaPackageClass.delete(sym)
160+
}
161+
sym.markAbsent()
162+
}
150163
}
151164

152165
// eager load java enum definitions for exhaustivity check of pattern match
@@ -700,6 +713,10 @@ class ClassfileParser(
700713
}
701714
}
702715

716+
// Nothing$ and Null$ were incorrectly emitted with a Scala attribute
717+
// instead of ScalaSignature before 2.13.0-M2, see https://github.com/scala/scala/pull/5952
718+
private[this] val scalaUnpickleWhitelist = List(tpnme.nothingClass, tpnme.nullClass)
719+
703720
/** Parse inner classes. Expects `in.bp` to point to the superclass entry.
704721
* Restores the old `bp`.
705722
* @return true iff classfile is from Scala, so no Java info needs to be read.
@@ -760,6 +777,18 @@ class ClassfileParser(
760777
return unpickleTASTY(in.nextBytes(attrLen))
761778
}
762779

780+
if (scan(tpnme.ScalaATTR) && !scalaUnpickleWhitelist.contains(classRoot.name)) {
781+
// To understand the situation, it's helpful to know that:
782+
// - Scalac emits the `ScalaSig` attribute for classfiles with pickled information
783+
// and the `Scala` attribute for everything else.
784+
// - Dotty emits the `TASTY` attribute for classfiles with pickled information
785+
// and the `Scala` attribute for _every_ classfile.
786+
//
787+
// Therefore, if the `Scala` attribute is present but the `TASTY`
788+
// attribute isn't, this classfile is a compilation artifact.
789+
return Some(NoEmbedded)
790+
}
791+
763792
if (scan(tpnme.RuntimeAnnotationATTR)) {
764793
val attrLen = in.nextInt
765794
val nAnnots = in.nextChar

compiler/src/dotty/tools/dotc/interactive/Interactive.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ object Interactive {
8989
val boundaryCtx = ctx.withOwner(boundary)
9090
try
9191
prefix.memberDenots(completionsFilter, (name, buf) =>
92-
buf ++= prefix.member(name).altsWith(_.symbol.isAccessibleFrom(prefix)(boundaryCtx))
92+
buf ++= prefix.member(name).altsWith(d => !d.isAbsent && d.symbol.isAccessibleFrom(prefix)(boundaryCtx))
9393
).map(_.symbol).toList
9494
catch {
9595
case ex: TypeError => Nil

compiler/src/dotty/tools/dotc/transform/Pickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class Pickler extends Phase {
3333
/** Drop any elements of this list that are linked module classes of other elements in the list */
3434
private def dropCompanionModuleClasses(clss: List[ClassSymbol])(implicit ctx: Context): List[ClassSymbol] = {
3535
val companionModuleClasses =
36-
clss.filterNot(_ is Module).map(_.linkedClass).filterNot(_.isAbsent)
36+
clss.filterNot(_ is Module).map(_.linkedClass).filterNot(_.unforcedIsAbsent)
3737
clss.filterNot(companionModuleClasses.contains)
3838
}
3939

compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,7 @@ trait TypeAssigner {
145145
final def reallyExists(denot: Denotation)(implicit ctx: Context): Boolean = try
146146
denot match {
147147
case denot: SymDenotation =>
148-
denot.exists && {
149-
denot.ensureCompleted()
150-
!denot.isAbsent
151-
}
148+
denot.exists && !denot.isAbsent
152149
case denot: SingleDenotation =>
153150
val sym = denot.symbol
154151
(sym eq NoSymbol) || reallyExists(sym.denot)

tests/neg/classfile-artifacts.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// For some reason, if the imports are not scoped, only the first import error
2+
// is reported
3+
4+
class A {
5+
import scala.languageFeature$experimental$._ // error
6+
}
7+
8+
class B {
9+
import scala.language$Scala2$._ // error
10+
}
11+
12+
class C {
13+
import scala.languageFeature$._ // error
14+
}
15+

0 commit comments

Comments
 (0)