I'm trying to write a plugin that adds support for [[invariant]]
contracts in classes/structs to complete the Contracts support that was added recently in GCC.
This boils down to basically translating something like the below:
class Heap {
private:
// ... fields
[[invariant]]
void check_invariants()
{
assert(size >= 0);
assert(capacity >= 0);
assert(size <= capacity);
}
public:
// constructor, push, pop, etc
};
Into a form where calls to this->invariant();
are made on the entry/exit of every member function that is not the [[invariant]]
method itself.
So e.g., push()
becomes:
void push(int value)
{
this->check_invariants(); // AUTO-GENERATED
data[size] = value;
size++;
this->check_invariants(); // AUTO-GENERATED
}
Now, I've managed to get most of this functionality I think, but I'm running into an issue where I can't seem to get the insertion of the call to this->check_invariants();
to work.
What I have is:
/**
* Create a function call to 'this->invariant()' and insert it before the given
*/
static void
insert_instrumentation_fn(gimple* curr_stmt)
{
tree ctype = DECL_CONTEXT(current_function_decl);
tree fn_decl = lookup_member(ctype, // class type
get_identifier("invariant"), // function name
1, // allow protected access
0, // do not raise error if not found
tf_warning_or_error // error handling
);
// Extract the function declaration from the member function
if (TREE_CODE(fn_decl) == BASELINK)
{
fn_decl = BASELINK_FUNCTIONS(fn_decl);
}
// build new method call
// call is of the form: this->invariant()
// Get the 'this' pointer
tree this_ptr = cp_build_fold_indirect_ref(DECL_ARGUMENTS(current_function_decl));
vec<tree, va_gc>* args = make_tree_vector_single(this_ptr);
tree call = build_new_method_call(this_ptr, // object
fn_decl, // function
&args, // arguments
NULL_TREE, // conversion function
LOOKUP_NORMAL,
NULL, // function decl
tf_warning_or_error);
// build gimple call
gimple* call_stmt = gimple_build_call(call, 0);
// insert call before current statement
gimple_stmt_iterator gsi = gsi_for_stmt(curr_stmt);
gsi_insert_before(&gsi, call_stmt, GSI_SAME_STMT);
}
The issue is that this code produces an error like the below:
[build] > Inspecting function '__ct_base '
[build] Found invariant attribute: invariant
[build] adding function call before *this_3(D) ={v} {CLOBBER};
[build] /home/user/projects/gcc-invariant-plugin/test/test.cpp: In constructor 'Heap::Heap(int)':
[build] /home/user/projects/gcc-invariant-plugin/test/test.cpp:66:1: error: call to non-function 'void Heap::invariant()'
NOTE: I based this code off of examples in the GCC gcc/cp/coroutines.cc
code generator I found:
tree ash_m = lookup_member (coro_frame_type, coro_self_handle_id, 1,
0, tf_warning_or_error);
tree ash = build_class_member_access_expr (actor_frame, ash_m, NULL_TREE,
false, tf_warning_or_error);
/* So construct the self-handle from the frame address. */
tree hfa_m = lookup_member (handle_type, coro_from_address_identifier, 1,
0, tf_warning_or_error);
r = build1 (CONVERT_EXPR, build_pointer_type (void_type_node), actor_fp);
vec<tree, va_gc> *args = make_tree_vector_single (r);
tree hfa = build_new_method_call (ash, hfa_m, &args, NULL_TREE, LOOKUP_NORMAL,
NULL, tf_warning_or_error)