Skip to content

Special-case summonInline to yield implicitNotFound errors #12428

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ class Definitions {
@tu lazy val Compiletime_constValue : Symbol = CompiletimePackageClass.requiredMethod("constValue")
@tu lazy val Compiletime_constValueOpt: Symbol = CompiletimePackageClass.requiredMethod("constValueOpt")
@tu lazy val Compiletime_summonFrom : Symbol = CompiletimePackageClass.requiredMethod("summonFrom")
@tu lazy val Compiletime_summonInline : Symbol = CompiletimePackageClass.requiredMethod("summonInline")
@tu lazy val CompiletimeTestingPackage: Symbol = requiredPackage("scala.compiletime.testing")
@tu lazy val CompiletimeTesting_typeChecks: Symbol = CompiletimeTestingPackage.requiredMethod("typeChecks")
@tu lazy val CompiletimeTesting_typeCheckErrors: Symbol = CompiletimeTestingPackage.requiredMethod("typeCheckErrors")
Expand Down
15 changes: 14 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -701,7 +701,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
return Intrinsics.codeOf(arg, call.srcPos)
case _ =>

// Special handling of `constValue[T]` and `constValueOpt[T]`
// Special handling of `constValue[T]`, `constValueOpt[T], and summonInline[T]`
if (callTypeArgs.length == 1)
if (inlinedMethod == defn.Compiletime_constValue) {
val constVal = tryConstValue
Expand All @@ -718,6 +718,19 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
else New(defn.SomeClass.typeRef.appliedTo(constVal.tpe), constVal :: Nil)
)
}
else if (inlinedMethod == defn.Compiletime_summonInline) {
def searchImplicit(tpt: Tree) =
val evTyper = new Typer
val evCtx = ctx.fresh.setTyper(evTyper)
val evidence = evTyper.inferImplicitArg(tpt.tpe, tpt.span)(using evCtx)
evidence.tpe match
case fail: Implicits.SearchFailureType =>
val msg = evTyper.missingArgMsg(evidence, tpt.tpe, "")
errorTree(tpt, em"$msg")
case _ =>
evidence
return searchImplicit(callTypeArgs.head)
}

def paramTypess(call: Tree, acc: List[List[Type]]): List[List[Type]] = call match
case Apply(fn, args) =>
Expand Down
27 changes: 23 additions & 4 deletions docs/docs/reference/metaprogramming/compiletime-ops.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,11 +261,30 @@ inline def f: Any = summonFrom {
## `summonInline`

The shorthand `summonInline` provides a simple way to write a `summon` that is delayed until the call is inlined.

Unlike `summonFrom`, `summonInline` also yields the implicit-not-found error, if a given instance of the summoned
type is not found.
```scala
transparent inline def summonInline[T]: T = summonFrom {
case t: T => t
}
import scala.compiletime.summonInline
import scala.annotation.implicitNotFound

@implicitNotFound("Missing One")
trait Missing1

@implicitNotFound("Missing Two")
trait Missing2

trait NotMissing
given NotMissing = ???

transparent inline def summonInlineCheck[T <: Int](inline t : T) : Any =
inline t match
case 1 => summonInline[Missing1]
case 2 => summonInline[Missing2]
case _ => summonInline[NotMissing]

val missing1 = summonInlineCheck(1) // error: Missing One
val missing2 = summonInlineCheck(2) // error: Missing Two
val notMissing : NotMissing = summonInlineCheck(3)
```

## Reference
Expand Down
5 changes: 2 additions & 3 deletions library/src/scala/compiletime/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,8 @@ transparent inline def summonFrom[T](f: Nothing => T): T =
* @tparam T the type of the value to be summoned
* @return the given value typed as the provided type parameter
*/
transparent inline def summonInline[T]: T = summonFrom {
case t: T => t
}
transparent inline def summonInline[T]: T =
error("Compiler bug: `summonInline` was not evaluated by the compiler")

/** Given a tuple T, summons each of its member types and returns them in
* a Tuple.
Expand Down
10 changes: 10 additions & 0 deletions tests/neg/summonInline.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-- Error: tests/neg/summonInline.scala:19:32 ---------------------------------------------------------------------------
19 |val missing1 = summonInlineCheck(1) // error
| ^^^^^^^^^^^^^^^^^^^^
| Missing One
| This location contains code that was inlined from summonInline.scala:15
-- Error: tests/neg/summonInline.scala:20:32 ---------------------------------------------------------------------------
20 |val missing2 = summonInlineCheck(2) // error
| ^^^^^^^^^^^^^^^^^^^^
| Missing Two
| This location contains code that was inlined from summonInline.scala:16
21 changes: 21 additions & 0 deletions tests/neg/summonInline.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import scala.compiletime.summonInline
import scala.annotation.implicitNotFound

@implicitNotFound("Missing One")
trait Missing1

@implicitNotFound("Missing Two")
trait Missing2

trait NotMissing
given NotMissing = ???

transparent inline def summonInlineCheck[T <: Int](inline t : T) : Any =
inline t match
case 1 => summonInline[Missing1]
case 2 => summonInline[Missing2]
case _ => summonInline[NotMissing]

val missing1 = summonInlineCheck(1) // error
val missing2 = summonInlineCheck(2) // error
val notMissing : NotMissing = summonInlineCheck(3)