Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions regression/goto-instrument/const-ptr-to-function-ptr/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#include <stdio.h>

void f1(void)
{
printf("f1\n");
}
void f2(void)
{
printf("f2\n");
}
void f3(void)
{
printf("f3\n");
}

typedef void (*func_ptr)(void);

// Test 1: constant pointer to function pointer
void test_const_ptr_to_fp(void)
{
func_ptr fp = f1;
func_ptr *const ptr_to_fp = &fp;
(**ptr_to_fp)(); // Should resolve to f1
}

// Test 2: constant pointer to function pointer in array
void test_const_ptr_to_fp_array(void)
{
func_ptr fps[] = {f1, f2, f3};
func_ptr *const ptr_to_fp = &fps[1];
(**ptr_to_fp)(); // Should resolve to f2
}

// Test 3: struct with constant function pointer member
struct func_struct
{
func_ptr const fp;
int value;
};

void test_const_struct_member(void)
{
const struct func_struct fs = {f3, 42};
fs.fp(); // Should resolve to f3
}

// Test 4: constant pointer to struct containing function pointer
void test_const_ptr_to_struct(void)
{
struct func_struct fs = {f2, 10};
const struct func_struct *const ptr_fs = &fs;
ptr_fs->fp(); // Should resolve to f2
}

int main(void)
{
test_const_ptr_to_fp();
test_const_ptr_to_fp_array();
test_const_struct_member();
test_const_ptr_to_struct();
return 0;
}
10 changes: 10 additions & 0 deletions regression/goto-instrument/const-ptr-to-function-ptr/test.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CORE
main.c
--remove-const-function-pointers --show-goto-functions
^\s*CALL f1\(\)
^\s*CALL f2\(\)
^\s*CALL f3\(\)
^EXIT=0$
^SIGNAL=0$
--
^warning: ignoring
68 changes: 68 additions & 0 deletions regression/goto-instrument/const-struct-with-function-ptr/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#include <stdio.h>

void handler_a(void)
{
printf("A\n");
}
void handler_b(void)
{
printf("B\n");
}
void handler_c(void)
{
printf("C\n");
}

typedef void (*handler_func)(void);

// Test case 1: struct with const function pointer member
struct handler_config
{
handler_func const handler;
int id;
};

void test_const_member(void)
{
struct handler_config config = {handler_a, 1};
config.handler(); // Should resolve to handler_a
}

// Test case 2: const struct with function pointer member
struct handler_ops
{
handler_func on_event;
int priority;
};

void test_const_struct(void)
{
const struct handler_ops ops = {handler_b, 10};
ops.on_event(); // Should resolve to handler_b
}

// Test case 3: const pointer to struct with function pointer
void test_const_ptr_to_struct(void)
{
struct handler_ops ops = {handler_c, 5};
const struct handler_ops *const ptr = &ops;
ptr->on_event(); // Should resolve to handler_c
}

// Test case 4: Array of const structs with function pointers
void test_const_struct_array(void)
{
const struct handler_config configs[] = {
{handler_a, 1}, {handler_b, 2}, {handler_c, 3}};

configs[1].handler(); // Should resolve to handler_b
}

int main(void)
{
test_const_member();
test_const_struct();
test_const_ptr_to_struct();
test_const_struct_array();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CORE
main.c
--remove-const-function-pointers --show-goto-functions
^\s*CALL handler_a\(\)
^\s*CALL handler_b\(\)
^\s*CALL handler_c\(\)
^EXIT=0$
^SIGNAL=0$
--
^warning: ignoring
85 changes: 85 additions & 0 deletions regression/goto-instrument/nested-const-function-ptr/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#include <assert.h>

int result = 0;

void func_1(void)
{
result = 1;
}
void func_2(void)
{
result = 2;
}
void func_3(void)
{
result = 3;
}

typedef void (*func_ptr_t)(void);

// Test 1: Pointer to function pointer (double indirection)
void test_ptr_to_fp(void)
{
func_ptr_t fp = func_1;
func_ptr_t *ptr_to_fp = &fp;
(**ptr_to_fp)();
assert(result == 1);
}

// Test 2: Const pointer to function pointer
void test_const_ptr_to_fp(void)
{
func_ptr_t fp = func_2;
func_ptr_t *const const_ptr_to_fp = &fp;
(**const_ptr_to_fp)();
assert(result == 2);
}

// Test 3: Nested struct with function pointers
struct inner
{
func_ptr_t handler;
};

struct outer
{
struct inner in;
int id;
};

void test_nested_struct(void)
{
const struct outer obj = {{func_3}, 42};
obj.in.handler();
assert(result == 3);
}

// Test 4: Array of function pointer pointers
void test_fp_ptr_array(void)
{
func_ptr_t f1 = func_1;
func_ptr_t f2 = func_2;
func_ptr_t f3 = func_3;

func_ptr_t *const fp_array[] = {&f1, &f2, &f3};
(**fp_array[1])();
assert(result == 2);
}

// Test 5: Const array of function pointers
void test_const_fp_array(void)
{
const func_ptr_t fp_array[] = {func_1, func_2, func_3};
fp_array[2]();
assert(result == 3);
}

int main(void)
{
test_ptr_to_fp();
test_const_ptr_to_fp();
test_nested_struct();
test_fp_ptr_array();
test_const_fp_array();
return 0;
}
10 changes: 10 additions & 0 deletions regression/goto-instrument/nested-const-function-ptr/test.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CORE
main.c
--remove-const-function-pointers --show-goto-functions
^\s*CALL func_1\(\)
^\s*CALL func_2\(\)
^\s*CALL func_3\(\)
^EXIT=0$
^SIGNAL=0$
--
^warning: ignoring
67 changes: 64 additions & 3 deletions src/goto-programs/remove_const_function_pointers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,38 @@ bool remove_const_function_pointerst::try_resolve_dereference_function_call(
return false;
}

// Check if the dereferenced values are themselves pointers to
// function pointers that need further dereferencing
// (e.g., **fp where fp is func_ptr *const)
// This check helps ensure we have valid types before attempting resolution
bool all_are_valid_types = true;
for(const exprt &deref_val : potential_deref_values)
{
// Accept either function types (ID_code) or pointers to functions
if(deref_val.type().id() == ID_pointer)
{
const pointer_typet &ptr_type = to_pointer_type(deref_val.type());
if(ptr_type.base_type().id() != ID_code)
{
all_are_valid_types = false;
break;
}
}
else if(deref_val.type().id() != ID_code)
{
all_are_valid_types = false;
break;
}
}

if(!all_are_valid_types)
{
LOG(
"Dereferenced value has incompatible type for function resolution",
deref_expr);
return false;
}

return try_resolve_function_calls(potential_deref_values, out_functions);
}

Expand Down Expand Up @@ -606,7 +638,8 @@ bool remove_const_function_pointerst::try_resolve_index_of(
/// \param out_is_const: Is the squashed expression constant
/// \return Returns true if it was able to squash the member expression If this
/// is the case, out_expressions will contain the possible values this member
/// could return The out_is_const will return whether the struct is const.
/// could return The out_is_const will return whether the struct is const or
/// the specific member is const.
bool remove_const_function_pointerst::try_resolve_member(
const member_exprt &member_expr,
expressionst &out_expressions,
Expand All @@ -621,6 +654,9 @@ bool remove_const_function_pointerst::try_resolve_member(
member_expr.compound(), potential_structs, is_struct_const);
if(resolved_struct)
{
// Also check if the specific member being accessed is const
bool member_is_const = is_const_type(member_expr.type());

for(const exprt &potential_struct : potential_structs)
{
if(potential_struct.id()==ID_struct)
Expand Down Expand Up @@ -654,7 +690,9 @@ bool remove_const_function_pointerst::try_resolve_member(
return false;
}
}
out_is_const=is_struct_const;
// A member access is const if either the struct is const
// OR the member itself is const
out_is_const = is_struct_const || member_is_const;
return true;
}
else
Expand Down Expand Up @@ -711,8 +749,15 @@ bool remove_const_function_pointerst::try_resolve_dereference(
else
{
LOG("Failed to resolve value of a dereference", address_expr);
return false;
}
}
else if(pointer_val.is_constant() && pointer_val.is_zero())
{
// Null pointer - we can't dereference it but it's a valid constant
// Skip it but don't fail
continue;
}
else
{
LOG(
Expand Down Expand Up @@ -783,13 +828,29 @@ bool remove_const_function_pointerst::is_const_expression(
/// To evaluate the const-ness of the type.
/// \param type: The type to check
/// \return Returns true if the type has ID_C_constant or is an array since
/// arrays are implicitly const in C.
/// arrays are implicitly const in C. For pointers, checks if the pointer
/// itself is const. For structs, checks if the struct is const.
bool remove_const_function_pointerst::is_const_type(const typet &type) const
{
if(type.id() == ID_array)
{
return to_array_type(type).element_type().get_bool(ID_C_constant);
}
else if(type.id() == ID_pointer)
{
// For pointers, we check if the pointer itself is const
// (not what it points to)
return type.get_bool(ID_C_constant);
}
else if(type.id() == ID_struct_tag || type.id() == ID_struct)
{
// For structs, check if the struct type is const
return type.get_bool(ID_C_constant);
}
else
{
return type.get_bool(ID_C_constant);
}
}

/// To extract the value of the specific component within a struct
Expand Down
Loading