@@ -43,17 +43,10 @@ Other uses of heating and cooling are to _read_ local variables as operands. Thi
4343### Errors Related to Accessing Local Variables
4444
4545Access to a ` TypedLocal ` (whether reading or writing) may fail for a number of reasons.
46- It is an error to use a ` Moved ` local in any way, or to read an uninitialised ` NewLocal ` .
47- Also, locals are accessed via their index in list ` <locals> ` in a stack frame, which may be out of bounds.
48- Types are also checked, using the ` Ty ` (an opaque number assigned by the Stable MIR extraction).
49-
50- ``` k
51- syntax LocalAccessError ::= InvalidLocal ( Local )
52- | TypeMismatch( Local, MaybeTy, TypedValue )
53- | LocalMoved( Local )
54- | LocalNotMutable ( Local )
55- | LocalUninitialised( Local )
56- ```
46+ It is an error to read a ` Moved ` local or an uninitialised ` NewLocal ` .
47+ Also, locals are accessed via their index in list ` <locals> ` in a stack frame, which may be out of bounds
48+ (but the compiler should guarantee that all local indexes are valid).
49+ Types (` Ty ` , an opaque number assigned by the Stable MIR extraction) are not checked, the local's type is used.
5750
5851### Reading Operands (Local Variables and Constants)
5952
@@ -96,28 +89,7 @@ local value cannot be read, though, and the value should be initialised.
9689 andBool isTypedValue(LOCALS[I])
9790 [preserves-definedness] // valid list indexing checked
9891
99- rule <k> operandCopy(place(local(I) #as LOCAL, _))
100- =>
101- LocalMoved(LOCAL)
102- ...
103- </k>
104- <locals> LOCALS </locals>
105- requires 0 <=Int I
106- andBool I <Int size(LOCALS)
107- andBool isMovedLocal(LOCALS[I])
108- [preserves-definedness] // valid list indexing checked
109-
110- rule <k> operandCopy(place(local(I), _))
111- =>
112- LocalUninitialised(local(I))
113- ...
114- </k>
115- <locals> LOCALS </locals>
116- requires I <Int size(LOCALS)
117- andBool 0 <=Int I
118- andBool isNewLocal(LOCALS[I])
119- [preserves-definedness] // valid list indexing checked
120- // TODO how about zero-sized types
92+ // error cases (NewLocal, Moved) get stuck
12193```
12294
12395Reading an ` Operand ` using ` operandMove ` has to invalidate the respective local, to prevent any
@@ -135,27 +107,7 @@ further access. Apart from that, the same caveats apply as for operands that are
135107 andBool isTypedValue(LOCALS[I])
136108 [preserves-definedness] // valid list indexing checked
137109
138- rule <k> operandMove(place(local(I) #as LOCAL, _))
139- =>
140- LocalMoved(LOCAL)
141- ...
142- </k>
143- <locals> LOCALS </locals>
144- requires 0 <=Int I
145- andBool I <Int size(LOCALS)
146- andBool isMovedLocal(LOCALS[I])
147- [preserves-definedness] // valid list indexing checked
148-
149- rule <k> operandMove(place(local(I), _))
150- =>
151- LocalUninitialised(local(I))
152- ...
153- </k>
154- <locals> LOCALS </locals>
155- requires 0 <=Int I
156- andBool I <Int size(LOCALS)
157- andBool isNewLocal(LOCALS[I])
158- [preserves-definedness] // valid list indexing checked
110+ // error cases (NewLocal, Moved) get stuck
159111```
160112
161113#### Reading places with projections
@@ -298,7 +250,7 @@ In the simplest case, the reference refers to a local in the same stack frame (h
298250 andBool isTypedValue(LOCALS[I])
299251 [preserves-definedness] // valid list indexing checked
300252
301- // TODO case of MovedLocal and NewLocal
253+ // Moved and NewLocal get stuck
302254
303255 // why do we not have this automatically for user-defined lists?
304256 syntax ProjectionElems ::= appendP ( ProjectionElems , ProjectionElems ) [function, total]
@@ -392,51 +344,15 @@ The `#setLocalValue` operation writes a `TypedLocal` value to a given `Place` wi
392344``` k
393345 syntax KItem ::= #setLocalValue( Place, Evaluation ) [strict(2)]
394346
395- // error cases first
396- rule <k> #setLocalValue( place(local(I) #as LOCAL, _), _) => InvalidLocal(LOCAL) ... </k>
397- <locals> LOCALS </locals>
398- requires size(LOCALS) <=Int I orBool I <Int 0
399-
400- rule <k> #setLocalValue( place(local(I) #as LOCAL, .ProjectionElems), typedValue(_, TY, _) #as VAL)
401- =>
402- TypeMismatch(LOCAL, tyOfLocal({LOCALS[I]}:>TypedLocal), VAL)
403- ...
404- </k>
405- <locals> LOCALS </locals>
406- requires 0 <=Int I
407- andBool I <Int size(LOCALS)
408- andBool isTypedLocal(LOCALS[I])
409- andBool TY =/=K TyUnknown
410- andBool tyOfLocal({LOCALS[I]}:>TypedLocal) =/=K TY
411- [preserves-definedness] // list index checked before lookup
347+ // error cases (not checked, just matched below to prevent them)
412348
413- // setting a local which was Moved is an error
414- rule <k> #setLocalValue( place(local(I), .ProjectionElems), _)
415- =>
416- LocalMoved(local(I))
417- ...
418- </k>
419- <locals> LOCALS </locals>
420- requires 0 <=Int I
421- andBool I <Int size(LOCALS)
422- andBool isMovedLocal(LOCALS[I])
423- [priority(60), preserves-definedness] // list index checked before lookup
349+ // no type check, the local's type is used.
424350
425351 // setting a non-mutable local that is initialised is an error
426- rule <k> #setLocalValue( place(local(I) #as LOCAL, .ProjectionElems), _)
427- =>
428- LocalNotMutable(LOCAL)
429- ...
430- </k>
431- <locals> LOCALS </locals>
432- requires I <Int size(LOCALS)
433- andBool 0 <=Int I
434- andBool isTypedValue(LOCALS[I])
435- andBool mutabilityOf({LOCALS[I]}:>TypedLocal) ==K mutabilityNot
436352
437353 // if all is well, write the value
438354 // mutable local
439- rule <k> #setLocalValue(place(local(I), .ProjectionElems), typedValue(VAL:Value, TY , _ ))
355+ rule <k> #setLocalValue(place(local(I), .ProjectionElems), typedValue(VAL:Value, _ , _ ))
440356 =>
441357 .K
442358 ...
@@ -448,22 +364,34 @@ The `#setLocalValue` operation writes a `TypedLocal` value to a given `Place` wi
448364 andBool I <Int size(LOCALS)
449365 andBool isTypedValue(LOCALS[I])
450366 andBool mutabilityOf({LOCALS[I]}:>TypedLocal) ==K mutabilityMut
451- andBool (tyOfLocal({LOCALS[I]}:>TypedLocal) ==K TY orBool TY ==K TyUnknown) // matching or unknown type
452367 [preserves-definedness] // valid list indexing checked
453368
454- // special case for non-mutable uninitialised values: mutable once
455- rule <k> #setLocalValue(place(local(I), .ProjectionElems), typedValue(VAL:Value, TY , _ ))
369+ // non-mutable uninitialised values are mutable once
370+ rule <k> #setLocalValue(place(local(I), .ProjectionElems), typedValue(VAL:Value, _ , _ ))
456371 =>
457372 .K
458373 ...
459374 </k>
460- <locals>
461- LOCALS => LOCALS[I <- typedValue(VAL, tyOfLocal({LOCALS[I]}:>TypedLocal), mutabilityOf({LOCALS[I]}:>TypedLocal))]
375+ <locals>
376+ LOCALS => LOCALS[I <- typedValue(VAL, tyOfLocal({LOCALS[I]}:>TypedLocal), mutabilityOf({LOCALS[I]}:>TypedLocal))]
462377 </locals>
463378 requires 0 <=Int I
464379 andBool I <Int size(LOCALS)
465380 andBool isNewLocal(LOCALS[I])
466- andBool (tyOfLocal({LOCALS[I]}:>TypedLocal) ==K TY orBool TY ==K TyUnknown) // matching or unknown type
381+ [preserves-definedness] // valid list indexing checked
382+
383+ // reusing a local which was Moved is allowed
384+ rule <k> #setLocalValue(place(local(I), .ProjectionElems), typedValue(VAL:Value, _, _ ))
385+ =>
386+ .K
387+ ...
388+ </k>
389+ <locals>
390+ LOCALS => LOCALS[I <- typedValue(VAL, tyOfLocal({LOCALS[I]}:>TypedLocal), mutabilityOf({LOCALS[I]}:>TypedLocal))]
391+ </locals>
392+ requires 0 <=Int I
393+ andBool I <Int size(LOCALS)
394+ andBool isMovedLocal(LOCALS[I])
467395 [preserves-definedness] // valid list indexing checked
468396```
469397
@@ -711,7 +639,7 @@ The `RValue::Repeat` creates and array of (statically) fixed length by repeating
711639 rule <k> rvalueLen(PLACE) => #lengthU64(operandCopy(PLACE)) ... </k>
712640
713641 rule <k> #lengthU64(typedValue(Range(LIST), _, _))
714- =>
642+ =>
715643 typedValue(Integer(size(LIST), 64, false), TyUnknown, mutabilityNot) // returns usize
716644 ...
717645 </k>
@@ -724,7 +652,7 @@ Likewise built into the language are aggregates (tuples and `struct`s) and varia
724652Besides their list of arguments, ` enum ` s also carry a ` VariantIdx ` indicating which variant was used. For tuples and ` struct ` s, this index is always 0.
725653
726654Tuples, ` struct ` s, and ` enum ` s are built as ` Aggregate ` values with a list of argument values.
727- For ` enums ` , the ` VariantIdx ` is set, and for ` struct ` s and ` enum ` s, the type ID (` Ty ` ) is retrieved from a special mapping of ` AdtDef ` to ` Ty ` .
655+ For ` enums ` , the ` VariantIdx ` is set, and for ` struct ` s and ` enum ` s, the type ID (` Ty ` ) is retrieved from a special mapping of ` AdtDef ` to ` Ty ` .
728656
729657``` k
730658 rule <k> rvalueAggregate(KIND, ARGS) => #readOperands(ARGS) ~> #mkAggregate(KIND) ... </k>
@@ -852,7 +780,7 @@ rewriting `typedLocal(...) ~> #cast(...) ~> REST` to `typedLocal(...) ~> REST`.
852780
853781```
854782
855- ### Integer Type Casts
783+ ### Integer Type Casts
856784
857785Casts between signed and unsigned integral numbers of different width exist, with a
858786truncating semantics. These casts can only operate on the ` Integer ` variant of the ` Value ` type, adjusting
@@ -919,7 +847,7 @@ The `Value` sort above operates at a higher level than the bytes representation
919847
920848 // Integer: handled in separate module for numeric operations
921849 rule #decodeConstant(constantKindAllocated(allocation(BYTES, _, _, _)), TYPEINFO)
922- =>
850+ =>
923851 #decodeInteger(BYTES, #intTypeOf(TYPEINFO))
924852 requires #isIntType(TYPEINFO)
925853 andBool lengthBytes(BYTES) ==K #bitWidth(#intTypeOf(TYPEINFO)) /Int 8
@@ -972,29 +900,15 @@ There are also a few _unary_ operations (`UnOpNot`, `UnOpNeg`, `UnOpPtrMetadata`
972900``` k
973901 syntax MIRError ::= OperationError
974902
975- syntax OperationError ::= TypeMismatch ( BinOp, Ty, Ty )
976- | OperandMismatch ( BinOp, Value, Value )
977- | OperandError( BinOp, TypedLocal, TypedLocal)
978- | OperandMismatch ( UnOp, Value )
979- | OperandError( UnOp, TypedLocal)
980- // errors above are compiler bugs or invalid MIR
981- | OpNotimplemented ( BinOp, TypedValue, TypedValue)
982- // errors below are program errors
983- | "DivisionByZero"
903+ // (dynamic) program errors causing undefined behaviour
904+ syntax OperationError ::= "DivisionByZero"
984905 | "Overflow_U_B"
985906
986- // catch Moved or uninitialised operands
987- rule #compute(OP, ARG1, ARG2, _FLAG) => OperandError(OP, ARG1, ARG2)
988- requires notBool (isTypedValue(ARG1) andBool isTypedValue(ARG2))
907+ // errors caused by invalid MIR code get stuck
989908
990- rule #applyUnOp(OP, ARG) => OperandError(OP, ARG)
991- requires notBool isTypedValue(ARG)
909+ // Moved or uninitialised operands
992910
993- // catch-all rule to make `#compute` total
994- rule #compute(OP, ARG1, ARG2, _FLAG) => OpNotimplemented(OP, ARG1, ARG2)
995- requires isTypedValue(ARG1)
996- andBool isTypedValue(ARG2)
997- [owise]
911+ // specific errors for the particular operation types (argument or type mismatches)
998912```
999913
1000914#### Arithmetic
@@ -1040,6 +954,10 @@ The arithmetic operations require operands of the same numeric type.
1040954 requires Y =/=Int 0
1041955 // operation undefined otherwise
1042956
957+ // error cases for isArithmetic(BOP):
958+ // * arguments must have the same type (TY match)
959+ // * arguments must be Numbers
960+
1043961 // Checked operations return a pair of the truncated value and an overflow flag
1044962 // signed numbers: must check for wrap-around (operation specific)
1045963 rule #compute(
@@ -1167,22 +1085,6 @@ The arithmetic operations require operands of the same numeric type.
11671085 requires DIVISOR ==Int -1
11681086 andBool DIVIDEND ==Int 0 -Int (1 <<Int (WIDTH -Int 1)) // == minInt
11691087 [priority(40)]
1170-
1171- // error cases:
1172- // non-integer arguments
1173- rule #compute(BOP, typedValue(ARG1, TY, _), typedValue(ARG2, TY, _), _)
1174- =>
1175- OperandMismatch(BOP, ARG1, ARG2)
1176- requires isArithmetic(BOP)
1177- [owise]
1178-
1179- // different argument types
1180- rule #compute(BOP, typedValue(_, TY1, _), typedValue(_, TY2, _), _)
1181- =>
1182- TypeMismatch(BOP, TY1, TY2)
1183- requires TY1 =/=K TY2
1184- andBool isArithmetic(BOP)
1185- [owise]
11861088```
11871089
11881090#### Comparison operations
@@ -1218,9 +1120,9 @@ All operations except `binOpCmp` return a `BoolVal`. The argument types must be
12181120 rule cmpOpBool(binOpGe, X, Y) => cmpOpBool(binOpLe, Y, X)
12191121 rule cmpOpBool(binOpGt, X, Y) => cmpOpBool(binOpLt, Y, X)
12201122
1221- rule #compute(OP, typedValue(_, TY, _), typedValue(_, TY2, _), _) => TypeMismatch(OP, TY, TY2)
1222- requires isComparison(OP)
1223- andBool TY =/=K TY2
1123+ // error cases for isComparison and binOpCmp:
1124+ // * arguments must have the same type
1125+ // * arguments must be numbers or Bool
12241126
12251127 rule #compute(OP, typedValue(Integer(VAL1, WIDTH, SIGN), TY, _), typedValue(Integer(VAL2, WIDTH, SIGN), TY, _), _)
12261128 =>
@@ -1233,12 +1135,6 @@ All operations except `binOpCmp` return a `BoolVal`. The argument types must be
12331135 typedValue(BoolVal(cmpOpBool(OP, VAL1, VAL2)), TyUnknown, mutabilityNot)
12341136 requires isComparison(OP)
12351137 [preserves-definedness] // OP known to be a comparison
1236-
1237- rule #compute(OP, typedValue(ARG1, TY, _), typedValue(ARG2, TY, _), _)
1238- =>
1239- OperandMismatch(OP, ARG1, ARG2)
1240- requires isComparison(OP)
1241- [owise]
12421138```
12431139
12441140The ` binOpCmp ` operation returns ` -1 ` , ` 0 ` , or ` +1 ` (the behaviour of Rust's ` std::cmp::Ordering as i8 ` ), indicating ` LE ` , ` EQ ` , or ` GT ` .
@@ -1300,11 +1196,6 @@ The `unOpNot` operation works on boolean and integral values, with the usual sem
13001196 </k>
13011197```
13021198
1303- ``` k
1304- rule <k> #applyUnOp(OP, typedValue(VAL, _, _)) => OperandMismatch(OP, VAL) ... </k>
1305- [owise]
1306- ```
1307-
13081199#### Bit-oriented operations
13091200
13101201` binOpBitXor `
0 commit comments