diff options
author | Miles Sabin <miles@milessabin.com> | 2016-08-10 09:30:49 +0100 |
---|---|---|
committer | Miles Sabin <miles@milessabin.com> | 2016-08-15 18:32:45 +0100 |
commit | d171b2cc16cc129e0f3aa03c3df9b2fb86208aa6 (patch) | |
tree | d95c542dddf2ae9e8c12843cd1b6478a7e5ad2c6 | |
parent | 81a67eeacc7d2622ee364a21203b227142e2043e (diff) | |
download | scala-d171b2cc16cc129e0f3aa03c3df9b2fb86208aa6.tar.gz scala-d171b2cc16cc129e0f3aa03c3df9b2fb86208aa6.tar.bz2 scala-d171b2cc16cc129e0f3aa03c3df9b2fb86208aa6.zip |
Partial fix for SI-7046
21 files changed, 252 insertions, 13 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index a618b080c8..8d72fd76bd 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -214,6 +214,19 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } } + private var propCnt = 0 + @inline final def withPropagateCyclicReferences[T](t: => T): T = { + try { + propCnt = propCnt+1 + t + } finally { + propCnt = propCnt-1 + assert(propCnt >= 0) + } + } + + def propagateCyclicReferences: Boolean = propCnt > 0 + /** Representing ASTs as graphs */ object treeBrowsers extends { val global: Global.this.type = Global.this diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 727f09290a..80cccaf2ae 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -614,9 +614,6 @@ trait ContextErrors { def ParentFinalInheritanceError(parent: Tree, mixin: Symbol) = NormalTypeError(parent, "illegal inheritance from final "+mixin) - def ParentSealedInheritanceError(parent: Tree, psym: Symbol) = - NormalTypeError(parent, "illegal inheritance from sealed " + psym ) - def ParentSelfTypeConformanceError(parent: Tree, selfType: Type) = NormalTypeError(parent, "illegal inheritance;\n self-type "+selfType+" does not conform to "+ @@ -1135,6 +1132,9 @@ trait ContextErrors { def MissingParameterOrValTypeError(vparam: Tree) = issueNormalTypeError(vparam, "missing parameter type") + def ParentSealedInheritanceError(parent: Tree, psym: Symbol) = + NormalTypeError(parent, "illegal inheritance from sealed " + psym ) + def RootImportError(tree: Tree) = issueNormalTypeError(tree, "_root_ cannot be imported") diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 4ad81b60ae..ee64a6646f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -110,7 +110,7 @@ trait Namers extends MethodSynthesis { protected def owner = context.owner def contextFile = context.unit.source.file def typeErrorHandler[T](tree: Tree, alt: T): PartialFunction[Throwable, T] = { - case ex: TypeError => + case ex: TypeError if !global.propagateCyclicReferences => // H@ need to ensure that we handle only cyclic references TypeSigError(tree, ex) alt @@ -912,12 +912,33 @@ trait Namers extends MethodSynthesis { private def templateSig(templ: Template): Type = { val clazz = context.owner + + val parentTrees = typer.typedParentTypes(templ) + + val pending = mutable.ListBuffer[AbsTypeError]() + parentTrees foreach { tpt => + val ptpe = tpt.tpe + if(!ptpe.isError) { + val psym = ptpe.typeSymbol + val sameSourceFile = context.unit.source.file == psym.sourceFile + + if (psym.isSealed && !phase.erasedTypes) + if (sameSourceFile) + psym addChild context.owner + else + pending += ParentSealedInheritanceError(tpt, psym) + if (psym.isLocalToBlock && !phase.erasedTypes) + psym addChild context.owner + } + } + pending.foreach(ErrorUtils.issueTypeError) + def checkParent(tpt: Tree): Type = { if (tpt.tpe.isError) AnyRefTpe else tpt.tpe } - val parents = typer.typedParentTypes(templ) map checkParent + val parents = parentTrees map checkParent enterSelf(templ.self) @@ -1678,6 +1699,12 @@ trait Namers extends MethodSynthesis { abstract class TypeCompleter extends LazyType { val tree: Tree + override def forceDirectSuperclasses: Unit = { + tree.foreach { + case dt: DefTree => global.withPropagateCyclicReferences(Option(dt.symbol).map(_.maybeInitialize)) + case _ => + } + } } def mkTypeCompleter(t: Tree)(c: Symbol => Unit) = new LockingTypeCompleter with FlagAgnosticCompleter { diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 6b73a538df..508d205424 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1643,7 +1643,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper supertpts mapConserve (tpt => checkNoEscaping.privates(context.owner, tpt)) } catch { - case ex: TypeError => + case ex: TypeError if !global.propagateCyclicReferences => // fallback in case of cyclic errors // @H none of the tests enter here but I couldn't rule it out // upd. @E when a definition inherits itself, we end up here @@ -1702,11 +1702,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper context.deprecationWarning(parent.pos, psym, msg) } - if (psym.isSealed && !phase.erasedTypes) - if (sameSourceFile) - psym addChild context.owner - else - pending += ParentSealedInheritanceError(parent, psym) val parentTypeOfThis = parent.tpe.dealias.typeOfThis if (!(selfType <:< parentTypeOfThis) && @@ -5421,6 +5416,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } try runTyper() catch { + case ex: CyclicReference if global.propagateCyclicReferences => + throw ex case ex: TypeError => tree.clearType() // The only problematic case are (recoverable) cyclic reference errors which can pop up almost anywhere. diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala index cca33253be..3df31b538c 100644 --- a/src/reflect/scala/reflect/internal/StdAttachments.scala +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -52,4 +52,10 @@ trait StdAttachments { /** Untyped list of subpatterns attached to selector dummy. */ case class SubpatternsAttachment(patterns: List[Tree]) + + /** Attached to a class symbol to indicate that its children have been observed + * via knownDirectSubclasses. Children added subsequently will trigger an + * error to indicate that the earlier observation was incomplete. + */ + case object KnownDirectSubclassesCalled extends PlainAttachment } diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 3b9ee9048a..6116952c70 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -110,6 +110,16 @@ trait Symbols extends api.Symbols { self: SymbolTable => def knownDirectSubclasses = { // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method. if (!isCompilerUniverse && !isThreadsafe(purpose = AllOps)) initialize + + enclosingPackage.info.decls.foreach { sym => + if(sourceFile == sym.sourceFile) { + sym.rawInfo.forceDirectSuperclasses + } + } + + if(!isPastTyper) + updateAttachment(KnownDirectSubclassesCalled) + children } @@ -3351,7 +3361,12 @@ trait Symbols extends api.Symbols { self: SymbolTable => private[this] var childSet: Set[Symbol] = Set() override def children = childSet - override def addChild(sym: Symbol) { childSet = childSet + sym } + override def addChild(sym: Symbol) { + if(!isPastTyper && hasAttachment[KnownDirectSubclassesCalled.type] && !childSet.contains(sym)) + globalError(s"knownDirectSubclasses of ${this.name} observed before subclass ${sym.name} registered") + + childSet = childSet + sym + } def anonOrRefinementString = { if (hasCompleteInfo) { diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 9697e16da7..3a645616eb 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -310,6 +310,11 @@ trait Types /** If this is a lazy type, assign a new type to `sym`. */ def complete(sym: Symbol) {} + /** If this is a lazy type corresponding to a subclass add it to its + * parents children + */ + def forceDirectSuperclasses: Unit = () + /** The term symbol associated with the type * Note that the symbol of the normalized type is returned (@see normalize) */ diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index 7725e4a2f0..8481cd8996 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -41,6 +41,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.ForAttachment this.SyntheticUnitAttachment this.SubpatternsAttachment + this.KnownDirectSubclassesCalled this.noPrint this.typeDebug this.Range diff --git a/test/files/neg/t7046-2.check b/test/files/neg/t7046-2.check new file mode 100644 index 0000000000..b4efd8b5e9 --- /dev/null +++ b/test/files/neg/t7046-2.check @@ -0,0 +1,3 @@ +error: knownDirectSubclasses of Foo observed before subclass Bar registered +error: knownDirectSubclasses of Foo observed before subclass Baz registered +two errors found diff --git a/test/files/neg/t7046-2/Macros_1.scala b/test/files/neg/t7046-2/Macros_1.scala new file mode 100644 index 0000000000..2a5bf82f62 --- /dev/null +++ b/test/files/neg/t7046-2/Macros_1.scala @@ -0,0 +1,15 @@ +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context + +object Macros { + def impl[T](c: Context)(implicit ttag: c.WeakTypeTag[T]): c.Expr[List[String]] = { + import c.universe._; + val ttpe = ttag.tpe + val tsym = ttpe.typeSymbol.asClass + val subclasses = tsym.knownDirectSubclasses.toList.map(_.name.toString) + + c.Expr[List[String]](q"$subclasses") + } + + def knownDirectSubclasses[T]: List[String] = macro impl[T] +} diff --git a/test/files/neg/t7046-2/Test_2.scala b/test/files/neg/t7046-2/Test_2.scala new file mode 100644 index 0000000000..18a2ebcbc2 --- /dev/null +++ b/test/files/neg/t7046-2/Test_2.scala @@ -0,0 +1,14 @@ +object Test extends App { + def nested: Unit = { + val subs = Macros.knownDirectSubclasses[Foo] + assert(subs == List("Bar", "Baz")) + + sealed trait Foo + object Foo { + trait Bar extends Foo + trait Baz extends Foo + } + } + + nested +} diff --git a/test/files/neg/t7046.check b/test/files/neg/t7046.check new file mode 100644 index 0000000000..689520a0aa --- /dev/null +++ b/test/files/neg/t7046.check @@ -0,0 +1,3 @@ +error: knownDirectSubclasses of Foo observed before subclass Local registered +error: knownDirectSubclasses of Foo observed before subclass Riddle registered +two errors found diff --git a/test/files/neg/t7046/Macros_1.scala b/test/files/neg/t7046/Macros_1.scala new file mode 100644 index 0000000000..2a5bf82f62 --- /dev/null +++ b/test/files/neg/t7046/Macros_1.scala @@ -0,0 +1,15 @@ +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context + +object Macros { + def impl[T](c: Context)(implicit ttag: c.WeakTypeTag[T]): c.Expr[List[String]] = { + import c.universe._; + val ttpe = ttag.tpe + val tsym = ttpe.typeSymbol.asClass + val subclasses = tsym.knownDirectSubclasses.toList.map(_.name.toString) + + c.Expr[List[String]](q"$subclasses") + } + + def knownDirectSubclasses[T]: List[String] = macro impl[T] +} diff --git a/test/files/neg/t7046/Test_2.scala b/test/files/neg/t7046/Test_2.scala new file mode 100644 index 0000000000..fcb3e46a0f --- /dev/null +++ b/test/files/neg/t7046/Test_2.scala @@ -0,0 +1,35 @@ +object Test extends App { + val subs = Macros.knownDirectSubclasses[Foo] + assert(subs == List("Wibble", "Wobble", "Bar", "Baz")) +} + +sealed trait Foo +object Foo { + trait Wibble extends Foo + case object Wobble extends Foo +} + +trait Bar extends Foo + +object Blah { + type Quux = Foo +} + +import Blah._ + +trait Baz extends Quux + +class Boz[T](t: T) +class Unrelated extends Boz(Test.subs) + +object Enigma { + locally { + // local class not seen + class Local extends Foo + } + + def foo: Unit = { + // local class not seen + class Riddle extends Foo + } +} diff --git a/test/files/pos/t7046-2/Macros_1.scala b/test/files/pos/t7046-2/Macros_1.scala new file mode 100644 index 0000000000..07c0c61281 --- /dev/null +++ b/test/files/pos/t7046-2/Macros_1.scala @@ -0,0 +1,14 @@ +package p1 + +import scala.reflect.macros.blackbox._ +import language.experimental._ + +object Macro { + def impl(c: Context): c.Tree = { + import c.universe._ + val tsym = rootMirror.staticClass("p1.Base") + val subclasses = tsym.knownDirectSubclasses.toList.map(_.name.toString) + q"$subclasses" + } + def p1_Base_knownDirectSubclasses: List[String] = macro impl +} diff --git a/test/files/pos/t7046-2/Test_2.scala b/test/files/pos/t7046-2/Test_2.scala new file mode 100644 index 0000000000..74e30a863d --- /dev/null +++ b/test/files/pos/t7046-2/Test_2.scala @@ -0,0 +1,9 @@ +package p1 + +sealed trait Base + +object Test { + val x = Macro.p1_Base_knownDirectSubclasses +} + +case class B(val b: Test.x.type) diff --git a/test/files/run/reflection-mem-typecheck.scala b/test/files/run/reflection-mem-typecheck.scala index e3cabf689d..f1fe983ede 100644 --- a/test/files/run/reflection-mem-typecheck.scala +++ b/test/files/run/reflection-mem-typecheck.scala @@ -11,7 +11,7 @@ object Test extends MemoryTest { cm.mkToolBox() } - override def maxDelta = 10 + override def maxDelta = 12 override def calcsPerIter = 8 override def calc() { var snippet = """ diff --git a/test/files/run/t7046-1/Macros_1.scala b/test/files/run/t7046-1/Macros_1.scala new file mode 100644 index 0000000000..2a5bf82f62 --- /dev/null +++ b/test/files/run/t7046-1/Macros_1.scala @@ -0,0 +1,15 @@ +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context + +object Macros { + def impl[T](c: Context)(implicit ttag: c.WeakTypeTag[T]): c.Expr[List[String]] = { + import c.universe._; + val ttpe = ttag.tpe + val tsym = ttpe.typeSymbol.asClass + val subclasses = tsym.knownDirectSubclasses.toList.map(_.name.toString) + + c.Expr[List[String]](q"$subclasses") + } + + def knownDirectSubclasses[T]: List[String] = macro impl[T] +} diff --git a/test/files/run/t7046-1/Test_2.scala b/test/files/run/t7046-1/Test_2.scala new file mode 100644 index 0000000000..28459fde72 --- /dev/null +++ b/test/files/run/t7046-1/Test_2.scala @@ -0,0 +1,23 @@ +object Test extends App { + val subs = Macros.knownDirectSubclasses[Foo] + assert(subs == List("Wibble", "Wobble", "Bar", "Baz")) +} + +sealed trait Foo +object Foo { + trait Wibble extends Foo + case object Wobble extends Foo +} + +trait Bar extends Foo + +object Blah { + type Quux = Foo +} + +import Blah._ + +trait Baz extends Quux + +class Boz[T](t: T) +class Unrelated extends Boz(Test.subs) diff --git a/test/files/run/t7046-2/Macros_1.scala b/test/files/run/t7046-2/Macros_1.scala new file mode 100644 index 0000000000..2a5bf82f62 --- /dev/null +++ b/test/files/run/t7046-2/Macros_1.scala @@ -0,0 +1,15 @@ +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context + +object Macros { + def impl[T](c: Context)(implicit ttag: c.WeakTypeTag[T]): c.Expr[List[String]] = { + import c.universe._; + val ttpe = ttag.tpe + val tsym = ttpe.typeSymbol.asClass + val subclasses = tsym.knownDirectSubclasses.toList.map(_.name.toString) + + c.Expr[List[String]](q"$subclasses") + } + + def knownDirectSubclasses[T]: List[String] = macro impl[T] +} diff --git a/test/files/run/t7046-2/Test_2.scala b/test/files/run/t7046-2/Test_2.scala new file mode 100644 index 0000000000..79407f522f --- /dev/null +++ b/test/files/run/t7046-2/Test_2.scala @@ -0,0 +1,14 @@ +object Test extends App { + def nested: Unit = { + sealed trait Foo + object Foo { + trait Bar extends Foo + trait Baz extends Foo + } + + val subs = Macros.knownDirectSubclasses[Foo] + assert(subs == List("Bar", "Baz")) + } + + nested +} |