Skip to content

Commit 20dabed

Browse files
committed
Implement summonFrom as a replacement for implicit match
1 parent 3403879 commit 20dabed

29 files changed

+149
-139
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

+1
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ class Definitions {
220220
@tu lazy val Compiletime_constValue : Symbol = CompiletimePackageObject.requiredMethod("constValue")
221221
@tu lazy val Compiletime_constValueOpt: Symbol = CompiletimePackageObject.requiredMethod("constValueOpt")
222222
@tu lazy val Compiletime_code : Symbol = CompiletimePackageObject.requiredMethod("code")
223+
@tu lazy val Compiletime_summonFrom : Symbol = CompiletimePackageObject.requiredMethod("summonFrom")
223224
@tu lazy val CompiletimeTestingPackageObject: Symbol = ctx.requiredModule("scala.compiletime.testing.package")
224225
@tu lazy val CompiletimeTesting_typeChecks : Symbol = CompiletimeTestingPackageObject.requiredMethod("typeChecks")
225226

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

+8-1
Original file line numberDiff line numberDiff line change
@@ -890,7 +890,14 @@ trait Applications extends Compatibility {
890890
case err: ErrorType => cpy.Apply(tree)(fun1, proto.unforcedTypedArgs).withType(err)
891891
case TryDynamicCallType => typedDynamicApply(tree, pt)
892892
case _ =>
893-
if (originalProto.isDropped) fun1
893+
if originalProto.isDropped then fun1
894+
else if fun1.symbol == defn.Compiletime_summonFrom then
895+
tree.args match {
896+
case (arg @ Match(EmptyTree, cases)) :: Nil =>
897+
typed(untpd.InlineMatch(EmptyTree, cases).withSpan(arg.span), pt)
898+
case _ =>
899+
errorTree(tree, em"argument to summonFrom must be a pattern matching closure")
900+
}
894901
else
895902
tryEither {
896903
simpleApply(fun1, proto)

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

+7-8
Original file line numberDiff line numberDiff line change
@@ -796,9 +796,9 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
796796
/** Reduce an inline match
797797
* @param mtch the match tree
798798
* @param scrutinee the scrutinee expression, assumed to be pure, or
799-
* EmptyTree for a delegate match
799+
* EmptyTree for a summonFrom
800800
* @param scrutType its fully defined type, or
801-
* ImplicitScrutineeTypeRef for a delegate match
801+
* ImplicitScrutineeTypeRef for a summonFrom
802802
* @param typer The current inline typer
803803
* @return optionally, if match can be reduced to a matching case: A pair of
804804
* bindings for all pattern-bound variables and the RHS of the case.
@@ -1072,11 +1072,10 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
10721072

10731073
override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree =
10741074
constToLiteral(betaReduce(super.typedApply(tree, pt))) match {
1075-
case res: Apply
1076-
if res.symbol == defn.InternalQuoted_exprSplice &&
1077-
level == 0 &&
1078-
call.symbol.is(Macro) &&
1079-
!suppressInline =>
1075+
case res: Apply if res.symbol == defn.InternalQuoted_exprSplice
1076+
&& level == 0
1077+
&& call.symbol.is(Macro)
1078+
&& !suppressInline =>
10801079
expandMacro(res.args.head, tree.span)
10811080
case res => res
10821081
}
@@ -1115,7 +1114,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
11151114
def patStr(cdef: untpd.CaseDef) = i"case ${cdef.pat}${guardStr(cdef.guard)}"
11161115
val msg =
11171116
if (tree.selector.isEmpty)
1118-
em"""cannot reduce delegate match with
1117+
em"""cannot reduce summonFrom with
11191118
| patterns : ${tree.cases.map(patStr).mkString("\n ")}"""
11201119
else
11211120
em"""cannot reduce inline match with

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1090,7 +1090,7 @@ class Typer extends Namer
10901090
tree.selector match {
10911091
case EmptyTree =>
10921092
if (tree.isInline) {
1093-
checkInInlineContext("delegate match", tree.posd)
1093+
checkInInlineContext("summonFrom", tree.posd)
10941094
val cases1 = tree.cases.mapconserve {
10951095
case cdef @ CaseDef(pat @ Typed(Ident(nme.WILDCARD), _), _, _) =>
10961096
// case _ : T --> case evidence$n : T

library/src/scala/compiletime/package.scala

+15
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,20 @@ package object compiletime {
3737

3838
inline def constValue[T]: T = ???
3939

40+
/** Summons first given matching one of the listed cases. E.g. in
41+
*
42+
* given B { ... }
43+
*
44+
* summonFrom {
45+
* case given A => 1
46+
* case given B => 2
47+
* case given C => 3
48+
* case _ => 4
49+
* }
50+
*
51+
* the returned value would be `2`.
52+
*/
53+
inline def summonFrom(f: Nothing => Any) <: Any = ???
54+
4055
type S[X <: Int] <: Int
4156
}

library/src/scala/tasty/reflect/Core.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ trait Core {
216216
/** Tree representing a pattern match `x match { ... }` in the source code */
217217
type Match = internal.Match
218218

219-
/** Tree representing a pattern match `delegate match { ... }` in the source code */
219+
/** Tree representing a pattern match `delegate match { ... }` in the source code */ // TODO: drop
220220
type ImpliedMatch = internal.ImpliedMatch
221221

222222
/** Tree representing a try catch `try x catch { ... } finally { ... }` in the source code */

tests/neg/cannot-reduce-delegate-match.scala renamed to tests/neg/cannot-reduce-summonFrom.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
object Test {
22

33
inline def bar() =
4-
delegate match { // error
4+
compiletime.summonFrom { // error
55
case _: Int =>
66
}
77

tests/neg/implicitMatch-syntax.scala

-33
This file was deleted.

tests/neg/implicit-match-ambiguous-bind.scala renamed to tests/neg/summonFrom-ambiguous-bind.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ object `implicit-match-ambiguous-bind` {
22
case class Box[T](value: T)
33
implicit val ibox: Box[Int] = Box(0)
44
implicit val sbox: Box[String] = Box("")
5-
inline def unbox = delegate match {
5+
inline def unbox = compiletime.summonFrom {
66
case b: Box[t] => b.value // error
77
}
88
val unboxed = unbox

tests/invalid/neg/implicitMatch-ambiguous.scala renamed to tests/neg/summonFrom-ambiguous.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ object Test {
44
implicit val a1: A = new A
55
implicit val a2: A = new A
66

7-
inline def f: Any = delegate match {
7+
inline def f: Any = compiletime.summonFrom {
88
case _: A => ??? // error: ambiguous implicits
99
}
1010

tests/neg/typeclass-derivation2.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,10 @@ trait Show[T] {
210210
def show(x: T): String
211211
}
212212
object Show {
213-
import scala.compiletime.{erasedValue, error}
213+
import scala.compiletime.{erasedValue, error, summonFrom}
214214
import TypeLevel._
215215

216-
inline def tryShow[T](x: T): String = delegate match {
216+
inline def tryShow[T](x: T): String = summonFrom {
217217
case s: Show[T] => s.show(x)
218218
}
219219

tests/pending/pos/implicit-match.scala renamed to tests/pending/pos/summonFrom.scala

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
// delegate matches that bind parameters don't work yet.
2-
object `implicit-match` {
1+
// summonFroms that bind parameters don't work yet.
2+
import compiletime.summonFrom
3+
object summonFroms {
34
object invariant {
45
case class Box[T](value: T)
56
implicit val box: Box[Int] = Box(0)
6-
inline def unbox <: Any = delegate match {
7+
inline def unbox <: Any = summonFrom {
78
case b: Box[t] => b.value
89
}
910
val i: Int = unbox
@@ -14,7 +15,7 @@ object `implicit-match` {
1415
object covariant {
1516
case class Box[+T](value: T)
1617
implicit val box: Box[Int] = Box(0)
17-
inline def unbox <: Any = delegate match {
18+
inline def unbox <: Any = summonFrom {
1819
case b: Box[t] => b.value
1920
}
2021
val i: Int = unbox
@@ -25,7 +26,7 @@ object `implicit-match` {
2526
object contravariant {
2627
case class TrashCan[-T](trash: T => Unit)
2728
implicit val trashCan: TrashCan[Int] = TrashCan { i => ; }
28-
inline def trash <: Nothing => Unit = delegate match {
29+
inline def trash <: Nothing => Unit = summonFrom {
2930
case c: TrashCan[t] => c.trash
3031
}
3132
val t1: Int => Unit = trash

tests/pos-special/typeclass-scaling.scala

+7-6
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ object datatypes {
207207
}
208208

209209
object typeclasses {
210+
import compiletime.summonFrom
210211
// A typeclass
211212
trait Eq[T] {
212213
def eql(x: T, y: T): Boolean
@@ -217,7 +218,7 @@ object typeclasses {
217218
import compiletime._
218219
import scala.deriving._
219220

220-
inline def tryEql[TT](x: TT, y: TT): Boolean = delegate match {
221+
inline def tryEql[TT](x: TT, y: TT): Boolean = summonFrom {
221222
case eq: Eq[TT] => eq.eql(x, y)
222223
}
223224

@@ -237,7 +238,7 @@ object typeclasses {
237238
inline erasedValue[Alts] match {
238239
case _: (alt *: alts1) =>
239240
if (ord == n)
240-
delegate match {
241+
summonFrom {
241242
case m: Mirror.ProductOf[`alt`] => eqlElems[m.MirroredElemTypes](0)(x, y)
242243
}
243244
else eqlCases[alts1](n + 1)(x, y, ord)
@@ -274,7 +275,7 @@ object typeclasses {
274275

275276
def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1)
276277

277-
inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = delegate match {
278+
inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = summonFrom {
278279
case pkl: Pickler[T] => pkl.pickle(buf, x)
279280
}
280281

@@ -290,14 +291,14 @@ object typeclasses {
290291
inline erasedValue[Alts] match {
291292
case _: (alt *: alts1) =>
292293
if (ord == n)
293-
delegate match {
294+
summonFrom {
294295
case m: Mirror.ProductOf[`alt`] => pickleElems[m.MirroredElemTypes](0)(buf, x)
295296
}
296297
else pickleCases[alts1](n + 1)(buf, x, ord)
297298
case _: Unit =>
298299
}
299300

300-
inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = delegate match {
301+
inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = summonFrom {
301302
case pkl: Pickler[T] => pkl.unpickle(buf)
302303
}
303304

@@ -324,7 +325,7 @@ object typeclasses {
324325
inline erasedValue[Alts] match {
325326
case _: (alt *: alts1) =>
326327
if (ord == n)
327-
delegate match {
328+
summonFrom {
328329
case m: Mirror.ProductOf[`alt` & T] =>
329330
unpickleCase[`alt` & T, m.MirroredElemTypes](buf, m)
330331
}

tests/pos/i5938.scala

+9-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1+
import compiletime.summonFrom
2+
13
trait Link[T, A]
24

3-
inline def link[T] = delegate match {
4-
case _: Link[T, s] =>
5-
delegate match {
6-
case stuff: s => stuff
7-
}
8-
}
5+
inline def link[T] =
6+
summonFrom {
7+
case _: Link[T, s] =>
8+
summonFrom {
9+
case stuff: s => stuff
10+
}
11+
}
912

1013
class Foo
1114
object Foo {

tests/pos/i6014-gadt.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ object Test1 {
1010
}
1111

1212
object Test2 {
13-
inline def summon[T] = delegate match {
13+
inline def summon[T] = summonFrom {
1414
case t: T => t
1515
}
1616

@@ -25,7 +25,7 @@ object Test2 {
2525
}
2626

2727
object Test3 {
28-
inline def summon[T] = delegate match {
28+
inline def summon[T] = summonFrom {
2929
case t: T => t
3030
}
3131

@@ -42,7 +42,7 @@ object Test3 {
4242
}
4343

4444
object Test4 {
45-
inline def summon[T] = delegate match {
45+
inline def summon[T] = summonFrom {
4646
case t: T => t
4747
}
4848

tests/pos/implicit-match-and-inline-match.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ object `implicit-match-and-inline-match` {
55
implicit val ibox: Box[Int] = Box(0)
66

77
object a {
8-
inline def isTheBoxInScopeAnInt = delegate match {
8+
inline def isTheBoxInScopeAnInt = summonFrom {
99
case _: Box[t] => inline erasedValue[t] match {
1010
case _: Int => true
1111
}
@@ -14,7 +14,7 @@ object `implicit-match-and-inline-match` {
1414
}
1515

1616
object b {
17-
inline def isTheBoxInScopeAnInt = delegate match {
17+
inline def isTheBoxInScopeAnInt = summonFrom {
1818
case _: Box[t] => inline 0 match {
1919
case _: t => true
2020
}

tests/pos/implicit-match-nested.scala

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
object `implicit-match-nested` {
2+
import compiletime.summonFrom
3+
24
case class A[T]()
35
case class B[T]()
46

57
implicit val a: A[Int] = A[Int]()
68
implicit val b1: B[Int] = B[Int]()
79
implicit val b2: B[String] = B[String]()
810

9-
inline def locateB <: B[_] = delegate match {
10-
case _: A[t] => delegate match {
11-
case b: B[`t`] => b
11+
inline def locateB <: B[_] =
12+
summonFrom {
13+
case _: A[t] =>
14+
summonFrom {
15+
case b: B[`t`] => b
16+
}
1217
}
13-
}
1418

1519
locateB
1620
}

tests/pos/inline-separate/A_1.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
object A {
2-
inline def summon[T] = delegate match {
2+
inline def summon[T] = compiletime.summonFrom {
33
case t: T => t
44
}
55
}

0 commit comments

Comments
 (0)