From c418126bc289fa9508523acf09e8c177378e2b89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Fri, 17 Nov 2023 10:08:50 +0100 Subject: [PATCH 1/2] Make `Constants.Constant` final. This is technically a compatibility breach, both at the binary and TASTy level. However, I don't see how anyone could have meaningfully extended `Constant` (also we have 2 users so far). --- build.sbt | 7 +++++++ .../shared/src/main/scala/tastyquery/Constants.scala | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index bf3ffd98..3b10b25d 100644 --- a/build.sbt +++ b/build.sbt @@ -112,6 +112,8 @@ lazy val tastyQuery = mimaBinaryIssueFilters ++= { import com.typesafe.tools.mima.core.* Seq( + // !!! Compatibility breach, but there was no way someone could have maningfully extended `Constant` + ProblemFilters.exclude[FinalClassProblem]("tastyquery.Constants$Constant"), // Everything in tastyquery.reader is private[tastyquery] at most ProblemFilters.exclude[Problem]("tastyquery.reader.*"), ) @@ -119,10 +121,15 @@ lazy val tastyQuery = tastyMiMaPreviousArtifacts := mimaPreviousArtifacts.value, tastyMiMaConfig ~= { prev => + import tastymima.intf._ prev .withMoreArtifactPrivatePackages(java.util.Arrays.asList( "tastyquery", )) + .withMoreProblemFilters(java.util.Arrays.asList( + // !!! Compatibility breach, but there was no way someone could have maningfully extended `Constant` + ProblemMatcher.make(ProblemKind.RestrictedOpenLevelChange, "tastyquery.Constants.Constant"), + )) }, ) .jvmSettings( diff --git a/tasty-query/shared/src/main/scala/tastyquery/Constants.scala b/tasty-query/shared/src/main/scala/tastyquery/Constants.scala index 19927a05..01edd96c 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/Constants.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/Constants.scala @@ -20,7 +20,7 @@ object Constants { final val NullTag = 11 final val ClazzTag = 12 - class Constant(val value: Matchable, val tag: Int) { + final class Constant(val value: Matchable, val tag: Int) { def wideType(using Context): Type = tag match case UnitTag => defn.UnitType case BooleanTag => defn.BooleanType From 15dff420f1e2f65523a83437485b010493cb5be4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Fri, 17 Nov 2023 10:17:36 +0100 Subject: [PATCH 2/2] Deprecate the unsafe constructor of `Constant`. --- .../src/main/scala/tastyquery/Constants.scala | 51 ++++++++++++++----- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/tasty-query/shared/src/main/scala/tastyquery/Constants.scala b/tasty-query/shared/src/main/scala/tastyquery/Constants.scala index 01edd96c..4bf2a3e3 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/Constants.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/Constants.scala @@ -1,5 +1,7 @@ package tastyquery +import scala.annotation.switch + import scala.compiletime.asMatchable import tastyquery.Contexts.* @@ -20,7 +22,30 @@ object Constants { final val NullTag = 11 final val ClazzTag = 12 - final class Constant(val value: Matchable, val tag: Int) { + final class Constant private (val value: Matchable, val tag: Int, internal: Boolean) { + @deprecated("this constructor is unsafe; use the `apply` methods in the companion instead", since = "1.1.0") + def this(value: Matchable, tag: Int) = + this(value, tag, internal = true) + + // Check that the tag matches the value: + val valid = (tag: @switch) match + case UnitTag => value == () + case BooleanTag => value.isInstanceOf[Boolean] + case CharTag => value.isInstanceOf[Char] + case ByteTag => value.isInstanceOf[Byte] + case ShortTag => value.isInstanceOf[Short] + case IntTag => value.isInstanceOf[Int] + case LongTag => value.isInstanceOf[Long] + case FloatTag => value.isInstanceOf[Float] + case DoubleTag => value.isInstanceOf[Double] + case StringTag => value.isInstanceOf[String] + case NullTag => value == null + case ClazzTag => value.isInstanceOf[Type] + case _ => false + + require(valid, s"Illegal combination of value and tag for Constant($value, $tag)") + end this + def wideType(using Context): Type = tag match case UnitTag => defn.UnitType case BooleanTag => defn.BooleanType @@ -160,18 +185,18 @@ object Constants { } object Constant { - def apply(x: Null): Constant = new Constant(x, NullTag) - def apply(x: Unit): Constant = new Constant(x, UnitTag) - def apply(x: Boolean): Constant = new Constant(x, BooleanTag) - def apply(x: Byte): Constant = new Constant(x, ByteTag) - def apply(x: Short): Constant = new Constant(x, ShortTag) - def apply(x: Int): Constant = new Constant(x, IntTag) - def apply(x: Long): Constant = new Constant(x, LongTag) - def apply(x: Float): Constant = new Constant(x, FloatTag) - def apply(x: Double): Constant = new Constant(x, DoubleTag) - def apply(x: String): Constant = new Constant(x, StringTag) - def apply(x: Char): Constant = new Constant(x, CharTag) - def apply(x: Type): Constant = new Constant(x, ClazzTag) + def apply(x: Null): Constant = new Constant(x, NullTag, internal = true) + def apply(x: Unit): Constant = new Constant(x, UnitTag, internal = true) + def apply(x: Boolean): Constant = new Constant(x, BooleanTag, internal = true) + def apply(x: Byte): Constant = new Constant(x, ByteTag, internal = true) + def apply(x: Short): Constant = new Constant(x, ShortTag, internal = true) + def apply(x: Int): Constant = new Constant(x, IntTag, internal = true) + def apply(x: Long): Constant = new Constant(x, LongTag, internal = true) + def apply(x: Float): Constant = new Constant(x, FloatTag, internal = true) + def apply(x: Double): Constant = new Constant(x, DoubleTag, internal = true) + def apply(x: String): Constant = new Constant(x, StringTag, internal = true) + def apply(x: Char): Constant = new Constant(x, CharTag, internal = true) + def apply(x: Type): Constant = new Constant(x, ClazzTag, internal = true) def unapply(c: Constant): Constant = c }