Skip to content

Commit 4b341ec

Browse files
Fix goto-instrument --dump-c parameter/local variable name collision
When goto-instrument --dump-c outputs C code, local variables that share a base name with a function parameter produce invalid C (redeclaration in the same scope). Collect parameter base names before conversion and rename any colliding local declarations by appending a unique suffix. Store func_name by value instead of by reference to avoid undefined behavior when a temporary irep_idt is passed at construction. Clear used_local_names at the start of each conversion run so that stale entries do not cause unnecessary renaming. Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
1 parent 5719027 commit 4b341ec

4 files changed

Lines changed: 107 additions & 1 deletion

File tree

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: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ 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+
used_local_names.clear();
43+
4044
// see whether var args are in use, identify va_list symbol
4145
scan_for_varargs();
4246

@@ -101,6 +105,27 @@ void goto_program2codet::build_dead_map()
101105
}
102106
}
103107

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

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

src/goto-instrument/goto_program2code.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class goto_program2codet
8181
void operator()();
8282

8383
protected:
84-
const irep_idt &func_name;
84+
const irep_idt func_name;
8585
const goto_programt &goto_program;
8686
symbol_tablet &symbol_table;
8787
const namespacet ns;
@@ -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)