diff --git a/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala b/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala index 7043169e2ae3..cda74e710047 100644 --- a/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala +++ b/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala @@ -10,6 +10,7 @@ import Names.TermName import NameKinds.{InlineAccessorName, InlineBinderName, InlineScrutineeName} import config.Printers.inlining import util.SimpleIdentityMap +import CheckRealizable.{Realizable, realizability} import collection.mutable @@ -417,18 +418,34 @@ class InlineReducer(inliner: Inliner)(using Context): for (bindings, expr) <- recur(cases) yield // drop unusable vals and check that no referenes to unusable symbols remain val cleanupUnusable = new TreeMap: + + /** Whether we are currently in a type position */ + var inType: Boolean = false + override def transform(tree: Tree)(using Context): Tree = tree match case tree: ValDef if unusable.contains(tree.symbol) => EmptyTree case id: Ident if unusable.contains(id.symbol) => - report.error( - em"""${id.symbol} is unusable in ${ctx.owner} because it refers to an erased expression - |in the selector of an inline match that reduces to - | - |${Block(bindings, expr)}""", - tree.srcPos) + // This conditions allows references to erased values in type + // positions provided the types of these references are + // realizable. See erased-inline-product.scala and + // tests/neg/erased-inline-unrealizable-path.scala. + if !inType || (realizability(id.tpe.widen) ne Realizable) then + report.error( + em"""${id.symbol} is unusable in ${ctx.owner} because it refers to an erased expression + |in the selector of an inline match that reduces to + | + |${Block(bindings, expr)}""", + tree.srcPos) tree - case _ => super.transform(tree) + case _ if tree.isType => + val saved = inType + inType = true + val tree1 = super.transform(tree) + inType = saved + tree1 + case _ => + super.transform(tree) val bindings1 = bindings.mapConserve(cleanupUnusable.transform).collect: case mdef: MemberDef => mdef diff --git a/tests/neg/erased-inline-unrealizable-path.scala b/tests/neg/erased-inline-unrealizable-path.scala new file mode 100644 index 000000000000..6b44e891f585 --- /dev/null +++ b/tests/neg/erased-inline-unrealizable-path.scala @@ -0,0 +1,22 @@ +import scala.compiletime.erasedValue + +class Box[T] + +trait T: + type L + +class A extends T: + type L <: Int + +class B extends T: + type L >: String + +inline def f() = + inline erasedValue[A & B] match + case x: (A & B) => + val y: String = "String" + val z: x.L = y + z: Int + +@main def Test = + println(f().abs) // error diff --git a/tests/run/erased-inline-product-1.scala b/tests/run/erased-inline-product-1.scala new file mode 100644 index 000000000000..91e8b8d1dd9b --- /dev/null +++ b/tests/run/erased-inline-product-1.scala @@ -0,0 +1,21 @@ +import scala.compiletime.{erasedValue, summonInline, error} + +inline def sizeTuple[T<: Tuple](): Long = + inline erasedValue[T] match + case _: EmptyTuple => 0 + case _: (h *: t) => size[h] + sizeTuple[t]() + +inline def sizeProduct[T <: Product](m: scala.deriving.Mirror.ProductOf[T]): Long = + sizeTuple[m.MirroredElemTypes]() + +inline def size[T]: Long = + inline erasedValue[T] match + case _: Char => 2 + case _: Int => 4 + case _: Long => 8 + case _: Double => 8 + case p: Product => sizeProduct(summonInline[scala.deriving.Mirror.ProductOf[p.type]]) + case _ => error(s"unsupported type") + +@main def Test = + assert(size[(Int, Long)] == 12) diff --git a/tests/run/erased-inline-product-2.scala b/tests/run/erased-inline-product-2.scala new file mode 100644 index 000000000000..d7210088b89d --- /dev/null +++ b/tests/run/erased-inline-product-2.scala @@ -0,0 +1,21 @@ +import scala.compiletime.{erasedValue, summonInline, error} + +inline def sizeTuple[T<: Tuple](): Long = + inline erasedValue[T] match + case _: EmptyTuple => 0 + case _: (h *: t) => size[h] + sizeTuple[t]() + +inline def sizeProduct[T <: Product](m: scala.deriving.Mirror.ProductOf[T]): Long = + sizeTuple[m.MirroredElemTypes]() + +inline def size[T]: Long = + inline erasedValue[T] match + case _: Char => 2 + case _: Int => 4 + case _: Long => 8 + case _: Double => 8 + case _: (p & Product) => sizeProduct[(p & Product)](summonInline[scala.deriving.Mirror.ProductOf[p & Product]]) + case _ => error(s"unsupported type") + +@main def Test = + assert(size[(Int, Long)] == 12) diff --git a/tests/run/erased-inline-product-3.scala b/tests/run/erased-inline-product-3.scala new file mode 100644 index 000000000000..7101847e548e --- /dev/null +++ b/tests/run/erased-inline-product-3.scala @@ -0,0 +1,21 @@ +import scala.compiletime.{erasedValue, summonInline, error} + +inline def sizeTuple[T <: Tuple](): Long = + inline erasedValue[T] match + case _: EmptyTuple => 0 + case _: (h *: t) => size[h] + sizeTuple[t]() + +inline def sizeProduct[T](m: scala.deriving.Mirror.ProductOf[T]): Long = + sizeTuple[m.MirroredElemTypes]() + +inline def size[T]: Long = + inline erasedValue[T] match + case _: Char => 2 + case _: Int => 4 + case _: Long => 8 + case _: Double => 8 + case _: Product => sizeProduct[T](summonInline[scala.deriving.Mirror.ProductOf[T]]) + case _ => error(s"unsupported type") + +@main def Test = + assert(size[(Int, Long)] == 12)