-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Allow references to erased values in types #24421
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
da3f431 to
c9ff3ad
Compare
|
For a path-dependent type to be sound, we need evidence that the value in the path can be created. That is not the case for erased values, I think. |
Ah. Could we maybe use |
|
Another simpler workaround; removing the 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)
|
|
@odersky mentioned that erased values should always be checked for realizability. But currently the following compile: import scala.compiletime.erasedValue
inline def f() =
inline erasedValue[String & Int] match
case _: String => "String"
@main def Test = println(f())Isn't that a problem? |
|
No, there's a special exception for inline matches. If you use erased val x = compiletime.erasedValue[Int & String] |
|
Okay, but that's still a problem inside inline matches, in its current state this PR would allow: 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)So we need an additional inhabitance check, don't we? |
|
I tried to only allow references to erased values if their types are realizable. This allows to accept the motivating example from this PR, but to still reject the bad bounds example above. However, it would accept: import scala.compiletime.erasedValue
class Box[T]
inline def f() =
inline erasedValue[String & Int] match
case x: (String & Int) =>
val y = Box[x.type]
y
@main def Test =
println(f())because apparently |
|
Yes, as long as intersections don't have any type members we don't care. |
c9ff3ad to
e1a4242
Compare
This PR allows references to erased values from within types. This should be safe as types are also erased.
Motivation
@asunluer found that the following code (
tests/run/erased-inline-product.scala) fails to compile:This currently fails with:
which is unfortunate, because this looks like a useful pattern. I don’t know of a better way to decompose a
Productin an inline function, and references to erased values from within types should be safe.As a workaround, we tried introducing a type variable using
p & Product, which is demonstrated intests/run/erased-inline-product-2.scala. That version already works without any compiler changes, but it feels more fragile and less readable.We could probably also use match types, but we would prefer to stay at the term level if possible.