8000 Make bootstrapped DottyPredef an empty object · scala/scala3@61cc830 · GitHub
[go: up one dir, main page]

Skip to content

Commit 61cc830

Browse files
committed
Make bootstrapped DottyPredef an empty object
Once we have a new bootstrap compiler we can drop it entirely. Instead of using DottyPredef, patch the Predef class when unpickling it. The patch adds all methods from a patch class scala.runtime.stdLibPatches.Predef to scala.Predef.
1 parent 4bb944b commit 61cc830

File tree

12 files changed

+189
-29
lines changed

12 files changed

+189
-29
lines changed

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import unpickleScala2.Scala2Unpickler.ensureConstructor
99
import scala.collection.mutable
1010
import collection.mutable
1111
import Denotations.SingleDenotation
12-
import util.SimpleIdentityMap
12+
import util.{SimpleIdentityMap, SourceFile, NoSource}
1313
import typer.ImportInfo.RootRef
1414

1515
import scala.annotation.tailrec
@@ -186,6 +186,13 @@ class Definitions {
186186
cls
187187
}
188188

189+
/** If `sym` is a patched library class, the source file of its patch class,
190+
* otherwise `NoSource`
191+
*/
192+
def patchSource(sym: Symbol): SourceFile =
193+
if sym == ScalaPredefModuleClass then ScalaPredefModuleClassPatch.source
194+
else NoSource
195+
189196
@tu lazy val RootClass: ClassSymbol = newPackageSymbol(
190197
NoSymbol, nme.ROOT, (root, rootcls) => ctx.base.rootLoader(root)).moduleClass.asClass
191198
@tu lazy val RootPackage: TermSymbol = newSymbol(
@@ -481,13 +488,16 @@ class Definitions {
481488
newPermanentSymbol(ScalaPackageClass, tpnme.IMPLICITkw, EmptyFlags, TypeBounds.empty).entered
482489
def ImplicitScrutineeTypeRef: TypeRef = ImplicitScrutineeTypeSym.typeRef
483490

484-
485491
@tu lazy val ScalaPredefModule: Symbol = requiredModule("scala.Predef")
486492
@tu lazy val Predef_conforms : Symbol = ScalaPredefModule.requiredMethod(nme.conforms_)
487493
@tu lazy val Predef_classOf : Symbol = ScalaPredefModule.requiredMethod(nme.classOf)
488494
@tu lazy val Predef_identity : Symbol = ScalaPredefModule.requiredMethod(nme.identity)
489495
@tu lazy val Predef_undefined: Symbol = ScalaPredefModule.requiredMethod(nme.???)
490496

497+
@tu lazy val ScalaPredefModuleClass: ClassSymbol = ScalaPredefModule.moduleClass.asClass
498+
@tu lazy val ScalaPredefModuleClassPatch: Symbol =
499+
getModuleIfDefined("scala.runtime.stdLibPatches.Predef").moduleClass
500+
491501
@tu lazy val SubTypeClass: ClassSymbol = requiredClass("scala.<:<")
492502
@tu lazy val SubType_refl: Symbol = SubTypeClass.companionModule.requiredMethod(nme.refl)
493503

@@ -510,7 +520,7 @@ class Definitions {
510520
// will return "null" when called recursively, see #1856.
511521
def DottyPredefModule: Symbol = {
512522
if (myDottyPredefModule == null) {
513-
myDottyPredefModule = requiredModule("dotty.DottyPredef")
523+
myDottyPredefModule = getModuleIfDefined("dotty.DottyPredef")
514524
assert(myDottyPredefModule != null)
515525
}
516526
myDottyPredefModule
@@ -782,6 +792,7 @@ class Definitions {
782792
@tu lazy val Mirror_SingletonProxyClass: ClassSymbol = requiredClass("scala.deriving.Mirror.SingletonProxy")
783793

784794
@tu lazy val LanguageModule: Symbol = requiredModule("scala.language")
795+
@tu lazy val LanguageModuleClass: Symbol = LanguageModule.moduleClass.asClass
785796
@tu lazy val LanguageExperimentalModule: Symbol = requiredModule("scala.language.experimental")
786797
@tu lazy val NonLocalReturnControlClass: ClassSymbol = requiredClass("scala.runtime.NonLocalReturnControl")
787798
@tu lazy val SelectableClass: ClassSymbol = requiredClass("scala.Selectable")

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

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -428,23 +428,21 @@ object Symbols {
428428
private var mySource: SourceFile = NoSource
429429

430430
final def sourceOfClass(using Context): SourceFile = {
431-
if (!mySource.exists && !denot.is(Package))
431+
if !mySource.exists && !denot.is(Package) then
432432
// this allows sources to be added in annotations after `sourceOfClass` is first called
433-
mySource = {
434-
val file = associatedFile
435-
if (file != null && file.extension != "class") ctx.getSource(file)
436-
else {
437-
def sourceFromTopLevel(using Context) =
438-
denot.topLevelClass.unforcedAnnotation(defn.SourceFileAnnot) match {
439-
case Some(sourceAnnot) => sourceAnnot.argumentConstant(0) match {
433+
val file = associatedFile
434+
if file != null && file.extension != "class" then
435+
mySource = ctx.getSource(file)
436+
else
437+
mySource = defn.patchSource(this)
438+
if !mySource.exists then
439+
mySource = atPhaseNoLater(flattenPhase) {
440+
denot.topLevelClass.unforcedAnnotation(defn.SourceFileAnnot) match
441+
case Some(sourceAnnot) => sourceAnnot.argumentConstant(0) match
440442
case Some(Constant(path: String)) => ctx.getSource(path)
441443
case none => NoSource
442-
}
443444
case none => NoSource
444-
}
445-
atPhaseNoLater(flattenPhase)(sourceFromTopLevel)
446-
}
447-
}
445+
}
448446
mySource
449447
}
450448

@@ -880,6 +878,13 @@ object Symbols {
880878
staticRef(name).requiredSymbol("object", name)(_.is(Module)).asTerm
881879
}
882880

881+
/** Get module symbol if the module is either defined in current compilation run
882+
* or present on classpath. Returns NoSymbol otherwise.
883+
*/
884+
def getModuleIfDefined(path: PreName)(using Context): Symbol =
885+
staticRef(path.toTermName, generateStubs = false)
886+
.disambiguate(_.is(Module)).symbol
887+
883888
def requiredModuleRef(path: PreName)(using Context): TermRef = requiredModule(path).termRef
884889

885890
def requiredMethod(path: PreName)(using Context): TermSymbol = {

compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,32 @@ object Scala2Unpickler {
111111

112112
denot.info = tempInfo.finalized(normalizedParents)
113113
denot.ensureTypeParamsInCorrectOrder()
114+
if denot.name == tpnme.Predef.moduleClassName && denot.symbol == defn.ScalaPredefModuleClass then
115+
denot.sourceModule.info = denot.typeRef // we run into a cyclic reference when patching if this line is omitted
116+
patchStdLibClass(denot, defn.ScalaPredefModuleClassPatch)
114117
}
118+
119+
/** A finalizer that patches standard library classes.
120+
* It copies all non-private, non-synthetic definitions from `patchCls`
121+
* to `denot` while changing their owners to `denot`. Before that it deletes
122+
* any definitions of `denot` that have the same name as one of the copied
123+
* definitions.
124+
*
125+
* To avpid running into cycles on bootstrap, patching happens only if `patchCls`
126+
* is read from a classfile.
127+
*/
128+
private def patchStdLibClass(denot: ClassDenotation, patchCls: Symbol)(using Context): Unit =
129+
val scope = denot.info.decls.openForMutations
130+
if patchCls.exists then
131+
val patches = patchCls.info.decls.filter(patch =>
132+
!patch.isConstructor && !patch.isOneOf(PrivateOrSynthetic))
133+
for patch <- patches do
134+
val e = scope.lookupEntry(patch.name)
135+
if e != null then scope.unlink(e)
136+
for patch <- patches do
137+
patch.ensureCompleted()
138+
patch.denot = patch.denot.copySymDenotation(owner = denot.symbol)
139+
scope.enter(patch)
115140
}
116141

117142
/** Unpickle symbol table information descending from a class and/or module root

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,13 @@ class TreeChecker extends Phase with SymTransformer {
417417
assert(tree.qual.tpe.isInstanceOf[ThisType], i"expect prefix of Super to be This, actual = ${tree.qual}")
418418
super.typedSuper(tree, pt)
419419

420+
/** Definition of `sym` should be excluded from checks.
421+
* We need to do that for stdlib patch classes, since their symbols have been
422+
* appropriated by other stdlib classes.
423+
*/
424+
private def exclude(sym: Symbol)(using Context): Boolean =
425+
sym == defn.ScalaPredefModuleClassPatch
426+
420427
private def checkOwner(tree: untpd.Tree)(using Context): Unit = {
421428
def ownerMatches(symOwner: Symbol, ctxOwner: Symbol): Boolean =
422429
symOwner == ctxOwner ||
@@ -445,12 +452,14 @@ class TreeChecker extends Phase with SymTransformer {
445452

446453
val symbolsNotDefined = decls -- defined - constr.symbol
447454

448-
assert(symbolsNotDefined.isEmpty,
455+
if exclude(cls) then
456+
promote(cdef)
457+
else
458+
assert(symbolsNotDefined.isEmpty,
449459
i" $cls tree does not define members: ${symbolsNotDefined.toList}%, %\n" +
450460
i"expected: ${decls.toList}%, %\n" +
451461
i"defined: ${defined}%, %")
452-
453-
super.typedClassDef(cdef, cls)
462+
super.typedClassDef(cdef, cls)
454463
}
455464

456465
override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(using Context): Tree =
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package dotty
2+
3+
object DottyPredef
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package scala.runtime.stdLibPatches
2+
3+
object Predef:
4+
import compiletime.summonFrom
5+
6+
inline def assert(inline assertion: Boolean, inline message: => Any): Unit =
7+
if !assertion then scala.runtime.Scala3RunTime.assertFailed(message)
8+
9+
inline def assert(inline assertion: Boolean): Unit =
10+
if !assertion then scala.runtime.Scala3RunTime.assertFailed()
11+
12+
/**
13+
* Retrieve the single value of a type with a unique inhabitant.
14+
*
15+
* @example {{{
16+
* object Foo
17+
* val foo = valueOf[Foo.type]
18+
* // foo is Foo.type = Foo
19+
*
20+
* val bar = valueOf[23]
21+
* // bar is 23.type = 23
22+
* }}}
23+
* @group utilities
24+
*/
25+
inline def valueOf[T]: T = summonFrom {
26+
case ev: ValueOf[T] => ev.value
27+
}
28+
29+
/** Summon a given value of type `T`. Usually, the argument is not passed explicitly.
30+
*
31+
* @tparam T the type of the value to be summoned
32+
* @return the given value typed as the provided type parameter
33+
*/
34+
inline def summon[T](using x: T): x.type = x
35+
36+
// Extension methods for working with explicit nulls
37+
38+
/** Strips away the nullability from a value.
39+
* e.g.
40+
* val s1: String|Null = "hello"
41+
* val s: String = s1.nn
42+
*
43+
* Note that `.nn` performs a checked cast, so if invoked on a null value it'll throw an NPE.
44+
*/
45+
extension [T](x: T | Null) inline def nn: x.type & T =
46+
scala.runtime.Scala3RunTime.nn(x)
47+
end Predef
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package scala.runtime.stdLibPatches
2+
3+
/** Scala 3 additions to the `scala.language` object.
4+
*/
5+
object language:
6+
7+
/** The experimental object contains features that have been recently added but have not
8+
* been thoroughly tested in production yet.
9+
*
10+
* Experimental features '''may undergo API changes''' in future releases, so production
11+
* code should not rely on them.
12+
*
13+
* Programmers are encouraged to try out experimental features and
14+
* [[http://issues.scala-lang.org report any bugs or API inconsistencies]]
15+
* they encounter so they can be improved in future releases.
16+
*
17+
* @group experimental
18+
*/
19+
object experimental:
20+
21+
/** Experimental support for richer dependent types */
22+
object dependent
23+
24+
/** Experimental support for named type arguments */
25+
object namedTypeArguments
26+
27+
/** Experimental support for generic number literals */
28+
object genericNumberLiterals
29+
end experimental
30+
31+
/** Where imported, auto-tupling is disabled */
32+
object noAutoTupling
33+
34+
/** Where imported, loose equality using eqAny is disabled */
35+
object strictEquality
36+
37+
/** Where imported, ad hoc extensions of non-open classes in other
38+
* compilation units are allowed.
39+
*
40+
* '''Why control the feature?''' Ad-hoc extensions should usually be avoided
41+
* since they typically cannot rely on an "internal" contract between a class
42+
* and its extensions. Only open classes need to specify such a contract.
43+
* Ad-hoc extensions might break for future versions of the extended class,
44+
* since the extended class is free to change its implementation without
45+
* being constrained by an internal contract.
46+
*
47+
* '''Why allow it?''' An ad-hoc extension can sometimes be necessary,
48+
* for instance when mocking a class in a testing framework, or to work
49+
* around a bug or missing feature in the original class. Nevertheless,
50+
* such extensions should be limited in scope and clearly documented.
51+
* That's why the language import is required for them.
52+
*/
53+
object adhocExtensions
54+
55+
/** Source version */
56+
object `3.0-migration`
57+
object `3.0`
58+
object `3.1-migration`
59+
object `3.1`
60+
end language

tests/neg-macros/i9014.check

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
-- Error: tests/neg-macros/i9014/Test_2.scala:1:23 ---------------------------------------------------------------------
33
1 |val tests = summon[Bar] // error
44
| ^
5-
| no implicit argument of type Bar was found for parameter x of method summon in object DottyPredef.
6-
| I found:
5+
| no implicit argument of type Bar was found for parameter x of method summon in object Predef.
6+
| I found:
77
|
8-
| given_Bar
8+
| given_Bar
99
|
10-
| But method given_Bar does not match type Bar.
10+
| But method given_Bar does not match type Bar.

tests/neg/i9014.check

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
-- Error: tests/neg/i9014.scala:4:25 -----------------------------------------------------------------------------------
22
4 | val tests = summon[Bar] // error
33
| ^
4-
| no implicit argument of type Bar was found for parameter x of method summon in object DottyPredef.
5-
| I found:
4+
| no implicit argument of type Bar was found for parameter x of method summon in object Predef.
5+
| I found:
66
|
7-
| Bar.given_Bar
7+
| Bar.given_Bar
88
|
9-
| But method given_Bar in object Bar does not match type Bar.
9+
| But method given_Bar in object Bar does not match type Bar.

tests/neg/i9958.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
-- Error: tests/neg/i9958.scala:1:30 -----------------------------------------------------------------------------------
22
1 |val x = summon[[X] =>> (X, X)] // error
33
| ^
4-
| no implicit argument of type [X] =>> (X, X) was found for parameter x of method summon in object DottyPredef
4+
| no implicit argument of type [X] =>> (X, X) was found for parameter x of method summon in object Predef
55
-- [E007] Type Mismatch Error: tests/neg/i9958.scala:8:10 --------------------------------------------------------------
66
8 |def b = f(a) // error
77
| ^

tests/run/cochis-example.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
import Predef.{$conforms => _}
2+
import Predef.{assert, $conforms => _}
33
trait A {
44
given id[X] as (X => X) = x => x
55
def trans[X](x: X)(using f: X => X) = f(x) // (2)

0 commit comments

Comments
 (0)
0