From 91c34cedc9ee54244f6b49dda691cbe0be182037 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 10 Oct 2014 11:24:14 +1000 Subject: SI-3439 Fix use of implicit constructor params in super call When typechecking the primary constructor body, the symbols of constructor parameters of a class are owned by the class's owner. This is done make scoping work; you shouldn't be able to refer to class members in that position. However, other parts of the compiler weren't so happy about this arrangement. The enclosed test case shows that our checks for invalid, top-level implicits was spuriously triggered, and implicit search itself would fail. Furthermore, we had to hack `Run#compiles` to special case top-level early-initialized symbols. See SI-7264 / 86e6e9290. This commit: - introduces an intermediate local dummy term symbol which will act as the owner for constructor parameters and early initialized members - adds this to the `Run#symSource` map if it is top level - simplifies `Run#compiles` accordingly - tests this all in a top-level class, and one nested in another class. --- src/compiler/scala/tools/nsc/Global.scala | 5 ++--- .../scala/tools/nsc/typechecker/Typers.scala | 4 +++- src/reflect/scala/reflect/internal/Symbols.scala | 7 ++++++ test/files/neg/t7636.check | 2 +- test/files/pos/t3439.scala | 26 ++++++++++++++++++++++ test/pending/pos/t3439.scala | 2 -- 6 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 test/files/pos/t3439.scala delete mode 100644 test/pending/pos/t3439.scala diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 452081cff1..76d8b7e5ef 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1232,13 +1232,12 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** does this run compile given class, module, or case factory? */ // NOTE: Early initialized members temporarily typechecked before the enclosing class, see typedPrimaryConstrBody! - // Here we work around that wrinkle by claiming that a top-level, early-initialized member is compiled in + // Here we work around that wrinkle by claiming that a early-initialized member is compiled in // *every* run. This approximation works because this method is exclusively called with `this` == `currentRun`. def compiles(sym: Symbol): Boolean = if (sym == NoSymbol) false else if (symSource.isDefinedAt(sym)) true - else if (sym.isTopLevel && sym.isEarlyInitialized) true - else if (!sym.isTopLevel) compiles(sym.enclosingTopLevelClass) + else if (!sym.isTopLevel) compiles(sym.enclosingTopLevelClassOrDummy) else if (sym.isModuleClass) compiles(sym.sourceModule) else false diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index aae2d24b32..70acb03584 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1516,7 +1516,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val cbody1 = treeCopy.Block(cbody, preSuperStats, superCall1) val clazz = context.owner assert(clazz != NoSymbol, templ) - val cscope = context.outer.makeNewScope(ctor, context.outer.owner) + val dummy = context.outer.owner.newLocalDummy(templ.pos) + val cscope = context.outer.makeNewScope(ctor, dummy) + if (dummy.isTopLevel) currentRun.symSource(dummy) = currentUnit.source.file val cbody2 = { // called both during completion AND typing. val typer1 = newTyper(cscope) // XXX: see about using the class's symbol.... diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index b0c23ef45d..f3f11076bd 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -2157,6 +2157,12 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (isClass) this else moduleClass } else owner.enclosingTopLevelClass + /** The top-level class or local dummy symbol containing this symbol. */ + def enclosingTopLevelClassOrDummy: Symbol = + if (isTopLevel) { + if (isClass) this else moduleClass.orElse(this) + } else owner.enclosingTopLevelClassOrDummy + /** Is this symbol defined in the same scope and compilation unit as `that` symbol? */ def isCoDefinedWith(that: Symbol) = ( !rawInfoIsNoType @@ -3505,6 +3511,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def enclClassChain = Nil override def enclClass: Symbol = this override def enclosingTopLevelClass: Symbol = this + override def enclosingTopLevelClassOrDummy: Symbol = this override def enclosingPackageClass: Symbol = this override def enclMethod: Symbol = this override def associatedFile = NoAbstractFile diff --git a/test/files/neg/t7636.check b/test/files/neg/t7636.check index f70d50bee3..12391cccc8 100644 --- a/test/files/neg/t7636.check +++ b/test/files/neg/t7636.check @@ -4,7 +4,7 @@ t7636.scala:3: error: illegal inheritance; ^ t7636.scala:3: error: type mismatch; found : Either[_$2,_$3(in constructor C)] where type _$3(in constructor C), type _$2 - required: Either[_, _$3(in object Main)] where type _$3(in object Main) + required: Either[_, _$3(in value )] where type _$3(in value ) class C extends ResultTable(Left(5):Either[_,_])(5) ^ two errors found diff --git a/test/files/pos/t3439.scala b/test/files/pos/t3439.scala new file mode 100644 index 0000000000..ccc75cc4cf --- /dev/null +++ b/test/files/pos/t3439.scala @@ -0,0 +1,26 @@ +class Base[M](i: Int) + +// was "implicit modifier not allowed on top level objects" +class D1()(implicit i: Int) extends Base({println(i); 0}) + +// what "no implicit value of type Int found" +class D2()(implicit i: Int) extends Base(implicitly[Int]) + + +abstract class ParametricMessage[M: Manifest](msg: M) { def message = msg } +case class ParametricMessage1[M: Manifest](msg: M, p1: Class[_]) extends ParametricMessage(msg) + + +class Wrap { + class Base[M](i: Int) + + // was "implicit modifier not allowed on top level objects" + class D1()(implicit i: Int) extends Base({println(i); 0}) + + // what "no implicit value of type Int found" + class D2()(implicit i: Int) extends Base(implicitly[Int]) + + + abstract class ParametricMessage[M: Manifest](msg: M) { def message = msg } + case class ParametricMessage1[M: Manifest](msg: M, p1: Class[_]) extends ParametricMessage(msg) +} diff --git a/test/pending/pos/t3439.scala b/test/pending/pos/t3439.scala deleted file mode 100644 index 425f1aeeb5..0000000000 --- a/test/pending/pos/t3439.scala +++ /dev/null @@ -1,2 +0,0 @@ -abstract class ParametricMessage[M: Manifest](msg: M) { def message = msg } -case class ParametricMessage1[M: Manifest](msg: M, p1: Class[_]) extends ParametricMessage(msg) -- cgit v1.2.3