Skip to content

Commit 61c99bf

Browse files
committed
Fix goto-instrument --dump-c parameter/local variable name collision
Local variables are automatically renamed with a unique suffix when a name collision is detected, producing valid compilable C code. Co-authored-by: Kiro autonomous agent Fixes: #6268
1 parent 76dd20f commit 61c99bf

File tree

4 files changed

+105
-0
lines changed

4 files changed

+105
-0
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Test case for parameter/local variable name collision in goto-instrument --dump-c
2+
// This should generate valid C code with renamed local variables
3+
4+
int help(int x)
5+
{
6+
unsigned int x; // Should be renamed to x$1 in output
7+
x = 42;
8+
return (int)x;
9+
}
10+
11+
int test_two_params(int y, int z)
12+
{
13+
int y; // Should be renamed to y$1
14+
int z; // Should be renamed to z$1
15+
y = 10;
16+
z = 20;
17+
return y + z;
18+
}
19+
20+
int main()
21+
{
22+
int result = help(1);
23+
result += test_two_params(5, 6);
24+
return result;
25+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
CORE
2+
main.c
3+
--dump-c
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
--
7+
^warning: ignoring
8+
--
9+
This test ensures that goto-instrument --dump-c produces valid C code when a
10+
local variable has the same name as a function parameter. Previously, this
11+
would generate invalid C code like:
12+
int help(int x) { unsigned int x; }
13+
which causes a compilation error. The fix renames the local variable to avoid
14+
the collision, e.g.:
15+
int help(int x) { unsigned int x$1; }
16+
17+
The generated C code should be valid and compilable.
18+
19+
Related to issue #6268 (similar to issue #6242 but for a different case).

src/goto-instrument/goto_program2code.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ void goto_program2codet::operator()()
3737
// gather variable scope information
3838
build_dead_map();
3939

40+
// gather function parameter names to avoid collision with local variables
41+
build_param_names();
42+
4043
// see whether var args are in use, identify va_list symbol
4144
scan_for_varargs();
4245

@@ -101,6 +104,27 @@ void goto_program2codet::build_dead_map()
101104
}
102105
}
103106

107+
void goto_program2codet::build_param_names()
108+
{
109+
param_names.clear();
110+
111+
// Get function parameters from the symbol table
112+
const symbolt *func_symbol = nullptr;
113+
if(!ns.lookup(func_name, func_symbol) && func_symbol->type.id() == ID_code)
114+
{
115+
const code_typet &code_type = to_code_type(func_symbol->type);
116+
const code_typet::parameterst &parameters = code_type.parameters();
117+
118+
// Store the base names of parameters (as they appear in C output)
119+
for(const auto &param : parameters)
120+
{
121+
const irep_idt &param_base_name = param.get_base_name();
122+
if(!param_base_name.empty())
123+
param_names.insert(param_base_name);
124+
}
125+
}
126+
}
127+
104128
void goto_program2codet::scan_for_varargs()
105129
{
106130
va_list_expr.clear();
@@ -455,6 +479,40 @@ goto_programt::const_targett goto_program2codet::convert_decl(
455479
code_frontend_declt d = code_frontend_declt{target->decl_symbol()};
456480
symbol_exprt &symbol = d.symbol();
457481

482+
// Check if the local variable's base name conflicts with a function
483+
// parameter's base name or with another local variable's base name. If so,
484+
// rename the local variable to avoid collision.
485+
const symbolt *local_symbol_ptr = nullptr;
486+
if(!ns.lookup(symbol.get_identifier(), local_symbol_ptr))
487+
{
488+
const symbolt &local_symbol = *local_symbol_ptr;
489+
irep_idt base_name = local_symbol.base_name;
490+
491+
if(
492+
!base_name.empty() &&
493+
(param_names.find(base_name) != param_names.end() ||
494+
used_local_names.find(base_name) != used_local_names.end()))
495+
{
496+
// Generate a unique base name by appending a suffix
497+
irep_idt new_base_name;
498+
unsigned suffix = 1;
499+
do
500+
{
501+
new_base_name = id2string(base_name) + "$" + std::to_string(suffix);
502+
++suffix;
503+
} while(param_names.find(new_base_name) != param_names.end() ||
504+
used_local_names.find(new_base_name) != used_local_names.end());
505+
506+
// Update the symbol in the symbol table with the new base name
507+
symbolt &sym = symbol_table.get_writeable_ref(symbol.get_identifier());
508+
sym.base_name = new_base_name;
509+
base_name = new_base_name;
510+
}
511+
512+
// Track this local variable's base name
513+
used_local_names.insert(base_name);
514+
}
515+
458516
goto_programt::const_targett next=target;
459517
++next;
460518
CHECK_RETURN(next != goto_program.instructions.end());

src/goto-instrument/goto_program2code.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,14 @@ class goto_program2codet
100100
std::unordered_set<irep_idt> local_static_set;
101101
std::unordered_set<irep_idt> type_names_set;
102102
std::unordered_set<irep_idt> const_removed;
103+
std::unordered_set<irep_idt> param_names;
104+
std::unordered_set<irep_idt> used_local_names;
103105

104106
void copy_source_location(goto_programt::const_targett, codet &dst);
105107

106108
void build_loop_map();
107109
void build_dead_map();
110+
void build_param_names();
108111
void scan_for_varargs();
109112

110113
void cleanup_code(codet &code, const irep_idt parent_stmt);

0 commit comments

Comments
 (0)