# pEpACIDLang - Language Mapping Specification for C99 (LMS-C99) * [pEpACIDLang - Language Mapping Specification for C99 (LMS-C99)](#pepacidlang---language-mapping-specification-for-c99-lms-c99) * [About This Document](#about-this-document) * [IDL-Types](#idl-types) * [Type Classes](#type-classes) * [IDL-Type Mapping To C](#idl-type-mapping-to-c) * [Creation and Destruction](#creation-and-destruction) * [Primitive Types](#primitive-types) * [Object Types](#object-types) * [Allocators](#allocators) * [Destructors](#destructors) * [Method Parameters](#method-parameters) * [Rules For Parameter Allocation](#rules-for-parameter-allocation) * [Parameter Passing Modes](#parameter-passing-modes) * [Mapping Table for C](#mapping-table-for-c) * [Rules For Parameters Of Primitive-Type](#rules-for-parameters-of-primitive-type) * [MBORROW](#mborrow) * [Rules For Parameters Of Object-Type](#rules-for-parameters-of-object-type) * [BORROW](#borrow) * [MBORROW](#mborrow-1) * [CLAIM](#claim) * [PROVIDE](#provide) * [Rules For Multithreading](#rules-for-multithreading) This is the document specifying the pEpACIDLang language mapping for C99. C99, because this is the minimum language level for the generated target code. We will use features of a higher language level once there are good reasons to do that. The interface generator implements parts of the language mapping specification. ## About This Document State: Draft, to be reviewed with fdik and positron. **ATTENTION, IMPORTANT** In this document, all types expressed as `T` are the IDL-types, not their corresponding c-types. An example for clarity: If `T` is the type `string`, the IDL-type `string` is meant, so the c-type of `T*` is `char**`. Usually a variable typename is expressed as `T`. But sometimes `T` is being used to refer to an instance of type `T`. This imprecision is used for brevity, but only where the context does not allow for an ambiguity. ## IDL-Types ### Type Classes There are only two type-classes. * Primitive Types * Object Types ### IDL-Type Mapping To C | Type-Class | IDL-Type | C-Type | |------------|-----------|--------------| | Primitive | int | int | | Primitive | uint | unsigned int | | Primitive | size | size_t | | Primitive | bool | bool | | Primitive | enum | enum | | Object | struct | struct* | | Object | string | char* | | Object | binary | char* | | Object | opaque | void* | | Object | list\ | list_T* | | Object | pair | pair_F_S* | ### Creation and Destruction #### Primitive Types * Primitive types are always statically allocated * Primitive types are trivially constructable and destroyable * All storage classes (auto/static/extern/register) are allowed #### Object Types For any object-type `T`, corresponding `new` and `free` methods are being generated. To create an object of type `T` correctly, there are two ways: 1. Using its allocator-method 2. Using any method that has a parameter of type `T` with mode `PROVIDE` To destroy an object of type `T` correctly, the only way is to use its destructor-method. ##### Allocators The allocator-method has the signature: `IDL_STATUS new_T(T*)` * The memory for `T` has to be dynamically allocated * An empty object is being created. Any object-types reachable through `T` are pointing to `NULL`. Result: STATUS_OK - `T*` is pointing to a valid object STATUS_ERROR - Dereferencing of `T*` is undefined behaviour ##### Destructors The destructor-method has the signature: `IDL_STATUS free_T(T*)` 1. For all object-type members of `T` itself, their respective destructor-methods are being called 2. The memory for `T` is freed compatibly to how it was allocated using its allocator-method 3. `T*` is set to `NULL` Result: STATUS_OK - All memory reachable through `T*` is freed and `T*` is NULL STATUS_ERROR - FATAL (most implementations of `free()` can guarantee this will never happen) ## Method Parameters The main concerns when sharing memory (e.g. in form of parameters) are: * Ownership: defined as who is allowed and responsible to destroy an object. * Mutability: Who is allowed to change the contents of the memory location that is known to several parties. In environments where memory management is explicit, the lifecycle of data in memory is: 1. Memory being allocated 2. Memory being shared with producers/consumers (e.g. as a parameter `BORROW`/`MBORROW`/`CLAIM`) 3. Read/write operations by multiple parties (needs synchronization if multithreaded environment) 3. Make sure this memory will never be accessed again by nobody 4. Memory has to be freed exactly once and compatibly to how it was allocated Every such lifecycle must be completed. All of this must be guaranteed. ### Rules For Parameter Allocation Parameters have to be allocated as follows: * Primitive-types: The general rules outlined under [Creation and Destruction](#creation-and-destruction) apply * Object-types: Using the allocator- and destructor-methods outlined under [Creation and Destruction](#creation-and-destruction) These rules apply regardless of the parameter passing mode. ### Parameter Passing Modes The modes are named by describing what the callee does, from the perspective of the callee, using one verb. * `BORROW` - Immutable, ownership remains with caller * `MBORROW` - Mutable, ownership remains with caller * `CLAIM` - Mutable, ownership goes to callee * `PROVIDE` - Immutable, ownership goes to caller #### Mapping Table for C Type of `T` as a function of type-class and mode. Where `T` any of the respective IDL-Types (not their respective c-type, as mentioned in [About This Document](#about-this-document)). | Mode/Type-Class | BORROW | MBORROW | CLAIM | PROVIDE | |------------------|------|--------|---------|---------| | Primitive | T | T* | - (1) | - (1) | | Object | T | T* | T | T* | #### Rules For Parameters Of Primitive-Type (1) A primitive-type cannot be claimed or provided because it's always statically allocated. ### MBORROW The caller has to allocate memory for `T*`. The callee is allowed to mutate the memory pointed to by `T*`. The caller is responsible to free the memory pointed to by `T*`. #### Rules For Parameters Of Object-Type ##### BORROW The caller has to guarantee that either-or: * `T` is `NULL` * `T` is pointing to a valid object of type `*T` (allocated as defined under 'Rules For Parameter Allocation') This guarantee applies transitively for all object-type members of `*T`. The callee is not allowed to mutate or free any memory that can be reached through `T`. The caller is responsible to free all the memory that can be reached through `T`. ##### MBORROW The caller has to guarantee that: * All object-types reachable through `T*` are either `NULL` or pointing to a valid object (allocated as defined under [Rules For Parameter Allocation](#rules-for-parameter-allocation)) The callee is allowed to mutate the object in two ways: 1. Mutate any primitive-types reachable through `T*` 2. Free or replace any object-types reachable trough `T*` The callee has to guarantee, that upon return of the method: * `T*` is pointing to a valid `*T` (allocated as defined under [Rules For Parameter Allocation](#rules-for-parameter-allocation)) * All object-type members reachable through `T*` are either `NULL` or pointing to a valid object of type `*T` (allocated as defined under [Rules For Parameter Allocation](#rules-for-parameter-allocation)) Upon success of the method call: The caller must replace all pointers to `*T` and to any of its transitive members by dereferencing `T*` and its respective transitive members. The caller must free `T` and any of its transitive members. TODO: Upon any failure of the method call: ##### CLAIM The same rules for mode `BORROW` apply, with the exception that the caller has to guarantee to never access `T` again in any way. ##### PROVIDE The caller is allowed to pass a `T*` with an undefined value. TODO... #### Rules For Multithreading These rules apply to all memory that can be accessed through a parameter. Such memory must be allocated in the calling thread No such memory can be shared with other TODO....