This commit introduces the type system of the model along with several
various other improvements to the model and its users.
* Introduce the type system.
* Introduce possibility to tag certain fields in the model as to be
optional during YAML serialization.
* All the `Name` fields have been replaced in favor of `CustomName` plus
a `name` method that will use `CustomName` if available, or an
automatically generated name otherwise.
* Make TupleTreeReferences behavior more robust: now you either need to
have a valid pointer to `Root` and a `Path` or be default constructed
(`nullptr` for `Root` and an empty `Path`). Any other configuration is
invalid.
* The type system introduces `RawFunctionType`: this superseds the
previous way in which we were specifying arguments and return
values. Users of such information have been updated accordingly.
Fix compilation error: the expression 'co_await
__promise.final_suspend()' is required to be non-throwing
This compilation error started being triggered when switching to
clang-12.
Add a specialization for
CallByPathVisitorWithInstance::visitContainerElement to handle cases
where the element of the container is an UpcastablePointer, that needs
to be unwrapped before visiting.
This fixes a problem causing bad TupleTree traversal with
TupleTreeReference. Specifically, CallByPathVisitorWithInstance did not
know how to visitContainerElement when the element of the container was
an UpcastablePointer. This caused the visit to always return nullptr
even when the TupleTreeReference was well-formed. This has been fixed by
adding an overload with proper concept constraint, that teaches
CallByPathVisitorWithInstance how to properly traverse
UpcastablePointers.
The second template parameter, defaulted to void, was only used for
SFINAE with enable_if. Now that all uses of enable_if are gone replaced
by concepts, this parameter does not serve any purpose anymore.
This commit drops the KeyTraits in favor of a std::any-like solution.
Basically, we type erase any key the user wants to employ, just exposing
a virtual version of the destructor, a comparison operator and a clone
primitive.
Before this commit, the execution logic of RecursiveCoroutine used an
underlying std::stack allocated on the heap to hold all the
coroutine_handles. It also manually managed passing return values from
callees to callers.
This commit drops this unnecessary auxiliary stack.
When a RecursiveCoroutine co_awaits another one, the handle of the
awaiter is injected into the awaitee, so that when the awaitee is done
it can directly execute the remaining part of the awaiter as a
continuation.
This commit:
* Drops `KeyTraits::toString`: if needed, use `getNameFromYAMLScalar`.
* Makes many methods in TupleTree.h return `nullptr` or `std::optional`
in order to gracefully handle failures.
* Provides `KeyTraits` specializations for integral types and tuple-like
composed by types providing `KeyTraits`.
* Introduces `CompositeScalar`, which enables tuple-like objects to be
YAML-serializable scalars by joining the YAML-serialization of its
members through a customziable character.
* Implements `PathMatcher`, a very simple "regular expression" mechanism
for paths on tuple trees.
* Introduce testing for the Model.
Before this commit, recursive coroutines did not work properly if they
had out arguments with reference type.
The reason is that `rc_run` was inferring the type of its arguments from
the arguments themselves, not from the prototype of the recursive
coroutine.
Hence, code snippets like the following did not work properly, because
`rc_run` was taking x by value, not by reference.
```
RecursiveCoroutine<void> accumulate_on_i(int &i) {
// ...
}
int f() {
int x = 0;
rc_run(accumulate_on_i, x);
return x;
}
```
This commit fixes the problem. Now the arguments of `rc_run` are
properly forwarded to the recursive coroutine.
This commit fixes a couple of bugs preventing SortedVector and
MutableSet from being serialized.
Also, it introduces minimal testing for serialization.
These data structures are substitutes for a `std::map<Key, Value>` where
`Key` is embedded in `Value`. Their main goal is to be serializable in a
YAML sequence while preserving the order enforced by the key.
`MutableSet` is implemented using a map.
`SortedVector` is implemented using a sorted vector.
Problem:
1. `ForwardNode::getConstNeighbor` returns a reference to a non-`const`
object, but the referenced pointer is `const`
2. `FowardNode::toNeighborRange` checks if the type of the argument is
const, but since `Successors` is not declared const the resulting
`toNeighborRange(Successors)` is never executed in the const version
Solution:
1. Make `getConstNeighbor()` return a reference to a `const` pointer
2. Explicitly declare a separate `toNeighborRange()` function which
returns a `const_child_iterator` and remove the template parameter
RecursiveCoroutines are a facility intended to be used as-drop in
replacement of recursive functions.
They provide the following features.
- They can be written almost as regular recursive functions,
with 4 caveats.
1. A recursive coroutine that returns a type `T`, needs to be declared
to return a `RecursiveCoroutine<T>`.
2. Inside the body of a recursive coroutine, when recursively calling
another recursive coroutine, the recursive call needs to be
prepended by the new keyword `rc_recur`.
3. Inside the body of a recursive coroutine, the `return` statement
needs to be substituted with `rc_return`.
4. When launching a recursive coroutine `A` from a function that is
not a recursive coroutine, `A` needs to be called with the provided
dedicated template wrapper `rc_run`.
The syntax is the following `rc_run(A, arg0, arg1, ...)`.
This is necessary to enable swapping off recursive coroutine and
fall back to regular recursion for debug.
See below for how to do it.
- Unlike regular recursive functions, they don't use the system stack
for recursion. They use a custom heap-allocated stack to manage
recursion. This makes them more robust for implementing recursive
functions that manipulate user-defined input, because they are much
less likely to trigger stack overflow.
- They can be turned off compiling with
`-DDISABLE_RECURSIVE_COROUTINES`, falling back to regular recursion,
for debug purposes.
- They support both direct and indirect recursion, i.e. a recursive
coroutine A can recursively call itself, or it can recursively call
another recursive coroutine B, which in turns recursively calls A.
These are necessary for this iterator to be a forward_iterator according
to the standard.
Missing these causes compilation errors in some cases when using
functions from the Standard Template Library with these iterators.
This patch adds a two markers for llvm::GraphTraits:
- NodePairFilteredGraph
- EdgeFilteredGraph
Both these markers allow to specify a static predicate that is used to
filter edges.
This predicate is a boolean function such that:
- for NodePairFilteredGraph, it takes a pair of const NodeRef & that
are used to represent an edge, and it evaluates a given property of
that pair;
- for EdgeFilteredGraph, it takes a const EdgeRef & that represents an
edge, and it evaluates a given property on the edge.
The filtered graph contains only the edges for which the predicate
evaluates true.
Notice that the predicate must have static lifetime, meaning that all
the edge properties must be entirely evaluated on the pair of node (for
NodePairFilteredGraph) or on the edge (for EdgeFilteredGraph).
This means that you cannot pass mutable state to the predicate at
runtime.
The new markers are designed to interoperate well with llvm::Inverse and
to allow you to traverse the marked graphs with llvm::depth_first,
llvm::inverse_depth_first, llvm::breadth_first, and to compute dominator
trees and post-dominator trees on filtered graphs.
* Drop unused argument names from function prototypes
* Make `static` some methods
* Disable some copy constructors
* Fix casing of Doxygen `\file` directives
* Add some casts to make the compiler happy
* Initialize `hasRelocationAddend` for AArch64
* Use references in range-for where possible
* Drop default for `switch` statements covering all the entries of an `enum`
* Make some global variables `static`
* Drop dead functions