Skip to content

Commit 4d8344e

Browse files
authored
Updates from master (#865)
* `sizeOf` and `alignOf` * `union` values and their fields * one more byte simplification
2 parents dcc138f + b4182cd commit 4d8344e

File tree

62 files changed

+8343
-4297
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+8343
-4297
lines changed

Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ update-exec-smir:
132132

133133
# Update checked-in smir.json files (using stable-mir-json dependency and jq)
134134
# file paths for spans in the the updated smir are truncated to known infixes
135+
# Compiles run-smir-random files as libraries, others as binaries
135136
.PHONY: update-smir-json
136137
update-smir-json: TARGETS = $(shell git ls-files | grep -e ".*\.smir\.json$$" | grep -v -e pinocchio)
137138
update-smir-json: SMIR = cargo -q -Z unstable-options -C deps/stable-mir-json run --
@@ -140,7 +141,11 @@ update-smir-json: stable-mir-json
140141
dir=$$(realpath $$(dirname $$file)); \
141142
rust=$$dir/$$(basename $${file%.smir.json}.rs); \
142143
[ -f "$$rust" ] || (echo "Source file $$rust missing."; exit 1); \
143-
${SMIR} -Zno-codegen --out-dir $$dir $$rust; \
144+
if echo "$$file" | grep -q "run-smir-random"; then \
145+
${SMIR} --crate-type=lib --out-dir $$dir $$rust; \
146+
else \
147+
${SMIR} -Zno-codegen --out-dir $$dir $$rust; \
148+
fi; \
144149
jq '.spans[].[1].[0] |= sub("/.*lib/rustlib"; "rustlib") | .spans[].[1].[0] |= sub("/.*/integration/data"; "data")' $$file > $$file.tmp; \
145150
mv $$file.tmp $$file; \
146151
done

kmir/src/kmir/kdist/mir-semantics/kmir.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,25 @@ will effectively be no-ops at this level).
100100
```k
101101
102102
// all memory accesses relegated to another module (to be added)
103-
rule [execStmt]: <k> #execStmt(statement(statementKindAssign(PLACE, RVAL), _SPAN))
103+
rule [execStmt]: <k> #execStmt(statement(statementKindAssign(place(local(I), _PROJ) #as PLACE, RVAL), _SPAN))
104104
=>
105105
#setLocalValue(PLACE, RVAL)
106106
...
107107
</k>
108+
<locals> LOCALS </locals>
109+
requires 0 <=Int I andBool I <Int size(LOCALS)
110+
andBool notBool #isUnionType(lookupTy(tyOfLocal(getLocal(LOCALS, I))))
111+
[preserves-definedness]
112+
113+
rule [execStmt.union]: <k> #execStmt(statement(statementKindAssign(place(local(I), _PROJ) #as PLACE, RVAL), _SPAN))
114+
=>
115+
#setLocalValue(PLACE, #evalUnion(RVAL))
116+
...
117+
</k>
118+
<locals> LOCALS </locals>
119+
requires 0 <=Int I andBool I <Int size(LOCALS)
120+
andBool #isUnionType(lookupTy(tyOfLocal(getLocal(LOCALS, I))))
121+
[preserves-definedness]
108122
109123
// RVAL evaluation is implemented in rt/data.md
110124

kmir/src/kmir/kdist/mir-semantics/lemmas/kmir-lemmas.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,25 @@ This avoids building up large expressions related to overflow checks and vacuous
208208
[simplification, preserves-definedness, symbolic]
209209
```
210210

211+
Another more general simplification relates a `NUMBER` and the bytes from its representation.
212+
If the number is initially masked at 64 bits (`&Int bitmask64`), it is guaranteed to be positive
213+
and therefore the masked value equals its bytes taken individually and multiplied.
214+
Terms like this have been observed as branching conditions in a proof that heavily uses `[u8;8] <--> u64` conversions.
215+
The simplification eliminates the vacuous branches instantly.
216+
217+
```k
218+
rule ((NUMBER &Int bitmask64) &Int bitmask8) +Int 256 *Int (
219+
((NUMBER &Int bitmask64) >>Int 8 &Int bitmask8) +Int 256 *Int (
220+
((NUMBER &Int bitmask64) >>Int 8 >>Int 8 &Int bitmask8) +Int 256 *Int (
221+
((NUMBER &Int bitmask64) >>Int 8 >>Int 8 >>Int 8 &Int bitmask8) +Int 256 *Int (
222+
((NUMBER &Int bitmask64) >>Int 8 >>Int 8 >>Int 8 >>Int 8 &Int bitmask8) +Int 256 *Int (
223+
((NUMBER &Int bitmask64) >>Int 8 >>Int 8 >>Int 8 >>Int 8 >>Int 8 &Int bitmask8) +Int 256 *Int (
224+
((NUMBER &Int bitmask64) >>Int 8 >>Int 8 >>Int 8 >>Int 8 >>Int 8 >>Int 8 &Int bitmask8) +Int 256 *Int (
225+
((NUMBER &Int bitmask64) >>Int 8 >>Int 8 >>Int 8 >>Int 8 >>Int 8 >>Int 8 >>Int 8 &Int bitmask8))))))))
226+
&Int bitmask64
227+
=> NUMBER &Int bitmask64
228+
[simplification, preserves-definedness, symbolic(NUMBER)]
229+
```
211230

212231
For the case where the (symbolic) byte values are first converted to a number, the round-trip simplification requires different matching.
213232
First, the bit-masking with `&Int 255` eliminates `Bytes2Int(Int2Bytes(1, ..) +Bytes ..)` enclosing a byte-valued variable:

kmir/src/kmir/kdist/mir-semantics/rt/data.md

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ These helpers mark down, as we traverse the projection, what `Place` we are curr
312312
313313
// retains information about the value that was deconstructed by a projection
314314
syntax Context ::= CtxField( VariantIdx, List, Int , Ty )
315+
| CtxFieldUnion( FieldIdx, Value, Ty )
315316
| CtxIndex( List , Int ) // array index constant or has been read before
316317
| CtxSubslice( List , Int , Int ) // start and end always counted from beginning
317318
| CtxPointerOffset( List, Int, Int ) // pointer offset for accessing elements with an offset (Offset, Origin Length)
@@ -329,6 +330,10 @@ These helpers mark down, as we traverse the projection, what `Place` we are curr
329330
=> #buildUpdate(Aggregate(IDX, ARGS[I <- VAL]), CTXS)
330331
[preserves-definedness] // valid list indexing checked upon context construction
331332
333+
rule #buildUpdate(VAL, CtxFieldUnion(FIELD_IDX, _ARG, _TY) CTXS)
334+
=> #buildUpdate(Union(FIELD_IDX, VAL), CTXS)
335+
[preserves-definedness]
336+
332337
rule #buildUpdate(VAL, CtxIndex(ELEMS, I) CTXS)
333338
=> #buildUpdate(Range(ELEMS[I <- VAL]), CTXS)
334339
[preserves-definedness] // valid list indexing checked upon context construction
@@ -400,7 +405,7 @@ These helpers mark down, as we traverse the projection, what `Place` we are curr
400405
rule originSize(dynamicSize(SIZE)) => SIZE
401406
```
402407

403-
#### Aggregates
408+
#### Aggregates (not Union)
404409

405410
A `Field` access projection operates on `struct`s and tuples, which are represented as `Aggregate` values.
406411
The field is numbered from zero (in source order), and the field type is provided (not checked here).
@@ -445,6 +450,28 @@ This is done without consideration of the validity of the Downcast[^downcast].
445450
</k>
446451
```
447452

453+
#### Unions
454+
```k
455+
// Case: Union is in same state as field projection
456+
rule <k> #traverseProjection(
457+
DEST,
458+
Union(FIELD_IDX, ARG),
459+
projectionElemField(FIELD_IDX, TY) PROJS,
460+
CTXTS
461+
)
462+
=> #traverseProjection(
463+
DEST,
464+
ARG,
465+
PROJS,
466+
CtxFieldUnion(FIELD_IDX, ARG, TY) CTXTS
467+
)
468+
...
469+
</k>
470+
[preserves-definedness]
471+
472+
// TODO: Case: Union is in different state as field projection
473+
```
474+
448475
#### Ranges
449476

450477
An `Index` projection operates on an array or slice (`Range`) value, to access an element of the array.
@@ -934,6 +961,7 @@ Literal arrays are also built using this RValue.
934961
935962
// #mkAggregate produces an aggregate TypedLocal value of given kind from a preceeding list of values
936963
syntax Value ::= #mkAggregate ( AggregateKind )
964+
| #mkUnion ( FieldIdx )
937965
938966
rule <k> ARGS:List ~> #mkAggregate(aggregateKindAdt(_ADTDEF, VARIDX, _, _, _))
939967
=>
@@ -977,6 +1005,26 @@ Literal arrays are also built using this RValue.
9771005
</k>
9781006
```
9791007

1008+
While Unions are Aggregate in the MIR, we distinguish between them because the semantics
1009+
are different. For example, field accesses to not access different data, but interpret
1010+
that data as a different type.
1011+
```k
1012+
syntax Rvalue ::= #evalUnion ( Rvalue )
1013+
1014+
rule <k> #evalUnion(rvalueAggregate(aggregateKindAdt( _, _, _, _, someFieldIdx ( FIELD )), ARGS))
1015+
=>
1016+
#readOperands(ARGS) ~> #mkUnion(FIELD)
1017+
...
1018+
</k>
1019+
1020+
rule <k> ListItem(ARG) .List ~> #mkUnion(FIELD)
1021+
=>
1022+
Union(FIELD, ARG)
1023+
...
1024+
</k>
1025+
1026+
```
1027+
9801028
The `AggregateKind::RawPtr`, somewhat as a special case of a `struct` aggregate, constructs a raw pointer
9811029
from a given data pointer and metadata[^rawPtrAgg]. In case of a _thin_ pointer, the metadata is a unit value,
9821030
for _fat_ pointers it is a `usize` value indicating the data length.
@@ -1063,6 +1111,7 @@ This eliminates any `Deref` projections from the place, and also resolves `Index
10631111
rule #projectionsFor( CtxSubslice(_, I, J) CTXS, PROJS) => #projectionsFor(CTXS, projectionElemSubslice(I, J, false) PROJS)
10641112
// rule #projectionsFor(CtxPointerOffset(OFFSET, ORIGIN_LENGTH) CTXS, PROJS) => #projectionsFor(CTXS, projectionElemSubslice(OFFSET, ORIGIN_LENGTH, false) PROJS)
10651113
rule #projectionsFor(CtxPointerOffset( _, OFFSET, ORIGIN_LENGTH) CTXS, PROJS) => #projectionsFor(CTXS, PointerOffset(OFFSET, ORIGIN_LENGTH) PROJS)
1114+
rule #projectionsFor(CtxFieldUnion(F_IDX, _, TY) CTXS, PROJS) => #projectionsFor(CTXS, projectionElemField(F_IDX, TY) PROJS)
10661115
10671116
// Borrowing a zero-sized local that is still `NewLocal`: initialise it, then reuse the regular rule.
10681117
rule <k> rvalueRef(REGION, KIND, place(local(I), PROJS))
@@ -2075,12 +2124,18 @@ Since our arithmetic operations signal undefined behaviour on overflow independe
20752124

20762125
#### "Nullary" operations reifying type information
20772126

2078-
`nullOpSizeOf`
2079-
`nullOpAlignOf`
2080-
`nullOpOffsetOf(VariantAndFieldIndices)`
2127+
Operation `nullOpSizeOf` returns the size in bytes of a data structure or primitive type, as a `usize`.
2128+
Operation `nullOpAlignOf` returns the required alignment of a data structure or primitive type, as a `usize`.
2129+
This information is read from the layout in the `TypeInfo` if available, or a fixed constant for primitive types.
20812130

20822131
```k
20832132
// FIXME: 64 is hardcoded since usize not supported
2133+
rule <k> rvalueNullaryOp(nullOpSizeOf, TY)
2134+
=>
2135+
Integer(#sizeOf(lookupTy(TY)), 64, false)
2136+
...
2137+
</k>
2138+
requires lookupTy(TY) =/=K typeInfoVoidType
20842139
rule <k> rvalueNullaryOp(nullOpAlignOf, TY)
20852140
=>
20862141
Integer(#alignOf(lookupTy(TY)), 64, false)
@@ -2089,6 +2144,8 @@ rule <k> rvalueNullaryOp(nullOpAlignOf, TY)
20892144
requires lookupTy(TY) =/=K typeInfoVoidType
20902145
```
20912146

2147+
`nullOpOffsetOf(VariantAndFieldIndices)`
2148+
20922149
#### Other operations
20932150

20942151
The unary operation `unOpPtrMetadata`, when given a reference or pointer to a slice, will return the reference or pointer metadata.

kmir/src/kmir/kdist/mir-semantics/rt/numbers.md

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -147,31 +147,6 @@ This truncation function is instrumental in the implementation of Integer arithm
147147
[preserves-definedness]
148148
```
149149

150-
## Alignment of Primitives
151-
152-
```k
153-
// FIXME: Alignment is platform specific
154-
syntax Int ::= #alignOf( TypeInfo ) [function]
155-
rule #alignOf( typeInfoPrimitiveType(primTypeBool) ) => 1
156-
rule #alignOf( typeInfoPrimitiveType(primTypeChar) ) => 4
157-
rule #alignOf( typeInfoPrimitiveType(primTypeInt(intTyIsize)) ) => 8 // FIXME: Hard coded since usize not implemented
158-
rule #alignOf( typeInfoPrimitiveType(primTypeInt(intTyI8)) ) => 1
159-
rule #alignOf( typeInfoPrimitiveType(primTypeInt(intTyI16)) ) => 2
160-
rule #alignOf( typeInfoPrimitiveType(primTypeInt(intTyI32)) ) => 4
161-
rule #alignOf( typeInfoPrimitiveType(primTypeInt(intTyI64)) ) => 8
162-
rule #alignOf( typeInfoPrimitiveType(primTypeInt(intTyI128)) ) => 16
163-
rule #alignOf( typeInfoPrimitiveType(primTypeUint(uintTyUsize)) ) => 8 // FIXME: Hard coded since usize not implemented
164-
rule #alignOf( typeInfoPrimitiveType(primTypeUint(uintTyU8)) ) => 1
165-
rule #alignOf( typeInfoPrimitiveType(primTypeUint(uintTyU16)) ) => 2
166-
rule #alignOf( typeInfoPrimitiveType(primTypeUint(uintTyU32)) ) => 4
167-
rule #alignOf( typeInfoPrimitiveType(primTypeUint(uintTyU64)) ) => 8
168-
rule #alignOf( typeInfoPrimitiveType(primTypeUint(uintTyU128)) ) => 16
169-
rule #alignOf( typeInfoPrimitiveType(primTypeFloat(floatTyF16)) ) => 2
170-
rule #alignOf( typeInfoPrimitiveType(primTypeFloat(floatTyF32)) ) => 4
171-
rule #alignOf( typeInfoPrimitiveType(primTypeFloat(floatTyF64)) ) => 8
172-
rule #alignOf( typeInfoPrimitiveType(primTypeFloat(floatTyF128)) ) => 16
173-
```
174-
175150
```k
176151
endmodule
177152
```

kmir/src/kmir/kdist/mir-semantics/rt/types.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ Pointers to structs with a single zero-offset field are compatible with pointers
8888
rule #layoutOffsets(_) => .MachineSizes [owise]
8989
```
9090

91+
Helper function to identify an `union` type, this is needed so `#setLocalValue`
92+
will not create an `Aggregate` instead of a `Union` `Value`.
93+
```k
94+
syntax Bool ::= #isUnionType ( TypeInfo ) [function, total]
95+
// --------------------------------------------------------
96+
rule #isUnionType(typeInfoUnionType(_NAME, _ADTDEF, _FIELDS, _LAYOUT) ) => true
97+
rule #isUnionType(_) => false [owise]
98+
```
99+
91100
## Determining types of places with projection
92101

93102
A helper function `getTyOf` traverses type metadata (using the type metadata map `Ty -> TypeInfo`) along the applied projections to determine the `Ty` of the projected place.
@@ -203,6 +212,72 @@ Slices, `str`s and dynamic types require it, and any `Ty` that `is_sized` does
203212
rule #zeroSizedType(_) => false [owise]
204213
```
205214

215+
## Alignment and Size of Types as per `TypeInfo`
216+
217+
The `alignOf` and `sizeOf` nullary operations return the alignment / size in bytes as a `usize`.
218+
This information is either hard-wired for primitive types (numbers, first and foremost), or read from the layout in `TypeInfo`.
219+
220+
```k
221+
syntax Int ::= #sizeOf ( TypeInfo ) [function, total]
222+
| #alignOf ( TypeInfo ) [function, total]
223+
224+
// primitive int types: use bit width (both for size and alignment)
225+
rule #sizeOf(typeInfoPrimitiveType(primTypeInt(NUMTY))) => #bitWidth(NUMTY) /Int 8 [preserves-definedness]
226+
rule #alignOf(typeInfoPrimitiveType(primTypeInt(NUMTY))) => #bitWidth(NUMTY) /Int 8 [preserves-definedness]
227+
rule #sizeOf(typeInfoPrimitiveType(primTypeUint(NUMTY))) => #bitWidth(NUMTY) /Int 8 [preserves-definedness]
228+
rule #alignOf(typeInfoPrimitiveType(primTypeUint(NUMTY))) => #bitWidth(NUMTY) /Int 8 [preserves-definedness]
229+
rule #sizeOf(typeInfoPrimitiveType(primTypeFloat(NUMTY))) => #bitWidth(NUMTY) /Int 8 [preserves-definedness]
230+
rule #alignOf(typeInfoPrimitiveType(primTypeFloat(NUMTY))) => #bitWidth(NUMTY) /Int 8 [preserves-definedness]
231+
// bool and char
232+
rule #sizeOf(typeInfoPrimitiveType(primTypeBool)) => 1
233+
rule #alignOf(typeInfoPrimitiveType(primTypeBool)) => 1
234+
rule #sizeOf(typeInfoPrimitiveType(primTypeChar)) => 4
235+
rule #alignOf(typeInfoPrimitiveType(primTypeChar)) => 4
236+
// The str primitive has alignment of a Char but size 0 (indicating dynamic size)
237+
rule #sizeOf(typeInfoPrimitiveType(primTypeStr)) => 0
238+
rule #alignOf(typeInfoPrimitiveType(primTypeStr)) => 4
239+
// enums, structs , and tuples provide the values from their layout information
240+
rule #sizeOf(typeInfoEnumType(_, _, _, _, someLayoutShape(layoutShape(_, _, _, _, machineSize( BITS ))))) => BITS /Int 8 [preserves-definedness]
241+
rule #sizeOf(typeInfoEnumType(_, _, _, _, someLayoutShape(layoutShape(_, _, _, _, machineSize(mirInt(BITS)))))) => BITS /Int 8 [preserves-definedness]
242+
rule #sizeOf(typeInfoEnumType(_, _, _, _, noLayoutShape)) => 0
243+
rule #alignOf(typeInfoEnumType(_, _, _, _, someLayoutShape(layoutShape(_, _, _, align(BYTES),_)))) => BYTES
244+
rule #alignOf(typeInfoEnumType(_, _, _, _, noLayoutShape)) => 1
245+
// struct
246+
rule #sizeOf(typeInfoStructType(_, _, _, someLayoutShape(layoutShape(_, _, _, _, machineSize( BITS ))))) => BITS /Int 8 [preserves-definedness]
247+
rule #sizeOf(typeInfoStructType(_, _, _, someLayoutShape(layoutShape(_, _, _, _, machineSize(mirInt(BITS)))))) => BITS /Int 8 [preserves-definedness]
248+
rule #sizeOf(typeInfoStructType(_, _, _, noLayoutShape)) => 0
249+
rule #alignOf(typeInfoStructType(_, _, _, someLayoutShape(layoutShape(_, _, _, align(BYTES),_)))) => BYTES
250+
rule #alignOf(typeInfoStructType(_, _, _, noLayoutShape)) => 1
251+
// tuple
252+
rule #sizeOf(typeInfoTupleType(_, someLayoutShape(layoutShape(_, _, _, _, machineSize( BITS ))))) => BITS /Int 8 [preserves-definedness]
253+
rule #sizeOf(typeInfoTupleType(_, someLayoutShape(layoutShape(_, _, _, _, machineSize(mirInt(BITS)))))) => BITS /Int 8 [preserves-definedness]
254+
rule #sizeOf(typeInfoTupleType(_, noLayoutShape)) => 0
255+
rule #alignOf(typeInfoTupleType(_, someLayoutShape(layoutShape(_, _, _, align(BYTES),_)))) => BYTES
256+
rule #alignOf(typeInfoTupleType(_, noLayoutShape)) => 1
257+
// union
258+
rule #sizeOf(typeInfoUnionType(_, _, _, someLayoutShape(layoutShape(_, _, _, _, machineSize( BITS ))))) => BITS /Int 8 [preserves-definedness]
259+
rule #sizeOf(typeInfoUnionType(_, _, _, someLayoutShape(layoutShape(_, _, _, _, machineSize(mirInt(BITS)))))) => BITS /Int 8 [preserves-definedness]
260+
rule #sizeOf(typeInfoUnionType(_, _, _, noLayoutShape)) => 0
261+
rule #alignOf(typeInfoUnionType(_, _, _, someLayoutShape(layoutShape(_, _, _, align(BYTES),_)))) => BYTES
262+
rule #alignOf(typeInfoUnionType(_, _, _, noLayoutShape)) => 1
263+
// arrays with known length have the alignment of the element type, and a size multiplying element count and element size
264+
rule #sizeOf(typeInfoArrayType(ELEM_TY, someTyConst(tyConst(KIND, _)))) => #sizeOf(lookupTy(ELEM_TY)) *Int readTyConstInt(KIND)
265+
rule #sizeOf(typeInfoArrayType( _ , noTyConst )) => 0
266+
rule #alignOf(typeInfoArrayType(ELEM_TY, _)) => #alignOf(lookupTy(ELEM_TY))
267+
// thin ptr and ref types have the size of `usize` and twice that for fat pointers/refs. Alignment is that of `usize`
268+
rule #sizeOf(typeInfoPtrType(POINTEE_TY))
269+
=> #sizeOf(typeInfoPrimitiveType(primTypeUint(uintTyUsize)))
270+
*Int (#if #metadataSize(POINTEE_TY) ==K dynamicSize(1) #then 2 #else 1 #fi)
271+
rule #sizeOf(typeInfoRefType(POINTEE_TY))
272+
=> #sizeOf(typeInfoPrimitiveType(primTypeUint(uintTyUsize)))
273+
*Int (#if #metadataSize(POINTEE_TY) ==K dynamicSize(1) #then 2 #else 1 #fi)
274+
rule #alignOf(typeInfoPtrType(_)) => #alignOf(typeInfoPrimitiveType(primTypeUint(uintTyUsize)))
275+
rule #alignOf(typeInfoRefType(_)) => #alignOf(typeInfoPrimitiveType(primTypeUint(uintTyUsize)))
276+
// other types (fun and void types) have size and alignment 0
277+
rule #sizeOf(_) => 0 [owise]
278+
rule #alignOf(_) => 0 [owise]
279+
```
280+
206281
```k
207282
endmodule
208283
```

kmir/src/kmir/kdist/mir-semantics/rt/value.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ The special `Moved` value represents values that have been used and should not b
3636
| StringVal( String ) [symbol(Value::StringVal)]
3737
// UTF-8 encoded Unicode string
3838
| Aggregate( VariantIdx , List ) [symbol(Value::Aggregate)]
39-
// heterogenous value list for tuples and structs (standard, tuple, or anonymous)
39+
// heterogenous value list for tuples, enum, and structs (standard, tuple, or anonymous)
40+
| Union( FieldIdx, Value ) [symbol(Value::Union)]
41+
// A union is an Aggregate, but we differentiate it from the other Aggregates.
42+
// The Value is the data, and FieldIdx determines the type from the union's fields
4043
| Float( Float, Int ) [symbol(Value::Float)]
4144
// value, bit-width for f16-f128
4245
| Reference( Int , Place , Mutability , Metadata )

0 commit comments

Comments
 (0)