Skip to content

Commit 93c56e4

Browse files
minokiclaude
andcommitted
FSyntax: Add BoxedType and Box/Unbox primitives for WasmGC
Add BoxedType constructor to FSyntax.Ty and UnboxedTy datatype to enumerate fixed-size primitive types. Add BoxOp/UnboxOp to PrimOp for converting between boxed and unboxed representations. Update type compatibility rules so that BoxedType is compatible with all types except unboxed primitives. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 8ec3457 commit 93c56e4

File tree

8 files changed

+116
-2
lines changed

8 files changed

+116
-2
lines changed

src/cps/check.sml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,21 @@ struct
316316
(env, fn () => "LuaMethodNOp", F.Types.lua_value)) args
317317
; List.tabulate (n, fn _ => F.Types.lua_value)
318318
)
319-
| _ => raise TypeError "invalid LuaMethodNOp"))
319+
| _ => raise TypeError "invalid LuaMethodNOp")
320+
| F.BoxOp fsp =>
321+
(case (tyargs, args) of
322+
([], [e]) =>
323+
( checkValue (env, fn () => "BoxOp", F.unboxedTyToTy fsp) e
324+
; [F.BoxedType]
325+
)
326+
| _ => raise TypeError "invalid BoxOp")
327+
| F.UnboxOp fsp =>
328+
(case (tyargs, args) of
329+
([], [e]) =>
330+
( checkValue (env, fn () => "UnboxOp", F.BoxedType) e
331+
; [F.unboxedTyToTy fsp]
332+
)
333+
| _ => raise TypeError "invalid UnboxOp"))
320334
| inferSimpleExp (env, C.Record fields) =
321335
[F.RecordType (Syntax.LabelMap.map (inferValue env) fields)]
322336
| inferSimpleExp (env as {tyEnv, ...}, C.ExnTag {name = _, payloadTy}) =
@@ -332,6 +346,7 @@ struct
332346
^ Printer.build (FPrinter.doTy 0 recordTy) ^ ", label="
333347
^ Syntax.print_Label label))
334348
| anyTy as F.AnyType F.TypeKind => [anyTy]
349+
| F.BoxedType => [F.BoxedType]
335350
| recordTy =>
336351
raise Fail
337352
("invalid record type for projection: "

src/cps/erase-poly.sml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ struct
3838
F.AnyType kind2 => F.AnyType (F.ArrowKind (kind, kind2))
3939
| ty => F.TypeFn (tv, kind, goTy env ty))
4040
| goTy _ (ty as F.AnyType _) = ty
41+
| goTy _ (ty as F.BoxedType) = ty
4142
| goTy env (ty as F.DelayedSubst _) =
4243
goTy env (F.forceTy ty)
4344
fun goValue ({valMap, ...}: env) (v as C.Var vid) =

src/cps/syntax.sml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,8 @@ struct
320320
isDiscardablePrimEffect e
321321
| isDiscardable (PrimOp {primOp = F.LuaMethodNOp _, args = [], ...}) =
322322
false (* should not occur *)
323+
| isDiscardable (PrimOp {primOp = F.BoxOp _, ...}) = true
324+
| isDiscardable (PrimOp {primOp = F.UnboxOp _, ...}) = true
323325
| isDiscardable (Record _) = true
324326
| isDiscardable (ExnTag _) = true
325327
| isDiscardable (Projection _) = true
@@ -661,7 +663,9 @@ struct
661663
| F.LuaCallNOp _ => "PrimOp(LuaCallNOp)"
662664
| F.LuaMethodOp _ => "PrimOp(LuaMethodOp)"
663665
| F.LuaMethod1Op _ => "PrimOp(LuaMethod1Op)"
664-
| F.LuaMethodNOp _ => "PrimOp(LuaMethodNOp)")
666+
| F.LuaMethodNOp _ => "PrimOp(LuaMethodNOp)"
667+
| F.BoxOp _ => "PrimOp(BoxOp)"
668+
| F.UnboxOp _ => "PrimOp(UnboxOp)")
665669
| simpleExpToString (Record _) = "Record"
666670
| simpleExpToString (ExnTag _) = "ExnTag"
667671
| simpleExpToString (Projection _) = "Projection"
@@ -734,6 +738,7 @@ struct
734738
| goTy (F.TypeFn (tv, kind, ty)) =
735739
F.TypeFn (tv, kind, goTy ty)
736740
| goTy (ty as F.AnyType _) = ty
741+
| goTy (ty as F.BoxedType) = ty
737742
| goTy (ty as F.DelayedSubst _) =
738743
goTy (F.forceTy ty)
739744
in

src/cps/unpack-record-parameter.sml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ in
353353
SOME fieldTy => fieldTy
354354
| NONE => raise Fail "missing field")
355355
| anyTy as FSyntax.AnyType FSyntax.TypeKind => anyTy
356+
| boxedTy as FSyntax.BoxedType => boxedTy
356357
| recordTy =>
357358
raise Fail
358359
("invalid record type: "
@@ -391,6 +392,7 @@ in
391392
| NONE => raise Fail "missing field")
392393
| anyTy as FSyntax.AnyType FSyntax.TypeKind =>
393394
anyTy
395+
| boxedTy as FSyntax.BoxedType => boxedTy
394396
| recordTy =>
395397
raise Fail
396398
("invalid record type: "

src/f/check.sml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ struct
7777
F.ArrowKind
7878
(kind, kindOf (TypedSyntax.TyVarMap.insert (env, tv, kind)) ty)
7979
| kindOf _ (F.AnyType kind) = kind
80+
| kindOf _ F.BoxedType = F.TypeKind
8081
| kindOf env (ty as F.DelayedSubst _) =
8182
kindOf env (F.forceTy ty)
8283
and checkKind (env, expectedKind) ty =
@@ -135,6 +136,7 @@ struct
135136
| normalizeType env (F.TypeFn (tv, kind, ty)) =
136137
F.TypeFn (tv, kind, normalizeType env ty)
137138
| normalizeType _ (ty as F.AnyType _) = ty
139+
| normalizeType _ (ty as F.BoxedType) = ty
138140
| normalizeType env (ty as F.DelayedSubst _) =
139141
normalizeType env (F.forceTy ty)
140142

@@ -206,6 +208,11 @@ struct
206208
end
207209
| sameType'' _ (F.AnyType _, _) = true
208210
| sameType'' _ (_, F.AnyType _) = true
211+
| sameType'' _ (F.BoxedType, F.BoxedType) = true
212+
| sameType'' _ (F.BoxedType, ty) =
213+
not (F.isUnboxedTy ty)
214+
| sameType'' _ (ty, F.BoxedType) =
215+
not (F.isUnboxedTy ty)
209216
| sameType'' _ _ = false
210217
(*: val sameType : F.Ty * F.Ty -> bool *)
211218
val sameType =
@@ -602,6 +609,10 @@ struct
602609
List.app (fn a => checkExp (env, F.Types.lua_value, a)) args;
603610
loop (n, Syntax.LabelMap.empty)
604611
end
612+
| inferExp (env, F.PrimExp (F.BoxOp fsp, [], [e])) =
613+
(checkExp (env, F.unboxedTyToTy fsp, e); F.BoxedType)
614+
| inferExp (env, F.PrimExp (F.UnboxOp fsp, [], [e])) =
615+
(checkExp (env, F.BoxedType, e); F.unboxedTyToTy fsp)
605616
| inferExp (_, F.PrimExp (p, _, _)) =
606617
raise TypeError
607618
("PrimOp with invalid arguments: "

src/f/printer.sml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ struct
6969
doTyVar tv
7070
@ P.Fragment " : " :: doKind 0 kind @ P.Fragment ". " :: doTy 0 ty)
7171
| doTy _ (F.AnyType _) = [P.Fragment "Any"]
72+
| doTy _ F.BoxedType = [P.Fragment "Boxed"]
7273
| doTy prec (ty as F.DelayedSubst _) =
7374
doTy prec (F.forceTy ty)
7475
fun doPrimOp (F.IntConstOp x) =
@@ -137,6 +138,8 @@ struct
137138
| doPrimOp (F.LuaMethodOp _) = [P.Fragment "LuaMethod"]
138139
| doPrimOp (F.LuaMethod1Op _) = [P.Fragment "LuaMethod1"]
139140
| doPrimOp (F.LuaMethodNOp _) = [P.Fragment "LuaMethodN"]
141+
| doPrimOp (F.BoxOp _) = [P.Fragment "Box"]
142+
| doPrimOp (F.UnboxOp _) = [P.Fragment "Unbox"]
140143
fun doPat _ (F.WildcardPat _) = [P.Fragment "_"]
141144
| doPat _ (F.SConPat {scon = F.IntegerConstant x, ...}) =
142145
[P.Fragment (IntInf.toString x)]

src/f/syntax.sml

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,23 @@ sig
1515
| ExistsType of TyVar * Kind * Ty
1616
| TypeFn of TyVar * Kind * Ty (* type-level function *)
1717
| AnyType of Kind (* erased type *)
18+
| BoxedType (* boxed value type *)
1819
| DelayedSubst of DelayedSubst ref
1920
and DelayedSubst =
2021
UnevaluatedSubst of Ty TypedSyntax.TyVarMap.map * Ty
2122
| EvaluatedSubst of Ty
2223
datatype ConBind = ConBind of TypedSyntax.VId * Ty option
2324
datatype DatBind = DatBind of TyVar list * TyVar * ConBind list
25+
datatype UnboxedTy =
26+
UBTyInt32
27+
| UBTyInt64
28+
| UBTyWord32
29+
| UBTyWord64
30+
| UBTyReal
31+
| UBTyChar
32+
| UBTyChar16
33+
| UBTyChar32
34+
| UBTyBool
2435
datatype PrimOp =
2536
IntConstOp of IntInf.int (* 1 type argument *)
2637
| WordConstOp of IntInf.int (* 1 type argument *)
@@ -63,6 +74,8 @@ sig
6374
| LuaMethodNOp of
6475
string
6576
* int (* returnArity (int), value argument: prim_effect, object, arguments *)
77+
| BoxOp of UnboxedTy (* 0 type args, 1 value arg *)
78+
| UnboxOp of UnboxedTy (* 0 type args, 1 value arg *)
6679
datatype PatternSCon =
6780
IntegerConstant of IntInf.int
6881
| WordConstant of IntInf.int
@@ -201,10 +214,13 @@ sig
201214
val lua_value: Ty
202215
val js_value: Ty
203216
val prim_effect: Ty
217+
val boxed: Ty
204218
val list: Ty -> Ty
205219
val vector: Ty -> Ty
206220
val array: Ty -> Ty
207221
end
222+
val isUnboxedTy: Ty -> bool
223+
val unboxedTyToTy: UnboxedTy -> Ty
208224
val AsciiStringAsDatatypeTag: TargetInfo.target_info * string -> Exp
209225
val strIdToVId: TypedSyntax.StrId -> TypedSyntax.VId
210226
val SimplifyingAndalsoExp: Exp * Exp -> Exp
@@ -257,12 +273,23 @@ struct
257273
| ExistsType of TyVar * Kind * Ty
258274
| TypeFn of TyVar * Kind * Ty (* type-level function *)
259275
| AnyType of Kind (* erased type *)
276+
| BoxedType (* boxed value type *)
260277
| DelayedSubst of DelayedSubst ref
261278
and DelayedSubst =
262279
UnevaluatedSubst of Ty TypedSyntax.TyVarMap.map * Ty
263280
| EvaluatedSubst of Ty
264281
datatype ConBind = ConBind of TypedSyntax.VId * Ty option
265282
datatype DatBind = DatBind of TyVar list * TyVar * ConBind list
283+
datatype UnboxedTy =
284+
UBTyInt32
285+
| UBTyInt64
286+
| UBTyWord32
287+
| UBTyWord64
288+
| UBTyReal
289+
| UBTyChar
290+
| UBTyChar16
291+
| UBTyChar32
292+
| UBTyBool
266293
datatype PrimOp =
267294
IntConstOp of IntInf.int (* 1 type argument *)
268295
| WordConstOp of IntInf.int (* 1 type argument *)
@@ -305,6 +332,8 @@ struct
305332
| LuaMethodNOp of
306333
string
307334
* int (* returnArity (int), value argument: prim_effect, object, arguments *)
335+
| BoxOp of UnboxedTy (* 0 type args, 1 value arg *)
336+
| UnboxOp of UnboxedTy (* 0 type args, 1 value arg *)
308337
datatype PatternSCon =
309338
IntegerConstant of IntInf.int
310339
| WordConstant of IntInf.int
@@ -447,13 +476,34 @@ struct
447476
val lua_value = TyVar PrimTypes.Names.lua_value
448477
val js_value = TyVar PrimTypes.Names.js_value
449478
val prim_effect = TyVar PrimTypes.Names.prim_effect
479+
val boxed = BoxedType
450480
fun list ty =
451481
AppType {applied = TyVar PrimTypes.Names.list, arg = ty}
452482
fun vector ty =
453483
AppType {applied = TyVar PrimTypes.Names.vector, arg = ty}
454484
fun array ty =
455485
AppType {applied = TyVar PrimTypes.Names.array, arg = ty}
456486
end
487+
fun unboxedTyToTy UBTyInt32 = Types.int32
488+
| unboxedTyToTy UBTyInt64 = Types.int64
489+
| unboxedTyToTy UBTyWord32 = Types.word32
490+
| unboxedTyToTy UBTyWord64 = Types.word64
491+
| unboxedTyToTy UBTyReal = Types.real
492+
| unboxedTyToTy UBTyChar = Types.char
493+
| unboxedTyToTy UBTyChar16 = Types.char16
494+
| unboxedTyToTy UBTyChar32 = Types.char32
495+
| unboxedTyToTy UBTyBool = Types.bool
496+
fun isUnboxedTy (TyVar tv) =
497+
TypedSyntax.eqTyVar (tv, PrimTypes.Names.int32)
498+
orelse TypedSyntax.eqTyVar (tv, PrimTypes.Names.int64)
499+
orelse TypedSyntax.eqTyVar (tv, PrimTypes.Names.word32)
500+
orelse TypedSyntax.eqTyVar (tv, PrimTypes.Names.word64)
501+
orelse TypedSyntax.eqTyVar (tv, PrimTypes.Names.real)
502+
orelse TypedSyntax.eqTyVar (tv, PrimTypes.Names.char)
503+
orelse TypedSyntax.eqTyVar (tv, PrimTypes.Names.char16)
504+
orelse TypedSyntax.eqTyVar (tv, PrimTypes.Names.char32)
505+
orelse TypedSyntax.eqTyVar (tv, PrimTypes.Names.bool)
506+
| isUnboxedTy _ = false
457507
fun FnType (param, result) =
458508
MultiFnType ([param], result)
459509
fun TupleType xs =
@@ -575,6 +625,7 @@ struct
575625
TypeFn (tv, kind, ty)
576626
end
577627
| go subst (ty as AnyType _) = ty
628+
| go subst (ty as BoxedType) = ty
578629
| go subst (DelayedSubst r) =
579630
(case !r of
580631
EvaluatedSubst ty => go subst ty
@@ -601,6 +652,7 @@ struct
601652
NONE => ty
602653
| SOME replacement => replacement)
603654
| lazySubstTy subst (ty as AnyType _) = ty
655+
| lazySubstTy subst (ty as BoxedType) = ty
604656
| lazySubstTy subst ty =
605657
if TypedSyntax.TyVarMap.isEmpty subst then ty
606658
else DelayedSubst (ref (UnevaluatedSubst (subst, ty)))
@@ -630,6 +682,7 @@ struct
630682
| go subst (TypeFn (tv, kind, ty)) =
631683
TypeFn (tv, kind, lazySubstTy subst ty)
632684
| go subst (ty as AnyType _) = ty
685+
| go subst (ty as BoxedType) = ty
633686
| go subst (DelayedSubst r) =
634687
(case !r of
635688
EvaluatedSubst ty => go subst ty
@@ -660,6 +713,7 @@ struct
660713
| weakNormalizeTy (ty as ExistsType _) = ty
661714
| weakNormalizeTy (ty as TypeFn _) = ty
662715
| weakNormalizeTy (ty as AnyType _) = ty
716+
| weakNormalizeTy (ty as BoxedType) = ty
663717
| weakNormalizeTy (ty as DelayedSubst _) =
664718
substAndWeakNormalizeTy TypedSyntax.TyVarMap.empty ty
665719

@@ -678,6 +732,7 @@ struct
678732
| check (TypeFn (tv', _, ty)) =
679733
if TypedSyntax.eqTyVar (tv, tv') then false else check ty
680734
| check (AnyType _) = false
735+
| check BoxedType = false
681736
| check (ty as DelayedSubst _) =
682737
check (forceTy ty)
683738
in
@@ -700,6 +755,7 @@ struct
700755
| go (TypeFn (TypedSyntax.MkTyVar (_, i), _, ty), j) =
701756
go (ty, Int.min (i, j))
702757
| go (AnyType _, i) = i
758+
| go (BoxedType, i) = i
703759
| go (ty as DelayedSubst _, i) =
704760
go (forceTy ty, i)
705761
in
@@ -752,6 +808,7 @@ struct
752808
else
753809
TypeFn (tv', kind, go ty')
754810
| go (ty as AnyType _) = ty
811+
| go (ty as BoxedType) = ty
755812
| go (ty as DelayedSubst _) =
756813
go (forceTy ty)
757814
in
@@ -772,6 +829,7 @@ struct
772829
| isRelevant (ExistsType (_, _, ty)) = isRelevant ty (* approximation *)
773830
| isRelevant (TypeFn (_, _, ty)) = isRelevant ty (* approximation *)
774831
| isRelevant (AnyType _) = false
832+
| isRelevant BoxedType = false
775833
| isRelevant (DelayedSubst _) = true (* conservative *)
776834
fun doTy (ty as TyVar tv) =
777835
(case TypedSyntax.TyVarMap.find (subst, tv) of
@@ -848,6 +906,7 @@ struct
848906
) (* TODO: use fresh tyvar if necessary *)
849907
| NONE => TypeFn (tv, kind, doTy ty)) *)
850908
| doTy (ty as AnyType _) = ty
909+
| doTy (ty as BoxedType) = ty
851910
| doTy (ty as DelayedSubst _) = substAndForceTy subst ty
852911
val doTy = fn ty =>
853912
if isRelevant ty then doTy ty else ty (* optimization *)
@@ -1022,6 +1081,7 @@ struct
10221081
NONE => ty
10231082
| SOME replacement => replacement)
10241083
| doTy (ty as AnyType _) = ty
1084+
| doTy (ty as BoxedType) = ty
10251085
| doTy ty =
10261086
DelayedSubst (ref (UnevaluatedSubst (subst, ty)))
10271087
val doTy =
@@ -1207,6 +1267,7 @@ struct
12071267
| freeTyVarsInTy (bound, TypeFn (tv, _, ty)) acc =
12081268
freeTyVarsInTy (TypedSyntax.TyVarSet.add (bound, tv), ty) acc
12091269
| freeTyVarsInTy (_, AnyType _) acc = acc
1270+
| freeTyVarsInTy (_, BoxedType) acc = acc
12101271
| freeTyVarsInTy (bound, ty as DelayedSubst _) acc =
12111272
freeTyVarsInTy (bound, forceTy ty) acc
12121273
fun freeTyVarsInPat (_, WildcardPat _) acc = acc
@@ -1616,6 +1677,7 @@ struct
16161677
| print_Ty (TypeFn (tv, _, x)) =
16171678
"TypeFn(" ^ print_TyVar tv ^ "," ^ print_Ty x ^ ")"
16181679
| print_Ty (AnyType _) = "AnyType"
1680+
| print_Ty BoxedType = "BoxedType"
16191681
| print_Ty (ty as DelayedSubst _) =
16201682
print_Ty (forceTy ty)
16211683
fun print_PrimOp (IntConstOp x) = "IntConstOp " ^ IntInf.toString x
@@ -1674,6 +1736,19 @@ struct
16741736
| print_PrimOp (LuaMethodOp _) = "LuaMethodOp"
16751737
| print_PrimOp (LuaMethod1Op _) = "LuaMethod1Op"
16761738
| print_PrimOp (LuaMethodNOp _) = "LuaMethodNOp"
1739+
| print_PrimOp (BoxOp fsp) =
1740+
"BoxOp(" ^ print_UnboxedTy fsp ^ ")"
1741+
| print_PrimOp (UnboxOp fsp) =
1742+
"UnboxOp(" ^ print_UnboxedTy fsp ^ ")"
1743+
and print_UnboxedTy UBTyInt32 = "Int32"
1744+
| print_UnboxedTy UBTyInt64 = "Int64"
1745+
| print_UnboxedTy UBTyWord32 = "Word32"
1746+
| print_UnboxedTy UBTyWord64 = "Word64"
1747+
| print_UnboxedTy UBTyReal = "Real"
1748+
| print_UnboxedTy UBTyChar = "Char"
1749+
| print_UnboxedTy UBTyChar16 = "Char16"
1750+
| print_UnboxedTy UBTyChar32 = "Char32"
1751+
| print_UnboxedTy UBTyBool = "Bool"
16771752
fun print_Pat (WildcardPat _) = "WildcardPat"
16781753
| print_Pat
16791754
(SConPat

src/f/transform.sml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,8 @@ struct
716716
| isDiscardablePrimOp (F.LuaMethodOp _) = false
717717
| isDiscardablePrimOp (F.LuaMethod1Op _) = false
718718
| isDiscardablePrimOp (F.LuaMethodNOp _) = false
719+
| isDiscardablePrimOp (F.BoxOp _) = true
720+
| isDiscardablePrimOp (F.UnboxOp _) = true
719721
fun isDiscardable (F.PrimExp (primOp, _, args)) =
720722
isDiscardablePrimOp primOp andalso List.all isDiscardable args
721723
| isDiscardable (F.VarExp _) = true

0 commit comments

Comments
 (0)