Skip to content

Commit 9afc750

Browse files
committed
Remove reflect Constant.value
To use this we usually need to use an `asInstanceOf` which cannot be checked in the case of ClassOf. Doing so without being careful will introduce an unsoundness. It seems better to remove this method and let users match all the cases. In the future, we could reintroduce a restricted version of this API that only works on a subset of the constants.
1 parent 6f4b958 commit 9afc750

File tree

4 files changed

+74
-26
lines changed

4 files changed

+74
-26
lines changed

library/src-bootstrapped/scala/quoted/FromExpr.scala

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,69 +29,100 @@ object FromExpr {
2929
* - Unlifts `'{false}` into `Some(false)`
3030
* - Otherwise unlifts to `None`
3131
*/
32-
given BooleanFromExpr[T <: Boolean]: FromExpr[T] = new PrimitiveFromExpr
32+
given BooleanFromExpr[T <: Boolean]: FromExpr[T] with
33+
def unapply(expr: Expr[T])(using Quotes) =
34+
import quotes.reflect._
35+
for BooleanConstant(v) <- nestedConstant(expr.asTerm)
36+
yield v.asInstanceOf[T]
3337

3438
/** Default implementation of `FromExpr[Byte]`
3539
* - Unlifts `'{n}` into `Some(n)` for a literal `n` of type `Byte`
3640
* - Otherwise unlifts to `None`
3741
*/
38-
given ByteFromExpr[T <: Byte]: FromExpr[T] = new PrimitiveFromExpr
42+
given ByteFromExpr[T <: Byte]: FromExpr[T] with
43+
def unapply(expr: Expr[T])(using Quotes) =
44+
import quotes.reflect._
45+
for ByteConstant(v) <- nestedConstant(expr.asTerm)
46+
yield v.asInstanceOf[T]
3947

4048
/** Default implementation of `FromExpr[Short]`
4149
* - Unlifts `'{n}` into `Some(n)` for a literal `n` of type `Short`
4250
* - Otherwise unlifts to `None`
4351
*/
44-
given ShortFromExpr[T <: Short]: FromExpr[T] = new PrimitiveFromExpr
52+
given ShortFromExpr[T <: Short]: FromExpr[T] with
53+
def unapply(expr: Expr[T])(using Quotes) =
54+
import quotes.reflect._
55+
for ShortConstant(v) <- nestedConstant(expr.asTerm)
56+
yield v.asInstanceOf[T]
4557

4658
/** Default implementation of `FromExpr[Int]`
4759
* - Unlifts `'{n}` into `Some(n)` for a literal `n` of type `Int`
4860
* - Otherwise unlifts to `None`
4961
*/
50-
given IntFromExpr[T <: Int]: FromExpr[T] = new PrimitiveFromExpr
62+
given IntFromExpr[T <: Int]: FromExpr[T] with
63+
def unapply(expr: Expr[T])(using Quotes) =
64+
import quotes.reflect._
65+
for IntConstant(v) <- nestedConstant(expr.asTerm)
66+
yield v.asInstanceOf[T]
5167

5268
/** Default implementation of `FromExpr[Long]`
5369
* - Unlifts `'{n}` into `Some(n)` for a literal `n` of type `Long`
5470
* - Otherwise unlifts to `None`
5571
*/
56-
given LongFromExpr[T <: Long]: FromExpr[T] = new PrimitiveFromExpr
72+
given LongFromExpr[T <: Long]: FromExpr[T] with
73+
def unapply(expr: Expr[T])(using Quotes) =
74+
import quotes.reflect._
75+
for LongConstant(v) <- nestedConstant(expr.asTerm)
76+
yield v.asInstanceOf[T]
5777

5878
/** Default implementation of `FromExpr[Float]`
5979
* - Unlifts `'{n}` into `Some(n)` for a literal `n` of type `Float`
6080
* - Otherwise unlifts to `None`
6181
*/
62-
given FloatFromExpr[T <: Float]: FromExpr[T] = new PrimitiveFromExpr
82+
given FloatFromExpr[T <: Float]: FromExpr[T] with
83+
def unapply(expr: Expr[T])(using Quotes) =
84+
import quotes.reflect._
85+
for FloatConstant(v) <- nestedConstant(expr.asTerm)
86+
yield v.asInstanceOf[T]
6387

6488
/** Default implementation of `FromExpr[Double]`
6589
* - Unlifts `'{n}` into `Some(n)` for a literal `n` of type `Double`
6690
* - Otherwise unlifts to `None`
6791
*/
68-
given DoubleFromExpr[T <: Double]: FromExpr[T] = new PrimitiveFromExpr
92+
given DoubleFromExpr[T <: Double]: FromExpr[T] with
93+
def unapply(expr: Expr[T])(using Quotes) =
94+
import quotes.reflect._
95+
for DoubleConstant(v) <- nestedConstant(expr.asTerm)
96+
yield v.asInstanceOf[T]
6997

7098
/** Default implementation of `FromExpr[Char]`
7199
* - Unlifts `'{c}` into `Some(c)` for a literal `c` of type `Char`
72100
* - Otherwise unlifts to `None`
73101
*/
74-
given CharFromExpr[T <: Char]: FromExpr[T] = new PrimitiveFromExpr
102+
given CharFromExpr[T <: Char]: FromExpr[T] with
103+
def unapply(expr: Expr[T])(using Quotes) =
104+
import quotes.reflect._
105+
for CharConstant(v) <- nestedConstant(expr.asTerm)
106+
yield v.asInstanceOf[T]
75107

76108
/** Default implementation of `FromExpr[String]`
77109
* - Unlifts `'{str}` into `Some(str)` for a literal `str` of type `String`
78110
* - Otherwise unlifts to `None`
79111
*/
80-
given StringFromExpr[T <: String]: FromExpr[T] = new PrimitiveFromExpr
81-
82-
/** Lift a quoted primitive value `'{ x }` into `x` */
83-
private class PrimitiveFromExpr[T <: Boolean | Byte | Short | Int | Long | Float | Double | Char | String] extends FromExpr[T] {
112+
given StringFromExpr[T <: String]: FromExpr[T] with
84113
def unapply(expr: Expr[T])(using Quotes) =
85114
import quotes.reflect._
86-
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)
89-
case Typed(e, _) => rec(e)
90-
case Inlined(_, Nil, e) => rec(e)
91-
case _ => None
92-
}
93-
rec(expr.asTerm)
94-
}
115+
for StringConstant(v) <- nestedConstant(expr.asTerm)
116+
yield v.asInstanceOf[T]
117+
118+
private def nestedConstant(using Quotes)(tree: quotes.reflect.Term): Option[quotes.reflect.Constant] =
119+
import quotes.reflect._
120+
tree match
121+
case Literal(c) => Some(c)
122+
case Block(Nil, e) => nestedConstant(e)
123+
case Typed(e, _) => nestedConstant(e)
124+
case Inlined(_, Nil, e) => nestedConstant(e)
125+
case _ => None
95126

96127
/** Default implementation of `FromExpr[Option]`
97128
* - Unlifts `'{Some(x)}` into `Some(Some(x))` if `x` is unliftable

library/src/scala/quoted/Const.scala

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,14 @@ object Const {
2626
case Literal(c) =>
2727
c match
2828
case NullConstant() | UnitConstant() | ClassOfConstant(_) => None
29-
case _ => Some(c.value.asInstanceOf[T])
29+
case BooleanConstant(x) => Some(x.asInstanceOf[T])
30+
case ShortConstant(x) => Some(x.asInstanceOf[T])
31+
case IntConstant(x) => Some(x.asInstanceOf[T])
32+
case LongConstant(x) => Some(x.asInstanceOf[T])
33+
case FloatConstant(x) => Some(x.asInstanceOf[T])
34+
case DoubleConstant(x) => Some(x.asInstanceOf[T])
35+
case CharConstant(x) => Some(x.asInstanceOf[T])
36+
case StringConstant(x) => Some(x.asInstanceOf[T])
3037
case Block(Nil, e) => rec(e)
3138
case Typed(e, _) => rec(e)
3239
case Inlined(_, Nil, e) => rec(e)

library/src/scala/quoted/Quotes.scala

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2754,9 +2754,6 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
27542754
/** Extension methods of `Constant` */
27552755
trait ConstantMethods {
27562756
extension (self: Constant)
2757-
/** Returns the value of the constant */
2758-
def value: Any
2759-
27602757
/** Shows the constant as a String */
27612758
def show(using Printer[Constant]): String
27622759

tests/run-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,20 @@ class Interpreter[Q <: Quotes & Singleton](using q0: Q) extends TreeInterpreter[
8383

8484
def interpretUnit(): AbstractAny = ().asInstanceOf[Object]
8585

86-
def interpretLiteral(const: Constant): Result = const.value
86+
def interpretLiteral(const: Constant): Result =
87+
const match
88+
case UnitConstant() => ()
89+
case NullConstant() => null
90+
case BooleanConstant(v) => v
91+
case ByteConstant(v) => v
92+
case ShortConstant(v) => v
93+
case IntConstant(v) => v
94+
case LongConstant(v) => v
95+
case FloatConstant(v) => v
96+
case DoubleConstant(v) => v
97+
case CharConstant(v) => v
98+
case StringConstant(v) => v
99+
case ClassOfConstant(v) => ???
87100

88101
def interpretIsInstanceOf(o: AbstractAny, tpt: TypeTree): Result =
89102
jvmReflection.getClassOf(tpt.symbol).isInstance(o)

0 commit comments

Comments
 (0)