diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index ea6652705e60..0034e8b2d9ac 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -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") diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 4a8f92f30407..b611c25ca40e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -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 @@ -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) => diff --git a/docs/docs/reference/metaprogramming/compiletime-ops.md b/docs/docs/reference/metaprogramming/compiletime-ops.md index 189ee704ca57..03920ba4be41 100644 --- a/docs/docs/reference/metaprogramming/compiletime-ops.md +++ b/docs/docs/reference/metaprogramming/compiletime-ops.md @@ -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 diff --git a/library/src/scala/compiletime/package.scala b/library/src/scala/compiletime/package.scala index 84ac4fde4e29..b465a8b5e01e 100644 --- a/library/src/scala/compiletime/package.scala +++ b/library/src/scala/compiletime/package.scala @@ -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. diff --git a/tests/neg/summonInline.check b/tests/neg/summonInline.check new file mode 100755 index 000000000000..d118299d6dc5 --- /dev/null +++ b/tests/neg/summonInline.check @@ -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 diff --git a/tests/neg/summonInline.scala b/tests/neg/summonInline.scala new file mode 100755 index 000000000000..463e506bdbdc --- /dev/null +++ b/tests/neg/summonInline.scala @@ -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) \ No newline at end of file