Skip to content

Commit ff476f7

Browse files
Merge pull request #8483 from dotty-staging/add-compiletime-summoninline
Add scala.compiletime.summonInline
2 parents ad0f495 + e5e0f9b commit ff476f7

17 files changed

+76
-124
lines changed

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ class CompilationTests extends ParallelTesting {
154154
compileDir("tests/neg-custom-args/adhoc-extension", defaultOptions.and("-strict", "-feature", "-Xfatal-warnings")),
155155
compileFile("tests/neg/i7575.scala", defaultOptions.and("-language:_")),
156156
compileFile("tests/neg-custom-args/kind-projector.scala", defaultOptions.and("-Ykind-projector")),
157+
compileFile("tests/neg-custom-args/typeclass-derivation2.scala", defaultOptions.and("-Yerased-terms")),
157158
).checkExpectedErrors()
158159
}
159160

docs/docs/reference/contextual/derivation.md

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ worked out example of such a library, see [shapeless 3](https://github.com/miles
165165
#### How to write a type class `derived` method using low level mechanisms
166166

167167
The low-level method we will use to implement a type class `derived` method in this example exploits three new
168-
type-level constructs in Dotty: inline methods, inline matches, and implicit searches via `summonFrom`. Given this definition of the
168+
type-level constructs in Dotty: inline methods, inline matches, and implicit searches via `summonInline` or `summonFrom`. Given this definition of the
169169
`Eq` type class,
170170

171171

@@ -194,17 +194,14 @@ call sites (for instance the compiler generated instance definitions in the comp
194194

195195
The body of this method (1) first materializes the `Eq` instances for all the child types of type the instance is
196196
being derived for. This is either all the branches of a sum type or all the fields of a product type. The
197-
implementation of `summonAll` is `inline` and uses Dotty's `summonFrom` construct to collect the instances as a
197+
implementation of `summonAll` is `inline` and uses Dotty's `summonInline` construct to collect the instances as a
198198
`List`,
199199

200200
```scala
201-
inline def summonAll[T]: T = summonFrom {
202-
case t: T => t
203-
}
204201

205202
inline def summonAll[T <: Tuple]: List[Eq[_]] = inline erasedValue[T] match {
206203
case _: Unit => Nil
207-
case _: (t *: ts) => summon[Eq[t]] :: summonAll[ts]
204+
case _: (t *: ts) => summonInline[Eq[t]] :: summonAll[ts]
208205
}
209206
```
210207

@@ -244,15 +241,11 @@ Pulling this all together we have the following complete implementation,
244241

245242
```scala
246243
import scala.deriving._
247-
import scala.compiletime.{erasedValue, summonFrom}
248-
249-
inline def summon[T]: T = summonFrom {
250-
case t: T => t
251-
}
244+
import scala.compiletime.{erasedValue, summonInline}
252245

253246
inline def summonAll[T <: Tuple]: List[Eq[_]] = inline erasedValue[T] match {
254247
case _: Unit => Nil
255-
case _: (t *: ts) => summon[Eq[t]] :: summonAll[ts]
248+
case _: (t *: ts) => summonInline[Eq[t]] :: summonAll[ts]
256249
}
257250

258251
trait Eq[T] {

docs/docs/reference/metaprogramming/inline.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,9 @@ power(expr, 10)
121121
```
122122

123123
Parameters of inline methods can have an `inline` modifier as well. This means
124-
that actual arguments to these parameters will be inlined in the body of the
125-
`inline def`. `inline` parameters have call semantics equivalent to by-name parameters
126-
but allow for duplication of the code in the argument. It is usually useful when constant
124+
that actual arguments to these parameters will be inlined in the body of the
125+
`inline def`. `inline` parameters have call semantics equivalent to by-name parameters
126+
but allow for duplication of the code in the argument. It is usually useful when constant
127127
values need to be propagated to allow further optimizations/reductions.
128128

129129
The following example shows the difference in translation between by-value, by-name and `inline`
@@ -552,6 +552,15 @@ inline def f: Any = summonFrom {
552552
}
553553
```
554554

555+
## `summonInline`
556+
557+
The shorthand `summonInline` provides a simple way to write a `summon` that is delayed until the call is inlined.
558+
```scala
559+
inline def summonInline[T] <: T = summonFrom {
560+
case t: T => t
561+
}
562+
```
563+
555564
### Reference
556565

557566
For more info, see [PR #4768](https://github.com/lampepfl/dotty/pull/4768),

library/src/scala/compiletime/package.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@ package object compiletime {
5252
*/
5353
inline def summonFrom[T](f: Nothing => T) <: T = ???
5454

55+
56+
/** Summon a given value of type `T`. Usually, the argument is not passed explicitly.
57+
* The summoning is delayed until the call has been fully inlined.
58+
*
59+
* @tparam T the type of the value to be summoned
60+
* @return the given value typed as the provided type parameter
61+
*/
62+
inline def summonInline[T] <: T = summonFrom {
63+
case t: T => t
64+
}
65+
66+
5567
/** Succesor of a natural number where zero is the type 0 and successors are reduced as if the definition was
5668
*
5769
* type S[N <: Int] <: Int = N match {

tests/neg-custom-args/typeclass-derivation2.scala

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

216-
inline def tryShow[T](x: T): String = summonFrom {
217-
case s: Show[T] => s.show(x)
218-
}
216+
inline def tryShow[T](x: T): String = summonInline[Show[T]].show(x)
219217

220218
inline def showElems[Elems <: Tuple](elems: Mirror, n: Int): List[String] =
221219
inline erasedValue[Elems] match {

tests/pending/pos/summonFrom.scala

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ object summonFroms {
44
object invariant {
55
case class Box[T](value: T)
66
implicit val box: Box[Int] = Box(0)
7-
inline def unbox <: Any = summonFrom {
8-
case b: Box[t] => b.value
9-
}
7+
inline def unbox <: Any = summonInline[Box[t]].value
108
val i: Int = unbox
119
val i2 = unbox
1210
val i3: Int = i2
@@ -15,9 +13,7 @@ object summonFroms {
1513
object covariant {
1614
case class Box[+T](value: T)
1715
implicit val box: Box[Int] = Box(0)
18-
inline def unbox <: Any = summonFrom {
19-
case b: Box[t] => b.value
20-
}
16+
inline def unbox <: Any = summonInline[Box[t]].value
2117
val i: Int = unbox
2218
val i2 = unbox
2319
val i3: Int = i2
@@ -26,9 +22,7 @@ object summonFroms {
2622
object contravariant {
2723
case class TrashCan[-T](trash: T => Unit)
2824
implicit val trashCan: TrashCan[Int] = TrashCan { i => ; }
29-
inline def trash <: Nothing => Unit = summonFrom {
30-
case c: TrashCan[t] => c.trash
31-
}
25+
inline def trash <: Nothing => Unit = summonInline[TrashCan[t]].trash
3226
val t1: Int => Unit = trash
3327
val t2 = trash
3428
val t3: Int => Unit = t2

tests/pos-macros/i7853/JsonEncoder_1.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,11 @@ trait JsonEncoder[T] {
66
}
77

88
object JsonEncoder {
9-
import scala.compiletime.{erasedValue, summonFrom}
9+
import scala.compiletime.{erasedValue, summonInline}
1010
import compiletime._
1111
import scala.deriving._
1212

13-
inline def encodeElem[T](elem: T): String = summonFrom {
14-
case encoder: JsonEncoder[T] => encoder.encode(elem)
15-
}
13+
inline def encodeElem[T](elem: T): String = summonInline[JsonEncoder[T]].encode(elem)
1614

1715
inline def encodeElems[Elems <: Tuple](idx: Int)(value: Any): List[String] =
1816
inline erasedValue[Elems] match {

tests/pos-macros/i7853/SummonJsonEncoderTest_2.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import scala.deriving._
22
import scala.quoted._
3-
4-
import scala.compiletime.{erasedValue, summonFrom}
53
import JsonEncoder.{given _, _}
64

75
object SummonJsonEncoderTest {

tests/pos-special/typeclass-scaling.scala

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,7 @@ object typeclasses {
218218
import compiletime._
219219
import scala.deriving._
220220

221-
inline def tryEql[TT](x: TT, y: TT): Boolean = summonFrom {
222-
case eq: Eq[TT] => eq.eql(x, y)
223-
}
221+
inline def tryEql[TT](x: TT, y: TT): Boolean = summonInline[Eq[TT]].eql(x, y)
224222

225223
inline def eqlElems[Elems <: Tuple](n: Int)(x: Any, y: Any): Boolean =
226224
inline erasedValue[Elems] match {
@@ -275,9 +273,7 @@ object typeclasses {
275273

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

278-
inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = summonFrom {
279-
case pkl: Pickler[T] => pkl.pickle(buf, x)
280-
}
276+
inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = summonInline[Pickler[T]].pickle(buf, x)
281277

282278
inline def pickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], x: Any): Unit =
283279
inline erasedValue[Elems] match {
@@ -298,9 +294,7 @@ object typeclasses {
298294
case _: Unit =>
299295
}
300296

301-
inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = summonFrom {
302-
case pkl: Pickler[T] => pkl.unpickle(buf)
303-
}
297+
inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = summonInline[Pickler[T]].unpickle(buf)
304298

305299
inline def unpickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], elems: ArrayProduct): Unit =
306300
inline erasedValue[Elems] match {

tests/run-custom-args/typeclass-derivation2.scala

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,7 @@ object Eq {
227227
import scala.compiletime.{erasedValue, error, summonFrom}
228228
import TypeLevel._
229229

230-
inline def tryEql[T](x: T, y: T) = summonFrom {
231-
case eq: Eq[T] => eq.eql(x, y)
232-
}
230+
inline def tryEql[T](x: T, y: T) = summonInline[Eq[T]].eql(x, y)
233231

234232
inline def eqlElems[Elems <: Tuple](xm: Mirror, ym: Mirror, n: Int): Boolean =
235233
inline erasedValue[Elems] match {
@@ -283,14 +281,12 @@ trait Pickler[T] {
283281
}
284282

285283
object Pickler {
286-
import scala.compiletime.{erasedValue, constValue, error, summonFrom}
284+
import scala.compiletime.{erasedValue, constValue, error, summonInline}
287285
import TypeLevel._
288286

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

291-
inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = summonFrom {
292-
case pkl: Pickler[T] => pkl.pickle(buf, x)
293-
}
289+
inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = summonInline[Pickler[T]].pickle(buf, x)
294290

295291
inline def pickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Mirror, n: Int): Unit =
296292
inline erasedValue[Elems] match {
@@ -321,9 +317,7 @@ object Pickler {
321317
case _: Unit =>
322318
}
323319

324-
inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = summonFrom {
325-
case pkl: Pickler[T] => pkl.unpickle(buf)
326-
}
320+
inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = summonInline[Pickler[T]].unpickle(buf)
327321

328322
inline def unpickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Array[AnyRef], n: Int): Unit =
329323
inline erasedValue[Elems] match {
@@ -382,9 +376,7 @@ object Show {
382376
import scala.compiletime.{erasedValue, error, summonFrom}
383377
import TypeLevel._
384378

385-
inline def tryShow[T](x: T): String = summonFrom {
386-
case s: Show[T] => s.show(x)
387-
}
379+
inline def tryShow[T](x: T): String = summonInline[Show[T]].show(x)
388380

389381
inline def showElems[Elems <: Tuple](elems: Mirror, n: Int): List[String] =
390382
inline erasedValue[Elems] match {

tests/run-custom-args/typeclass-derivation2c.scala

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import scala.collection.mutable
22
import scala.annotation.tailrec
3-
import scala.compiletime.summonFrom
3+
import scala.compiletime.summonInline
44

55
// Simulation of an alternative typeclass derivation scheme proposed in #6153
66

@@ -55,9 +55,7 @@ object Deriving {
5555
type CaseLabel <: String
5656

5757
/** The represented value */
58-
inline def singletonValue = summonFrom {
59-
case ev: ValueOf[T] => ev.value
60-
}
58+
inline def singletonValue = summonInline[ValueOf[T]].value
6159
}
6260
}
6361

@@ -213,9 +211,7 @@ trait Eq[T] {
213211
object Eq {
214212
import scala.compiletime.erasedValue
215213

216-
inline def tryEql[T](x: T, y: T) = summonFrom {
217-
case eq: Eq[T] => eq.eql(x, y)
218-
}
214+
inline def tryEql[T](x: T, y: T) = summonInline[Eq[T]].eql(x, y)
219215

220216
inline def eqlElems[Elems <: Tuple](n: Int)(x: Any, y: Any): Boolean =
221217
inline erasedValue[Elems] match {
@@ -268,9 +264,8 @@ object Pickler {
268264

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

271-
inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = summonFrom {
272-
case pkl: Pickler[T] => pkl.pickle(buf, x)
273-
}
267+
inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit =
268+
summonInline[Pickler[T]].pickle(buf, x)
274269

275270
inline def pickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], x: Any): Unit =
276271
inline erasedValue[Elems] match {
@@ -293,9 +288,7 @@ object Pickler {
293288
}
294289
else pickleCases[T](g, n + 1)(buf, x, ord)
295290

296-
inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = summonFrom {
297-
case pkl: Pickler[T] => pkl.unpickle(buf)
298-
}
291+
inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = summonInline[Pickler[T]].unpickle(buf)
299292

300293
inline def unpickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], elems: Array[AnyRef]): Unit =
301294
inline erasedValue[Elems] match {
@@ -358,9 +351,7 @@ trait Show[T] {
358351
object Show {
359352
import scala.compiletime.{erasedValue, constValue}
360353

361-
inline def tryShow[T](x: T): String = summonFrom {
362-
case s: Show[T] => s.show(x)
363-
}
354+
inline def tryShow[T](x: T): String = summonInline[Show[T]].show(x)
364355

365356
inline def showElems[Elems <: Tuple, Labels <: Tuple](n: Int)(x: Any): List[String] =
366357
inline erasedValue[Elems] match {

tests/run/typeclass-derivation-doc-example.scala

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
import scala.deriving._
2-
import scala.compiletime.{erasedValue, summonFrom}
3-
4-
inline def summon[T]: T = summonFrom {
5-
case t: T => t
6-
}
2+
import scala.compiletime.{erasedValue, summonInline}
73

84
inline def summonAll[T <: Tuple]: List[Eq[_]] = inline erasedValue[T] match {
95
case _: Unit => Nil
10-
case _: (t *: ts) => summon[Eq[t]] :: summonAll[ts]
6+
case _: (t *: ts) => summonInline[Eq[t]] :: summonAll[ts]
117
}
128

139
trait Eq[T] {

tests/run/typeclass-derivation1.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,7 @@ object Deriving {
3838
}
3939

4040
object Eq {
41-
inline def tryEq[T](x: T, y: T) = summonFrom {
42-
case eq: Eq[T] => eq.equals(x, y)
43-
}
41+
inline def tryEq[T](x: T, y: T) = summonInline[Eq[T]].equals(x, y)
4442

4543
inline def deriveForSum[Alts <: Tuple](x: Any, y: Any): Boolean = inline erasedValue[Alts] match {
4644
case _: (alt *: alts1) =>

0 commit comments

Comments
 (0)