From aa613d8159b88339c841d9d75235f4f6194c0185 Mon Sep 17 00:00:00 2001 From: odersky Date: Fri, 3 Jun 2022 19:32:44 +0200 Subject: [PATCH] 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 --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 9 +++++---- tests/pos/i15347.scala | 8 ++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 tests/pos/i15347.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 9b7db49f8843..bdc6a03f6949 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -176,8 +176,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer * In addition: * - if we are in a constructor of a pattern, we ignore all definitions - * which are methods and not accessors (note: if we don't do that - * case x :: xs in class List would return the :: method). + * which are parameterized (including nullary) methods and not accessors + * (note: if we don't do that case x :: xs in class List would return the :: method). * - Members of the empty package can be accessed only from within the empty package. * Note: it would be cleaner to never nest package definitions in empty package definitions, * 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 * tools, we did not want to take that step. */ def qualifies(denot: Denotation): Boolean = + def isRealMethod(sd: SingleDenotation): Boolean = + sd.symbol.is(Method, butNot = Accessor) && !sd.info.isParameterless reallyExists(denot) - && (!pt.isInstanceOf[UnapplySelectionProto] - || denot.hasAltWith(sd => !sd.symbol.is(Method, butNot = Accessor))) + && (!pt.isInstanceOf[UnapplySelectionProto] || denot.hasAltWith(!isRealMethod(_))) && !denot.symbol.is(PackageClass) && { var owner = denot.symbol.maybeOwner diff --git a/tests/pos/i15347.scala b/tests/pos/i15347.scala new file mode 100644 index 000000000000..ac2078eb6a11 --- /dev/null +++ b/tests/pos/i15347.scala @@ -0,0 +1,8 @@ +object Test: + enum E[T]: + case A(i: Int) + + export E.* + + def f(x: E[Int]) = x match + case A(i) => i