summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bincompat-backward.whitelist.conf4
-rw-r--r--bincompat-forward.whitelist.conf4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala20
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala11
-rw-r--r--src/reflect/scala/reflect/internal/Scopes.scala24
-rw-r--r--src/reflect/scala/reflect/runtime/SymbolLoaders.scala2
-rw-r--r--src/reflect/scala/reflect/runtime/SynchronizedOps.scala3
-rw-r--r--test/files/neg/double-def-top-level.check7
-rw-r--r--test/files/neg/double-def-top-level/A_1.scala4
-rw-r--r--test/files/neg/double-def-top-level/B_2.scala2
-rw-r--r--test/files/neg/double-def-top-level/C_3.scala2
-rw-r--r--test/files/neg/double-def-top-level/D_3.scala2
-rw-r--r--test/junit/scala/reflect/internal/ScopeTest.scala54
13 files changed, 112 insertions, 27 deletions
diff --git a/bincompat-backward.whitelist.conf b/bincompat-backward.whitelist.conf
index 703c5add42..ffacbf0442 100644
--- a/bincompat-backward.whitelist.conf
+++ b/bincompat-backward.whitelist.conf
@@ -181,6 +181,10 @@ filter {
{
matchName="scala.reflect.api.Internals#ReificationSupportApi.SyntacticExistentialType"
problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.reflect.runtime.SynchronizedOps.newNestedScope"
+ problemName=MissingMethodProblem
}
]
}
diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf
index ca8f7468eb..3cd985aeae 100644
--- a/bincompat-forward.whitelist.conf
+++ b/bincompat-forward.whitelist.conf
@@ -230,6 +230,10 @@ filter {
{
matchName="scala.reflect.runtime.JavaMirrors#JavaMirror.scala$reflect$runtime$JavaMirrors$JavaMirror$$followStatic"
problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.reflect.runtime.SynchronizedOps.newNestedScope"
+ problemName=MissingMethodProblem
}
]
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index 4382a2c6f7..099031d536 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -1040,10 +1040,10 @@ trait Namers extends MethodSynthesis {
* so the resulting type is a valid external method type, it does not contain (references to) skolems.
*/
def thisMethodType(restpe: Type) = {
- val checkDependencies = new DependentTypeChecker(context)(this)
- checkDependencies check vparamSymss
- // DEPMETTODO: check not needed when they become on by default
- checkDependencies(restpe)
+ if (vparamSymss.lengthCompare(0) > 0) { // OPT fast path for methods of 0-1 parameter lists
+ val checkDependencies = new DependentTypeChecker(context)(this)
+ checkDependencies check vparamSymss
+ }
val makeMethodType = (vparams: List[Symbol], restpe: Type) => {
// TODODEPMET: check that we actually don't need to do anything here
@@ -1177,7 +1177,13 @@ trait Namers extends MethodSynthesis {
}
}
- addDefaultGetters(meth, ddef, vparamss, tparams, overriddenSymbol(methResTp))
+ val overridden = {
+ val isConstr = meth.isConstructor
+ if (isConstr || !methOwner.isClass) NoSymbol else overriddenSymbol(methResTp)
+ }
+ val hasDefaults = mexists(vparamss)(_.symbol.hasDefault) || mexists(overridden.paramss)(_.hasDefault)
+ if (hasDefaults)
+ addDefaultGetters(meth, ddef, vparamss, tparams, overridden)
// fast track macros, i.e. macros defined inside the compiler, are hardcoded
// hence we make use of that and let them have whatever right-hand side they need
@@ -1219,7 +1225,7 @@ trait Namers extends MethodSynthesis {
* typechecked, the corresponding param would not yet have the "defaultparam"
* flag.
*/
- private def addDefaultGetters(meth: Symbol, ddef: DefDef, vparamss: List[List[ValDef]], tparams: List[TypeDef], overriddenSymbol: => Symbol) {
+ private def addDefaultGetters(meth: Symbol, ddef: DefDef, vparamss: List[List[ValDef]], tparams: List[TypeDef], overridden: Symbol) {
val DefDef(_, _, rtparams0, rvparamss0, _, _) = resetAttrs(ddef.duplicate)
// having defs here is important to make sure that there's no sneaky tree sharing
// in methods with multiple default parameters
@@ -1227,7 +1233,6 @@ trait Namers extends MethodSynthesis {
def rvparamss = rvparamss0.map(_.map(_.duplicate))
val methOwner = meth.owner
val isConstr = meth.isConstructor
- val overridden = if (isConstr || !methOwner.isClass) NoSymbol else overriddenSymbol
val overrides = overridden != NoSymbol && !overridden.isOverloaded
// value parameters of the base class (whose defaults might be overridden)
var baseParamss = (vparamss, overridden.tpe.paramss) match {
@@ -1745,7 +1750,6 @@ trait Namers extends MethodSynthesis {
for (p <- vps)
this(p.info)
// can only refer to symbols in earlier parameter sections
- // (if the extension is enabled)
okParams ++= vps
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 9fe693ce2a..66b1c2d87a 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -3022,7 +3022,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
|| (looker.hasAccessorFlag && !accessed.hasAccessorFlag && accessed.isPrivate)
)
- def checkNoDoubleDefs(stats: List[Tree]): Unit = {
+ def checkNoDoubleDefs: Unit = {
val scope = if (inBlock) context.scope else context.owner.info.decls
var e = scope.elems
while ((e ne null) && e.owner == scope) {
@@ -3057,8 +3057,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// the corresponding synthetics to the package class, only to the package object class.
def shouldAdd(sym: Symbol) =
inBlock || !context.isInPackageObject(sym, context.owner)
- for (sym <- scope if shouldAdd(sym))
- for (tree <- context.unit.synthetics get sym) {
+ for (sym <- scope)
+ for (tree <- context.unit.synthetics get sym if shouldAdd(sym)) { // OPT: shouldAdd is usually true. Call it here, rather than in the outer loop
newStats += typedStat(tree) // might add even more synthetics to the scope
context.unit.synthetics -= sym
}
@@ -3104,7 +3104,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val stats1 = stats mapConserve typedStat
if (phase.erasedTypes) stats1
else {
- checkNoDoubleDefs(stats1)
+ // As packages are open, it doesn't make sense to check double definitions here. Furthermore,
+ // it is expensive if the package is large. Instead, such double defininitions are checked in `Namers.enterInScope`
+ if (!context.owner.isPackageClass)
+ checkNoDoubleDefs
addSynthetics(stats1)
}
}
diff --git a/src/reflect/scala/reflect/internal/Scopes.scala b/src/reflect/scala/reflect/internal/Scopes.scala
index cf3f356daa..103f885ad4 100644
--- a/src/reflect/scala/reflect/internal/Scopes.scala
+++ b/src/reflect/scala/reflect/internal/Scopes.scala
@@ -48,22 +48,17 @@ trait Scopes extends api.Scopes { self: SymbolTable =>
* This is necessary because when run from reflection every scope needs to have a
* SynchronizedScope as mixin.
*/
- class Scope protected[Scopes] (initElems: ScopeEntry = null, initFingerPrints: Long = 0L) extends ScopeApi with MemberScopeApi {
+ class Scope protected[Scopes]() extends ScopeApi with MemberScopeApi {
- protected[Scopes] def this(base: Scope) = {
- this(base.elems)
- nestinglevel = base.nestinglevel + 1
- }
-
- private[scala] var elems: ScopeEntry = initElems
+ private[scala] var elems: ScopeEntry = _
/** The number of times this scope is nested in another
*/
- private var nestinglevel = 0
+ private[Scopes] var nestinglevel = 0
/** the hash table
*/
- private var hashtable: Array[ScopeEntry] = null
+ private[Scopes] var hashtable: Array[ScopeEntry] = null
/** a cache for all elements, to be used by symbol iterator.
*/
@@ -84,8 +79,6 @@ trait Scopes extends api.Scopes { self: SymbolTable =>
*/
private val MIN_HASH = 8
- if (size >= MIN_HASH) createHash()
-
/** Returns a new scope with the same content as this one. */
def cloneScope: Scope = newScopeWith(this.toList: _*)
@@ -435,7 +428,14 @@ trait Scopes extends api.Scopes { self: SymbolTable =>
}
/** Create a new scope nested in another one with which it shares its elements */
- def newNestedScope(outer: Scope): Scope = new Scope(outer)
+ final def newNestedScope(outer: Scope): Scope = {
+ val nested = newScope // not `new Scope`, we must allow the runtime reflection universe to mixin SynchronizedScopes!
+ nested.elems = outer.elems
+ nested.nestinglevel = outer.nestinglevel + 1
+ if (outer.hashtable ne null)
+ nested.hashtable = java.util.Arrays.copyOf(outer.hashtable, outer.hashtable.length)
+ nested
+ }
/** Create a new scope with given initial elements */
def newScopeWith(elems: Symbol*): Scope = {
diff --git a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala
index c56bc28d90..7ba68b8733 100644
--- a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala
+++ b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala
@@ -91,7 +91,7 @@ private[reflect] trait SymbolLoaders { self: SymbolTable =>
//
// Short of significantly changing SymbolLoaders I see no other way than just
// to slap a global lock on materialization in runtime reflection.
- class PackageScope(pkgClass: Symbol) extends Scope(initFingerPrints = -1L) // disable fingerprinting as we do not know entries beforehand
+ class PackageScope(pkgClass: Symbol) extends Scope
with SynchronizedScope {
assert(pkgClass.isType)
diff --git a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala
index c90901410a..4a8585d616 100644
--- a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala
+++ b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala
@@ -37,8 +37,7 @@ private[reflect] trait SynchronizedOps extends internal.SymbolTable
// Scopes
- override def newScope = new Scope() with SynchronizedScope
- override def newNestedScope(outer: Scope): Scope = new Scope(outer) with SynchronizedScope
+ override def newScope = new Scope with SynchronizedScope
trait SynchronizedScope extends Scope {
// we can keep this lock fine-grained, because methods of Scope don't do anything extraordinary, which makes deadlocks impossible
diff --git a/test/files/neg/double-def-top-level.check b/test/files/neg/double-def-top-level.check
new file mode 100644
index 0000000000..85b16e81e5
--- /dev/null
+++ b/test/files/neg/double-def-top-level.check
@@ -0,0 +1,7 @@
+D_3.scala:1: error: C is already defined as class C
+class C
+ ^
+D_3.scala:2: error: O is already defined as object O
+object O
+ ^
+two errors found
diff --git a/test/files/neg/double-def-top-level/A_1.scala b/test/files/neg/double-def-top-level/A_1.scala
new file mode 100644
index 0000000000..c3d68d9d05
--- /dev/null
+++ b/test/files/neg/double-def-top-level/A_1.scala
@@ -0,0 +1,4 @@
+package p
+
+class C
+object O
diff --git a/test/files/neg/double-def-top-level/B_2.scala b/test/files/neg/double-def-top-level/B_2.scala
new file mode 100644
index 0000000000..c328e8c964
--- /dev/null
+++ b/test/files/neg/double-def-top-level/B_2.scala
@@ -0,0 +1,2 @@
+class C /* noerror */
+object O /* noerror */ \ No newline at end of file
diff --git a/test/files/neg/double-def-top-level/C_3.scala b/test/files/neg/double-def-top-level/C_3.scala
new file mode 100644
index 0000000000..e1c327c15a
--- /dev/null
+++ b/test/files/neg/double-def-top-level/C_3.scala
@@ -0,0 +1,2 @@
+class C
+object O \ No newline at end of file
diff --git a/test/files/neg/double-def-top-level/D_3.scala b/test/files/neg/double-def-top-level/D_3.scala
new file mode 100644
index 0000000000..518e0d1c54
--- /dev/null
+++ b/test/files/neg/double-def-top-level/D_3.scala
@@ -0,0 +1,2 @@
+class C
+object O
diff --git a/test/junit/scala/reflect/internal/ScopeTest.scala b/test/junit/scala/reflect/internal/ScopeTest.scala
new file mode 100644
index 0000000000..5166620189
--- /dev/null
+++ b/test/junit/scala/reflect/internal/ScopeTest.scala
@@ -0,0 +1,54 @@
+package symtab
+
+import scala.tools.nsc.symtab
+
+import org.junit.Assert._
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+import scala.tools.testing.AssertUtil.assertThrows
+import scala.tools.nsc.symtab.SymbolTableForUnitTesting
+
+@RunWith(classOf[JUnit4])
+class ScopeTest {
+ object symbolTable extends SymbolTableForUnitTesting
+
+ import symbolTable._
+
+ @Test
+ def testNestedScopeSmall(): Unit = testNestedScope(0)
+ @Test
+ def testNestedScopeLarge(): Unit = testNestedScope(64) // exceeding MIN_HASH
+
+ private def testNestedScope(initSize: Int) {
+ def sym(termName: String): Symbol = NoSymbol.newValue(TermName(termName))
+ val foo = sym("foo")
+ val bar = sym("bar")
+
+ val outerElems = List.tabulate(initSize)(i => sym(i.toString))
+ val outer = newScopeWith(outerElems ++ List(foo, bar) : _*)
+ assertTrue(outer.containsName(foo.name))
+ assertTrue(outer.containsName(bar.name))
+
+ val baz = sym("baz")
+ val nested = newNestedScope(outer)
+
+ // Entries from the outer scope are entered in the nested.
+ assertTrue(outer.containsName(foo.name))
+ assertTrue(outer.containsName(bar.name))
+
+ // Nested scopes structurally share ScopeEntry-s with the outer.
+ assertSame(outer.lookupEntry(foo.name), nested.lookupEntry(foo.name))
+ nested.enter(baz)
+
+ // Symbols entered in the nested scope aren't visible in the outer.
+ assertTrue(nested.containsName(baz.name))
+ assertTrue(!outer.containsName(baz.name))
+
+ // Unlinking a symbol in the inner scope doesn't modify the outer
+ nested.unlink(bar)
+ assert(!nested.containsName(bar.name))
+ assert(outer.containsName(bar.name))
+ }
+}