Skip to content

Commit 8e98eaa

Browse files
committed
Execute destructors on goto exiting block statement
The case where it enters a scope is not handled at present, as I don't know how to phrase that in terms of goto-program decl statements. It's also illegal in C++ if a nontrivial constructor would be needed, and can't be constructed in Java source AFAIK.
1 parent c2d7554 commit 8e98eaa

File tree

3 files changed

+86
-19
lines changed

3 files changed

+86
-19
lines changed

src/goto-programs/goto_convert.cpp

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ Function: goto_convertt::finish_gotos
5757
5858
\*******************************************************************/
5959

60-
void goto_convertt::finish_gotos()
60+
void goto_convertt::finish_gotos(goto_programt &dest)
6161
{
6262
for(const auto &g_it : targets.gotos)
6363
{
64-
goto_programt::instructiont &i=*g_it;
64+
goto_programt::instructiont &i=*(g_it.first);
6565

6666
if(i.code.get_statement()=="non-deterministic-goto")
6767
{
@@ -81,7 +81,7 @@ void goto_convertt::finish_gotos()
8181
throw 0;
8282
}
8383

84-
i.targets.push_back(l_it->second);
84+
i.targets.push_back(l_it->second.first);
8585
}
8686
}
8787
else if(i.is_start_thread())
@@ -98,7 +98,7 @@ void goto_convertt::finish_gotos()
9898
throw 0;
9999
}
100100

101-
i.targets.push_back(l_it->second);
101+
i.targets.push_back(l_it->second.first);
102102
}
103103
else if(i.code.get_statement()==ID_goto)
104104
{
@@ -114,7 +114,48 @@ void goto_convertt::finish_gotos()
114114
}
115115

116116
i.targets.clear();
117-
i.targets.push_back(l_it->second);
117+
i.targets.push_back(l_it->second.first);
118+
119+
// If the goto recorded a destructor stack, execute as much as is
120+
// appropriate for however many automatic variables leave scope.
121+
// We don't currently handle variables *entering* scope.
122+
auto goto_stack=g_it.second;
123+
const auto& label_stack=l_it->second.second;
124+
bool stack_is_prefix=true;
125+
if(label_stack.size()>goto_stack.size())
126+
stack_is_prefix=false;
127+
for(unsigned i=0, ilim=label_stack.size();
128+
i!=ilim && stack_is_prefix;
129+
++i)
130+
{
131+
if(goto_stack[i]!=label_stack[i])
132+
stack_is_prefix=false;
133+
}
134+
135+
if(!stack_is_prefix)
136+
{
137+
warning() << "Encountered goto (" << goto_label <<
138+
") that enters one or more lexical blocks;" <<
139+
"omitting constructors and destructors." << eom;
140+
}
141+
else
142+
{
143+
auto unwind_to_size=label_stack.size();
144+
if(unwind_to_size<goto_stack.size())
145+
{
146+
status() << "Adding goto-destructor code on jump to " <<
147+
goto_label << eom;
148+
goto_programt destructor_code;
149+
unwind_destructor_stack(
150+
i.code.add_source_location(),
151+
unwind_to_size,
152+
destructor_code,
153+
goto_stack);
154+
dest.destructive_insert(g_it.first, destructor_code);
155+
// This should leave iterators intact, as long as
156+
// goto_programt::instructionst is std::list.
157+
}
158+
}
118159
}
119160
else
120161
{
@@ -169,7 +210,7 @@ void goto_convertt::finish_computed_gotos(goto_programt &goto_program)
169210
goto_programt::targett t=
170211
goto_program.insert_after(g_it);
171212

172-
t->make_goto(label.second);
213+
t->make_goto(label.second.first);
173214
t->source_location=i.source_location;
174215
t->guard=guard;
175216
}
@@ -213,7 +254,7 @@ void goto_convertt::goto_convert_rec(
213254
{
214255
convert(code, dest);
215256

216-
finish_gotos();
257+
finish_gotos(dest);
217258
finish_computed_gotos(dest);
218259
}
219260

@@ -282,8 +323,7 @@ void goto_convertt::convert_label(
282323
goto_programt::targett target=tmp.instructions.begin();
283324
dest.destructive_append(tmp);
284325

285-
targets.labels.insert(std::pair<irep_idt, goto_programt::targett>
286-
(label, target));
326+
targets.labels.insert({label, {target, targets.destructor_stack}});
287327
target->labels.push_front(label);
288328
}
289329

@@ -1622,13 +1662,21 @@ void goto_convertt::convert_goto(
16221662
const codet &code,
16231663
goto_programt &dest)
16241664
{
1665+
// Precede with a 'skip', which will be replaced by any pre-departure
1666+
// destructor code if appropriate. Without this the goto can be amalgamated
1667+
// into a control-flow structure, such as IF x THEN GOTO 1;, leaving
1668+
// nowhere for the destructors to go.
1669+
goto_programt::targett skip=dest.add_instruction(SKIP);
1670+
skip->source_location=code.source_location();
1671+
skip->code=code_skipt();
1672+
16251673
goto_programt::targett t=dest.add_instruction();
16261674
t->make_goto();
16271675
t->source_location=code.source_location();
16281676
t->code=code;
16291677

16301678
// remember it to do target later
1631-
targets.gotos.push_back(t);
1679+
targets.gotos.push_back(std::make_pair(t,targets.destructor_stack));
16321680
}
16331681

16341682
/*******************************************************************\

src/goto-programs/goto_convert_class.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,22 +216,28 @@ class goto_convertt:public messaget
216216
// exceptions
217217
//
218218

219+
typedef std::vector<codet> destructor_stackt;
220+
219221
symbol_exprt exception_flag();
220222
void unwind_destructor_stack(
221223
const source_locationt &,
222224
std::size_t stack_size,
223225
goto_programt &dest);
226+
void unwind_destructor_stack(
227+
const source_locationt &,
228+
std::size_t stack_size,
229+
goto_programt &dest,
230+
destructor_stackt &stack);
224231

225232
//
226233
// gotos
227234
//
228235

229-
void finish_gotos();
236+
void finish_gotos(goto_programt &dest);
230237
void finish_computed_gotos(goto_programt &dest);
231238

232-
typedef std::vector<codet> destructor_stackt;
233-
typedef std::map<irep_idt, goto_programt::targett> labelst;
234-
typedef std::list<goto_programt::targett> gotost;
239+
typedef std::map<irep_idt, std::pair<goto_programt::targett, destructor_stackt> > labelst;
240+
typedef std::list<std::pair<goto_programt::targett, destructor_stackt> > gotost;
235241
typedef std::list<goto_programt::targett> computed_gotost;
236242
typedef exprt::operandst caset;
237243
typedef std::list<std::pair<goto_programt::targett, caset> > casest;

src/goto-programs/goto_convert_exceptions.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -374,23 +374,36 @@ void goto_convertt::unwind_destructor_stack(
374374
const source_locationt &source_location,
375375
std::size_t final_stack_size,
376376
goto_programt &dest)
377+
{
378+
unwind_destructor_stack(
379+
source_location,
380+
final_stack_size,
381+
dest,
382+
targets.destructor_stack);
383+
}
384+
385+
void goto_convertt::unwind_destructor_stack(
386+
const source_locationt &source_location,
387+
std::size_t final_stack_size,
388+
goto_programt &dest,
389+
destructor_stackt &destructor_stack)
377390
{
378391
// There might be exceptions happening in the exception
379392
// handler. We thus pop off the stack, and then later
380393
// one restore the original stack.
381-
destructor_stackt old_stack=targets.destructor_stack;
394+
destructor_stackt old_stack=destructor_stack;
382395

383-
while(targets.destructor_stack.size()>final_stack_size)
396+
while(destructor_stack.size()>final_stack_size)
384397
{
385-
codet d_code=targets.destructor_stack.back();
398+
codet d_code=destructor_stack.back();
386399
d_code.add_source_location()=source_location;
387400

388401
// pop now to avoid doing this again
389-
targets.destructor_stack.pop_back();
402+
destructor_stack.pop_back();
390403

391404
convert(d_code, dest);
392405
}
393406

394407
// Now restore old stack.
395-
old_stack.swap(targets.destructor_stack);
408+
old_stack.swap(destructor_stack);
396409
}

0 commit comments

Comments
 (0)