Skip to content

Commit d124b36

Browse files
authored
Union Value Sort (#861)
This PR separates a `Value` sort for `Union` out of `Aggregate` as there are different semantics for the projections (and it also makes the code clearer). A `Union` has one argument which is data, but it can be interpreted as different types via field projections - this is significantly different from other field projections that access different data via different arguments. Currently the implementation only supports accessing fields of the same type as the `Union` construction (no switching between types right now).
1 parent 3363f4e commit d124b36

File tree

10 files changed

+125
-5
lines changed

10 files changed

+125
-5
lines changed

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

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

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

Lines changed: 50 additions & 1 deletion
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))

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

Lines changed: 9 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) ) => 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.

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 )

kmir/src/kmir/kmir.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,12 @@ def cut_point_rules(
7474
if break_on_thunk:
7575
cut_point_rules.append('RT-DATA.thunk')
7676
if break_every_statement or break_every_step:
77-
cut_point_rules.append('KMIR-CONTROL-FLOW.execStmt')
77+
cut_point_rules.extend(
78+
[
79+
'KMIR-CONTROL-FLOW.execStmt',
80+
'KMIR-CONTROL-FLOW.execStmt.union',
81+
]
82+
)
7883
if break_on_terminator_goto or break_every_terminator or break_every_step:
7984
cut_point_rules.append('KMIR-CONTROL-FLOW.termGoto')
8085
if break_on_terminator_switch_int or break_every_terminator or break_every_step:

kmir/src/tests/integration/data/prove-rs/iterator-simple-fail.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ fn main() {
44
for elem in arr {
55
assert!(elem != 0);
66
}
7-
}
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use std::mem::MaybeUninit;
2+
fn main() {
3+
let m_init = MaybeUninit::new(1);
4+
let init = unsafe { m_init.assume_init() };
5+
6+
assert!(init == 1);
7+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
┌─ 1 (root, init)
3+
│ #execTerminator ( terminator ( ... kind: terminatorKindCall ( ... func: operandC
4+
│ span: 0
5+
6+
│ (75 steps)
7+
└─ 3 (stuck, leaf)
8+
#traverseProjection ( toLocal ( 1 ) , Union ( fieldIdx ( 0 ) , Integer ( -1 , 8
9+
function: main
10+
span: 59
11+
12+
13+
┌─ 2 (root, leaf, target, terminal)
14+
│ #EndProgram ~> .K
15+
16+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
union Union {
2+
signed: i8,
3+
unsigned: u8,
4+
}
5+
6+
fn main() {
7+
let s = Union { signed: -1 };
8+
let u = Union { unsigned: 255 };
9+
unsafe {
10+
assert!(s.signed == -1);
11+
assert!(u.unsigned == 255);
12+
13+
assert!(s.unsigned == 255);
14+
assert!(u.signed == -1);
15+
}
16+
}

kmir/src/tests/integration/test_integration.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
'transmute-u8-to-enum-changed-discriminant-signed-fail',
5757
'assert-inhabited-fail',
5858
'iterator-simple-fail',
59+
'unions-fail',
5960
]
6061

6162

0 commit comments

Comments
 (0)