Migrating to Coco 1.0.0¶
The first release candidate of Coco 1.0.0 contains a number of changes that are incompatible with the betas leading up to Coco 1.0.0. This guide summarises the changes that have been made to the language, and also how to migrate code to the new version. Below, the changes are organised into two sections: the first section describes changes that require manual migration, whilst the second section describes changes for which there is automatic migration tooling in place. When migrating a project to Coco 1.0.0 we recommend that you firstly start with the manual steps and ensure that the verification passes or fails as expected, and then run the automated translation tooling to apply the rest of the changes automatically.
The automated migrations can either be executed via the Coco plugin for Eclipse, or via the command-line tooling. To execute them via Eclipse, right click on any node in the assertions tree and selecte the “Migrate to Coco 1” option. To migrate using the command-line tooling, see transform. These transformation commands were removed in Coco 1.4.0.
Manual changes¶
Derive¶
Coco no longer automatically derives Default
for structs or tagged enums or Eq
for tagged enums
(simple enums are unaffected) and requires the @derive
attribute to be specified instead. This will affect any
tagged enums or structs that you have defined and will typically result in error messages like:
no default value is known for type E; either give an explicit initialiser, use a Slot<E>, or define an instance of Default(E)
cannot compare E and E; could not find an instance of Eq(E) or NonVerifiedEq(E)
To fix this, add @derive(Default)
or @derive(Eq)
(or @derive(Default, Eq)
) to the declaration of the enum or
struct:
BoundedInt
¶
In addition to Coco’s BoundedInt
type being replaced by the new Bounded
type, which is automated, BoundedInt
is
also no longer implicitly convertible to Int
, or to other BoundedInt
types. For example, all of the following would
now be rejected:
function fn(x : Int) : BoundedInt<0, 2> = x
function fn(x : BoundedInt<0, 3>) : BoundedInt<0, 2> = x
To fix this, explicit conversions can be used:
function fn(x : Int) : BoundedInt<0, 2> = x as BoundedInt<0, 2>
function fn(x : BoundedInt<0, 3>) : BoundedInt<0, 2> = x as BoundedInt<0, 2>
We recommend that rather than using BoundedInt<0, 5>
directly, you instead create a type alias that gives a meaningful
name to the range:
type DeviceId = BoundedInt<0, 5>
This will make the casts easier to write and understand.
Spontaneous verification¶
A port is no longer allowed to perform an infinite sequence of empty spontaneous transitions, where an empty spontaneous transition is one that does not send any signals, or alter the state. This is now verified as part of the well-formedness checks on ports under a new assertion called No empty spontaneous.
The following example port will fail this verification check:
To fix this verification error, empty spontaneous transitions must be manually removed when they are identified via the verification. The most common cause of this will be spontaneouses like the above: to fix this, move the predicate to the outside of the spontaneous:
if (p) spontaneous = sig()
Abstracted¶
The type Abstracted
has now been removed from Coco. Instead of using Abstracted<From, To>
, instead define an
external type that contains a member of type To
. For example, suppose we previous had:
This can be replaced by:
If an external function had an argument s
of type SequenceNumber
, then the external function can now be modified to
use s.x
instead.
Compatible¶
Port inheritance now requires the @compatible
attribute to be used in order to specify which runtimes a port is
compatible with. In particular, any port that inherits a port with a state machine will now give an error like:
a port that inherits from another port must declare what runtimes it is usable in using the @compatible(…) attribute
To fix this, add @compatible(.SingleThreaded)
, @compatible(.MultiThreaded)
, or even
@compatible(.MultiThreaded, .SingleThreaded)
to the derived port. For example:
port Base { ... }
@compatible(.MultiThreaded)
port Derived : Base { ... }
@compatible
only has to be specified for ports with state machines.
Termination¶
The undocumented unsafeAutomaticTermination option has been removed in place of automatic safe termination by default.
Automated changes¶
There are also several changes that have been made to the language for which an automatic migration tool exists. These changes are described below in order to document them, but note that you do not need need to apply these manually: instead just run the automated migration tooling.
Syntax¶
Coco’s syntax has been changed with the aim of reducing confusion over where ;
are required or whether ‘;’ is a
separator or a sequencing operator, and also improve the consistency of the language.
An enum
declaration no longer uses ;
as a separator, and instead uses the keyword case
to denote
each enum case. For example:
enum E {
A;
B(x : Int);
}
is now written as:
enum E {
case A
case B(x : Int)
}
Structs, ports and components have all been altered to use the same same syntax as variable declaration in state
machines, as each Field
is now prefixed with var
or val
. For example:
struct S {
f : Int;
}
port P {
left : L;
}
component C {
p : D;
}
becomes:
struct S {
var f : Int
}
port P {
val left : L
}
component C {
val p : D
}
Port and component fields must always be marked as immutable using val
(since they are immutable references to mutable
objects), whereas struct fields must currently always be marked using var
.
Match
expressions, offer
handlers, and nondet
expressions now use ,
as a separator instead of ;
. For example, suppose we previously had:
match (m) {
Message.Empty => -1;
Message.Data(d) => d;
}
f(level : Bounded<0,6>) = offer {
if (level < 2) low();
if (level > 3) high();
otherwise medium;
}
check() = nondet { true; false; }
These are now written as:
match (m) {
.Empty => -1,
.Data(d) => d,
}
f(level : Bounded<0,6>) = offer {
if (level < 2) low(),
if (level > 3) high(),
otherwise medium,
}
check() = nondet { true, false, }
Queue Control¶
The following queue control functions have been renamed to improve consistency:
setFilter
has becomesetAllow
;unique
has becomesetUnique
;setShared
has becomesetAccess
.- The enum case
None
of typePortAccess
, which is passed as an argument tosetAccess
(previouslysetShared
has been renamed toOtherOnly
.
For example, suppose we previously had:
setShared(source.monitor, PortAccess.None)
This is now replaced with:
setAccess(source.monitor, PortAccess.OtherOnly)
In addition, setAllow
(previously called setFilter
) is now variadic, meaning that setFilter(r, [s1, s2])
is
now written as setAllow(r, s1, s2)
.
C++ attributes¶
The C++ attributes, @CPP.include
, @CPP.mapToType
, and @CPP.mapToValue
have all been
modified to take a variable number of parameters, rather than a list of headers. Therefore:
@CPP.mapToValue("kValue", [CPP.Header.User("header.h")])
can now be written as:
@CPP.mapToValue("kValue", CPP.Header.User("header.h"))
or even:
@CPP.mapToValue("kValue", .User("header.h"))
using implicit member expressions.
Slot members¶
The Coco Slot<T>
type used to provide several functions that could be used as follows:
These have now been converted into member functions and so are written as:
Runtime Attribute¶
If the runtime attribute was not specified, then Coco used to assume that the component should use the MultiThreaded
runtime. Due to the fact that Coco permits runtimes to be mixed, this was a common source of confusion. Therefore, the
runtime attribute is now mandatory on all components.
BoundedInt
¶
In addition to the changes outlined above, Coco’s BoundedInt<l, u>
type has been replaced by Bounded<l, u-1>
. The
automatic transformation will convert all uses of Bounded
to BoundedInt
, converting the upper bound as necessary.