Skip to content

Commit 8a413ba

Browse files
authored
Merge pull request scala#5846 from adriaanm/t10261-2.11
Actually retract clashing synthetic apply/unapply
2 parents 5167b69 + 77917e9 commit 8a413ba

File tree

8 files changed

+42
-3
lines changed

8 files changed

+42
-3
lines changed

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -691,8 +691,21 @@ trait Namers extends MethodSynthesis {
691691

692692
if (suppress) {
693693
sym setInfo ErrorType
694+
// There are two ways in which we exclude the symbol from being added in typedStats::addSynthetics,
695+
// because we don't know when the completer runs with respect to this loop in addSynthetics
696+
// for (sym <- scope)
697+
// for (tree <- context.unit.synthetics.get(sym) if shouldAdd(sym)) {
698+
// if (!sym.initialize.hasFlag(IS_ERROR))
699+
// newStats += typedStat(tree)
700+
// (1) If we're already in the loop, set the IS_ERROR flag and trigger the condition
701+
// `sym.initialize.hasFlag(IS_ERROR)` in typedStats::addSynthetics,
702+
// (2) Or, if we are not yet in the addSynthetics loop (and we're not going to emit an error anyway),
703+
// we unlink the symbol from its scope.
694704
sym setFlag IS_ERROR
695705

706+
// For good measure. Removing it from its owner's scope and setting the IS_ERROR flag is enough to exclude it from addSynthetics
707+
companionContext.unit.synthetics -= sym
708+
696709
// Don't unlink in an error situation to generate less confusing error messages.
697710
// Ideally, our error reporting would distinguish overloaded from recursive user-defined apply methods without signature,
698711
// but this would require some form of partial-completion of method signatures, so that we can
@@ -702,7 +715,7 @@ trait Namers extends MethodSynthesis {
702715
// I hesitate to provide more info, because it would involve a WildCard or something for its result type,
703716
// which could upset other code paths)
704717
if (!scopePartiallyCompleted)
705-
companionContext.scope.unlink(sym)
718+
companionContext.scope.unlink(sym) // (2)
706719
}
707720
}
708721
}
@@ -770,7 +783,7 @@ trait Namers extends MethodSynthesis {
770783
val completer =
771784
if (sym hasFlag SYNTHETIC) {
772785
if (name == nme.copy) copyMethodCompleter(tree)
773-
else if (sym hasFlag CASE) applyUnapplyMethodCompleter(tree, context)
786+
else if (settings.isScala212 && (sym hasFlag CASE)) applyUnapplyMethodCompleter(tree, context)
774787
else completerOf(tree)
775788
} else completerOf(tree)
776789

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3093,6 +3093,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
30933093
val scope = if (inBlock) context.scope else context.owner.info.decls
30943094
var newStats = new ListBuffer[Tree]
30953095
var moreToAdd = true
3096+
val retractErroneousSynthetics = settings.isScala212
3097+
30963098
while (moreToAdd) {
30973099
val initElems = scope.elems
30983100
// SI-5877 The decls of a package include decls of the package object. But we don't want to add
@@ -3101,7 +3103,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
31013103
inBlock || !context.isInPackageObject(sym, context.owner)
31023104
for (sym <- scope)
31033105
for (tree <- context.unit.synthetics get sym if shouldAdd(sym)) { // OPT: shouldAdd is usually true. Call it here, rather than in the outer loop
3104-
newStats += typedStat(tree) // might add even more synthetics to the scope
3106+
// if the completer set the IS_ERROR flag, retract the stat (currently only used by applyUnapplyMethodCompleter)
3107+
if (!(retractErroneousSynthetics && sym.initialize.hasFlag(IS_ERROR)))
3108+
newStats += typedStat(tree) // might add even more synthetics to the scope
31053109
context.unit.synthetics -= sym
31063110
}
31073111
// the type completer of a synthetic might add more synthetics. example: if the
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xsource:2.12
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xsource:2.12
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xsource:2.12

test/files/run/t10261.flags

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xsource:2.12
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)