Skip to content

Commit f71b5e7

Browse files
committed
Patch Predef from stdLibPatches.Predef
Definitions that should be added to predef are contained in `stdLibPatches.Predef`. They replaace correesponding definitions in Predef.
1 parent 793f88b commit f71b5e7

File tree

4 files changed

+171
-16
lines changed

4 files changed

+171
-16
lines changed

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

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ 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
14+
import typer.Namer
1415

1516
import scala.annotation.tailrec
1617

@@ -186,13 +187,45 @@ class Definitions {
186187
cls
187188
}
188189

190+
/** Run given finalizer function when `cls` is completed. The finalizer
191+
* gets the class and the current context as arguments.
192+
*/
189193
extension (cls: ClassSymbol)
190194
private def withFinalizer(f: ClassSymbol => Context ?=> Unit): ClassSymbol =
191195
cls.infoOrCompleter match
192196
case completer: SymbolLoader =>
193197
cls.info = completer.withFinalizer(d => f(d.asClass.classSymbol))
194198
cls
195199

200+
/** A finalizer that patches standard library classes.
201+
* It copies all non-private, non-synthetic definitions from `patchCls`
202+
* to `cls` while changing their owners to `cls`. Before that it deletes
203+
* any definitions of `cls` that have the same name as one of the copied
204+
* definitions.
205+
*
206+
* To avpid running into cycles on bootstrap, patching happens only if `patchCls`
207+
* is read from a classfile.
208+
*/
209+
private def patchStdLibClass(cls: ClassSymbol, patchCls: Symbol)(using Context): Unit =
210+
val scope = cls.info.decls.openForMutations
211+
if patchCls.exists && !patchCls.infoOrCompleter.isInstanceOf[Namer#ClassCompleter] then
212+
val patches = patchCls.info.decls.filter(patch =>
213+
!patch.isConstructor && !patch.isOneOf(PrivateOrSynthetic))
214+
for patch <- patches do
215+
val e = scope.lookupEntry(patch.name)
216+
if e != null then scope.unlink(e)
217+
for patch <- patches do
218+
patch.ensureCompleted()
219+
patch.denot = patch.denot.copySymDenotation(owner = cls)
220+
scope.enter(patch)
221+
222+
/** If `sym` is a patched library class, the source file of its patch class,
223+
* otherwise `NoSource`
224+
*/
225+
def patchSource(sym: Symbol): SourceFile =
226+
if sym == ScalaPredefModuleClass then ScalaPredefModuleClassPatch.source
227+
else NoSource
228+
196229
@tu lazy val RootClass: ClassSymbol = newPackageSymbol(
197230
NoSymbol, nme.ROOT, (root, rootcls) => ctx.base.rootLoader(root)).moduleClass.asClass
198231
@tu lazy val RootPackage: TermSymbol = newSymbol(
@@ -488,13 +521,18 @@ class Definitions {
488521
newPermanentSymbol(ScalaPackageClass, tpnme.IMPLICITkw, EmptyFlags, TypeBounds.empty).entered
489522
def ImplicitScrutineeTypeRef: TypeRef = ImplicitScrutineeTypeSym.typeRef
490523

491-
492524
@tu lazy val ScalaPredefModule: Symbol = requiredModule("scala.Predef")
493525
@tu lazy val Predef_conforms : Symbol = ScalaPredefModule.requiredMethod(nme.conforms_)
494526
@tu lazy val Predef_classOf : Symbol = ScalaPredefModule.requiredMethod(nme.classOf)
495527
@tu lazy val Predef_identity : Symbol = ScalaPredefModule.requiredMethod(nme.identity)
496528
@tu lazy val Predef_undefined: Symbol = ScalaPredefModule.requiredMethod(nme.???)
497529

530+
@tu lazy val ScalaPredefModuleClass: ClassSymbol = ScalaPredefModule.moduleClass.asClass
531+
.withFinalizer(patchStdLibClass(_, ScalaPredefModuleClassPatch))
532+
533+
@tu private lazy val ScalaPredefModuleClassPatch: Symbol =
534+
getModuleIfDefined("scala.runtime.stdLibPatches.Predef").moduleClass
535+
498536
@tu lazy val SubTypeClass: ClassSymbol = requiredClass("scala.<:<")
499537
@tu lazy val SubType_refl: Symbol = SubTypeClass.companionModule.requiredMethod(nme.refl)
500538

@@ -779,6 +817,7 @@ class Definitions {
779817
@tu lazy val Mirror_SingletonProxyClass: ClassSymbol = requiredClass("scala.deriving.Mirror.SingletonProxy")
780818

781819
@tu lazy val LanguageModule: Symbol = requiredModule("scala.language")
820+
@tu lazy val LanguageModuleClass: Symbol = LanguageModule.moduleClass.asClass
782821
@tu lazy val LanguageExperimentalModule: Symbol = requiredModule("scala.language.experimental")
783822
@tu lazy val NonLocalReturnControlClass: ClassSymbol = requiredClass("scala.runtime.NonLocalReturnControl")
784823
@tu lazy val SelectableClass: ClassSymbol = requiredClass("scala.Selectable")
@@ -1650,7 +1689,11 @@ class Definitions {
16501689
ScalaPackageClass.enter(m)
16511690

16521691
// force initialization of every symbol that is synthesized or hijacked by the compiler
1653-
val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() :+ JavaEnumClass
1692+
val forced =
1693+
syntheticCoreClasses
1694+
++ syntheticCoreMethods
1695+
++ ScalaValueClasses()
1696+
++ List(JavaEnumClass, ScalaPredefModuleClass)
16541697

16551698
isInitialized = true
16561699
}

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 = {
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

0 commit comments

Comments
 (0)