Skip to content

Commit aa613d8

Browse files
committed
Refine criterion when to skip identifiers in pattern constructors
There's a strange and almost forgotten rule that disqualifies method symbols from direct lookup when the identifier is the constructor of a pattern. This is done to make code like this work: ``` class List[T]: def :: (that: T): List[T] def f(...) = this match case x :: xs => ... object `::`: def unapply... ``` Without the rule, the `::` in the pattern would resolve to the `::` method in `List` which does not have an `unapply`. We need to skip that method to get to the outer `::` object. The rule plays badly with export forwarders, which are methods, and therefore were ineligible for pattern constructurs. We now change the rule so that methods are also accepted as unqualified `unapply` prefixes as long as they are parameterless. Fixes #15347
1 parent 78dbc59 commit aa613d8

File tree

2 files changed

+13
-4
lines changed

2 files changed

+13
-4
lines changed

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
176176
177177
* In addition:
178178
* - if we are in a constructor of a pattern, we ignore all definitions
179-
* which are methods and not accessors (note: if we don't do that
180-
* case x :: xs in class List would return the :: method).
179+
* which are parameterized (including nullary) methods and not accessors
180+
* (note: if we don't do that case x :: xs in class List would return the :: method).
181181
* - Members of the empty package can be accessed only from within the empty package.
182182
* Note: it would be cleaner to never nest package definitions in empty package definitions,
183183
* but then we'd have to give up the fiction that a compilation unit consists of
@@ -187,9 +187,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
187187
* tools, we did not want to take that step.
188188
*/
189189
def qualifies(denot: Denotation): Boolean =
190+
def isRealMethod(sd: SingleDenotation): Boolean =
191+
sd.symbol.is(Method, butNot = Accessor) && !sd.info.isParameterless
190192
reallyExists(denot)
191-
&& (!pt.isInstanceOf[UnapplySelectionProto]
192-
|| denot.hasAltWith(sd => !sd.symbol.is(Method, butNot = Accessor)))
193+
&& (!pt.isInstanceOf[UnapplySelectionProto] || denot.hasAltWith(!isRealMethod(_)))
193194
&& !denot.symbol.is(PackageClass)
194195
&& {
195196
var owner = denot.symbol.maybeOwner

tests/pos/i15347.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
object Test:
2+
enum E[T]:
3+
case A(i: Int)
4+
5+
export E.*
6+
7+
def f(x: E[Int]) = x match
8+
case A(i) => i

0 commit comments

Comments
 (0)