Skip to content

Improve error message for inline vals with non-constants #11856

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 2 commits into from
Mar 23, 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
10 changes: 7 additions & 3 deletions compiler/src/dotty/tools/dotc/transform/InlineVals.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package transform

import dotty.tools.dotc.core.Contexts._
import dotty.tools.dotc.core.Decorators._
import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.core.Flags._
import dotty.tools.dotc.core.Types._
import dotty.tools.dotc.transform.MegaPhase.MiniPhase
Expand Down Expand Up @@ -37,7 +38,10 @@ class InlineVals extends MiniPhase:
if !isPureExpr(rhs) then
val details = if enclosingInlineds.isEmpty then "" else em"but was: $rhs"
report.error(s"inline value must be pure$details", rhs.srcPos)
case _ =>
val pos = if tpt.span.isZeroExtent then rhs.srcPos else tpt.srcPos
report.error(em"inline value must have a literal constant type", pos)
case tp =>
if tp.derivesFrom(defn.StringClass) || defn.ScalaValueClasses().exists(tp.derivesFrom(_)) then
val pos = if tpt.span.isZeroExtent then rhs.srcPos else tpt.srcPos
report.error(em"inline value must have a literal constant type", pos)
else
report.error(em"inline value must contain a literal constant value.\n\nTo inline more complex types consider using `inline def`", rhs)
}
10 changes: 6 additions & 4 deletions library/src/scala/quoted/FromExpr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,13 @@ object FromExpr {
def unapply(expr: Expr[T])(using Quotes) =
import quotes.reflect._
def rec(tree: Term): Option[T] = tree match {
case Literal(c) if c.value != null => Some(c.value.asInstanceOf[T])
case Block(Nil, e) => rec(e)
case Block(stats, e) => if stats.isEmpty then rec(e) else None
case Inlined(_, bindings, e) => if bindings.isEmpty then rec(e) else None
case Typed(e, _) => rec(e)
case Inlined(_, Nil, e) => rec(e)
case _ => None
case _ =>
tree.tpe.widenTermRefByName match
case ConstantType(c) => Some(c.value.asInstanceOf[T])
case _ => None
}
rec(expr.asTerm)
}
Expand Down
24 changes: 24 additions & 0 deletions tests/neg/i11854.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
-- Error: tests/neg/i11854.scala:4:14 ----------------------------------------------------------------------------------
4 |inline val j: Int = 2 // error
| ^^^
| inline value must have a literal constant type
-- Error: tests/neg/i11854.scala:5:14 ----------------------------------------------------------------------------------
5 |inline val b: Boolean = true // error
| ^^^^^^^
| inline value must have a literal constant type
-- Error: tests/neg/i11854.scala:6:14 ----------------------------------------------------------------------------------
6 |inline val s: String = "" // error
| ^^^^^^
| inline value must have a literal constant type
-- Error: tests/neg/i11854.scala:7:18 ----------------------------------------------------------------------------------
7 |inline val bagA = new Bag(Seq('a', 'b', 'c')) // error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
| inline value must contain a literal constant value.
|
| To inline more complex types consider using `inline def`
-- Error: tests/neg/i11854.scala:8:23 ----------------------------------------------------------------------------------
8 |inline val bagB: Bag = new Bag(Seq('a', 'b', 'c')) // error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
| inline value must contain a literal constant value.
|
| To inline more complex types consider using `inline def`
4 changes: 2 additions & 2 deletions tests/run-macros/expr-map-1.check
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ kcolb
neht
esle
lav
vals
slav
fed
defs
sfed
fed
rab
yrt
Expand Down
4 changes: 2 additions & 2 deletions tests/run-macros/expr-map-1/Test_2.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ object Test {

rewrite {
val s: "vals" = "vals"
println(s) // prints "foo" not "oof"
println(s)
}

rewrite {
Expand All @@ -41,7 +41,7 @@ object Test {

rewrite {
def s: "defs" = "defs"
println(s) // prints "foo" not "oof"
println(s)
}

rewrite {
Expand Down
8 changes: 8 additions & 0 deletions tests/run-macros/i11856/Main_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
@main def Test: Unit =
inline def str1 = "Hello, "
inline val str2 = "Scala 3"
println(Str.concat(str1, str2))

inline def i1 = 1
inline val i2 = 2
println(I.sum(i1, i2))
19 changes: 19 additions & 0 deletions tests/run-macros/i11856/Test_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import scala.quoted.*

object Str:
inline def concat(inline a: String, inline b: String): String =
${ evalConcat('a, 'b) }

def evalConcat(expra: Expr[String], exprb: Expr[String])(using Quotes): Expr[String] =
val a = expra.valueOrError
val b = exprb.valueOrError
Expr(a ++ b)

object I:
inline def sum(inline a: Int, inline b: Int): Int =
${ evalConcat('a, 'b) }

def evalConcat(expra: Expr[Int], exprb: Expr[Int])(using Quotes): Expr[Int] =
val a = expra.valueOrError
val b = exprb.valueOrError
Expr(a + b)