From 2c4889e295bb6faa2278ac16f2fd9ab77e6d1706 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Wed, 29 May 2024 14:31:52 +0200 Subject: [PATCH] Allow `apply` adaptation on Selectable with Fields Just like when using a regular Selectable with refinements, this change allows: fromFlds.xs(0) to be expanded to: fromFlds.xs.apply(0) --- .../src/dotty/tools/dotc/typer/Typer.scala | 4 +-- tests/neg/named-tuple-selectable.scala | 29 +++++++++++++++++++ tests/pos/named-tuple-selectable.scala | 29 +++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 tests/neg/named-tuple-selectable.scala create mode 100644 tests/pos/named-tuple-selectable.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index e44da20814dd..24d28aa8b4e6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -821,10 +821,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // Otherwise, if the qualifier derives from class Selectable, // and the selector name matches one of the element of the `Fields` type member, - // and the selector is neither applied nor assigned to, + // and the selector is not assigned to, // expand to a typed dynamic dispatch using selectDynamic wrapped in a cast if qual.tpe.derivesFrom(defn.SelectableClass) && !isDynamicExpansion(tree) - && !pt.isInstanceOf[FunOrPolyProto] && pt != LhsProto + && pt != LhsProto then val pre = if !TypeOps.isLegalPrefix(qual.tpe) then SkolemType(qual.tpe) else qual.tpe val fieldsType = pre.select(tpnme.Fields).dealias.simplified diff --git a/tests/neg/named-tuple-selectable.scala b/tests/neg/named-tuple-selectable.scala new file mode 100644 index 000000000000..5cf7e68654ef --- /dev/null +++ b/tests/neg/named-tuple-selectable.scala @@ -0,0 +1,29 @@ +import scala.language.experimental.namedTuples + +class FromFields extends Selectable: + type Fields = (i: Int) + def selectDynamic(key: String) = + List(1, 2, 3) + +trait FromRefs extends Selectable: + def selectDynamic(key: String) = + List(1, 2, 3) + +def test( + fromFlds: FromFields, + fromRefs: FromRefs { val i: Int } +): Unit = + fromFlds.i(0) // error + fromRefs.i(0) // error + + fromFlds.i.apply(0) // error + fromRefs.i.apply(0) // error + + fromFlds.i[Int](List(1)) // error + fromRefs.i[Int](List(1)) // error + + fromFlds.i(List(1)) // error + fromRefs.i(List(1)) // error + + fromFlds.i.apply(List(1)) // error + fromRefs.i.apply(List(1)) // error diff --git a/tests/pos/named-tuple-selectable.scala b/tests/pos/named-tuple-selectable.scala new file mode 100644 index 000000000000..be5f0400e58c --- /dev/null +++ b/tests/pos/named-tuple-selectable.scala @@ -0,0 +1,29 @@ +import scala.language.experimental.namedTuples + +class FromFields extends Selectable: + type Fields = (xs: List[Int], poly: [T] => (x: List[T]) => Option[T]) + def selectDynamic(key: String) = + List(1, 2, 3) + +trait FromRefs extends Selectable: + def selectDynamic(key: String) = + List(1, 2, 3) + +def test( + fromFlds: FromFields, + fromRefs: FromRefs { val xs: List[Int]; val poly: [T] => (x: List[T]) => Option[T] } +): Unit = + fromFlds.xs(0) + fromRefs.xs(0) + + fromFlds.xs.apply(0) + fromRefs.xs.apply(0) + + fromFlds.poly[Int](List(1)): Option[Int] + fromRefs.poly[Int](List(1)): Option[Int] + + fromFlds.poly(List(1)) + fromRefs.poly(List(1)) + + fromFlds.poly.apply(List(1)) + fromRefs.poly.apply(List(1))