I'm trying to create as many seat belts and idiot lights as possible. Using C macros and automatic function declaration generation makes this much easier for me to do, and is far more maintainable.

Headerizer creates function declarations based on function definitions. It scans the source files passed to it and extracts the function declarations. Then it puts them into the appropriate .h file or, in the case of static functions, back into the source file itself.

The headerizer also adds function attributes as specified by the decorations on the source.

All of these macros are GCC-specific right now, but soon will have equivalent semantics for lint added to them. This will make lint a far more powerful tool. If/when we ever get splint going, too, we can add semantics there as well.

Also, where it says "compiler", it could also mean "lint or any other static analysis tool like splint."

What's a shim? ^

Think of "shim" as shorthand for "placeholder". It's 64% shorter.

GCC (and lint and splint) likes to complain (as well it should) if you pass an argument into a function and don't use it. If we know that we're not going to use an argument, we can either remove the argument from the function declaration, or mark it as unused.

Throwing the argument away is not always possible. Usually, it's because the function is one that gets referred to by a function pointer, and all functions of this group must have the same, say, three args: Interp, Foo and Bar. Maybe a given function doesn't use Foo, but we still have to accept Foo. In this case, we can in the body of the func, UNUSED(Foo), if we plan to use it in the future. Or, if we never will use it, mark it as a SHIM(Foo) in the declaration.

Function Decorators ^


Tells the compiler to warn if the function is called, but the result is ignored.


Tells the compiler that it's OK to ignore the function's return value


Functions marked with this are flagged as having received malloced memory. This lets the compiler do analysis on memory leaks.


The function is a deterministic one that will always return the same value if given the same arguments, every time. Examples include functions like mod or max. An anti-example is rand() which returns a different value every time.


Less stringent than PARROT_CONST_FUNCTION, these functions only operate on their arguments and the data they point to. Examples include strlen() or strchr().


For functions that can't return, like Parrot_exit(). This helps the compiler's flow analysis.


For functions that return a pointer, but the pointer is guaranteed to not be NULL.


For functions that return a pointer that could be null.



For function arguments and variables that must never have NULL assigned to them, or passed into them. For example, if we were defining strlen() in Parrot, we'd do it as strlen(NOTNULL(const char *p)).


For function arguments and variables where it's OK to pass in NULL. For example, if we wrote free() in Parrot, it would be strlen(NULLOK(void *p)).

SHIM(x) ^


Most of the time, if you need an interpreter in your function, define that argument as PARROT_INTERP. If your interpreter is a shim, then use SHIM_INTERP, not SHIM(PARROT_INTERP).

Examples ^

    string_str_index(PARROT_INTERP, NOTNULL(const STRING *s),
            NOTNULL(const STRING *s2), INTVAL start)

string_str_index is part of the Parrot API, and returns an INTVAL. The interpreter is used somewhere in the function. String s and s2 cannot be NULL. If the calling function ignores the return value, it's an error, because you'd never want to call string_str_index() without wanting to know its value.

    parrot_hash_size(SHIM_INTERP, NOTNULL(const Hash *hash))
        return hash->entries;

This function is a pure function because it only looks at its parameters or global memory. The interpreter doesn't get used, but needs to be passed because all PARROT_API functions have interpreters passed, so is flagged as a SHIM_INTERP.

We could put PARROT_WARN_UNUSED_RESULT on this function, but since all PARROT_PURE_FUNCTIONs and PARROT_CONST_FUNCTIONs get flagged that way anyway, there's no need.

Splint to-do ^

Annotate functions with /*@only@*/ and /*@keep@*/

Figure out where /*@modifies@*/ is different from /*@out@*/

More safety stuff ^

Look at snprintf instead of sprintf

Use the return values from sprintf where we would be using strlen of the result

Make the VTABLE_type func take a const PMC, because hey, your type should never change, right?

Sync the specs in docs/pdds/pdd17_pmc.pod with the function headers.

Hoist the repetitive iter_init() in src/encodings