Skip to content

Commit 595c5d7

Browse files
authored
Add a check for correct Array shape in quotes.reflect.ClassOfConstant (#22033)
Closes #21916 I tried to supply the ClassOfConstant with multiple other broken Types, but I was unable to break it beyond the linked issue, so I ended up adding the check for only that one case. This makes sense - the backend (and thus erasure) needs to know if the Array type parameter is a primitive type, but in other cases the erasure phase needs to know only the class, without the type parameters. It's impossible to call classOf through the quoted code (`'{classOf[t]}` with a boundless t will error out), so we don't need that additional check there. There does appear to be an issue with being able to set `'{List[Array]}` resulting in a crash, but that is beyond the scope of this fix - I will prepare a separate issue for that (edit: reported [here](#22034)).
2 parents d4421d0 + 80ae408 commit 595c5d7

File tree

4 files changed

+38
-1
lines changed

4 files changed

+38
-1
lines changed

compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

+11-1
Original file line numberDiff line numberDiff line change
@@ -2530,7 +2530,17 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
25302530

25312531
object ClassOfConstant extends ClassOfConstantModule:
25322532
def apply(x: TypeRepr): ClassOfConstant =
2533-
// TODO check that the type is a valid class when creating this constant or let Ycheck do it?
2533+
// We only check if the supplied TypeRepr is valid if it contains an Array,
2534+
// as so far only that Array could cause issues
2535+
def correctTypeApplicationForArray(typeRepr: TypeRepr): Boolean =
2536+
val isArray = typeRepr.typeSymbol != dotc.core.Symbols.defn.ArrayClass
2537+
typeRepr match
2538+
case AppliedType(_, targs) if !targs.isEmpty => true
2539+
case _ => isArray
2540+
xCheckMacroAssert(
2541+
correctTypeApplicationForArray(x),
2542+
"Illegal empty Array type constructor. Please supply a type parameter."
2543+
)
25342544
dotc.core.Constants.Constant(x)
25352545
def unapply(constant: ClassOfConstant): Some[TypeRepr] = Some(constant.typeValue)
25362546
end ClassOfConstant

tests/neg-macros/i21916.check

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
-- Error: tests/neg-macros/i21916/Test_2.scala:3:27 --------------------------------------------------------------------
3+
3 |@main def Test = Macro.test() // error
4+
| ^^^^^^^^^^^^
5+
| Exception occurred while executing macro expansion.
6+
| java.lang.AssertionError: Illegal empty Array type constructor. Please supply a type parameter.
7+
| at Macro$.testImpl(Macro_1.scala:8)
8+
|
9+
|---------------------------------------------------------------------------------------------------------------------
10+
|Inline stack trace
11+
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
12+
|This location contains code that was inlined from Macro_1.scala:3
13+
3 | inline def test() = ${testImpl}
14+
| ^^^^^^^^^^^
15+
---------------------------------------------------------------------------------------------------------------------

tests/neg-macros/i21916/Macro_1.scala

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import scala.quoted._
2+
object Macro:
3+
inline def test() = ${testImpl}
4+
def testImpl(using Quotes): Expr[Any] = {
5+
import quotes.reflect._
6+
val tpe = TypeRepr.of[Array[Byte]] match
7+
case AppliedType(tycons, _) => tycons
8+
Literal(ClassOfConstant(tpe)).asExpr
9+
}

tests/neg-macros/i21916/Test_2.scala

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// lack of type ascription is on purpose,
2+
// as that was part of what would cause the crash before.
3+
@main def Test = Macro.test() // error

0 commit comments

Comments
 (0)