Skip to content

Commit

Permalink
Fix missing type conversion in generated code for appending to sequence
Browse files Browse the repository at this point in the history
Ref. #965
  • Loading branch information
treiher committed Mar 23, 2022
1 parent 072ce8e commit db30038
Show file tree
Hide file tree
Showing 32 changed files with 6,104 additions and 70 deletions.
153 changes: 87 additions & 66 deletions rflx/generator/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -1895,7 +1895,7 @@ def _state_action(
)

elif isinstance(action, stmt.Append):
result = self._append(action, exception_handler, is_global)
result = self._append(action, exception_handler, is_global, state)

elif isinstance(action, stmt.Extend):
fail(
Expand Down Expand Up @@ -2344,19 +2344,7 @@ def _assign_to_message_aggregate(
self._session_context.used_types_body.append(const.TYPES_BIT_LENGTH)

size = self._message_size(message_aggregate)
size_variables = [
v for v in size.variables() if isinstance(v.type_, (rty.Message, rty.Sequence))
]
required_space = (
size.substituted(self._substitution(is_global))
.substituted(
lambda x: expr.Call(const.TYPES_BIT_LENGTH, [x])
if (isinstance(x, expr.Variable) and isinstance(x.type_, rty.AnyInteger))
or (isinstance(x, expr.Selected) and x.type_ != rty.OPAQUE)
else x
)
.ada_expr()
)
required_space, required_space_precondition = self._required_space(size, is_global, state)
target_type = ID(message_aggregate.type_.identifier)
target_context = context_id(target, is_global)
parameter_values = [
Expand Down Expand Up @@ -2409,29 +2397,12 @@ def _assign_to_message_aggregate(
*exception_handler.execute_deferred(),
]

if size_variables:
expressions = " or ".join(f'"{v}"' for v in size_variables)
if required_space_precondition:
return [
self._if(
AndThen(
*[
e
for v in size_variables
if isinstance(v.type_, (rty.Message, rty.Sequence))
for s in [
expr.Size(v).substituted(self._substitution(is_global)).ada_expr()
]
for e in [
LessEqual(
s,
Number(self._allocator.get_size(v.identifier, state) * 8),
),
Equal(Mod(s, Size(const.TYPES_BYTE)), Number(0)),
]
]
),
required_space_precondition,
assign_to_message_aggregate,
f"unexpected size of {expressions}",
"unexpected size",
exception_handler,
)
]
Expand Down Expand Up @@ -2955,40 +2926,62 @@ def _append(
append: stmt.Append,
exception_handler: ExceptionHandler,
is_global: Callable[[ID], bool],
state: rid.ID,
) -> Sequence[Statement]:
assert isinstance(append.type_, rty.Sequence)

self._session_context.used_types_body.append(const.TYPES_BIT_LENGTH)

def check(sequence_type: ID, required_space: Expr) -> Statement:
return IfStatement(
[
(
Or(
Not(
Call(
sequence_type * "Has_Element",
[Variable(sequence_context)],
def check(
sequence_type: ID, required_space: Expr, precondition: Expr = None
) -> list[Statement]:
return [
*(
[
IfStatement(
[
(
Not(precondition),
[
*self._debug_output("Error: unexpected size"),
*exception_handler.execute(),
],
)
),
Less(
Call(
sequence_type * "Available_Space",
[Variable(sequence_context)],
]
)
]
if precondition
else []
),
IfStatement(
[
(
Or(
Not(
Call(
sequence_type * "Has_Element",
[Variable(sequence_context)],
)
),
Less(
Call(
sequence_type * "Available_Space",
[Variable(sequence_context)],
),
required_space,
),
required_space,
),
),
[
*self._debug_output(
"Error: insufficient space for appending to sequence"
f' "{sequence_context}"'
),
*exception_handler.execute(),
],
)
]
)
[
*self._debug_output(
"Error: insufficient space for appending to sequence"
f' "{sequence_context}"'
),
*exception_handler.execute(),
],
)
]
),
]

if isinstance(append.type_.element, (rty.Integer, rty.Enumeration)):
assert isinstance(append.parameter, expr.Variable)
Expand All @@ -2998,7 +2991,7 @@ def check(sequence_type: ID, required_space: Expr) -> Statement:
element_type = ID(append.type_.element.identifier)

return [
check(sequence_type, Size(element_type)),
*check(sequence_type, Size(element_type)),
CallStatement(
sequence_type * "Append_Element",
[Variable(sequence_context), append.parameter.ada_expr()],
Expand All @@ -3014,17 +3007,15 @@ def check(sequence_type: ID, required_space: Expr) -> Statement:
self._session_context.referenced_types_body.append(element_type)

if isinstance(append.parameter, expr.MessageAggregate):
required_space = (
self._message_size(append.parameter)
.substituted(self._substitution(is_global))
.ada_expr()
required_space, required_space_precondition = self._required_space(
self._message_size(append.parameter), is_global, state
)
else:
_unsupported_expression(append.parameter, "in Append statement")

with exception_handler.local() as local_exception_handler:
return [
check(sequence_type, required_space),
*check(sequence_type, required_space, required_space_precondition),
Declare(
[ObjectDeclaration([element_context], element_type * "Context")],
[
Expand Down Expand Up @@ -3101,6 +3092,36 @@ def _message_size(self, message_aggregate: expr.MessageAggregate) -> expr.Expr:
assert isinstance(message, model.Message)
return message.size({model.Field(f): v for f, v in message_aggregate.field_values.items()})

def _required_space(
self, size: expr.Expr, is_global: Callable[[ID], bool], state: rid.ID
) -> tuple[Expr, Optional[Expr]]:
required_space = (
size.substituted(
lambda x: expr.Call(const.TYPES_BIT_LENGTH, [x])
if (isinstance(x, expr.Variable) and isinstance(x.type_, rty.AnyInteger))
or (isinstance(x, expr.Selected) and x.type_ != rty.OPAQUE)
else x
)
.substituted(self._substitution(is_global))
.ada_expr()
)
# ISSUE: Componolit/RecordFlux#691
# Validity of fields accessed in `size` must be checked before access.
precondition = [
e
for v in size.variables()
if isinstance(v.type_, (rty.Message, rty.Sequence))
for s in [expr.Size(v).substituted(self._substitution(is_global)).ada_expr()]
for e in [
LessEqual(
s,
Number(self._allocator.get_size(v.identifier, state) * 8),
),
Equal(Mod(s, Size(const.TYPES_BYTE)), Number(0)),
]
]
return (required_space, AndThen(*precondition) if precondition else None)

def _substitution(self, is_global: Callable[[ID], bool]) -> Callable[[expr.Expr], expr.Expr]:
# pylint: disable = too-many-statements

Expand Down
13 changes: 13 additions & 0 deletions tests/integration/session_sequence_append/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
input:
Channel:
- 1 0 2 0 3
output:
- Channel
sequence: |
Write Channel: 1 0 2 0 3
State: Start
State: Process
Read Channel: 5 0 5 1 0 2 0 3
State: Reply
# ISSUE: Componolit/RecordFlux#691
# prove:
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
pragma Style_Checks ("N3aAbcdefhiIklnOprStux");

package body RFLX.RFLX_Arithmetic with
SPARK_Mode
is

function Shift_Left (Value : U64; Amount : Natural) return U64 with
Import,
Convention => Intrinsic,
Global => null;

function Shift_Right (Value : U64; Amount : Natural) return U64 with
Import,
Convention => Intrinsic,
Global => null;

function Shift_Add (V : U64;
Data : U64;
Amount : Natural;
Bits : Natural) return U64
is
pragma Unreferenced (Bits);
begin
return Shift_Left (V, Amount) + Data;
end Shift_Add;

function Right_Shift (V : U64; Amount : Natural; Size : Natural) return U64
is
pragma Unreferenced (Size);
begin
return Shift_Right (V, Amount);
end Right_Shift;

function Left_Shift (V : U64; Amount : Natural; Size : Natural) return U64
is
pragma Unreferenced (Size);
Result : constant U64 := Shift_Left (V, Amount);
begin
return Result;
end Left_Shift;

function Mask_Lower (V : U64; Mask, Bits : Natural) return U64
is
Result : constant U64 := Shift_Left (Shift_Right (V, Mask), Mask);
begin
pragma Assert
(if Bits < U64'Size then Result <= 2 ** Bits - 2 ** Mask
elsif Mask < U64'Size then Result <= U64'Last - 2 ** Mask + 1);
return Result;
end Mask_Lower;

function Mask_Upper (V : U64; Mask : Natural) return U64
is
begin
return V and (2 ** Mask - 1);
end Mask_Upper;

function Add (A : U64; B : U64; Total_Bits, Lower_Bits : Natural) return U64
is
pragma Unreferenced (Total_Bits, Lower_Bits);
begin
return A + B;
end Add;

end RFLX.RFLX_Arithmetic;
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
pragma Style_Checks ("N3aAbcdefhiIklnOprStux");

package RFLX.RFLX_Arithmetic with
SPARK_Mode
is

type U64 is mod 2**64 with
Annotate => (GNATprove, No_Wrap_Around);

-- Express that V contains at most Bits non-zero bits, in the least
-- significant part (the rest is zero).
pragma Warnings (Off, "postcondition does not mention function result");
function Fits_Into (V : U64; Bits : Natural) return Boolean
is (if Bits < U64'Size then V < 2 ** Bits)
with Post => True;

-- Express that V contains (U64'Size - Bits) leading zero bits, then (Bits -
-- Lower) bits of data, then Lower bits of zeros.
-- |- (U64'Size - bits) -|- (Bits-Lower) -|- Lower -|
-- |000000000000000000000|xxxxxxxxxxxxxxxx|000000000|
function Fits_Into_Upper (V : U64; Bits, Lower : Natural) return Boolean
is (if Bits < U64'Size then V <= 2 ** Bits - 2 ** Lower
elsif Lower > 0 and then Lower < U64'Size then V <= U64'Last - 2 ** Lower + 1)
with Pre => Bits <= U64'Size and then Lower <= Bits,
Post => True;
pragma Warnings (On, "postcondition does not mention function result");

-- V is assumed to contain Bits bits of data. Add the Amount bits contained
-- in Data by shifting V to the left and adding Data. The result contains
-- (Bits + Amount) bits of data.
function Shift_Add (V : U64;
Data : U64;
Amount : Natural;
Bits : Natural) return U64
with Pre =>
Bits < U64'Size
and then Amount < U64'Size
and then Fits_Into (V, Bits)
and then U64'Size - Amount >= Bits
and then Fits_Into (Data, Amount),
Post => Fits_Into (Shift_Add'Result, Bits + Amount);

-- Wrapper of Shift_Right that expresses the operation in terms of
-- Fits_Into.
function Right_Shift (V : U64; Amount : Natural; Size : Natural) return U64 with
Pre =>
Size <= U64'Size
and then Fits_Into (V, Size)
and then Amount <= Size
and then Size - Amount < U64'Size,
Post => Fits_Into (Right_Shift'Result, Size - Amount);

-- Wrapper of Shift_Left that expresses the operation in terms of
-- Fits_Into/Fits_Into_Upper.
function Left_Shift (V : U64; Amount : Natural; Size : Natural) return U64 with
Pre =>
Size < U64'Size
and then Amount < U64'Size
and then Fits_Into (V, Size)
and then Size + Amount < U64'Size,
Post => Fits_Into_Upper (Left_Shift'Result, Size + Amount, Amount);

-- V is assumed to have Bits bits of data. Set the lower bits of V to zero.
function Mask_Lower (V : U64; Mask, Bits : Natural) return U64
with Pre => Bits <= U64'Size and then Fits_Into (V, Bits) and then Mask <= Bits and then Mask >= 1,
Post => Fits_Into_Upper (Mask_Lower'Result, Bits, Mask);

-- Set the upper bits of V to zero.
function Mask_Upper (V : U64; Mask : Natural) return U64
with Pre => Mask < U64'Size,
Post => Fits_Into (Mask_Upper'Result, Mask);

-- Add A and B in the special case where A only uses the upper bits and B
-- only the lower bits.
function Add (A : U64; B : U64; Total_Bits, Lower_Bits : Natural) return U64
with Pre =>
Total_Bits <= U64'Size
and then Lower_Bits <= Total_Bits
and then (if Total_Bits = U64'Size then Lower_Bits /= U64'Size)
and then Fits_Into_Upper (A, Total_Bits, Lower_Bits)
and then Fits_Into (B, Lower_Bits),
Post => Add'Result = A + B and Fits_Into (Add'Result, Total_Bits),
Global => null;

end RFLX.RFLX_Arithmetic;
Loading

0 comments on commit db30038

Please sign in to comment.