diff options
author | Adriaan Moors <adriaan@lightbend.com> | 2016-11-30 17:45:59 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-11-30 17:45:59 -0800 |
commit | c2eb299b0e1ed2f321a81d4afbdb36762e2c0e7b (patch) | |
tree | b184b8e08dc9077a39b4f48e86e5e4959ffb3c01 /src/reflect/scala/reflect | |
parent | 038c15e405b9498863a2236707686a7933748c60 (diff) | |
parent | dde13b56f421a6f956abebc58f041acec8744149 (diff) | |
download | scala-c2eb299b0e1ed2f321a81d4afbdb36762e2c0e7b.tar.gz scala-c2eb299b0e1ed2f321a81d4afbdb36762e2c0e7b.tar.bz2 scala-c2eb299b0e1ed2f321a81d4afbdb36762e2c0e7b.zip |
Merge pull request #5284 from milessabin/topic/si-7046
SI-7046 reflection doesn't see all knownDirectSubclasses
This appears to do the right thing in the most typical scenarios in which `knownDirectSubclasses` would be used. The missing 5% is that subclasses defined in local scopes might not be seen by `knownDirectSubclasses` (see `Local` and `Riddle` in the test below). In mitigation, though, it is almost certain that a local subclass would represent an error in any scenario where `knownDirectSubclasses` might be used.
Errors for such situations are reported by recording (via a symbol attachment) that `knownDirectSubclasses` has been called and reporting an error if any additional children are added subsequently.
Despite these limitations and caveats, I believe that this represents a huge improvement over the status quo, and would eliminate 100% of the failures that I've seen in practice with people using shapeless for type class derivation.
Diffstat (limited to 'src/reflect/scala/reflect')
4 files changed, 28 insertions, 1 deletions
diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala index fd8f51cfb1..fc49de1cf6 100644 --- a/src/reflect/scala/reflect/internal/StdAttachments.scala +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -81,4 +81,10 @@ trait StdAttachments { /** An attachment carrying information between uncurry and erasure */ case class TypeParamVarargsAttachment(val typeParamRef: Type) + + /** 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 0da153349a..e664b5ad08 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -121,6 +121,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 } @@ -3298,7 +3308,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 dc35053835..fa5a1a25ad 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -313,6 +313,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 dbafbfc6ba..d5d62b2203 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -48,6 +48,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.OuterArgCanBeElided this.UseInvokeSpecial this.TypeParamVarargsAttachment + this.KnownDirectSubclassesCalled this.noPrint this.typeDebug this.Range |