Zulip Chat Archive
Stream: lean4
Topic: Type specialization & boxing
Mac Malone (Oct 02 2024 at 16:54):
A fun little discrepancy I noticed with how the runtime representation of types is determined:
/-- i.e., `Subtype` -/
structure Generic {α : Type u} (P : α → Prop) where
val : α
property : P val
/-
def irrelevant : ◾ :=
ret ◾
-/
set_option trace.compiler.ir.result true in
def irrelevant : Generic (fun _ : Type u => True) :=
⟨PUnit, .intro⟩
structure Specialized (P : Type u → Prop) where
val : Type u
property : P val
/-
def boxed : obj :=
let x_1 : obj := ctor_0[Specialized.mk];
ret x_1
-/
set_option trace.compiler.ir.result true in
def boxed : Specialized (fun _ : Type u => True) :=
⟨PUnit, .intro⟩
As Specialized
is simply a specialization of Generic
one might expect them to receive the same representation, but they do not. In fact, Generic
arguably receives the better (more efficient) one.
Mario Carneiro (Oct 02 2024 at 19:06):
I don't think this is documented at ffi.md
Mac Malone (Oct 02 2024 at 19:07):
Yeah, I suspect it might be a bug (though I am not sure in which direction).
Mario Carneiro (Oct 02 2024 at 19:09):
lol, there is at least one almost-certainly-a-bug documented there
Mario Carneiro (Oct 02 2024 at 19:10):
I suspect in this particular case that they are getting the same repr though
Mario Carneiro (Oct 02 2024 at 19:10):
it might not be the same in more nested situations?
Mac Malone (Oct 02 2024 at 19:11):
Mario Carneiro said:
I suspect in this particular case that they are getting the same repr though
What do you mean?
Mac Malone (Oct 02 2024 at 19:12):
In the above example, they are definitely behaving differently.
Mario Carneiro (Oct 02 2024 at 19:15):
are you sure? Both ◾
and ctor_0[Specialized.mk]
will be represented as lean_box(0)
Mario Carneiro (Oct 02 2024 at 19:15):
i.e if another part of the code decided that actually Specialized
should be getting the same repr as Generic
, there would not be an ABI mismatch
Mac Malone (Oct 02 2024 at 19:16):
They can be both lean_box(0)
, but they do not always have the same properities. Parameters of type ◾
will be erased for FFI (whereas the boxed version will not) and the IR will delete operations on objects of ◾
if can (whereas it will perserve the boxed versions).
Mac Malone (Oct 02 2024 at 19:25):
For an example of how this causes different behavior:
-- Stub just for show
def IsNat := fun _ : Type => True
def GNat := Generic IsNat
instance : Nonempty GNat := ⟨PUnit, .intro⟩
opaque GNat.ofNat (n : Nat) : GNat := unsafe unsafeCast n
opaque GNat.toNat (n : GNat) : Nat := unsafe unsafeCast n
instance : Repr GNat where
reprPrec n prec := reprPrec n.toNat prec
/-- info: 0 -/
#guard_msgs in #eval GNat.ofNat 3
def SNat := Specialized IsNat
instance : Nonempty SNat := ⟨PUnit, .intro⟩
opaque SNat.ofNat (n : Nat) : SNat := unsafe unsafeCast n
opaque SNat.toNat (n : SNat) : Nat := unsafe unsafeCast n
instance : Repr SNat where
reprPrec n prec := reprPrec n.toNat prec
/-- info: 3 -/
#guard_msgs in #eval SNat.ofNat 3
Last updated: May 02 2025 at 03:31 UTC