FLEXIBLE CORRECT-BY-CONSTRUCTION PROGRAMMING

. Correctness-by-Construction (CbC) is an incremental program construction process to construct functionally correct programs. The programs are constructed stepwise along with a specification that is inherently guaranteed to be satisfied. CbC is complex to use without specialized tool support, since it needs a set of predefined refinement rules of fixed granularity which are additional rules on top of the programming language. Each refinement rule introduces a specific programming statement and developers cannot depart from these rules to construct programs. CbC allows to develop software in a structured and incremental way to ensure correctness, but the limited flexibility is a disadvantage of CbC. In this work, we compare classic CbC with CbC-Block and TraitCbC . Both approaches CbC-Block and TraitCbC , are related to CbC, but they have new language constructs that enable a more flexible software construction approach. We provide for both approaches a programming guideline, which similar to CbC, leads to well-structured programs. CbC-Block extends CbC by adding a refinement rule to insert any block of statements. Therefore, we introduce CbC-Block as an extension of CbC. TraitCbC implements correctness-by-construction on the basis of traits with specified methods. We formally introduce TraitCbC and prove soundness of the construction strategy. All three development approaches are qualitatively compared regarding their programming constructs, tool support, and usability to assess which is best suited for certain tasks and developers.


Introduction
Correctness-by-Construction (CbC) [Dij76,Gri87,KW12,Mor94] is a methodology in the field of formal methods to incrementally construct functionally correct programs guided by a pre-/postcondition specification. 1 In contrast to post-hoc verification, where a program is typically specified and verified after implementing it, CbC is based around successively creating a program together with the specification.This is achieved by applying refinement rules from a small set of defined rules where in each refinement step, an abstract statement (i.e., a hole in the program) is refined to a more concrete implementation that can still contain some nested abstract statements.While refining the program, the correctness of the whole program is guaranteed through applicability conditions that are defined in the refinement rules.The construction ends when no abstract statement is left.
The underlying idea of this specification-first, refinement-based approach is that developers are forced to think about their algorithm more thoroughly rather than having a trial-and-error verification approach.This trial-and-error verification can oftentimes be experienced with post-hoc verification because programs are implemented first and therefore not well-structured for the verification process which leads to tedious verification work.Additionally, through the structured reasoning discipline that is enforced by the refinement rules in CbC, errors are more likely to be detected earlier in the design process, and it is argued that program quality increases and verification effort is reduced [KW12,WKSC16].
Despite these benefits, CbC intuitively has a drawback: The flexibility of creating a program is limited to the set of refinement rules and the rigid, rule-based construction process of applying one rule at a time.This is even increased by the granularity of the rules which explicitly only allow to use one language construct at a time (e.g., one assignment to a variable).Additionally, the refinement rules extend the programming language (i.e., refinement rules are an additional linguistic construct to transform programs), and therefore special tool support (e.g., CorC [RSC + 19, BCK + 22]) is necessary to introduce the CbC refinement process to a programming language.As a result, the barrier to construct programs using CbC is large because the approach at first seems unintuitive and requires effort, knowledge, and special tool support.
In this article, we introduce two alternative correctness-by-construction development approaches that relax the inflexible CbC construction approach without losing the benefits of CbC itself.Both introduce more flexible language constructs to create programs which allow to condense construction steps that tackle the complex and strict programming style of CbC.The goal is to propose a usable CbC apporach that offers reasonable constructs to develop programs correctly.Therefore, we qualitatively discuss our two proposed approaches and the original CbC approach regarding their programming constructs, tool support, and usability to assess their benefits and drawbacks.
First, we present CbC-Block which adds new refinement rules.This introduction of new refinement rules should not be seen as a further restriction, but as a relaxation of the procedure.These new refinement rules increase the ways in which programs can be developed as they allow to refine abstract statements to a specified block of code that fulfills its specification.This basically means that this block can contain multiple assignments, The approach should not be confused with other CbC approaches such as CbyC of Hall and Chapman [HC02].CbyC is a software development process that uses formal modeling techniques and analysis for various stages of development (architectural design, detailed design, code) to detect and eliminate defects as early as possible [Cha06].We also exclude data refinement from abstract data types to concrete ones during code generation as for example in Isabelle/HOL [HKKN13].
Definition 2.1: Refinement Rules for the Correctness-by-Construction Approach Let P be the precondition, Q be the postcondition, and S be an abstract statement.Then, the Hoare triple {P }S{Q} is refinable to and ∀i ∈ {1 . . .n} : {P ∧ G i }S i {Q} holds • Repetition: {P }do [I, V ] G → S od{Q} iff P implies I and I ∧ ¬G implies Q and {I ∧ G}S{I} holds and {I ∧ G ∧ V = V 0 }S{I ∧ 0 ≤ V < V 0 } holds • Weaken precondition: {P ′ }S{Q} iff P implies P ′ • Strengthen postcondition: {P }S{Q ′ } iff Q ′ implies Q • Method Call: {P }m(a 1 , . . ., a n ) → b{Q} iff method {P ′ }m(p 1 , . . ., p n ) → r{Q ′ } exists and P implies P ′ [p i \ a i ] and concrete statements (i.e., statements in the guarded command language [Dij76] that can contain further abstract statements).The process stops, when all abstract statements are refined to concrete statements so that no holes remain in the program.As each refinement rule is sound and each correct application of a refinement rule guarantees to satisfy the starting Hoare triple, the resulting program is correct by construction [KW12].The CbC approach is strictly tied to this set of predefined refinement rules.A developer cannot deviate from this concept.In Definition 2.1, we present the eight refinement rules of CbC by Kourie and Watson [KW12].The concrete program statements are written in the guarded command language [Dij75].To apply a refinement rule, it has to be checked that side conditions of the rule application are satisfied.This is done by pen-and-paper or with specialized tools [RSC + 19].For example, the skip rule introduces an empty statement that does not alter the program state.This refinement is applicable if and only if the precondition P implies the postcondition Q.The composition rule splits the Hoare triple {P }S{Q} into two Hoare triples by using an intermediate condition M .This refinement is applicable, if and only if the two new Hoare triples are correct.Of course, the statements S 1 and S 2 are still abstract and can be further refined.

CbC-Block-CbC With Block Contracts
In this section, we introduce the CbC-Block approach that adds two new refinement rules to classic CbC.The new refinement rules increase the ways to construct programs.Therefore, the rigid CbC approach is loosened while retaining the benefits of a structured program construction approach.A block rule refines an abstract statement to a block that is specified with a block contract (i.e., a pre-/postcondition specification for that block) [ABB + 16].The block is a special statement that can be further refined in two ways.Similar to an abstract statement, any CbC refinement rules can be applied.Additionally, the block can 16:5 be instantiated by any sequence of concrete statements and further blocks with a blockinstantiation rule.Thus, a block can be used to condense the application of several CbC refinement rules.For example, a block can be instantiated with a while-loop that already contains a concrete body.This instantiation replaces the application of the repetition rule and at least one assignment rule.We introduce CbC-Block with a motivating example and present the block rules to introduce and instantiate blocks.In the end of this section, we present CbC-Block implemented in the CbC tool CorC 3 and evaluate its usability with a user study.
3.1.Motivating Example.In this section, we exemplify the CbC-Block approach by implementing a maxElement algorithm.The maxElement algorithm searches the largest element in a list of integers.The list supports a get-method which returns the element at the specified position in this list.A contains-method checks that the result is a member of the list.We iterate with a while-loop through the list and use local variables to temporally save the current largest element.We use Java and JML [LBR98] as programming and specification language in the example.
In Listing 1, we start implementing method maxElement that is specified with a pre-and postcondition contract.The precondition states that the list must contain at least one element.The postcondition states that the largest element in the list is returned.In this example, we start with a program where some CbC refinement rules are already applied, and then, apply the block rules to finish the implementation.
The program is already split into three parts using the composition refinement rule with two intermediate conditions between them.In the first part, two local variables i and j are introduced with the assignment rule.The variable i is used to temporally store the largest element.In the beginning, the largest element (up to that point) is the first element in the list.The variable j is our loop variable to iterate through the list.In the third program part, variable i is returned.We start with this program state to show that CbC-Block also supports the standard CbC approach, but we will now use the block rules to exemplify the benefits of CbC-Block.
In the second part between the two intermediate conditions, the block rule of CbC-Block is applied.The block B1 is specified with a block contract in lines 13-16.For the functional behavior, we specify the values of i and j, and the size of the list in the precondition.This specification is equal to the preceding intermediate condition.The postcondition of the block states that i is the largest element.This postcondition meets the intermediate condition after the block.Therefore, we know that the program is correct under the assumption that block B1 fulfills its specification.In the next steps, we concretize the block, and the applied refinement rule guarantees that the instantiated block is correct according to its specification.We can concretize the block either in one step, by instantiating the block with concrete Java code or stepwise by instantiating the block with some Java statements and other blocks.
We decide to partially implement the block.In Listing 2, we define the block that should be refined by referring to block B1 in line 1 and repeat the specification of that block.Inside the curly brackets the instantiation is shown.We implement the block with a while-loop.We iterate through the list as long as variable j is smaller than the size of the list.This is stated in the loop guard.The loop is specified with a loop invariant in lines 10-12.Thereby, 1 /* @ public normal_behavior 2 @ requires list .size () > 0; 3 @ ensures list .contains ( \result ) && 4 @ ( \forall int q ; q >= 0 && q < list .size (); \result >= list .get ( q )); 5 @ */ 6 public int maxElement ( List list ) { 7 int i = list .get (0); 8 int j = 1; 9 10 // @ Intm: list .size () > 0 && i == list .get (0) && j == 1; 11 12 /* @ 13 @ normal_behavior 14 @ requires list .size () > 0 && i == list .get (0) && j == 1; 15 @ ensures list .contains ( i ) && 16 @ ( \forall int q ; q >= 0 && q < list .size (); i >= list .get ( q )); 17 @ */ 18 { \Block B1 ; } 19 20 // @ Intm: list .contains ( i ) && 21 // @ ( \forall int q ; q >= 0 && q < list .size (); i >= list .get ( q )); 22 23 return i ; 24 } Listing 1: Initial program of maxElement the variable i stores the largest element of the already checked elements up to the index j.The index j is inside the bounds of the list.We use the difference between the size of the list and j as loop variant.As variable j increases in each iteration, the difference decreases, and the loop thus terminates.The increase of j is already implemented at the end of the while-loop.The body of the loop contains another block B2 in lines 15-22.The precondition of the block is the loop invariant with the difference that we know that variable j is smaller than the size of the list.This block should update variable i that contains the largest element.We want to compare the largest element with the next element in the list.
If that element is larger, variable i is updated.We checked one more element of the list, and therefore, we increase the range of the universal quantifier in the postcondition.This instantiation condenses the application of three CbC refinement rules, the repetition rule to create the loop, a composition rule, and an assignment rule for the loop body.The next step is to verify that the instantiation satisfies the block contract.Starting with the precondition and after executing the introduced instantiation, the postcondition of the block contract must be fulfilled.The details of checking this instantiation are explained in the next section.When the correctness of this instantiation is shown, we can continue to instantiate the next block B2.
In Listing 3, the instantiation of block B2 implements the case when a larger element is found.The functional pre-and postcondition of the block differ by the range of considered elements.In the postcondition, the range is increased by one.In this block, we compare the current largest element i with the element at index j.If the element at index j is larger, we 1 Block B1 ; 2 3 /* @ 4 @ normal_behavior 5 @ requires list .size () > 0; 6 @ ensures list .contains ( i ) && 7 @ ( \forall int q ; q >= 0 && q < list .size (); i >= list .get ( q )); 8 @ */ 9 { 10 // @ loop_invariant list .contains ( i ) && j > 0 && j <= list .size () && 11 // @ ( \forall int q ; q >= 0 && q < j ; i >= list .get ( q )); 12 // @ decreases list .size () -j ; 13 while ( j < list .size ()) { 14 15 /* @ 16 @ normal_behavior 17 @ requires list .contains ( i ) && j > 0 && j < list .size () && 18 @ ( \forall int q ; q >= 0 && q < j ; i >= list .get ( q )); 19 @ ensures list .contains ( i ) && j > 0 && j < list .size () && 20 @ ( \forall int q ; q >= 0 && q < j +1; i >= list .get ( q )); Listing 2: Refinement of block B1 update variable i.In the other case, i is still the largest element and not updated.Again, we condense CbC refinement rules by instantiating the block with concrete code.We have to verify that the instantiation is correct.If this is done, we have finished the refinement process because no further block or any abstract statement is left.By guaranteeing the correctness of all refinement steps, we can conclude that the whole program is correct by construction.The resulting program is shown in Listing 4. Here, the blocks are recursively replaced with their instantiation.The specification is limited to the method contract and the loop invariant and variant annotations.By stepwise refining the program, we can detect errors when proving single refinement steps.This locality of information helps to track down errors more easily than with monolithic post-hoc verification.
3.2.Block Refinement Rules of CbC-Block.In this section, we describe how refinement rules are added to establish the CbC-Block approach.We describe the refinement rule to introduce a block and the refinement rule to instantiate a block with concrete code.
For the block rules to be syntactically applicable, we extend Java to write a block with a name.Normally, a block in Java is just a sequence of Java statements inside curly brackets.In addition, Ahrendt et al.[ABB + 16] defined block contracts to specify the behavior of a Java block similar to a method [Mey92,Lei95].To establish a CbC refinement process, we introduce a specified block as an abstract statement in CbC-Block with an according 1 Block B2 ; 2 3 /* @ 4 @ normal_behavior 5 @ requires list .contains ( i ) && j > 0 && j < list .size () && 6 @ ( \forall int q ; q >= 0 && q < j ; i >= list .get ( q )); 7 @ ensures list .contains ( i ) && j > 0 && j < list .size () && 8 @ ( \forall int q ; q >= 0 && q < j +1; i >= list .get ( q )); Listing 3: Refinement of block B2 1 /* @ public normal_behavior 2 @ requires requires list .size () > 0; 3 @ ensures list .contains ( \result ) && 4 @ ( \forall int q ; q >= 0 && q < list .size (); \result >= list .get ( q )); 5 @ */ 6 public int maxElement ( List list ) { 7 int i = list .get (0); 8 int j = 1; 9 // @ loop_invariant list .contains ( i ) && j > 0 && j <= list .size () && 10 // @ ( \forall int q ; q >= 0 && q < j ; i >= list .get ( q )); 11 // @ decreases list .Listing 4: Final implementation of maxElement refinement rule.In the refinement rule, we use the Hoare triple notation that is also used for the classic CbC refinement rules.We focus on functional pre-/postconditions and exclude regular and irregular termination of blocks for CbC-Block.For the instantiation of a block (e.g., to write a sequence of Java statements that fulfill the specification), we follow the syntax of a concrete block in Java, but we add a name for reference.An abstract statement is refined by the block-introduction rule to a block with a name and a block contract.Thus, a block name is an abstract placeholder.The side condition of the refinement rule guarantees the correctness of the program to be developed.For the block-introduction rule, we have to check three parts.First, the precondition of the refined abstract statement must imply the precondition of the block.This ensures that the pre-state of the block is satisfied, and the block can be executed.Second, the postcondition of the block must imply the postcondition of the refined abstract statement to continue the program after the block.Third, the block must satisfy its own contract.As the block can be seen as a Hoare triple, any CbC refinement rule can be applied to the block.The check of the side condition of the applied refinement rule guarantees the correctness of the block under development.
With the block-instantiation rule, we allow to instantiate a block with concrete code that can contain further blocks (see the instantiation in Listing 2).For application, it must be checked that this instantiation fulfills the block contract.We use the capabilities of program verification.We translate the block to a method and verify whether this translated block-method fulfills its contract.Thus, we have to prove that the dynamic formula P → <statement;...>Q is fulfilled.Assuming the precondition, the postcondition must be satisfied after executing the statements in the block.Dynamic logic extends first-order logic with two operators.A diamond modality <p>Q and a box modality [p]Q with a program p and a dynamic logic formula Q. Intuitively, the diamond modality states total correctness of the program, and partial correctness is stated with the box modality.
The translation from a block to a block-method is as follows.The block contract is translated to the contract of the block-method.The translated block-method is added to the same class as the method in which the block is declared.The statements within the block become the body of the block-method.As block could introduce local variables that are already declared in the surrounding method [KFFD86], an α-conversion [Bar84] is necessary to safely rename identifiers.A block does not have the same scope of a complete method and neither has parameters nor a return type.Declarations of parameters and local variables have to be added to the block-method, so that is has the same scope as the method.Therefore, we translate accessible variables of the block to parameters of the block-method, and assignable variables of the block to fields of the class containing the block-method.This differentiation is done because a contract can only access parameter values before execution of the method, but it can access the modified values of fields.Accessible or assignable fields of the class are usable because the block-method is added to the class for verification purposes.The return type of the block-method is void because we exclude the use of return statements inside the block.This transformation is limited in its expressiveness as we are excluding irregular termination, but sufficient to demonstrate the correctness-by-construction process for normal execution.Rule 3.2 (Block-Instantiation).Hoare triple {P } Block B {Q} is refinable to {P } <statement;...> {Q} iff P → <statement;...> Q, where <statement;...> is any sequence of concrete program statements possibly containing further blocks.
3.3.Discussion.In this subsection, we discuss the block refinement rules in comparison to related approaches that allow to introduce code sequences, such as method calls, macro expansions, and abstract execution.
Difference to the Method Call Rule.The difference between the block refinement rules and the method call refinement rule is that for a method call only the contract is used to verify correctness of the caller.The content of the method is assumed to be correct with respect to the method's contract.With the block rules, both the contract and the content of the block are always checked for correctness.A big difference between the block rules and the method call rule is their scope.In a method, only variables of the method are changed and no local variables of the calling context.A block allows the modification of local variables as demonstrated in the motivating example.
Difference to Macro Expansion.Macro expansion is a textual transformation of input source code.A preprocessor replaces macros with concrete source code.This is similar to the block-instantiation rule, where a block name is replaced with concrete source code.As for our block-instantiation rule, a macro expansion can capture identifiers already used in the surrounding scope.Therefore, hygienic macro expansion uses α-conversion [Bar84] to rename identifiers.The difference to CbC-Block is that CbC-Block demands a specification for a block that is introduced in the block-introduction rule.Additionally, the block-instantiation rule starts a procedure that verifies whether the block instantiation fulfills its specification.A macro expansion is just a transformation of code.
Abstract Execution for Correctness-by-Construction.Abstract execution [SH19] is a technique to specify and verify programs with partially abstract parts.Abstract execution generalizes symbolic execution.It is tailored to Java, but the principles are applicable to other sequential languages.Java and JML are extended with the concept of abstract program element (APE); an abstract statement or an abstract expression.An APE is a placeholder for any program part with or without side effects.To verify the correctness of programs containing abstract program elements, these elements are specified with a contract similar to a block contract.The extended specification language of abstract execution allows to specify the behavior of the program element in cases of regular or irregular termination including side effects [SH19].The strength of abstract execution is the reasoning of irregular termination that we exclude in CbC-Block.
APEs can be used similar to blocks of CbC-Block to establish a process for refinementbased program construction.With abstract execution, we write programs containing APEs.These programs can be verified to be correct under the assumption that the APEs fulfill their specifications.In a refinement step, an APE is replaced by a program part that contains concrete statements and possibly other abstract program elements.We have to verify that the insertion fulfills the specification of the refined APE.This refinement is repeated until no APE remains.Similar to classic CbC, this process does not require a program to be monolithically verified, but it is sufficient that each APE replacement is verified to conclude that the program is correct by construction.This process is the same as for CbC-Block if we always instantiate a block without using any other CbC refinement rule.We still argue that the application of other CbC refinement in tandem with blocks is beneficial because they enforce a structured program construction process where developers think about the implementation more thoroughly.Therefore, we decided for CbC-Block as presented instead of utilizing abstract execution because CbC-Block is the sweet spot between expressiveness and changes to the program construction process of classic CbC.
Combining classic CbC refinement rules with abstract execution requires major changes to classic CbC, so that the strength of abstract execution is usable (i.e., the refinement rules must be adapted to consider irregular termination).
3.4.Implementation.In this subsection, we describe the implemented tool support for CbC-Block.Classic CbC is already supported by the CorC tool [RSC + 19].CorC has a graphical and a textual editor to develop programs.In this work, we extend CorC with the new rules of CbC-Block.The textual IDE is implemented in Eclipse with Xtext. 4text provides the functionality to develop IDEs for domain-specific languages.We use Xtext to establish an editor for CorC-programs that consist of JML, Java, and the CbCspecific keywords.The grammar of a CorC-program, which represents a refinement-based construction according to CbC, is defined in Xtext.Based on this grammar, Xtext supports syntax checks, highlighting and auto completion.
In CorC, we implemented the refinement rules of Definition 2.1.For CbC-Block, we added refinement rules of block-introduction and block-instantiation.An instantiation is written in a separated program starting with the name of the refined block and followed by the contract and the block's implementation.To verify that a block-instantiation fulfills its block contract, a generator is implemented.It transforms a block-instantiation to a method and starts the verification process by calling KeY [ABB + 16].The transformation to a method follows the concept presented for the block-instantiation rule before.The generator also creates the final method implementation if all refinement steps are proven.All block instantiations must be recursively inserted to get the final method implementation.The final method implementation can be integrated into an existing code base.This generator can also construct partial methods when some parts are not fully refined.This is helpful in intermediate steps of the construction to retain an overview of the current method.
3.5.Evaluation with a User Study.We evaluate CbC-Block with a user study.We compare CbC-Block with classic CbC by (dis)allowing the use of the block rules to answer the following research question.RQ1: Does the CbC-Block approach improve classic CbC in terms of usability?
In the user study, we engaged five participants that know classic CbC and the CorC tool.Knowledge of classic CbC and CorC is a necessary prerequisite because a new feature was evaluated that could only be understood if the participants already knew the classic CbC approach in CorC.Then, they can estimate the benefits of the new block refinement rules.With this small number of participants, it was possible that everyone could solve the study tasks consecutively.
Each participant had to implement two algorithms, one algorithm with CbC-Block and the block rules and one algorithm with classic CbC and without the block rules.The algorithms are maxElement and dutchFlag.The maxElement algorithm was already introduced as the motivating example.The dutchFlag algorithm sorts a list containing only three different elements.In the original description, each element has either a red, a blue or a white color.The elements of the list are to be reordered so that the list results in the national flag of the Netherlands (red, white, blue).We adapted the task to a list that contains an unknown quantity of the numbers 0, 1, and 2. Both algorithms can be implemented in a few lines of code with one loop through the list of elements.As both algorithms are explained to the participants, we expected that the correct implementation is possible without major problems.For each task, they had 30 minutes.We split the participants in two groups using the Latin Square design [WRH + 12].Each group implemented the algorithms in the same order, but the approaches were used crosswise to address possible learning effects through an order of the tools.After implementing both algorithms, we conducted a structured interview.The questions of the interview are presented in Appendix A.
We summarize the most important answers of the participants and discuss the findings.The tasks were correctly solved by all participants.In general, the participants needed more time for the task with CbC-Block than for the task with classic CbC.As we only had five participants, these statistics are only of limited significance.The participants followed the CbC-Block approach and refined a program stepwise using the block rule.All participants considered the introduction of the block rules to be useful.The rules save the application of other CbC refinement rules during construction.For CbC-Block, the participants positively mentioned the familiarity with a textual editor, the grouping of statements for one refinement step by using a block, and the freedom to not be bound to the classic CbC refinement rules.For classic CbC, the answers are in line with previous user studies [RTC + 19, RBTS21].The participants positively mentioned the visual overview of the refinements and the status of the verification.They liked the fine-grained feedback for every applied refinement rule.As CbC-Block extends CbC, these positive answers also apply for CbC-Block.The participants stated for both approaches that the incremental construction helps to track down errors.The participants still miss more assistance if a proof cannot be closed.
While we observed the participants, we noticed that both approaches need a correct and sufficient specification as a starting point.If that is the case, refining and checking side-conditions can be very successful.If, on the contrary, the specification needs to be adjusted in the process, the effort to verify the program increases drastically.With classic CbC, the participants are forced into the process of refining and verifying top-down.With CbC-Block, the participants have more freedom to develop the program.
Regarding the finished tasks, all participants had experience with CorC.Therefore, it is not surprising that all participants finished the task.They never used CbC-Block before, but the participants conceptually understood the features of CbC-Block and accepted the expansion well.Nonetheless, the participants need more time to fully understand the IDE and the programming workflow of CbC-Block.This results in a longer time to implement the algorithms.
We can answer RQ1 that CbC-Block is a promising feature to increase the usability of CorC, but as for each new feature, developers need time to get used to.Some answers of the participants highlight that with more training and better tool support, they are willing to use the CbC-Block approach to construct correctness-critical programs.
Threats to Validity.In our user study, we had only five participants.Due to the small number of participants, the qualitative results that we collected in the structured interview are not generalizable.Nevertheless, the results are relevant, since the users are experts in CorC and can therefore assess the advantages of the extension.A more comprehensive evaluation with non-experts is not possible because they cannot properly interact with the tool.The participants only implemented and verified two small algorithms in our experiment, and therefore, we cannot generalize the results to larger problems.

TraitCbC
In this section, we introduce TraitCbC with a motivational example.We present TraitCbC formally and prove soundness of the TratiCbC program construction approach.In the end of this section, we show the proof-of-concept implementation and a feasibility evaluation.
TraitCbC uses method abstraction and method composition to enable an incremental CbC-based development approach.This approach on method level allows a flexible way to construct the desired program with any number and size of auxiliary methods.A developer starts by implementing a method (e.g., a method a) in a first trait.Similar to classic CbC, the method can contain holes that are refined in subsequent steps.A hole in TraitCbC is an abstract method (e.g., an abstract method b) that is called in method a; that is, a call to an abstract method corresponds to an abstract statement in classic CbC.In the next step, one of these new abstract methods (e.g., b) is implemented in a second trait, again more abstract methods can be declared for the implementation.To be correct, it must be proven that each implemented method satisfies its specifications.Afterwards, the traits are composed; the composition operation checks that the specification of the concrete method b in the second trait fulfills the specification of the abstract method b in the first trait.This incremental program construction approach stops when the last abstract method is implemented, and all traits are composed.4.1.Motivating Example.We illustrate using an example of how TraitCbC enables CbC using traits.We use an object-oriented language in the code examples.In Listing 5, we construct a method maxElement that finds the maximum element in a list of numbers.We slightly adjust the implementation of the algorithm to better fit for TraitCbC.With TraitCbC, we have an abstraction on method level.We utilize methods to outsource program pieces that can be reused (i.e., we want to implement methods that are verified once, but called several times in a program to reduce verification effort).
In this maxElement example, a list has a head and a tail.Only non-empty lists have a maximum element.This is explicit in the precondition of our specification, where we require that the list has at least one element.In the postcondition, we specify that the result is in the list and larger than or equal to every other element.In the first step, we create a trait MaxETrait1 that defines the abstract method maxElement.The method maxElement is abstract, i.e., equivalent to an abstract statement in CbC.
abstract Num maxElement ( List list ); 6 } Listing 5: Initial trait for maxElement In the second step in trait MaxETrait2 in Listing 6, we implement the method maxElement using two abstract methods.We introduce an if-elseif-else-expression where the branches invoke abstract methods.The guards check whether the list has only one element or whether the current element is larger than or equal to the maximum of the rest of the list.The abstract method accessHead returns the current element, and the abstract method maxTail returns the maximum in the remaining list.So, we recursively search the list for the largest element by comparing the maximum element of the list tail with the current element until we reach the end of the list.The correct implementation of the method maxElement can be guaranteed under the assumptions that all introduced abstract methods are correctly implemented.Similar to post-hoc verification, a program verifier conducts a proof of method maxElement and uses the introduced specifications of the methods accessHead and maxTail.If the proof succeeds, we know that the first method is correctly implemented.In our incremental CbCTrait approach, we verify each method implementation directly after construction; and so we are able to reuse each implemented method in the following steps (e.g., by calling the method in the body of other methods).
When the composition of two verified traits is successful, the result is also a verified trait.Note that the composed trait does not need to be verified directly by a program verifier in TraitCbC because it is correct by construction.In this example, the specifications are the same, thus checking for a successful composition is trivial, but this is not generally the case.In particular, the logic needs to take into account ill-founded specifications and recursion in the specification.We discuss more about the difficulties of handling those cases in previous work [RPTS22].
The methods accessHead and maxTail are implemented in the next two construction steps in traits MaxETrait3 and MaxETrait4 5 .The implementations are shown in Listing 7 and in Listing 8.As we implement a recursive method, the method maxTail calls the maxElement method, thus maxElement is introduced as an abstract method in this trait.We have to verify that the method accessHead satisfies its specification using a program verifier.Similarly, we have to verify the correctness of the method maxTail.
Num accessHead ( List list ) = list .element () 5 } Listing 7: Implementation of accessHead As before, all traits are composed, and it is checked that the specifications of the concrete methods fulfill the specifications of the abstract ones.As we have no contradicting specifications for the same methods, the composition is well-formed.In Listing 9, the final program MaxE is shown.All traits are composed.
Listing 9: Trait composition The already proven auxiliary methods in traits can be reused.For example, if we want to implement a minElement method as shown in Listing 10, we could reuse already implemented traits to reduce the programming and verification effort.The method minElement is implemented in the following in trait MinE with one abstract method.The specification of the method accessHead is the same as for the method accessHead above, so MaxETrait3 can be reused.In this example, we show the flexible granularity of TraitCbC by directly implementing the else branch, instead of introducing an auxiliary method as for maxElement.The correctness of minElement is verified with the specifications of the method accessHead.By composing MinE with MaxETrait3, we get a correct implementation of minElement.Note how this verification process supports abstraction: as long as the contracts are compatible, methods can be implemented in different styles by different developers to best meet nonfunctional requirements while preserving the specified observable behavior [tBCSW18].A completely different implementation of maxElement can be used if it fulfills the specification of the abstract method maxElement in trait MaxETrait1.This decoupling of specification and corresponding satisfying implementations facilitates an incremental program construction approach where a specified code base is extended with suitable implementations [DDJS14].
4.2.Object-Oriented Trait-Based Language.In this section, we formally introduce the syntax, type system, reduction, and flattening semantics of a minimal core calculus for TraitCbC.We keep this calculus for TraitCbC parametric in the specification logic so that it can be used with a suitable program verifier and associated logic.The presented rules to compose traits are conventional.The focus of our work is to enable a CbC approach using traits that developers can easily adopt.Therefore, we present the calculus to prove soundness of TraitCbC, but focus on the presentation of the advantages of incremental trait-based programming in this paper.Indeed, languages with traits and with a suitable specification language intrinsically enable incremental program construction.4.2.1.Syntax.The concrete syntax of our core calculus for TraitCbC is shown in Fig. 1, where non-terminals ending with 's' are implicitly defined as a sequence of non-terminals, i.e., vs ::= v 1 . . .v n .We use the metavariables t for trait names, C for class names and m for method names.A program consists of trait and class definitions.Each definition has a name and a trait expression E .The trait expression can be a Body, a trait name, a composition of two trait expressions E , or a trait expression E where a method is made abstract, written as E[makeAbstract m].A Body has a flag interface to define an interface, a set of implemented interfaces Cs and a list of methods Ms. Methods have a method header MH consisting of a specification S, the return type, a method name, and a list of parameters.Methods have an optional method body.In the method body, we have 16:17 standard expressions, such as variable references, method calls, and object initializations.
For simplicity, we exclude updatable state.Field declarations are emulated by method declarations, and field accesses are emulated by method calls.The specification S in each method header is used to verify that methods are correctly implemented.The specification is written in some logic.In our examples, we will use first-order logic (cf. the example in Section 4.1).A well-formed program respects the following conditions: Every Name in Ds must be unique so that Ds can be seen as a map from names to trait expressions.Trait expressions E can refer to trait names t.A well-formed Ds does not have any circular trait definitions like t = t or t 1 = t 2 and t 2 = t 1 .In a Body, all names of implemented interfaces must be unique and all method names must be unique, so that Body is a map from method names to method definitions.In a method header, parameters must have unique names, and no explicit parameter can be called this.4.2.2.Typing Rules.In our type system, we have a typing context Γ ::= x 1 : C 1 . . .x n : C n which assigns types C i to variables x i .We define typing rules for our three kinds of expressions: x, method calls, and object initialization.We combine typing and verification in our type checking Γ ⊢ e : C ⊣ P 0 |= P 1 .This judgment can be read as: under typing context Γ, the expression e has type C, where under the knowledge P 0 we need to prove P 1 .The knowledge P 0 is our collected information that we use to prove a method correct.That means, in our typing rules, we collect the knowledge about the parameters and expressions in a method body to verify that this method body fulfills the specification defined in the method header.The verification obligation P 1 should follow from the knowledge P 0 .
We check if methods are well-typed with judgments of form Ds; Name ⊢ M : OK .This judgment can be read as: in the definition table, the method M defined under the definition Name is correct.The typing rules of Fig. 2 are explained in the following.The first four rules type different expressions and collect the information of these expressions to prove with rule MOK that a method fulfills its specification.In the rule MOK with keyword verify, Γ ⊢ e 0 .m(e 1 . . .en) : we call a verifier to prove each method once.Abstract methods (AbsOK) are always correct.Rule BodyTyped ensures that all methods in a body are correctly typed.
x : As usual, the type of a variable is stored in the environment Γ.From the verification perspective, we do not need to prove anything to be allowed to use a variable; thus we use true.We know that the result of evaluating a variable is the value of such variable, and that such value is of the type of the variable; thus we have result : Γ (x ) & result = x .The result is the returned value of evaluating this expression, and variable : type is a predicate in our system.As you can notice, we are assuming that our parametric logic supports at least a logical and (&); but of course other ways to merge knowledge could work too.Method: As usual, to type a method call, we inductively type the receiver and all the parameters.In this way, we obtain all the types C 0 . . .C n , all the knowledge P 0 . . .P n , and all the verification obligations P ′ 0 . . .P ′ n .Inside of all conditions P i |= P ′ i we call the result of e i result.We cannot simply merge the knowledge of P 0 . . .P n , since their result refers to different concepts.Thus, we chose fresh x ′ 0 . . .x ′ n variables, and we rename result of P i and P ′ i into x ′ i .Similarly, S ′ is the specification of the method adapted using x ′ 0 . . .x ′ n .The verification obligation of course contains all the obligations of the receiver and the parameters, but also requires the precondition of the method to hold.
The knowledge contains the knowledge of the receiver and the parameters, and the method specification in implication form.Naively, one could expect that since the precondition is already in the obligation we could simply add the postcondition to the knowledge.This would be unsound.By using the specification in implication form, the system prevents circular reasoning: we could otherwise use the postcondition to prove the precondition.Instead, when the system shows that the precondition of S ′ holds, it can assume the postcondition of S ′ .Similar to logical and above, we are assuming that our parametric logic supports at least logical implication, but of course other forms of logical consequence could work too.
Note that the postcondition will contain information about the result of the method body as information on the result variable.New: As usual, to type an object instantiation, we inductively type all the parameters.
In this way we obtain all the types C 1 . . .C n , all the knowledge P 1 . . .P n , and all the verification obligations P ′ 1 . . .P ′ n .As we did for Method we use fresh variables to be able to compose predicates.
As we mentioned above, we rely on abstract state operations to represent state: that is, all the abstract methods in C need to be of form S i method C i x i (); where this.x i () returns the value of field x i , that in turn was initialized with the result of expression e i .The function getters(C) returns all methods of this form.
Knowledge P ′′ i contains the knowledge of P i (from expression e i ) and it links such knowledge to the result of calling method result.xi (), so that calling a getter on the created object will return the expected value.However, the information is conditional over verifying the precondition of such getter.Note that we do not need to add the knowledge of the postcondition of x i () here; this will be handled by the Method rule when x i () is called.
Knowledge P is simply merging the accumulated knowledge; while the final obligation in addition to merging the accumulated obligations also requires that the precondition of all the getters hold.In this way the getter preconditions behave like the precondition of the constructor.By requiring those preconditions, we ensure that we can call the getters on all the created objects.Sub: The subsumption rule is standard.We allow subtyping between class names.Note that we do not apply weakening and strengthening of conditions here.
Besides of typing correct programs, the typing rules of Trait-CbC have the goal to verify the correctness of method implementations.The following rules check whether a method or a Body are correct.The check for a correct method declaration in MOK calls a program verifier to verify the correctness.We need just one verifier call for the verification of each method because the rules above collected all needed knowledge and obligations.

MOK:
In MOK, we construct a Γ, and we type the method body, obtaining knowledge P and obligation P ′ .The program verifier will know the type information of Γ, the premise of the method, and the knowledge P , and will prove the obligation P ′ and the postcondition of the method.This verification in the typing rule is indicated by the keyword verify.Here, we use implication, but a different program verifier may use a different form of logical consequence.The program verifier can access the specification of all the other methods since we also provide the declaration table.AbsMOK: Abstract methods are correctly typed.4.2.3.Reduction Rules.We formulate three reduction rules for our system to evaluate input expressions to final values.We introduce an evaluation context E v in our syntax in Fig. 1 to define the order of evaluation.The rules of Fig. 3 are explained in the following.Ctx: This is the conventional contextual rule, allowing the execution of subexpressions.
Mcall: We reduce a method call to an expression e, where the receiver is replaced with new C(vs), and each parameter x i with the actual value v i .We also ensure that the method is declared in the class C. Getter: In our formalism, abstract methods without arguments represents getters.Notation abs(Body) returns the set of all abstract methods in Body.A valid class can only have abstract methods without arguments, and they will all represent getters.4.2.4.Flattening Semantics.When we implement methods in several traits, we have to check that these traits are compatible when they are composed.This process to derive a complete class from a set of traits is called flattening.We follow the traditional flattening semantics [DNS + 06].A class that is defined by composing several traits is obtained by flattening rules.All methods are direct members of the class [DNS + 06].Overall, our flattening process works as a big step reduction arrow, where we reduce a trait expression into a well-typed and verified body.
To introduce our flattening rules in Fig 4, we first define the helper functions.The function allMeth collects all method headers with the same name as m in all input bodies (Definition 4.1).When two Bodys are composed (Definition 4.2), the implemented interfaces are united and the methods are composed.The composition of methods (Definition 4.3) collects methods that are only defined in one of the input sets.If a method is in both sets, it is composed (Definition 4.4).Here, we distinguish four cases.If one method is abstract and the other is concrete, we have to show that the precondition of the abstract method implies the precondition of the concrete method.Additionally, the postcondition of the concrete one has to imply the postcondition of the abstract one.This is similar to Liskov's substitution principle [LW94].The second case is the symmetric variant of the first case.In the third and fourth case, two abstract methods are composed.Here, the specification of one abstract method has to imply the specification of the other abstract method such that an implementation can still satisfy all specifications of abstract methods.If both methods are correct (Theorem 4.8: General Soundness).In turn, to prove General Soundness, we need two lemmas which state that the composition of traits is correct (Lemma 4.6) and that a trait after the makeAbstract operation is still correct (Lemma 4.7).
In Lemma 4.6, we have well-typed definitions Ds, and two well-typed and verified traits in Ds, and the resulting trait/class is also well-typed and verified.Proof.We prove by contradiction.We assume the resulting Body is ill typed.By definition of BodyTyped, it means that one of the methods cannot be typed with either AbsMOK or MOK.The list of methods that need to be typed is obtained by Definition 4.2.
Abstract methods can only be typed with AbsMOK and are never wrong.Implemented methods can only be typed with MOK.If Γ ⊢ e : C ⊣ P |= P ′ or the other precondition verify Ds ⊢ (Γ & Pre(S) & P ) |= (P ′ & Post(S)) does not hold, it means that there was a method m i with expression e i in Body 1 (or symmetrically for Body 2 ) that was well-typed under Ds; t 1 ⊢ Body 1 .That means that all of its implemented methods were well-typed and verified.Typing e i produces P i |= P ′ i by using a Γ t1 containing this : t 1 .If Ds; Name ⊢ Body : OK is not applicable, the same expression e i was typed using a Γ Name containing this : Name.It produced ) holds by our assumption.By Definition 4.4, the contracts of the methods in Body are simply stronger than the contracts of the methods in Body 1 .The only difference between P ′′ i |= P ′′′ i and P i |= P ′ i is in the contracts of methods called on this.Assuming that our parametric logic implication is transitive, we know that ), thus we reach a contradiction.Lemma 4.7 shows that if we have a well-typed and verified trait, the operation make− Abstract results in a trait/class that is also well-typed and verified.Lemma 4.7 (MakeAbstract correct).If Ds(t) = Body, Ds(Name) = Body ′ , Ds; t ⊢ Body : OK , and Body[makeAbstract m] = Body ′ , then Ds; Name ⊢ Body ′ : OK Proof.We prove by contradiction.We assume the resulting Body ′ is ill typed.By definition of BodyTyped, it means that one of the methods cannot be typed with either AbsMOK or MOK.The list of methods that need to be typed is obtained by Definition 4.2.
Abstract methods can only be typed with AbsMOK and are never wrong.We know that Body is typable by our assumption.The only difference between Body and Body ′ is that the method m is made abstract.As we have seen for Lemma 4.6, we are typing Body ′ in a different Γ.This case is even simpler than Lemma 4.6 because Body and Body ′ have exactly the same specifications.The abstract method m and thus Body ′ cannot be ill typed.
With these Lemmas, we can prove Theorem 4.8.Given a sound and modular verification language, then all programs that flatten are well-typed and verified.In a modular verification language, a method can be fully verified using only the information contained in the method In Listing 11, we show the concrete syntax of our implementation.Each method in a trait is specified with JML with the keywords requires and ensures for the pre-and postcondition.To verify the correctness of programs, we need two steps.First, we verify the correctness of a method implemented in a trait w.r.t.its specification.Second, for trait composition, our implementation checks the correct composition for all methods (cf.Definition 4.2).The syntax of trait composition is shown in Listing 12.In a tc-file (a file to specify the traits to be composed), the name of the resulting trait is given and the composed traits are connected with a plus operator.In Listing 12, trait MaxElement1 is composed with trait MaxElement2.The trait MaxElement2 must implement the methods accessHead and maxTail, so that we obtain a correct result in which all methods are implemented.To verify correctness of the trait composition, it is checked that the specification of a concrete method satisfies the specification of the abstract one with the same signature (cf.Definition 4.4).These verification goals are sent to KeY, which starts an automatic verification attempt.Listing 11: Example of a trait in our implementation Listing 12: Example of a trait composition Evaluation.We evaluate our implementation by a feasibility study.First, we reimplemented an already verified case study in our trait-based language.We used the IntList [STAL11] case study, which is a small software product line (SPL) with a common code base and several features extending this code base.Here, we can show that our trait-based language also facilitates reuse.The IntList case study implements functionality to insert integers to a list in the base version.Extensions are the sorting of the list and different insert options (e.g., front /back).We implement five methods that exists in different variants with our trait-based CbC approach.We implement the case study in different granularities.The coarse-grained version is similar to the SPL implementation we started with [STAL11], confirming that traits are also amenable to implement SPLs as shown by Bettini et al. [BDS10].The fine-grained version implements the five methods incrementally with 12 construction steps.We can reuse 6 of these steps during the construction of method variants.
We also implement three more case studies (BankAccount [TSAH12], Email [Hal05], and Elevator [PR01]) with TraitCbC and classic CbC to show that it is feasible to implement object-oriented programs with both approaches.We used CorC [RSC + 19] as an instance of a classic CbC tool.We were able to implement 9 classes and verify 34 methods with a size of 1-20 lines of code.For future work, a user study is necessary to evaluate the usability of TraitCbC in comparison to classic CbC to empirically confirm our stated advantages.

The Different CbC-based Program Construction Approaches in Comparison
In this section, we discuss classic CbC in comparison to CbC-Block and TraitCbC.
In Table 1, we summarize how the three approaches compare regarding main aspects of developing correct programs using tool support.The aspects comprise the programming language, the tool support, the procedure to develop programs, and the verification of the program.
Language.All approaches need an underlying programming and specification language.
The defined refinement rules of the classic CbC approach are external to a programming language.That means, each refinement rule introduces some statement of the programming language by transforming the program.With CbC-Block and the block-instantiation rule more than one statement of the language can be introduced at once.Additional refinement rules for a programming language.Introduces a specified block of statements.Needs specification language.
Programming language with traits.Needs specification language.

Tool support
Pen and paper.Some specialized tools available.
Pen and paper.Some specialized tools available.Block instantiation rule relies on posthoc verification tools.
Relies on post-hoc verification tools.

Construction Rules
Specific refinement rules.Specific refinement rules.Construction by composition of traits.

Correctness/ Debugging
Guarantees the correctness of each refinement step.
Guarantees the correctness of each refinement step.Refinements can be condensed with the block rules.
Guarantees the correctness of each construction step.Each method is specified so that each constructed method can directly be verified.

Proof complexity
Many, but small proofs.
Any granularity of proofs.Any granularity of proofs.

Reuse
Refinement steps cannot be reused; only fully implemented methods can.
Refinement steps cannot be reused; only fully implemented methods can.
Each verified method in a trait can be reused.
Applications Focuses on small, but correctness-critical algorithms.
Focuses on correctness-critical algorithms.
As TraitCbC is based on post-hoc verification, it can be used in similar areas where post-hoc verification is used.Traits are beneficial for incremental development and software product lines.
Table 1: Comparison of TraitCbC with CbC-Block and classic CbC flexibility to construct programs without the need of external refinement rules.Methods can be developed freely and only need to be composed with respect to their specification.Nevertheless, TraitCbC supports to construct code in fine-grained steps, which are more amenable for verification than more complex methods.Correctness/Debugging.Classical CbC gives explicit information about the program states before and after execution of each statement by the Hoare triple notation.The correctness of each applied refinement step is guaranteed by proving the side conditions of the refinement rule.Some side conditions are not directly provable because abstract statements in Hoare triples must be concretized first.In the worst case, a problem in the program is found only after some refinement steps.The abstract statements in classic CbC are not explicitly specified by the developer.Additional specifications in classic CbC are introduced with some rules such as an intermediate condition in the composition rule.Then, these specifications are propagated through the program to be constructed.Again, due to a possible delayed check of a side condition, a wrong specification is found only after some refinement steps.
If errors occur in the program development process, TraitCbC gives early and detailed information on the level of verified methods.By specifying the method under development and any abstract method that is called by this method, we can directly verify the correctness of the method under development.We assume that the introduced abstract methods will be correctly implemented in further refinement steps.With each step, the developer gets closer to the solution until finally all abstract methods are implemented.CbC-Block combines the characteristics of the other two approaches.The refinement rules of classic CbC can be applied, or blocks of statements can be introduced.The specified block is verified similar to a method in TraitCbC.Proof Complexity.Classical CbC requires many small proofs to guarantee the correctness of a program.CbC-Block can condense the proofs into larger proofs using the block refinement rules.TraitCbC can have the same granularity and also the same proof effort as classic CbC, since each method implementation can correspond to just one refinement step.The advantage of TraitCbC and CbC-Block is that developers can freely implement a method body or a block.They must not stick to the same granularity as in the classic CbC refinement rules.Proof complexity can be balanced against verifier calls.Reuse.A fully refined method can be reused in all approaches.For TraitCbC, we can easily reuse even very small units of code, since they are represented as methods in the traits.In classic CbC and CbC-Block, no refinement step can be reused.Applications.The classic CbC approach does not scale well to development procedures for complete software system.Rather, individual algorithms can be developed with CbC [WKSC16].With the block rules, the scalability is improved because refinement steps that are easy to prove can be combined into one block.This saves the application of refinement rules and their corresponding correctness proofs.With ArchiCorC [KRS20], we can even scale CbC to the development of correct component-based architectures.By composing components specified with required and provided interfaces, we support the creation of software architectures correct by construction.
As soon as we scale TraitCbC to real languages, we have the same application scenarios as approaches that already use post-hoc program verification.As argued by Damiani et al. [DDJS14], traits enable an incremental process of specifying and verifying software.Bettini et al. [BDS10] proposed to use traits for software product line development and highlighted the benefits of fine-grained reuse mechanisms.Here, TraitCbC's guideline is suitable for constructing new product lines step by step from the beginning.
Since CbC-Block extends classic CbC and can be freely applied at any granularity of refinement steps, we propose to use CbC-Block for any implementation of correctnesscritical software, but the CbC approach must be well understood by the developer to be efficiently usable.In TraitCbC, methods are developed and composed directly, so less knowledge is needed to apply the approach, but developers can fall back into a post-hoc verification process and thus lose the benefits of CbC (e.g., if the developers first develop all methods and do not directly prove the correctness).In general, both approaches are usable for program development and the right choice depends on the preferences and prior knowledge of the developers.Summary.In summary, TraitCbC and CbC-Block allow more flexible program construction without losing the advantages of incremental correct-by-construction program development.CbC-Block loosens the strict guideline of classic CbC by adding the block refinement rules.CbC-Block still needs specialized tools, such as CorC to be applicable.TraitCbC enables a CbC approach for trait-based languages without introducing refinement rules.This program construction approach combined with the flexibility of traits allows correct methods to be developed in small and reusable steps.TraitCbC is independent of special CbC tools and requires only a program verifier.16:29

Related Work
In the following, we discuss related work for specifying and verifying software.We discuss related correctness-by-construction approaches and compare CorC with other tools for CbC.
Contracts and Program Verification.The implementation of CbC in CorC and the implementation of TraitCbC use JML, Java DL and Java to specify and write programs.For the verification, KeY [ABB + 16] is integrated in the backend.KeY is a deductive program verifier for Java programs specified with JML.In an intermediate step, the specified programs are translated to Java DL.Similarly, OpenJML [Cok11] verifies Java programs specified with JML.Besides Java/JML, many languages support pre-/postcondition contracts or other forms of specification to state program behavior.First, the programming language Eiffel introduced contracts and supported the design-by-contract approach [Mey88,Mey92].Eiffel is an objectoriented programming language, where classes are specified with invariants, and methods with pre-/postconditions contracts.For the verification, AutoProof [KRMJ16, TFNP15] is integrated that translates the specified program to a logic formula.Then, an SMT-solver proves the validity of the formulas.For C#, the language Spec# is an extension to introduce contracts and invariants [BLS04, BFL + 11].The verification is done by translating the proof obligations to an intermediate language BoogiePL that can be verified with Boogie [BCD + 05].For the C language, the VCC [CDH + 09] and Frama-C [CKK + 12] tools verify annotated C code.VCC reuses the Spec# tool chain.For Java and C, the VeriFast [JSP10] tool verifies C and Java programs.VerCors [ABD + 14] also support the verification of C and Java programs with a focus on concurrent and distributed software.Another language with integrated specifications and verification is Dafny [Lei10].Dafny is a functional language, but supports the compilation to other languages such as C#, Java, Go, and Python.Similarly, Whiley [PG13] is a designed language with associated verifier to simplify the verification of programs.The languages SPARK [Bar03] supports a subset of the Ada language to specify and verify Ada programs.In contrast to JML, the specification is not written as comments, but the Ada aspect-syntax is used to express contracts.The focus of all these languages and verification tools is the specification of program behavior and the verification that a program satisfies its specification.With CbC (CbC-Block and TraitCbC), we put the correct construction of programs in the foreground, instead of just verifying the correctness post-hoc.However, Watson et al. [WKSC16] argue that correctness-by-construction and post-hoc verification can be used together to combine their mutual strengths.
To verify trait languages, Damiani et al. [DDJS14] added specifications of methods in traits to verify correct trait composition.They proposed a modular and incremental verification process.Traits are introduced in many languages to support clean design and reuse, for example Smalltalk [DNS + 06], Java [BMN14] by utilizing default methods in interfaces, and other Java-like languages [BDSS13,LS08,SD05].None of these trait languages were used to formulate a CbC approach to create correct programs.They only focus on code reuse or post-hoc verification.
Refinement-Based Correctness-by-Construction.The main idea of correctness-byconstruction is the stepwise construction of a program from a starting specification with correctness guarantees for each step.We focused on correctness-by-construction by Kourie and Watson [KW12] that we called classic CbC.This classic CbC approach is based on Dijkstra [Dij76] and Gries [Gri87].In this paragraph, we discuss related refinement-based CbC approaches.All of these approaches create correct programs by refining an abstract program or system to a concrete implementation.This is the main difference to the composition-based CbC approach of TraitCbC, where atomic units of code are composed to whole programs.
Morgan's refinement calculus [Mor94] is similar to correctness-by-construction by Kourie and Watson [KW12].Both approaches have the same theoretical foundation, but Morgan's refinement calculus is more elaborated with a large number of different refinement rules, where many rules are only formally interesting.Kourie and Watson [KW12] reduced the refinement rules to a minimal but sufficient set, such that CbC becomes comprehensible for developers without a major background in formal methods.The language ArcAngel [OCW03] with the verifier ProofPower [ZOC09] implements Morgan's refinement calculus.The tool uses a tactic language to apply a sequence of refinement rules for program refinement.Thus, a tactic has the same benefit as the application of a block refinement in CbC-Block because the application of refinement rules is condensed to one refinement step.The difference is that for an introduced block of code in CbC-Block, it does not matter what classic CbC refinement rules would have to be applied to introduce that block of code.A tactic still applies the refinement rules stated in that tactic sequentially.
The invariant based programming [BW12,Bac09] shifts the focus from pre-/postcondition contracts as starting point for refinements to invariants.The tool SOCOS [Bac09, BEM07] implements Back's methodology.Similar to CorC, SOCOS has a graphical user interface to create a program in the form of a UML-style state chart.Refinement steps introduce new states and transitions in the state chart and check compliance with the invariants.A completely refined program is proved correct and executable code can be generated.In CorC, the graphical user interface present the refinement steps in a hierarchical tree structure that more directly represent the structure of the code (comparable with an abstract syntax tree).Therefore, CorC and also the implementation of TraitCbC are on the level of source code.
Further refinement-based methodologies are Event-B [Abr10, ABH + 10] for automatabased systems and Circus [OCW09,OGC08] for state-rich reactive systems.Both methodologies work on an abstraction level with abstract models instead of specified source code.In refinement steps these abstract models of the system are transformed to concrete and executable implementation.Here, each refined result guarantees conformations with the initial model.Event-B is supported by the tool Rodin [ABH + 10], and Circus is supported by the tool CRefine [OGC08].The main difference to CbC by Kourie and Watson [KW12], and TraitCbC is the abstraction level.We specify and verify source code rather than automata-based systems.
Data refinement [HKKN13, HL22, LT12, CDM13] is a related approach that focuses on the refinement of (abstract) programs with abstract types to correct and more efficient programs with concrete types.Haftmann et al. [HKKN13] examine how the Isabelle/HOL code generator applies data refinements to produce executable versions of abstract programs.Cohen et al. [CDM13] present an approach to refine Coq programs to enhance computational efficiency.Haslbeck and Lammich [HL22] not only ensure functional correctness during data refinement, they also verify worst-case complexity of algorithms at the LLVM level.The main difference to CbC by Kourie and Watson [KW12] is that data refinement approaches start with algorithms on abstract data structures that are refined to more concrete data structures, whether CbC by Kourie and Watson focuses on the incremental development of the algorithm itself.Therefore, both approaches can used in concert to develop more efficient algorithm.
Extensions to Correctness-by-Construction and CorC.CorC has been extended in several directions to allow the structured program development for larger software systems and further application areas.With ArchiCorC [KRS20], we integrate the construction of correct software architectures.We bundle CorC programs into reusable software components.The components communicate via required and provided interfaces where ArchiCorC guarantees the compatibility between them.With VarCorC [BRS20] software product lines are developed correct by construction.A software product lines is used to systematically construct a family of similar software programs instead of developing monolithic programs.VarCorC ensures the correctness of all possible software variants of the product line.In addition to functional correctness, correctness-by-construction and CorC are extended to guarantee nonfunctional properties.As a first example, we introduced CbC refinement rules to ensure that programs [RKTS20, RKS + 22] follow an information flow policy which defines the allowed flow of information in a program.In every refinement step, security and functional correctness of the program is guaranteed, such that insecure and incorrect programs are prohibited by construction.The goal of these extensions is that program development in CorC is scalable and that CbC can be used for additional application areas.Orthogonally, this article focuses on improving the flexibility of developing programs correct by construction (e.g., by introducing the block refinement rules).
Program and Specification Synthesis.Program synthesis is a technique that generates programs from user given specifications automatically.Pioneers in this field are Manna et al. [MW80].Gulwani et al. [GPS + 17] give an overview of state-of-the-art program synthesis approaches.For example, for Fortran, Stickel et al. [SWL + 94] deductively extract programs from user-given graphical specifications.They compose procedures from libraries to full implementations.Similarly, Gulwani et al. [GJTV10] synthesize programs by composing base components from a specified library.Polikarpova et al. [PKSL16] synthesize recursive programs from specifications by utilizing type information.Similarly, synthesis of function summaries [Hoa71, CDK + 15, SFS12] automatically generate pre-/postcondition specifications from programs to achieve modular verification and to improve verification time.With CbC (classic CbC, CbC-Block, or TraitCbC), developers have the task to specify and create programs according to that specification.Therefore, CbC is a program development approach where the developer determines the resulting program, while program synthesis generates one of possibly many programs that fulfills the specification.Contrary to this, the synthesis of a function summary generates one of possibly many specifications for a program.Synthesis has scalability limitations due to an enormous search space of programs/specifications and ambiguity of user intent.

Conclusion
In this article, we presented CbC-Block and TraitCbC two incremental program construction approaches that guide developers to implementations that are correct by construction.CbC-Block extends classic CbC with block refinement rules.These rules allow to condense the application of CbC refinement rules into one block refinement.Thus, CbC-Block increase flexibility in the development of programs because any sequence of statements can be introduced in a block, while still ensuring the correctness of that introduced block.
TraitCbC uses method calls and trait composition instead of refinement rules to guarantee functional correctness.We formalize the concept of a trait-based object-oriented language with a parametric specification language to allow a broader range of languages to adopt this concept.The main advantage of TraitCbC is the simplicity of the refinement process that supports code reuse.We compared classic CbC, CbC-Block, and TraitCbC qualitatively with regard to their programming constructs, tool support, and usability.CbC-Block and TraitCbC both relax the strict guideline of CbC without losing the benefits of a constructive program construction approach.
As future work, user studies could be conducted with all three approaches to further evaluate the usability of the approaches.We want to investigate how the more flexible construction approaches of TraitCbC and CbC-Block are received by developers.We also want to compare the development times and potential types of programming errors between the approaches.These user studies will help to develop concrete guidelines on which approach is appropriate under which circumstances and with which team.

Prog :: =
Figure 1: Syntax of the trait system

Lemma 4. 6 (
Composition correct).If Ds(t1) = Body 1 , Ds(t2) = Body 2 , Ds(Name) = Body, Ds; t 1 ⊢ Body 1 : OK , Ds; t 2 ⊢ Body 2 : OK , and Body 1 + Body 2 = Body, then Ds; Name ⊢ Body : OK [Lei10]BEM07]sable with languages that have traits.Methods can be implemented as defined by the language.No refinement rules are necessary.Tool Support.Tool support is helpful for any of the approaches.For classic CbC, mostly pen and paper is used.There are a few specialized tools such as CorC [RSC + 19], ArcAngel[OCW03], and SOCOS[Bac09,BEM07].These tools force a certain programming procedure on the user because refinement rules must be applied to implement programs.CbC-Block is implemented in CorC and extends the set of refinement rules with the new rules for blocks.To verify the correctness of block instantiations, program verifiers can be reused.There are program verifiers for many languages, such as Java [ABB + 16], C [CDH + 09], and C# [BFL + 11, BLS04].Other languages are integrated with their verifier from the start, e.g., Spec#[BLS04]and Dafny[Lei10].For TraitCbC, we also need a program verifier to prove the correctness of method implementations, but we do not need specialized tools to construct methods, such as CorC.Construction Rules.To construct a program, classic CbC has a strict concept of refinement rules that must be applied to construct a program.CbC-Block relaxes this strict guideline to construct programs.Programs can be constructed stepwise as with classic CbC, but if desired, any number of refinement steps can be condensed with the block rules.In the extreme case, a whole program can be developed in one step.TraitCbC offers this 16:27