Pointers are a notorious "defect attractor", in particular when dynamic memory management is involved. Ada mitigates these issues by having much less need for pointers overall (thanks to first-class arrays, parameter modes, generics) and stricter rules for pointer manipulations that limit access to dangling memory. Still, dynamic memory management in Ada may lead to use-after-free, double-free and memory leaks, and dangling memory issues may lead to runtime exceptions.
The SPARK subset of Ada is focused on making it possible to guarantee properties of the program statically, in particular the absence of programming language errors, with a mostly automatic analysis. For that reason, and because static analysis of pointers is notoriously hard to automate, pointers have been forbidden in SPARK until now. We are working at AdaCore since 2017 on including pointer support in SPARK by restricting the use of pointers in programs so that they respect "ownership" constraints, like what is found in Rust.
In this talk, I will present the current state of the ownership rules for pointer support in SPARK, and the current state of the implementation in the GNAT compiler and GNATprove prover, as well as our roadmap for the future.
4. Reduced use case for pointers in SPARK
Better language features
→ arrays are first-class objects with A’First, A’Last, A’Length
→ parameter modes in or out or in out
→ generic subprogram parameters
Better pointers (aka “access types” in SPARK)
→ no pointer arithmetic
→ strong typing (no void* / char* / implicit conversions...)
→ typing rules for pointer conversions (pool-specific /general / anonymous)
Still, reclamation possible through Ada.Unchecked_Deallocation
→ possible use-after-free, double-free, memory leaks, dangling pointers
5. Use cases for pointers in SPARK
Size of data structure evolves over time
→ typical of containers which need to grow
Data structure contains indefinite elements
→ type contains a pointer to the indefinite element, e.g. String
Recursive data structures
→ recursion goes through pointers in Ada, e.g. for list or tree
6. What changes with pointer ownership
Objective: Concurrent-Read-Exclusive-Write (CREW)
Already the basis for the support of “references” in SPARK
→ SPARK verifies absence of aliasing on writes through references
→ checks at call site that parameters/globals do not alias
Adapted for supporting pointers in SPARK
→ similar non-aliasing checks at call site
→ pointer assignment “moves” the ownership
→ local handles on data-with-pointers “borrow” or “observe” the data
8. Ownership “move” operation
On assignment of type-with-pointers (including in out and out parameters in calls)
→ rhs loses ownership of pointed data
→ rhs becomes unreadable
→ lhs gains ownership of pointed data
9.
10.
11. Ownership “borrow” operation (1/2)
On call with in parameter of named access type
→ actual loses ownership of pointed data for scope of call
→ actual regains ownership of pointed data after call
→ non-aliasing checks ensure CREW
12. Ownership “borrow” operation (2/2)
On assignment of type-with-pointers to anonymous access-to-variable object
→ rhs loses ownership of pointed data for limited scope
→ rhs becomes unwritable
→ lhs gains ownership of pointed data for limited scope
13.
14. Ownership “observe” operation (1/2)
On call with in parameter of record-or-array-type-with-pointers
→ actual becomes read-only for scope of call
→ formal is also read-only at any depth
→ actual regains full ownership of pointed data after call
→ non-aliasing checks ensure CREW
On constant initialization of record-or-array-type-with-pointers
→ rhs becomes read-only for scope of constant
→ constant is also read-only at any depth
→ rhs regains full ownership of pointed data after scope ends
15. Ownership “observe” operation (2/2)
On assignment of type-with-pointers to anonymous access-to-constant object
→ rhs becomes read-only for limited scope
→ lhs gains ownership of pointed data for limited scope
Y.all is possible here
16.
17. Limitations
Only pool-specific access types (without all or constant keyword)
→ no possibility to take the address of variables X’Access
Less powerful than the ownership of Rust
→ no annotations for lifetimes
→ borrowing/observing relationship is statically known
Borrowing/observing part of an array borrows/observes the whole array
→ E.g. must call swap procedure to swap elements of an array-with-pointers
See https://blog.adacore.com/using-pointers-in-spark
18. Roadmap
Already in GNAT Community Edition 2019
- Stabilized SPARK RM rules (see section 3.10 of
http://docs.adacore.com/spark2014-docs/html/lrm/index.html)
- Complete implementation of ownership checking
- Support in flow analysis and proof (subject to limitations)
For years 2019-2020
- Support local “borrow” and “observe” in proof
- Support proof over recursive data structures (including quantification)
- Check absence of memory leaks by proof
19. Resources
Download as part of GNAT Community Edition
https://www.adacore.com/download
Learn online
https://learn.adacore.com/
Ask questions on r/ada subreddit, Stack Overflow, comp.lang.ada, or email
https://lists.adacore.com/mailman/listinfo/spark2014-discuss
Open issues on GitHub
https://github.com/AdaCore/spark2014