diff options
author | Paul Phillips <paulp@improving.org> | 2012-02-11 13:16:06 -0800 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2012-02-11 23:05:22 -0800 |
commit | 7a6fa80937dec6c60efe53c915dfa3ba76b3af87 (patch) | |
tree | 76fbd019dfcb0caf284ba8f95bc2dfe962896506 /src/compiler | |
parent | c478eb770ddf27de64d55426f0fdd3fd81d11f22 (diff) | |
download | scala-7a6fa80937dec6c60efe53c915dfa3ba76b3af87.tar.gz scala-7a6fa80937dec6c60efe53c915dfa3ba76b3af87.tar.bz2 scala-7a6fa80937dec6c60efe53c915dfa3ba76b3af87.zip |
Another existential problem down.
There is a window of danger when multiple related elements are
being typed where something which is conceptually one thing can
slip into two things, and those two things can be incompatible
with one another. Less mysteriously, c478eb770d fixed this:
def f = { object Bob ; Bob } ; val g = f
But, it did not fix this:
def f = { case class Bob() ; Bob } ; val g = f
See test case pos/existentials-harmful.scala for an "in the wild"
code example fixed by this commit.
The root of the problem was that the getter and the field would each
independently derive the same existential type to describe Bob, but
those existentials were not the same as one another.
This has been the most elusive bug I have ever fixed. I want to cry when
I think of how much time I've put into it over the past half decade or
so. Unfortunately the way the repl works it is particularly good at
eliciting those grotesque found/required error messages and so I was
never able to let the thing go.
There is still a cosmetic issue (from the last commit really) where
compound types wind up with repeated parents.
Closes SI-1195, SI-1201.
Diffstat (limited to 'src/compiler')
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala | 43 |
1 files changed, 28 insertions, 15 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 0c32ff32c0..915d7a98db 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -326,22 +326,35 @@ trait MethodSynthesis { super.validate() } - // keep type tree of original abstract field - private def fixTypeTree(dd: DefDef): DefDef = { - dd.tpt match { - case tt: TypeTree if dd.rhs == EmptyTree => - tt setOriginal tree.tpt - case tpt => - tpt setPos tree.tpt.pos.focus - } - dd - } override def derivedTree: DefDef = { - fixTypeTree { - DefDef(derivedSym, - if (mods.isDeferred) EmptyTree - else gen.mkCheckInit(fieldSelection) - ) + // For existentials, don't specify a type for the getter, even one derived + // from the symbol! This leads to incompatible existentials for the field and + // the getter. Let the typer do all the work. You might think "why only for + // existentials, why not always," and you would be right, except: a single test + // fails, but it looked like some work to deal with it. Test neg/t0606.scala + // starts compiling (instead of failing like it's supposed to) because the typer + // expects to be able to identify escaping locals in typedDefDef, and fails to + // spot that brand of them. In other words it's an artifact of the implementation. + val tpt = derivedSym.tpe.finalResultType match { + case ExistentialType(_, _) => TypeTree() + case tp => TypeTree(tp) + } + tpt setPos focusPos(derivedSym.pos) + // keep type tree of original abstract field + if (mods.isDeferred) + tpt setOriginal tree.tpt + + // TODO - reconcile this with the DefDef creator in Trees (which + // at this writing presented no way to pass a tree in for tpt.) + atPos(derivedSym.pos) { + DefDef( + Modifiers(derivedSym.flags), + derivedSym.name.toTermName, + Nil, + Nil, + tpt, + if (mods.isDeferred) EmptyTree else gen.mkCheckInit(fieldSelection) + ) setSymbol derivedSym } } } |