Skip to content

Commit

Permalink
Merge pull request #10428 from dotty-staging/drop-scalaShadowing
Browse files Browse the repository at this point in the history
Drop scala shadowing and DottyPredef
  • Loading branch information
odersky committed Nov 24, 2020
2 parents 1b7d63c + c351094 commit 7bdf89e
Show file tree
Hide file tree
Showing 35 changed files with 267 additions and 84 deletions.
2 changes: 1 addition & 1 deletion community-build/community-projects/endpoints4s
2 changes: 1 addition & 1 deletion community-build/community-projects/scalaz
Submodule scalaz updated 1 files
+2 −1 project/build.scala
2 changes: 1 addition & 1 deletion community-build/community-projects/utest
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/Driver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@ class Driver {
inContext(ictx) {
if !ctx.settings.YdropComments.value || ctx.mode.is(Mode.ReadComments) then
ictx.setProperty(ContextDoc, new ContextDocstrings)
if Feature.enabledBySetting(nme.Scala2Compat) then
report.warning("-language:Scala2Compat will go away; use -source 3.0-migration instead")
val fileNames = CompilerCommand.checkUsage(summary, sourcesRequired)
fromTastySetup(fileNames, ctx)
}
Expand Down
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/config/Feature.scala
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ object Feature:
case Some(v) => v
case none => sourceVersionSetting

def migrateTo3(using Context): Boolean =
sourceVersion == `3.0-migration` || enabledBySetting(nme.Scala2Compat)
def migrateTo3(using Context): Boolean = sourceVersion == `3.0-migration`

/** If current source migrates to `version`, issue given warning message
* and return `true`, otherwise return `false`.
Expand Down
67 changes: 64 additions & 3 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import unpickleScala2.Scala2Unpickler.ensureConstructor
import scala.collection.mutable
import collection.mutable
import Denotations.SingleDenotation
import util.SimpleIdentityMap
import util.{SimpleIdentityMap, SourceFile, NoSource}
import typer.ImportInfo.RootRef

import scala.annotation.tailrec
Expand Down Expand Up @@ -252,6 +252,7 @@ class Definitions {
* or classes with the same name. But their binary artifacts are
* in `scalaShadowing` so they don't clash with the same-named `scala`
* members at runtime.
* It is used only for non-bootstrapped code
*/
@tu lazy val ScalaShadowingPackage: TermSymbol = requiredPackage(nme.scalaShadowing)

Expand Down Expand Up @@ -481,12 +482,12 @@ class Definitions {
newPermanentSymbol(ScalaPackageClass, tpnme.IMPLICITkw, EmptyFlags, TypeBounds.empty).entered
def ImplicitScrutineeTypeRef: TypeRef = ImplicitScrutineeTypeSym.typeRef


@tu lazy val ScalaPredefModule: Symbol = requiredModule("scala.Predef")
@tu lazy val Predef_conforms : Symbol = ScalaPredefModule.requiredMethod(nme.conforms_)
@tu lazy val Predef_classOf : Symbol = ScalaPredefModule.requiredMethod(nme.classOf)
@tu lazy val Predef_identity : Symbol = ScalaPredefModule.requiredMethod(nme.identity)
@tu lazy val Predef_undefined: Symbol = ScalaPredefModule.requiredMethod(nme.???)
@tu lazy val ScalaPredefModuleClass: ClassSymbol = ScalaPredefModule.moduleClass.asClass

@tu lazy val SubTypeClass: ClassSymbol = requiredClass("scala.<:<")
@tu lazy val SubType_refl: Symbol = SubTypeClass.companionModule.requiredMethod(nme.refl)
Expand All @@ -510,7 +511,7 @@ class Definitions {
// will return "null" when called recursively, see #1856.
def DottyPredefModule: Symbol = {
if (myDottyPredefModule == null) {
myDottyPredefModule = requiredModule("dotty.DottyPredef")
myDottyPredefModule = getModuleIfDefined("dotty.DottyPredef")
assert(myDottyPredefModule != null)
}
myDottyPredefModule
Expand Down Expand Up @@ -782,6 +783,7 @@ class Definitions {
@tu lazy val Mirror_SingletonProxyClass: ClassSymbol = requiredClass("scala.deriving.Mirror.SingletonProxy")

@tu lazy val LanguageModule: Symbol = requiredModule("scala.language")
@tu lazy val LanguageModuleClass: Symbol = LanguageModule.moduleClass.asClass
@tu lazy val LanguageExperimentalModule: Symbol = requiredModule("scala.language.experimental")
@tu lazy val NonLocalReturnControlClass: ClassSymbol = requiredClass("scala.runtime.NonLocalReturnControl")
@tu lazy val SelectableClass: ClassSymbol = requiredClass("scala.Selectable")
Expand Down Expand Up @@ -1101,6 +1103,65 @@ class Definitions {
|| sym.owner == CompiletimeOpsPackageObjectString.moduleClass && compiletimePackageStringTypes.contains(sym.name)
)

// ----- Scala-2 library patches --------------------------------------

/** The `scala.runtime.stdLibPacthes` package contains objects
* that contain defnitions that get added as members to standard library
* objects with the same name.
*/
@tu lazy val StdLibPatchesPackage: TermSymbol = requiredPackage("scala.runtime.stdLibPatches")
@tu private lazy val ScalaPredefModuleClassPatch: Symbol = getModuleIfDefined("scala.runtime.stdLibPatches.Predef").moduleClass
@tu private lazy val LanguageModuleClassPatch: Symbol = getModuleIfDefined("scala.runtime.stdLibPatches.language").moduleClass

/** If `sym` is a patched library class, the source file of its patch class,
* otherwise `NoSource`
*/
def patchSource(sym: Symbol)(using Context): SourceFile =
if sym == ScalaPredefModuleClass then ScalaPredefModuleClassPatch.source
else if sym == LanguageModuleClass then LanguageModuleClassPatch.source
else NoSource

/** A finalizer that patches standard library classes.
* It copies all non-private, non-synthetic definitions from `patchCls`
* to `denot` while changing their owners to `denot`. Before that it deletes
* any definitions of `denot` that have the same name as one of the copied
* definitions.
*
* If an object is present in both the original class and the patch class,
* it is not overwritten. Instead its members are copied recursively.
*
* To avpid running into cycles on bootstrap, patching happens only if `patchCls`
* is read from a classfile.
*/
def patchStdLibClass(denot: ClassDenotation)(using Context): Unit =

def patch2(denot: ClassDenotation, patchCls: Symbol): Unit =
val scope = denot.info.decls.openForMutations
def recurse(patch: Symbol) = patch.is(Module) && scope.lookup(patch.name).exists
if patchCls.exists then
val patches = patchCls.info.decls.filter(patch =>
!patch.isConstructor && !patch.isOneOf(PrivateOrSynthetic))
for patch <- patches if !recurse(patch) do
val e = scope.lookupEntry(patch.name)
if e != null then scope.unlink(e)
for patch <- patches do
patch.ensureCompleted()
if !recurse(patch) then
patch.denot = patch.denot.copySymDenotation(owner = denot.symbol)
scope.enter(patch)
else if patch.isClass then
patch2(scope.lookup(patch.name).asClass, patch)

def patchWith(patchCls: Symbol) =
denot.sourceModule.info = denot.typeRef // we run into a cyclic reference when patching if this line is omitted
patch2(denot, patchCls)

if denot.name == tpnme.Predef.moduleClassName && denot.symbol == ScalaPredefModuleClass then
patchWith(ScalaPredefModuleClassPatch)
else if denot.name == tpnme.language.moduleClassName && denot.symbol == LanguageModuleClass then
patchWith(LanguageModuleClassPatch)
end patchStdLibClass

// ----- Symbol sets ---------------------------------------------------

@tu lazy val AbstractFunctionType: Array[TypeRef] = mkArityArray("scala.runtime.AbstractFunction", MaxImplementedFunctionArity, 0)
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,6 @@ object StdNames {
val Ref: N = "Ref"
val RootPackage: N = "RootPackage"
val RootClass: N = "RootClass"
val Scala2Compat: N = "Scala2Compat"
val Select: N = "Select"
val Shape: N = "Shape"
val StringContext: N = "StringContext"
Expand Down
31 changes: 18 additions & 13 deletions compiler/src/dotty/tools/dotc/core/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -428,23 +428,21 @@ object Symbols {
private var mySource: SourceFile = NoSource

final def sourceOfClass(using Context): SourceFile = {
if (!mySource.exists && !denot.is(Package))
if !mySource.exists && !denot.is(Package) then
// this allows sources to be added in annotations after `sourceOfClass` is first called
mySource = {
val file = associatedFile
if (file != null && file.extension != "class") ctx.getSource(file)
else {
def sourceFromTopLevel(using Context) =
denot.topLevelClass.unforcedAnnotation(defn.SourceFileAnnot) match {
case Some(sourceAnnot) => sourceAnnot.argumentConstant(0) match {
val file = associatedFile
if file != null && file.extension != "class" then
mySource = ctx.getSource(file)
else
mySource = defn.patchSource(this)
if !mySource.exists then
mySource = atPhaseNoLater(flattenPhase) {
denot.topLevelClass.unforcedAnnotation(defn.SourceFileAnnot) match
case Some(sourceAnnot) => sourceAnnot.argumentConstant(0) match
case Some(Constant(path: String)) => ctx.getSource(path)
case none => NoSource
}
case none => NoSource
}
atPhaseNoLater(flattenPhase)(sourceFromTopLevel)
}
}
}
mySource
}

Expand Down Expand Up @@ -880,6 +878,13 @@ object Symbols {
staticRef(name).requiredSymbol("object", name)(_.is(Module)).asTerm
}

/** Get module symbol if the module is either defined in current compilation run
* or present on classpath. Returns NoSymbol otherwise.
*/
def getModuleIfDefined(path: PreName)(using Context): Symbol =
staticRef(path.toTermName, generateStubs = false)
.disambiguate(_.is(Module)).symbol

def requiredModuleRef(path: PreName)(using Context): TermRef = requiredModule(path).termRef

def requiredMethod(path: PreName)(using Context): TermSymbol = {
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -764,7 +764,9 @@ class TreePickler(pickler: TastyPickler) {

def pickle(trees: List[Tree])(using Context): Unit = {
trees.foreach(tree => if (!tree.isEmpty) pickleTree(tree))
def missing = forwardSymRefs.keysIterator.map(sym => sym.showLocated + "(line " + sym.srcPos.line + ")").toList
def missing = forwardSymRefs.keysIterator
.map(sym => i"${sym.showLocated} (line ${sym.srcPos.line}) #${sym.id}")
.toList
assert(forwardSymRefs.isEmpty, i"unresolved symbols: $missing%, % when pickling ${ctx.source}")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,7 @@ class TreeUnpickler(reader: TastyReader,
val stats = rdr.readIndexedStats(localDummy, end)
tparams ++ vparams ++ stats
})
defn.patchStdLibClass(cls)
setSpan(start,
untpd.Template(constr, mappedParents, Nil, self, lazyStats)
.withType(localDummy.termRef))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ object Scala2Unpickler {

denot.info = tempInfo.finalized(normalizedParents)
denot.ensureTypeParamsInCorrectOrder()
defn.patchStdLibClass(denot)
}
}

Expand Down
12 changes: 9 additions & 3 deletions compiler/src/dotty/tools/dotc/transform/TreeChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -446,9 +446,9 @@ class TreeChecker extends Phase with SymTransformer {
val symbolsNotDefined = decls -- defined - constr.symbol

assert(symbolsNotDefined.isEmpty,
i" $cls tree does not define members: ${symbolsNotDefined.toList}%, %\n" +
i"expected: ${decls.toList}%, %\n" +
i"defined: ${defined}%, %")
i" $cls tree does not define members: ${symbolsNotDefined.toList}%, %\n" +
i"expected: ${decls.toList}%, %\n" +
i"defined: ${defined}%, %")

super.typedClassDef(cdef, cls)
}
Expand Down Expand Up @@ -540,6 +540,12 @@ class TreeChecker extends Phase with SymTransformer {
super.typedWhileDo(tree)
}

override def typedPackageDef(tree: untpd.PackageDef)(using Context): Tree =
if tree.symbol == defn.StdLibPatchesPackage then
promote(tree) // don't check stdlib patches, since their symbols were highjacked by stdlib classes
else
super.typedPackageDef(tree)

override def ensureNoLocalRefs(tree: Tree, pt: Type, localSyms: => List[Symbol])(using Context): Tree =
tree

Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1199,6 +1199,7 @@ class Namer { typer: Typer =>
cls.setNoInitsFlags(parentsKind(parents), untpd.bodyKind(rest))
if (cls.isNoInitsClass) cls.primaryConstructor.setFlag(StableRealizable)
processExports(using localCtx)
defn.patchStdLibClass(denot.asClass)
}
}

Expand Down
3 changes: 3 additions & 0 deletions library/src-bootstrapped/dotty/DottyPredef.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package dotty

object DottyPredef
3 changes: 3 additions & 0 deletions library/src-bootstrapped/scalaShadowing/D_u_m_m_y.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package scalaShadowing

class D_u_m_m_y
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -225,9 +225,6 @@ object language {
object genericNumberLiterals
}

/** Where imported, a backwards compatibility mode for Scala2 is enabled */
object Scala2Compat

/** Where imported, auto-tupling is disabled */
object noAutoTupling

Expand Down
47 changes: 47 additions & 0 deletions library/src/scala/runtime/stdLibPatches/Predef.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package scala.runtime.stdLibPatches

object Predef:
import compiletime.summonFrom

inline def assert(inline assertion: Boolean, inline message: => Any): Unit =
if !assertion then scala.runtime.Scala3RunTime.assertFailed(message)

inline def assert(inline assertion: Boolean): Unit =
if !assertion then scala.runtime.Scala3RunTime.assertFailed()

/**
* Retrieve the single value of a type with a unique inhabitant.
*
* @example {{{
* object Foo
* val foo = valueOf[Foo.type]
* // foo is Foo.type = Foo
*
* val bar = valueOf[23]
* // bar is 23.type = 23
* }}}
* @group utilities
*/
inline def valueOf[T]: T = summonFrom {
case ev: ValueOf[T] => ev.value
}

/** Summon a given value of type `T`. Usually, the argument is not passed explicitly.
*
* @tparam T the type of the value to be summoned
* @return the given value typed as the provided type parameter
*/
inline def summon[T](using x: T): x.type = x

// Extension methods for working with explicit nulls

/** Strips away the nullability from a value.
* e.g.
* val s1: String|Null = "hello"
* val s: String = s1.nn
*
* Note that `.nn` performs a checked cast, so if invoked on a null value it'll throw an NPE.
*/
extension [T](x: T | Null) inline def nn: x.type & T =
scala.runtime.Scala3RunTime.nn(x)
end Predef
60 changes: 60 additions & 0 deletions library/src/scala/runtime/stdLibPatches/language.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package scala.runtime.stdLibPatches

/** Scala 3 additions and replacements to the `scala.language` object.
*/
object language:

/** The experimental object contains features that have been recently added but have not
* been thoroughly tested in production yet.
*
* Experimental features '''may undergo API changes''' in future releases, so production
* code should not rely on them.
*
* Programmers are encouraged to try out experimental features and
* [[http://issues.scala-lang.org report any bugs or API inconsistencies]]
* they encounter so they can be improved in future releases.
*
* @group experimental
*/
object experimental:

/** Experimental support for richer dependent types */
object dependent

/** Experimental support for named type arguments */
object namedTypeArguments

/** Experimental support for generic number literals */
object genericNumberLiterals
end experimental

/** Where imported, auto-tupling is disabled */
object noAutoTupling

/** Where imported, loose equality using eqAny is disabled */
object strictEquality

/** Where imported, ad hoc extensions of non-open classes in other
* compilation units are allowed.
*
* '''Why control the feature?''' Ad-hoc extensions should usually be avoided
* since they typically cannot rely on an "internal" contract between a class
* and its extensions. Only open classes need to specify such a contract.
* Ad-hoc extensions might break for future versions of the extended class,
* since the extended class is free to change its implementation without
* being constrained by an internal contract.
*
* '''Why allow it?''' An ad-hoc extension can sometimes be necessary,
* for instance when mocking a class in a testing framework, or to work
* around a bug or missing feature in the original class. Nevertheless,
* such extensions should be limited in scope and clearly documented.
* That's why the language import is required for them.
*/
object adhocExtensions

/** Source version */
object `3.0-migration`
object `3.0`
object `3.1-migration`
object `3.1`
end language

0 comments on commit 7bdf89e

Please sign in to comment.