Skip to content

Commit 9396d57

Browse files
Merge pull request #11856 from dotty-staging/fix-11854
Improve error message for `inline val`s with non-constants
2 parents 8ae5611 + 2e30df7 commit 9396d57

File tree

7 files changed

+68
-11
lines changed

7 files changed

+68
-11
lines changed

compiler/src/dotty/tools/dotc/transform/InlineVals.scala

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package transform
44

55
import dotty.tools.dotc.core.Contexts._
66
import dotty.tools.dotc.core.Decorators._
7+
import dotty.tools.dotc.core.Symbols._
78
import dotty.tools.dotc.core.Flags._
89
import dotty.tools.dotc.core.Types._
910
import dotty.tools.dotc.transform.MegaPhase.MiniPhase
@@ -37,7 +38,10 @@ class InlineVals extends MiniPhase:
3738
if !isPureExpr(rhs) then
3839
val details = if enclosingInlineds.isEmpty then "" else em"but was: $rhs"
3940
report.error(s"inline value must be pure$details", rhs.srcPos)
40-
case _ =>
41-
val pos = if tpt.span.isZeroExtent then rhs.srcPos else tpt.srcPos
42-
report.error(em"inline value must have a literal constant type", pos)
41+
case tp =>
42+
if tp.derivesFrom(defn.StringClass) || defn.ScalaValueClasses().exists(tp.derivesFrom(_)) then
43+
val pos = if tpt.span.isZeroExtent then rhs.srcPos else tpt.srcPos
44+
report.error(em"inline value must have a literal constant type", pos)
45+
else
46+
report.error(em"inline value must contain a literal constant value.\n\nTo inline more complex types consider using `inline def`", rhs)
4347
}

library/src/scala/quoted/FromExpr.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,13 @@ object FromExpr {
8484
def unapply(expr: Expr[T])(using Quotes) =
8585
import quotes.reflect._
8686
def rec(tree: Term): Option[T] = tree match {
87-
case Literal(c) if c.value != null => Some(c.value.asInstanceOf[T])
88-
case Block(Nil, e) => rec(e)
87+
case Block(stats, e) => if stats.isEmpty then rec(e) else None
88+
case Inlined(_, bindings, e) => if bindings.isEmpty then rec(e) else None
8989
case Typed(e, _) => rec(e)
90-
case Inlined(_, Nil, e) => rec(e)
91-
case _ => None
90+
case _ =>
91+
tree.tpe.widenTermRefByName match
92+
case ConstantType(c) => Some(c.value.asInstanceOf[T])
93+
case _ => None
9294
}
9395
rec(expr.asTerm)
9496
}

tests/neg/i11854.check

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
-- Error: tests/neg/i11854.scala:4:14 ----------------------------------------------------------------------------------
2+
4 |inline val j: Int = 2 // error
3+
| ^^^
4+
| inline value must have a literal constant type
5+
-- Error: tests/neg/i11854.scala:5:14 ----------------------------------------------------------------------------------
6+
5 |inline val b: Boolean = true // error
7+
| ^^^^^^^
8+
| inline value must have a literal constant type
9+
-- Error: tests/neg/i11854.scala:6:14 ----------------------------------------------------------------------------------
10+
6 |inline val s: String = "" // error
11+
| ^^^^^^
12+
| inline value must have a literal constant type
13+
-- Error: tests/neg/i11854.scala:7:18 ----------------------------------------------------------------------------------
14+
7 |inline val bagA = new Bag(Seq('a', 'b', 'c')) // error
15+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
16+
| inline value must contain a literal constant value.
17+
|
18+
| To inline more complex types consider using `inline def`
19+
-- Error: tests/neg/i11854.scala:8:23 ----------------------------------------------------------------------------------
20+
8 |inline val bagB: Bag = new Bag(Seq('a', 'b', 'c')) // error
21+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
22+
| inline value must contain a literal constant value.
23+
|
24+
| To inline more complex types consider using `inline def`

tests/run-macros/expr-map-1.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ kcolb
66
neht
77
esle
88
lav
9-
vals
9+
slav
1010
fed
11-
defs
11+
sfed
1212
fed
1313
rab
1414
yrt

tests/run-macros/expr-map-1/Test_2.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ object Test {
3131

3232
rewrite {
3333
val s: "vals" = "vals"
34-
println(s) // prints "foo" not "oof"
34+
println(s)
3535
}
3636

3737
rewrite {
@@ -41,7 +41,7 @@ object Test {
4141

4242
rewrite {
4343
def s: "defs" = "defs"
44-
println(s) // prints "foo" not "oof"
44+
println(s)
4545
}
4646

4747
rewrite {

tests/run-macros/i11856/Main_2.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@main def Test: Unit =
2+
inline def str1 = "Hello, "
3+
inline val str2 = "Scala 3"
4+
println(Str.concat(str1, str2))
5+
6+
inline def i1 = 1
7+
inline val i2 = 2
8+
println(I.sum(i1, i2))

tests/run-macros/i11856/Test_1.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import scala.quoted.*
2+
3+
object Str:
4+
inline def concat(inline a: String, inline b: String): String =
5+
${ evalConcat('a, 'b) }
6+
7+
def evalConcat(expra: Expr[String], exprb: Expr[String])(using Quotes): Expr[String] =
8+
val a = expra.valueOrError
9+
val b = exprb.valueOrError
10+
Expr(a ++ b)
11+
12+
object I:
13+
inline def sum(inline a: Int, inline b: Int): Int =
14+
${ evalConcat('a, 'b) }
15+
16+
def evalConcat(expra: Expr[Int], exprb: Expr[Int])(using Quotes): Expr[Int] =
17+
val a = expra.valueOrError
18+
val b = exprb.valueOrError
19+
Expr(a + b)

0 commit comments

Comments
 (0)