From 6f4b95865fa366f65c435f55e1359f4342edc3bb Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 11 Dec 2020 14:01:39 +0100 Subject: [PATCH 1/2] Add Printer to reflect Constant.show We can either print the structure with the pattern or the code like representation of the value. --- .../src/scala/quoted/runtime/impl/QuotesImpl.scala | 10 +++++++++- library/src/scala/quoted/Quotes.scala | 11 ++++++++++- scala3doc/src/dotty/dokka/tasty/BasicSupport.scala | 11 ++--------- scala3doc/src/dotty/dokka/tasty/TypesSupport.scala | 5 +---- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 66622a253a93..2e63b7ee00f6 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2092,7 +2092,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler given ConstantMethods: ConstantMethods with extension (self: Constant) def value: Any = self.value - def show: String = Extractors.showConstant(using QuotesImpl.this)(self) + def show(using printer: Printer[Constant]): String = printer.show(self) end extension end ConstantMethods @@ -2774,6 +2774,14 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def show(tpe: TypeRepr): String = Extractors.showType(using QuotesImpl.this)(tpe) + lazy val ConstantCode: Printer[Constant] = new Printer[Constant]: + def show(const: Constant): String = + const.show(using ctx.fresh.setSetting(ctx.settings.color, "never")) + + lazy val ConstantStructure: Printer[Constant] = new Printer[Constant]: + def show(const: Constant): String = + Extractors.showConstant(using QuotesImpl.this)(const) + end Printer end reflect diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index defdfe940bfb..e8dfcac20bba 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -2758,7 +2758,8 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => def value: Any /** Shows the constant as a String */ - def show: String + def show(using Printer[Constant]): String + end extension } @@ -4113,6 +4114,9 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** Default pinter for `TypeRepr` used when calling `tpe.show` */ given TypeReprPrinter: Printer[TypeRepr] = Printer.TypeReprCode + /** Default pinter for `Constant` used when calling `const.show` */ + given ConstantPrinter: Printer[Constant] = Printer.ConstantCode + /** Module object of `type Printer`. * Contains custom printers such as `TreeCode`, `TreeAnsiCode`, `TreeCases`, `TypeReprCode`, ..., `SymbolFullName` and `FlagsCombination`. */ @@ -4152,6 +4156,11 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => */ def TypeReprStructure: Printer[TypeRepr] + /** Prints the constant in source code. */ + def ConstantCode: Printer[Constant] + + /** Prints a pattern like representation of the `Constant`. */ + def ConstantStructure: Printer[Constant] } } diff --git a/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala b/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala index 91c8f8529765..1bc00cfd5d6a 100644 --- a/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala @@ -18,14 +18,8 @@ trait BasicSupport: val params = annotTerm match case Apply(target, appliedWith) => { appliedWith.flatMap { - case Literal(constant) => Some(Annotation.PrimitiveParameter(None, constant.value match { - case s: String => "\"" + s"$s" + "\"" - case other => other.toString() - })) - case NamedArg(name, Literal(constant)) => Some(Annotation.PrimitiveParameter(Some(name), constant.value match - case s: String => "\"" + s"$s" + "\"" - case other => other.toString() - )) + case Literal(constant) => Some(Annotation.PrimitiveParameter(None, constant.show)) + case NamedArg(name, Literal(constant)) => Some(Annotation.PrimitiveParameter(Some(name), constant.show)) case x @ Select(qual, name) => None case other => Some(Annotation.UnresolvedParameter(None, other.show)) } @@ -33,7 +27,6 @@ trait BasicSupport: Annotation(dri, params) - extension (sym: Symbol) def documentation = sym.docstring match case Some(docstring) => diff --git a/scala3doc/src/dotty/dokka/tasty/TypesSupport.scala b/scala3doc/src/dotty/dokka/tasty/TypesSupport.scala index 72adc698fa18..32ab6c4083c7 100644 --- a/scala3doc/src/dotty/dokka/tasty/TypesSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/TypesSupport.scala @@ -84,10 +84,7 @@ trait TypesSupport: case AndType(left, right) => inner(left) ++ texts(" & ") ++ inner(right) case ByNameType(tpe) => text("=> ") :: inner(tpe) case ConstantType(constant) => - texts(constant.value match - case c: Char => s"'$c'" - case other => other.toString - ) + texts(constant.show) case ThisType(tpe) => inner(tpe) case AnnotatedType(AppliedType(_, Seq(tpe)), annotation) if isRepeated(annotation) => inner(tpe) :+ text("*") From 9afc750598dedff4160f4d9605c8325648500941 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 11 Dec 2020 11:46:30 +0100 Subject: [PATCH 2/2] 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. --- .../scala/quoted/FromExpr.scala | 73 +++++++++++++------ library/src/scala/quoted/Const.scala | 9 ++- library/src/scala/quoted/Quotes.scala | 3 - .../interpreter/jvm/Interpreter.scala | 15 +++- 4 files changed, 74 insertions(+), 26 deletions(-) diff --git a/library/src-bootstrapped/scala/quoted/FromExpr.scala b/library/src-bootstrapped/scala/quoted/FromExpr.scala index 6f57c542af50..2bdb510c82d8 100644 --- a/library/src-bootstrapped/scala/quoted/FromExpr.scala +++ b/library/src-bootstrapped/scala/quoted/FromExpr.scala @@ -29,69 +29,100 @@ object FromExpr { * - Unlifts `'{false}` into `Some(false)` * - Otherwise unlifts to `None` */ - given BooleanFromExpr[T <: Boolean]: FromExpr[T] = new PrimitiveFromExpr + given BooleanFromExpr[T <: Boolean]: FromExpr[T] with + def unapply(expr: Expr[T])(using Quotes) = + import quotes.reflect._ + for BooleanConstant(v) <- nestedConstant(expr.asTerm) + yield v.asInstanceOf[T] /** Default implementation of `FromExpr[Byte]` * - Unlifts `'{n}` into `Some(n)` for a literal `n` of type `Byte` * - Otherwise unlifts to `None` */ - given ByteFromExpr[T <: Byte]: FromExpr[T] = new PrimitiveFromExpr + given ByteFromExpr[T <: Byte]: FromExpr[T] with + def unapply(expr: Expr[T])(using Quotes) = + import quotes.reflect._ + for ByteConstant(v) <- nestedConstant(expr.asTerm) + yield v.asInstanceOf[T] /** Default implementation of `FromExpr[Short]` * - Unlifts `'{n}` into `Some(n)` for a literal `n` of type `Short` * - Otherwise unlifts to `None` */ - given ShortFromExpr[T <: Short]: FromExpr[T] = new PrimitiveFromExpr + given ShortFromExpr[T <: Short]: FromExpr[T] with + def unapply(expr: Expr[T])(using Quotes) = + import quotes.reflect._ + for ShortConstant(v) <- nestedConstant(expr.asTerm) + yield v.asInstanceOf[T] /** Default implementation of `FromExpr[Int]` * - Unlifts `'{n}` into `Some(n)` for a literal `n` of type `Int` * - Otherwise unlifts to `None` */ - given IntFromExpr[T <: Int]: FromExpr[T] = new PrimitiveFromExpr + given IntFromExpr[T <: Int]: FromExpr[T] with + def unapply(expr: Expr[T])(using Quotes) = + import quotes.reflect._ + for IntConstant(v) <- nestedConstant(expr.asTerm) + yield v.asInstanceOf[T] /** Default implementation of `FromExpr[Long]` * - Unlifts `'{n}` into `Some(n)` for a literal `n` of type `Long` * - Otherwise unlifts to `None` */ - given LongFromExpr[T <: Long]: FromExpr[T] = new PrimitiveFromExpr + given LongFromExpr[T <: Long]: FromExpr[T] with + def unapply(expr: Expr[T])(using Quotes) = + import quotes.reflect._ + for LongConstant(v) <- nestedConstant(expr.asTerm) + yield v.asInstanceOf[T] /** Default implementation of `FromExpr[Float]` * - Unlifts `'{n}` into `Some(n)` for a literal `n` of type `Float` * - Otherwise unlifts to `None` */ - given FloatFromExpr[T <: Float]: FromExpr[T] = new PrimitiveFromExpr + given FloatFromExpr[T <: Float]: FromExpr[T] with + def unapply(expr: Expr[T])(using Quotes) = + import quotes.reflect._ + for FloatConstant(v) <- nestedConstant(expr.asTerm) + yield v.asInstanceOf[T] /** Default implementation of `FromExpr[Double]` * - Unlifts `'{n}` into `Some(n)` for a literal `n` of type `Double` * - Otherwise unlifts to `None` */ - given DoubleFromExpr[T <: Double]: FromExpr[T] = new PrimitiveFromExpr + given DoubleFromExpr[T <: Double]: FromExpr[T] with + def unapply(expr: Expr[T])(using Quotes) = + import quotes.reflect._ + for DoubleConstant(v) <- nestedConstant(expr.asTerm) + yield v.asInstanceOf[T] /** Default implementation of `FromExpr[Char]` * - Unlifts `'{c}` into `Some(c)` for a literal `c` of type `Char` * - Otherwise unlifts to `None` */ - given CharFromExpr[T <: Char]: FromExpr[T] = new PrimitiveFromExpr + given CharFromExpr[T <: Char]: FromExpr[T] with + def unapply(expr: Expr[T])(using Quotes) = + import quotes.reflect._ + for CharConstant(v) <- nestedConstant(expr.asTerm) + yield v.asInstanceOf[T] /** Default implementation of `FromExpr[String]` * - Unlifts `'{str}` into `Some(str)` for a literal `str` of type `String` * - Otherwise unlifts to `None` */ - given StringFromExpr[T <: String]: FromExpr[T] = new PrimitiveFromExpr - - /** Lift a quoted primitive value `'{ x }` into `x` */ - private class PrimitiveFromExpr[T <: Boolean | Byte | Short | Int | Long | Float | Double | Char | String] extends FromExpr[T] { + given StringFromExpr[T <: String]: FromExpr[T] with 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 Typed(e, _) => rec(e) - case Inlined(_, Nil, e) => rec(e) - case _ => None - } - rec(expr.asTerm) - } + for StringConstant(v) <- nestedConstant(expr.asTerm) + yield v.asInstanceOf[T] + + private def nestedConstant(using Quotes)(tree: quotes.reflect.Term): Option[quotes.reflect.Constant] = + import quotes.reflect._ + tree match + case Literal(c) => Some(c) + case Block(Nil, e) => nestedConstant(e) + case Typed(e, _) => nestedConstant(e) + case Inlined(_, Nil, e) => nestedConstant(e) + case _ => None /** Default implementation of `FromExpr[Option]` * - Unlifts `'{Some(x)}` into `Some(Some(x))` if `x` is unliftable diff --git a/library/src/scala/quoted/Const.scala b/library/src/scala/quoted/Const.scala index 7b65f5118a09..47fc62507a85 100644 --- a/library/src/scala/quoted/Const.scala +++ b/library/src/scala/quoted/Const.scala @@ -26,7 +26,14 @@ object Const { case Literal(c) => c match case NullConstant() | UnitConstant() | ClassOfConstant(_) => None - case _ => Some(c.value.asInstanceOf[T]) + case BooleanConstant(x) => Some(x.asInstanceOf[T]) + case ShortConstant(x) => Some(x.asInstanceOf[T]) + case IntConstant(x) => Some(x.asInstanceOf[T]) + case LongConstant(x) => Some(x.asInstanceOf[T]) + case FloatConstant(x) => Some(x.asInstanceOf[T]) + case DoubleConstant(x) => Some(x.asInstanceOf[T]) + case CharConstant(x) => Some(x.asInstanceOf[T]) + case StringConstant(x) => Some(x.asInstanceOf[T]) case Block(Nil, e) => rec(e) case Typed(e, _) => rec(e) case Inlined(_, Nil, e) => rec(e) diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index e8dfcac20bba..872b1c825387 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -2754,9 +2754,6 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** Extension methods of `Constant` */ trait ConstantMethods { extension (self: Constant) - /** Returns the value of the constant */ - def value: Any - /** Shows the constant as a String */ def show(using Printer[Constant]): String diff --git a/tests/run-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala index a01c3396c7e1..cab7689a8f51 100644 --- a/tests/run-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala +++ b/tests/run-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala @@ -83,7 +83,20 @@ class Interpreter[Q <: Quotes & Singleton](using q0: Q) extends TreeInterpreter[ def interpretUnit(): AbstractAny = ().asInstanceOf[Object] - def interpretLiteral(const: Constant): Result = const.value + def interpretLiteral(const: Constant): Result = + const match + case UnitConstant() => () + case NullConstant() => null + case BooleanConstant(v) => v + case ByteConstant(v) => v + case ShortConstant(v) => v + case IntConstant(v) => v + case LongConstant(v) => v + case FloatConstant(v) => v + case DoubleConstant(v) => v + case CharConstant(v) => v + case StringConstant(v) => v + case ClassOfConstant(v) => ??? def interpretIsInstanceOf(o: AbstractAny, tpt: TypeTree): Result = jvmReflection.getClassOf(tpt.symbol).isInstance(o)