Skip to content

Commit 96618c1

Browse files
authored
Merge pull request #6793 from dotty-staging/remove-LiftedExpr
Remove scala.internal.quoted.LiftedExpr
2 parents 28cf0cb + 65901c5 commit 96618c1

File tree

75 files changed

+296
-211
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+296
-211
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -758,13 +758,16 @@ class Definitions {
758758

759759
@threadUnsafe lazy val QuotedExprType: TypeRef = ctx.requiredClassRef("scala.quoted.Expr")
760760
def QuotedExprClass(implicit ctx: Context): ClassSymbol = QuotedExprType.symbol.asClass
761+
def QuotedExprModule(implicit ctx: Context): Symbol = QuotedExprClass.companionModule
761762

762763
@threadUnsafe lazy val QuoteContextType: TypeRef = ctx.requiredClassRef("scala.quoted.QuoteContext")
763764
def QuoteContextClass(implicit ctx: Context): ClassSymbol = QuoteContextType.symbol.asClass
764765

765766
@threadUnsafe lazy val QuoteContextModule: TermSymbol = ctx.requiredModule("scala.quoted.QuoteContext")
766767
@threadUnsafe lazy val QuoteContext_macroContext: TermSymbol = QuoteContextModule.requiredMethod("macroContext")
767768

769+
@threadUnsafe lazy val LiftableModule: TermSymbol = ctx.requiredModule("scala.quoted.Liftable")
770+
768771
@threadUnsafe lazy val InternalQuotedModuleRef: TermRef = ctx.requiredModuleRef("scala.internal.Quoted")
769772
def InternalQuotedModule: Symbol = InternalQuotedModuleRef.symbol
770773
@threadUnsafe lazy val InternalQuoted_exprQuoteR: TermRef = InternalQuotedModule.requiredMethodRef("exprQuote")
@@ -797,7 +800,6 @@ class Definitions {
797800
def QuotedMatchingBindingClass(implicit ctx: Context): ClassSymbol = QuotedMatchingBindingType.symbol.asClass
798801

799802
def Unpickler_unpickleExpr: TermSymbol = ctx.requiredMethod("scala.runtime.quoted.Unpickler.unpickleExpr")
800-
def Unpickler_liftedExpr: TermSymbol = ctx.requiredMethod("scala.runtime.quoted.Unpickler.liftedExpr")
801803
def Unpickler_unpickleType: TermSymbol = ctx.requiredMethod("scala.runtime.quoted.Unpickler.unpickleType")
802804

803805
@threadUnsafe lazy val TastyReflectionType: TypeRef = ctx.requiredClassRef("scala.tasty.Reflection")

compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,6 @@ object PickledQuotes {
4545
}
4646
}
4747
forceAndCleanArtefacts.transform(unpickled)
48-
case expr: LiftedExpr[T] =>
49-
expr.value match {
50-
case value: Class[_] => ref(defn.Predef_classOf).appliedToType(classToType(value))
51-
case value => Literal(Constant(value))
52-
}
5348
case expr: TastyTreeExpr[Tree] @unchecked => healOwner(expr.tree)
5449
case expr: FunctionAppliedTo[_] =>
5550
functionAppliedTo(quotedExprToTree(expr.f), expr.args.map(arg => quotedExprToTree(arg)).toList)
@@ -174,29 +169,6 @@ object PickledQuotes {
174169
seq(argVals.flatten, rec(fn))
175170
}
176171

177-
private def classToType(clazz: Class[_])(implicit ctx: Context): Type = {
178-
if (clazz.isPrimitive) {
179-
if (clazz == classOf[Boolean]) defn.BooleanType
180-
else if (clazz == classOf[Byte]) defn.ByteType
181-
else if (clazz == classOf[Char]) defn.CharType
182-
else if (clazz == classOf[Short]) defn.ShortType
183-
else if (clazz == classOf[Int]) defn.IntType
184-
else if (clazz == classOf[Long]) defn.LongType
185-
else if (clazz == classOf[Float]) defn.FloatType
186-
else if (clazz == classOf[Double]) defn.DoubleType
187-
else defn.UnitType
188-
} else if (clazz.isArray) {
189-
defn.ArrayType.appliedTo(classToType(clazz.getComponentType))
190-
} else if (clazz.isMemberClass) {
191-
val name = clazz.getSimpleName.toTypeName
192-
val enclosing = classToType(clazz.getEnclosingClass)
193-
if (enclosing.member(name).exists) enclosing.select(name)
194-
else {
195-
enclosing.classSymbol.companionModule.termRef.select(name)
196-
}
197-
} else ctx.getClassIfDefined(clazz.getCanonicalName).typeRef
198-
}
199-
200172
/** Make sure that the owner of this tree is `ctx.owner` */
201173
private def healOwner(tree: Tree)(implicit ctx: Context): Tree = {
202174
val getCurrentOwner = new TreeAccumulator[Option[Symbol]] {

compiler/src/dotty/tools/dotc/quoted/QuoteCompiler.scala

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ class QuoteCompiler extends Compiler {
4949
class QuotedFrontend extends Phase {
5050
import tpd._
5151

52-
5352
def phaseName: String = "quotedFrontend"
5453

5554
override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = {
@@ -65,7 +64,7 @@ class QuoteCompiler extends Compiler {
6564
cls.enter(ctx.newDefaultConstructor(cls), EmptyScope)
6665
val meth = ctx.newSymbol(cls, nme.apply, Method, ExprType(defn.AnyType), coord = pos).entered
6766

68-
val quoted = PickledQuotes.quotedExprToTree(checkValidRunExpr(exprUnit.exprBuilder.apply(new QuoteContext(ReflectionImpl(ctx)))))(ctx.withOwner(meth))
67+
val quoted = PickledQuotes.quotedExprToTree(exprUnit.exprBuilder.apply(new QuoteContext(ReflectionImpl(ctx))))(ctx.withOwner(meth))
6968

7069
getLiteral(quoted) match {
7170
case Some(value) =>
@@ -82,12 +81,6 @@ class QuoteCompiler extends Compiler {
8281
}
8382
}
8483

85-
private def checkValidRunExpr(expr: Expr[_]): Expr[_] = expr match {
86-
case expr: scala.internal.quoted.TastyTreeExpr[Tree] @unchecked =>
87-
throw new Exception("Cannot call `Expr.run` on an `Expr` that comes from a macro argument.")
88-
case _ => expr
89-
}
90-
9184
/** Get the literal value if this tree only contains a literal tree */
9285
@tailrec private def getLiteral(tree: Tree): Option[Any] = tree match {
9386
case Literal(lit) => Some(lit.value)

compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,29 @@ class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util.
10551055
case _ => Some(x)
10561056
}
10571057

1058+
def Type_apply(clazz: Class[_])(implicit ctx: Context): Type = {
1059+
if (clazz.isPrimitive) {
1060+
if (clazz == classOf[Boolean]) defn.BooleanType
1061+
else if (clazz == classOf[Byte]) defn.ByteType
1062+
else if (clazz == classOf[Char]) defn.CharType
1063+
else if (clazz == classOf[Short]) defn.ShortType
1064+
else if (clazz == classOf[Int]) defn.IntType
1065+
else if (clazz == classOf[Long]) defn.LongType
1066+
else if (clazz == classOf[Float]) defn.FloatType
1067+
else if (clazz == classOf[Double]) defn.DoubleType
1068+
else defn.UnitType
1069+
} else if (clazz.isArray) {
1070+
defn.ArrayType.appliedTo(Type_apply(clazz.getComponentType))
1071+
} else if (clazz.isMemberClass) {
1072+
val name = clazz.getSimpleName.toTypeName
1073+
val enclosing = Type_apply(clazz.getEnclosingClass)
1074+
if (enclosing.member(name).exists) enclosing.select(name)
1075+
else {
1076+
enclosing.classSymbol.companionModule.termRef.select(name)
1077+
}
1078+
} else ctx.getClassIfDefined(clazz.getCanonicalName).typeRef
1079+
}
1080+
10581081
def `Type_=:=`(self: Type)(that: Type)(implicit ctx: Context): Boolean = self =:= that
10591082

10601083
def `Type_<:<`(self: Type)(that: Type)(implicit ctx: Context): Boolean = self <:< that
@@ -1431,6 +1454,9 @@ class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util.
14311454

14321455
def Constant_value(const: Constant): Any = const.value
14331456

1457+
def matchConstant(constant: Constant): Option[Unit | Null | Int | Boolean | Byte | Short | Int | Long | Float | Double | Char | String | Type] =
1458+
Some(constant.asInstanceOf[Unit | Null | Int | Boolean | Byte | Short | Int | Long | Float | Double | Char | String | Type])
1459+
14341460
def matchConstant_Unit(x: Constant): Boolean = x.tag == Constants.UnitTag
14351461
def matchConstant_Null(x: Constant): Boolean = x.tag == Constants.NullTag
14361462
def matchConstant_Boolean(x: Constant): Option[Boolean] =
@@ -1454,6 +1480,9 @@ class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util.
14541480
def matchConstant_ClassTag(x: Constant): Option[Type] =
14551481
if (x.tag == Constants.ClazzTag) Some(x.typeValue) else None
14561482

1483+
def Constant_apply(x: Unit | Null | Int | Boolean | Byte | Short | Int | Long | Float | Double | Char | String | Type): Constant =
1484+
Constants.Constant(x)
1485+
14571486
def Constant_Unit_apply(): Constant = Constants.Constant(())
14581487
def Constant_Null_apply(): Constant = Constants.Constant(null)
14591488
def Constant_Boolean_apply(x: Boolean): Constant = Constants.Constant(x)
@@ -1809,6 +1838,7 @@ class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util.
18091838
def Definitions_ClassClass: Symbol = defn.ClassClass
18101839
def Definitions_ArrayClass: Symbol = defn.ArrayClass
18111840
def Definitions_PredefModule: Symbol = defn.ScalaPredefModule.asTerm
1841+
def Definitions_Predef_classOf: Symbol = defn.Predef_classOf.asTerm
18121842

18131843
def Definitions_JavaLangPackage: Symbol = defn.JavaLangPackageVal
18141844

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

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import typer.Implicits.SearchFailureType
2020

2121
import scala.collection.mutable
2222
import dotty.tools.dotc.core.Annotations.Annotation
23+
import dotty.tools.dotc.core.Names._
2324
import dotty.tools.dotc.core.StdNames._
2425
import dotty.tools.dotc.core.quoted._
2526
import dotty.tools.dotc.transform.TreeMapWithStages._
@@ -71,6 +72,8 @@ class ReifyQuotes extends MacroTransform {
7172

7273
override def phaseName: String = ReifyQuotes.name
7374

75+
override def allowsImplicitSearch: Boolean = true
76+
7477
override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = {
7578
tree match {
7679
case tree: RefTree if !ctx.inInlineMethod =>
@@ -199,8 +202,29 @@ class ReifyQuotes extends MacroTransform {
199202
}
200203

201204
private def pickledQuote(body: Tree, splices: List[Tree], originalTp: Type, isType: Boolean)(implicit ctx: Context) = {
202-
def pickleAsValue[T](value: T) =
203-
ref(defn.Unpickler_liftedExpr).appliedToType(originalTp.widen).appliedTo(Literal(Constant(value)))
205+
206+
def liftedValue[T](value: T, name: TermName, qctx: Tree) =
207+
ref(defn.LiftableModule).select(name).select("toExpr".toTermName).appliedTo(Literal(Constant(value))).appliedTo(qctx)
208+
209+
def pickleAsValue[T](value: T) = {
210+
val qctx = ctx.typer.inferImplicitArg(defn.QuoteContextType, body.span)
211+
if (qctx.tpe.isInstanceOf[SearchFailureType])
212+
ctx.error(ctx.typer.missingArgMsg(qctx, defn.QuoteContextType, ""), ctx.source.atSpan(body.span))
213+
value match {
214+
case null => ref(defn.QuotedExprModule).select("nullExpr".toTermName).appliedTo(qctx)
215+
case _: Unit => ref(defn.QuotedExprModule).select("unitExpr".toTermName).appliedTo(qctx)
216+
case _: Boolean => liftedValue(value, "Liftable_Boolean_delegate".toTermName, qctx)
217+
case _: Byte => liftedValue(value, "Liftable_Byte_delegate".toTermName, qctx)
218+
case _: Short => liftedValue(value, "Liftable_Short_delegate".toTermName, qctx)
219+
case _: Int => liftedValue(value, "Liftable_Int_delegate".toTermName, qctx)
220+
case _: Long => liftedValue(value, "Liftable_Long_delegate".toTermName, qctx)
221+
case _: Float => liftedValue(value, "Liftable_Float_delegate".toTermName, qctx)
222+
case _: Double => liftedValue(value, "Liftable_Double_delegate".toTermName, qctx)
223+
case _: Char => liftedValue(value, "Liftable_Char_delegate".toTermName, qctx)
224+
case _: String => liftedValue(value, "Liftable_String_delegate".toTermName, qctx)
225+
}
226+
}
227+
204228
def pickleAsTasty() = {
205229
val meth =
206230
if (isType) ref(defn.Unpickler_unpickleType).appliedToType(originalTp)

library/src-3.x/scala/quoted/Expr.scala

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,19 @@ package quoted {
4444
tg.untupled(args => new FunctionAppliedTo[R](f, args.toArray.map(_.asInstanceOf[Expr[_]])))
4545
}
4646

47-
/** Returns A expression containing a block with the given statements and ending with the expresion
47+
/** Returns a null expresssion equivalent to `'{null}` */
48+
def nullExpr given (qctx: QuoteContext): Expr[Null] = {
49+
import qctx.tasty._
50+
Literal(Constant(null)).seal.asInstanceOf[Expr[Null]]
51+
}
52+
53+
/** Returns a unit expresssion equivalent to `'{}` or `'{()}` */
54+
def unitExpr given (qctx: QuoteContext): Expr[Unit] = {
55+
import qctx.tasty._
56+
Literal(Constant(())).seal.asInstanceOf[Expr[Unit]]
57+
}
58+
59+
/** Returns an expression containing a block with the given statements and ending with the expresion
4860
* Given list of statements `s1 :: s2 :: ... :: Nil` and an expression `e` the resulting expression
4961
* will be equivalent to `'{ $s1; $s2; ...; $e }`.
5062
*/
@@ -67,13 +79,6 @@ package internal {
6779
override def toString: String = s"Expr(<pickled tasty>)"
6880
}
6981

70-
/** An Expr backed by a lifted value.
71-
* Values can only be of type Boolean, Byte, Short, Char, Int, Long, Float, Double, Unit, String or Null.
72-
*/
73-
final class LiftedExpr[+T](val value: T) extends Expr[T] {
74-
override def toString: String = s"Expr($value)"
75-
}
76-
7782
/** An Expr backed by a tree. Only the current compiler trees are allowed.
7883
*
7984
* These expressions are used for arguments of macros. They contain and actual tree
Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package scala.quoted
22

3-
import scala.runtime.quoted.Unpickler.liftedExpr
4-
53
/** A typeclass for types that can be turned to `quoted.Expr[T]`
64
* without going through an explicit `'{...}` operation.
75
*/
8-
abstract class Liftable[T] {
6+
trait Liftable[T] {
7+
8+
/** Lift a value into an expression containing the construction of that value */
99
def toExpr(x: T) given QuoteContext: Expr[T]
10+
1011
}
1112

1213
/** Some liftable base types. To be completed with at least all types
@@ -17,17 +18,29 @@ abstract class Liftable[T] {
1718
object Liftable {
1819

1920
implicit val Liftable_Boolean_delegate: Liftable[Boolean] = new PrimitiveLiftable
21+
implicit val Liftable_Byte_delegate: Liftable[Byte] = new PrimitiveLiftable
2022
implicit val Liftable_Short_delegate: Liftable[Short] = new PrimitiveLiftable
2123
implicit val Liftable_Int_delegate: Liftable[Int] = new PrimitiveLiftable
2224
implicit val Liftable_Long_delegate: Liftable[Long] = new PrimitiveLiftable
2325
implicit val Liftable_Float_delegate: Liftable[Float] = new PrimitiveLiftable
2426
implicit val Liftable_Double_delegate: Liftable[Double] = new PrimitiveLiftable
2527
implicit val Liftable_Char_delegate: Liftable[Char] = new PrimitiveLiftable
2628
implicit val Liftable_String_delegate: Liftable[String] = new PrimitiveLiftable
27-
implicit def ClassIsLiftable[T]: Liftable[Class[T]] = new PrimitiveLiftable
2829

29-
private class PrimitiveLiftable[T] extends Liftable[T] {
30-
override def toExpr(x: T) given QuoteContext: Expr[T] = liftedExpr(x)
30+
private class PrimitiveLiftable[T <: Unit | Null | Int | Boolean | Byte | Short | Int | Long | Float | Double | Char | String] extends Liftable[T] {
31+
/** Lift a primitive value `n` into `'{ n }` */
32+
def toExpr(x: T) given (qctx: QuoteContext): Expr[T] = {
33+
import qctx.tasty._
34+
Literal(Constant(x)).seal.asInstanceOf[Expr[T]]
35+
}
36+
}
37+
38+
implicit def ClassIsLiftable[T]: Liftable[Class[T]] = new Liftable[Class[T]] {
39+
/** Lift a `Class[T]` into `'{ classOf[T] }` */
40+
def toExpr(x: Class[T]) given (qctx: QuoteContext): Expr[Class[T]] = {
41+
import qctx.tasty._
42+
Ref(definitions.Predef_classOf).appliedToType(Type(x)).seal.asInstanceOf[Expr[Class[T]]]
43+
}
3144
}
3245

3346
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package scala
2+
3+
package object quoted {
4+
5+
def run[T](expr: given QuoteContext => Expr[T]) given (toolbox: Toolbox): T =
6+
throw new Exception("Non bootsrapped library")
7+
8+
def withQuoteContext[T](thunk: given QuoteContext => T) given (toolbox: Toolbox): T =
9+
throw new Exception("Non bootsrapped library")
10+
11+
}

library/src/scala/runtime/quoted/Unpickler.scala

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package scala.runtime.quoted
22

3-
import scala.internal.quoted.{LiftedExpr, TastyExpr, TastyType}
3+
import scala.internal.quoted.{TastyExpr, TastyType}
44
import scala.quoted.{Expr, Type}
55

66
/** Provides methods to unpickle `Expr` and `Type` trees. */
@@ -16,11 +16,6 @@ object Unpickler {
1616
*/
1717
def unpickleExpr[T](repr: Pickled, args: Seq[Any]): Expr[T] = new TastyExpr[T](repr, args)
1818

19-
/** Lift the `value` to an `Expr` tree.
20-
* Values can only be of type Boolean, Byte, Short, Char, Int, Long, Float, Double, Unit, String, Null or Class.
21-
*/
22-
def liftedExpr[T](value: T): Expr[T] = new LiftedExpr[T](value)
23-
2419
/** Unpickle `repr` which represents a pickled `Type` tree,
2520
* replacing splice nodes with `args`
2621
*/

library/src/scala/tasty/reflect/ConstantOps.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ trait ConstantOps extends Core {
1010
/** Module of Constant literals */
1111
object Constant {
1212

13+
def apply(x: Unit | Null | Int | Boolean | Byte | Short | Int | Long | Float | Double | Char | String | Type): Constant =
14+
kernel.Constant_apply(x)
15+
16+
def unapply(constant: Constant): Option[Unit | Null | Int | Boolean | Byte | Short | Int | Long | Float | Double | Char | String | Type] =
17+
kernel.matchConstant(constant)
18+
19+
// TODO remove all extractors bellow and use only use the two above
20+
1321
/** Module of Null literals */
1422
object Unit {
1523
/** Unit `()` literal */

library/src/scala/tasty/reflect/Kernel.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,8 @@ trait Kernel {
829829

830830
def matchType(x: TypeOrBounds)(implicit ctx: Context): Option[Type]
831831

832+
def Type_apply(clazz: Class[_])(implicit ctx: Context): Type
833+
832834
def `Type_=:=`(self: Type)(that: Type)(implicit ctx: Context): Boolean
833835
def `Type_<:<`(self: Type)(that: Type)(implicit ctx: Context): Boolean
834836

@@ -1176,6 +1178,7 @@ trait Kernel {
11761178

11771179
def Constant_value(const: Constant): Any
11781180

1181+
def matchConstant(constant: Constant): Option[Unit | Null | Int | Boolean | Byte | Short | Int | Long | Float | Double | Char | String | Type]
11791182
def matchConstant_Unit(constant: Constant): Boolean
11801183
def matchConstant_Null(constant: Constant): Boolean
11811184
def matchConstant_Boolean(constant: Constant): Option[Boolean]
@@ -1189,6 +1192,7 @@ trait Kernel {
11891192
def matchConstant_String(constant: Constant): Option[String]
11901193
def matchConstant_ClassTag(constant: Constant): Option[Type]
11911194

1195+
def Constant_apply(x: Unit | Null | Int | Boolean | Byte | Short | Int | Long | Float | Double | Char | String | Type): Constant
11921196
def Constant_Unit_apply(): Constant
11931197
def Constant_Null_apply(): Constant
11941198
def Constant_Boolean_apply(x: Boolean): Constant
@@ -1470,6 +1474,7 @@ trait Kernel {
14701474
def Definitions_ClassClass: Symbol
14711475
def Definitions_ArrayClass: Symbol
14721476
def Definitions_PredefModule: Symbol
1477+
def Definitions_Predef_classOf: Symbol
14731478

14741479
def Definitions_JavaLangPackage: Symbol
14751480

library/src/scala/tasty/reflect/StandardDefinitions.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ trait StandardDefinitions extends Core {
8585
/** The module symbol of module `scala.Predef`. */
8686
def PredefModule: Symbol = kernel.Definitions_PredefModule
8787

88+
/** The method symbol of method `scala.Predef.classOf`. */
89+
def Predef_classOf: Symbol = kernel.Definitions_Predef_classOf
90+
8891
/** The module symbol of package `java.lang`. */
8992
def JavaLangPackage: Symbol = kernel.Definitions_JavaLangPackage
9093

library/src/scala/tasty/reflect/TypeOrBoundsOps.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ trait TypeOrBoundsOps extends Core {
6464

6565
object Type {
6666

67+
def apply(clazz: Class[_])(implicit ctx: Context): Type = kernel.Type_apply(clazz)
68+
6769
object IsConstantType {
6870
/** Matches any ConstantType and returns it */
6971
def unapply(tpe: TypeOrBounds)(implicit ctx: Context): Option[ConstantType] =

0 commit comments

Comments
 (0)