From bbe2dde1ee10adc05ceece96ab82af8a66a21880 Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Mon, 28 Apr 2025 16:19:56 +0000 Subject: [PATCH 1/2] Fix `Constant` tag of `Expr(null: String)` Co-authored-by: Hamza Remmal Co-authored-by: HarrisL2 Co-authored-by: Abdullah Arif Jafri --- .../src/scala/quoted/runtime/impl/QuotesImpl.scala | 2 +- tests/run-macros/i23008.check | 1 + tests/run-macros/i23008/Macros_1.scala | 12 ++++++++++++ tests/run-macros/i23008/Test_2.scala | 2 ++ 4 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 tests/run-macros/i23008.check create mode 100644 tests/run-macros/i23008/Macros_1.scala create mode 100644 tests/run-macros/i23008/Test_2.scala diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index a93e010ddc34..09a61232d677 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2529,7 +2529,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler end StringConstantTypeTest object StringConstant extends StringConstantModule: - def apply(x: String): StringConstant = dotc.core.Constants.Constant(x) + def apply(x: String): StringConstant = dotc.core.Constants.Constant(x: Any) def unapply(constant: StringConstant): Some[String] = Some(constant.stringValue) end StringConstant diff --git a/tests/run-macros/i23008.check b/tests/run-macros/i23008.check new file mode 100644 index 000000000000..19765bd501b6 --- /dev/null +++ b/tests/run-macros/i23008.check @@ -0,0 +1 @@ +null diff --git a/tests/run-macros/i23008/Macros_1.scala b/tests/run-macros/i23008/Macros_1.scala new file mode 100644 index 000000000000..052760dbf833 --- /dev/null +++ b/tests/run-macros/i23008/Macros_1.scala @@ -0,0 +1,12 @@ +import scala.quoted.* + +object Macros { + inline def buildString = ${buildStringCode} + + def buildStringCode(using Quotes): Expr[String] = { + import quotes.reflect.* + val str: String = null + val exprString = Expr(str) + Expr(exprString.show) + } +} diff --git a/tests/run-macros/i23008/Test_2.scala b/tests/run-macros/i23008/Test_2.scala new file mode 100644 index 000000000000..cf3cfffb50ac --- /dev/null +++ b/tests/run-macros/i23008/Test_2.scala @@ -0,0 +1,2 @@ +@main def Test = + println(Macros.buildString) From e0fbfde87a5faf9115db18dda4bfcda8fdbc21a5 Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Wed, 30 Apr 2025 15:13:38 +0000 Subject: [PATCH 2/2] Accept `null` in `StringToExpr` but not in `StringConstant` --- compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala | 7 ++++++- library/src/scala/quoted/ToExpr.scala | 8 +++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 09a61232d677..9ff73ef96dd8 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2529,7 +2529,12 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler end StringConstantTypeTest object StringConstant extends StringConstantModule: - def apply(x: String): StringConstant = dotc.core.Constants.Constant(x: Any) + def apply(x: String): StringConstant = + require(x != null, "StringConstant cannot be null") + // A `null` constant must be represented as a `NullConstant`, c.f. a + // constant with `tag == NullTag`, which is not a `StringConstant`. + // See issue 23008. + dotc.core.Constants.Constant(x) def unapply(constant: StringConstant): Some[String] = Some(constant.stringValue) end StringConstant diff --git a/library/src/scala/quoted/ToExpr.scala b/library/src/scala/quoted/ToExpr.scala index 6c167c353d87..974f7b4865ee 100644 --- a/library/src/scala/quoted/ToExpr.scala +++ b/library/src/scala/quoted/ToExpr.scala @@ -77,7 +77,13 @@ object ToExpr { given StringToExpr[T <: String]: ToExpr[T] with { def apply(x: T)(using Quotes) = import quotes.reflect.* - Literal(StringConstant(x)).asExpr.asInstanceOf[Expr[T]] + val literal = + if (x: Any) == null then + // Can happen if called from code without `-Yexplicit-nulls`. + Literal(NullConstant()) + else + Literal(StringConstant(x)) + literal.asExpr.asInstanceOf[Expr[T]] } /** Default implementation of `ToExpr[Class[T]]` */