Skip to content

Commit 747e223

Browse files
committed
Actually retract clashing synthetic apply/unapply
The completer set the IS_ERROR flag and I assumed the typer dropped a synthetic tree with a symbol with that flag, because the tree was not shown in -Xprint output. It turns out, as explained by lrytz, that the mechanism was fragile because it relied on the order in which completers are run. We now cover both the case that: - the completer was run (and the `IS_ERROR` flag was set) before `addSynthetics` in `typedStat` iterates over the scope (since the symbol is already unlinked, the tree is not added, irrespective of its flags). For this case, we also remove the symbol from the synthetics in its unit. - the completer is triggered during the iteration in `addSynthetics`, which needs the check for the `IS_ERROR` flag during the iteration. Thankfully, the community build caught my mistake, and lrytz provided a good analysis and review. Fix scala/bug#10261
1 parent 12c240d commit 747e223

File tree

4 files changed

+31
-1
lines changed

4 files changed

+31
-1
lines changed

src/compiler/scala/tools/nsc/typechecker/Namers.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,17 @@ trait Namers extends MethodSynthesis {
664664

665665
if (suppress) {
666666
sym setInfo ErrorType
667+
668+
// There are two ways in which we exclude the symbol from being added in typedStats::addSynthetics,
669+
// because we don't know when the completer runs with respect to this loop in addSynthetics
670+
// for (sym <- scope)
671+
// for (tree <- context.unit.synthetics.get(sym) if shouldAdd(sym)) {
672+
// if (!sym.initialize.hasFlag(IS_ERROR))
673+
// newStats += typedStat(tree)
674+
// If we're already in the loop, set the IS_ERROR flag and trigger the condition `sym.initialize.hasFlag(IS_ERROR)`
667675
sym setFlag IS_ERROR
676+
// Or, if we are not yet in the addSynthetics loop, we can just retract our symbol from the synthetics for this unit.
677+
companionContext.unit.synthetics -= sym
668678

669679
// Don't unlink in an error situation to generate less confusing error messages.
670680
// Ideally, our error reporting would distinguish overloaded from recursive user-defined apply methods without signature,

src/compiler/scala/tools/nsc/typechecker/Typers.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3168,7 +3168,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
31683168
for (sym <- scope)
31693169
// OPT: shouldAdd is usually true. Call it here, rather than in the outer loop
31703170
for (tree <- context.unit.synthetics.get(sym) if shouldAdd(sym)) {
3171-
newStats += typedStat(tree) // might add even more synthetics to the scope
3171+
// if the completer set the IS_ERROR flag, retract the stat (currently only used by applyUnapplyMethodCompleter)
3172+
if (!sym.initialize.hasFlag(IS_ERROR))
3173+
newStats += typedStat(tree) // might add even more synthetics to the scope
31723174
context.unit.synthetics -= sym
31733175
}
31743176
// the type completer of a synthetic might add more synthetics. example: if the
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
trait Companion[T] {
2+
def parse(value: String): Option[T]
3+
def apply(value: String): T = parse(value).get
4+
}

test/files/run/t10261/Test_2.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import scala.util.Try
2+
3+
object C extends Companion[C] {
4+
def parse(v: String) = if (v.nonEmpty) Some(new C(v)) else None
5+
}
6+
7+
case class C(value: String)
8+
9+
object Test {
10+
def main(args: Array[String]): Unit = {
11+
assert(Try{C("")}.isFailure, "Empty value should fail to parse") // check that parse is used to validate input
12+
assert(C("a").value == "a", "Unexpected value")
13+
}
14+
}

0 commit comments

Comments
 (0)