diff options
author | Jason Zaugg <jzaugg@gmail.com> | 2014-01-07 14:01:24 +0100 |
---|---|---|
committer | Jason Zaugg <jzaugg@gmail.com> | 2014-01-07 15:53:45 +0100 |
commit | 5b9966d077b8fa7af95fa95d7782243892e6ccad (patch) | |
tree | bcc8bc2ca8252a5e1c13f06876623e7c9dd97664 | |
parent | 66a12fc264cae53107fda4feb5e42fda6879c1ae (diff) | |
download | scala-5b9966d077b8fa7af95fa95d7782243892e6ccad.tar.gz scala-5b9966d077b8fa7af95fa95d7782243892e6ccad.tar.bz2 scala-5b9966d077b8fa7af95fa95d7782243892e6ccad.zip |
SI-8120 Avoid tree sharing when typechecking patmat anon functions
When typechecking an empty selector `Match` corresponding to:
{ case ... => ... }: (A => B)
We wrap it in a `Function` and typecheck:
(x$1 => x$1 match { case ... => ... })
Local symbols in this expression (representing values bound by
the pattern, or just definitions in the body or guard) are then
owned by the anonymous function's symbol.
However, if we ever discard this `Function` and start anew with
the empty selector match, as happens during the fallback to
use a view on the receiver in `tryTypedApply`, we found that we
had mutated the cases of the original tree, and allowed orphaned
local symbols to escape into the compiler pipeline.
This commit uses duplicated trees for the the cases in the synthetic
`Match` to avoid this problem.
`duplicateAndKeepPositions` is used to preserve range positions;
without this scala-refactoring PrettyPrinterTest fails.
`Tree#duplicate` uses offset positions in the copied tree, which
is appropriate when both the original and the copy are going to end up
in the final tree.
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 6 | ||||
-rw-r--r-- | test/files/pos/t8120.scala | 9 |
2 files changed, 14 insertions, 1 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 910da77ca8..61e7367547 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4250,7 +4250,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } val ids = for (p <- params) yield Ident(p.name) val selector1 = atPos(tree.pos.focusStart) { if (arity == 1) ids.head else gen.mkTuple(ids) } - val body = treeCopy.Match(tree, selector1, cases) + // SI-8120 If we don't duplicate the cases, the original Match node will share trees with ones that + // receive symbols owned by this function. However if, after a silent mode session, we discard + // this Function and try a different approach (e.g. applying a view to the reciever) we end up + // with orphaned symbols which blows up far down the pipeline (or can be detected with -Ycheck:typer). + val body = treeCopy.Match(tree, selector1, (cases map duplicateAndKeepPositions).asInstanceOf[List[CaseDef]]) typed1(atPos(tree.pos) { Function(params, body) }, mode, pt) } } else diff --git a/test/files/pos/t8120.scala b/test/files/pos/t8120.scala new file mode 100644 index 0000000000..e06f38d5db --- /dev/null +++ b/test/files/pos/t8120.scala @@ -0,0 +1,9 @@ +object A { + class C { + def m(a: Nothing): Int = 0 + } + implicit class RichAny(a: Any) { + def m(a: Any): Int = 0 + } + (new C).m({ case (x, y) => x } : Any => Any) +} |