source: src/ControlStruct/MultiLevelExit.cpp@ 01510fe

Last change on this file since 01510fe was 4a40fca7, checked in by Andrew Beach <ajbeach@…>, 2 years ago

Clean-up in MultiLevelExit. Primary purpose is to remove some uneeded fill code.

  • Property mode set to 100644
File size: 21.5 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// MultiLevelExit.cpp -- Replaces CFA's local control flow with C's versions.
8//
9// Author : Andrew Beach
10// Created On : Mon Nov 1 13:48:00 2021
11// Last Modified By : Andrew Beach
12// Last Modified On : Wed Sep 6 12:00:00 2023
13// Update Count : 35
14//
15
16#include "MultiLevelExit.hpp"
17
18#include "AST/Pass.hpp"
19#include "AST/Stmt.hpp"
20#include "LabelGeneratorNew.hpp"
21
22#include <set>
23using namespace std;
24using namespace ast;
25
26namespace ControlStruct {
27
28namespace {
29
30class Entry {
31 public:
32 const Stmt * stmt;
33 private:
34 // Organized like a manual ADT. Avoids creating a bunch of dead data.
35 struct Target {
36 Label label;
37 bool used = false;
38 Target( const Label & label ) : label( label ) {}
39 Target() : label( CodeLocation(), "" ) {}
40 };
41 Target firstTarget;
42 Target secondTarget;
43
44 enum Kind {
45 ForStmtK, WhileDoStmtK, CompoundStmtK, IfStmtK, CaseClauseK, SwitchStmtK, TryStmtK
46 } kind;
47
48 bool fallDefaultValid = true;
49
50 static Label & useTarget( Target & target ) {
51 target.used = true;
52 return target.label;
53 }
54 public:
55 Entry( const ForStmt * stmt, Label breakExit, Label contExit ) :
56 stmt( stmt ), firstTarget( breakExit ), secondTarget( contExit ), kind( ForStmtK ) {}
57 Entry( const WhileDoStmt * stmt, Label breakExit, Label contExit ) :
58 stmt( stmt ), firstTarget( breakExit ), secondTarget( contExit ), kind( WhileDoStmtK ) {}
59 Entry( const CompoundStmt *stmt, Label breakExit ) :
60 stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( CompoundStmtK ) {}
61 Entry( const IfStmt *stmt, Label breakExit ) :
62 stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( IfStmtK ) {}
63 Entry( const CaseClause *, const CompoundStmt *stmt, Label fallExit ) :
64 stmt( stmt ), firstTarget( fallExit ), secondTarget(), kind( CaseClauseK ) {}
65 Entry( const SwitchStmt *stmt, Label breakExit, Label fallDefaultExit ) :
66 stmt( stmt ), firstTarget( breakExit ), secondTarget( fallDefaultExit ), kind( SwitchStmtK ) {}
67 Entry( const TryStmt *stmt, Label breakExit ) :
68 stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( TryStmtK ) {}
69
70 bool isContTarget() const { return kind <= WhileDoStmtK; }
71 bool isBreakTarget() const { return kind != CaseClauseK; }
72 bool isFallTarget() const { return kind == CaseClauseK; }
73 bool isFallDefaultTarget() const { return kind == SwitchStmtK; }
74
75 // These routines set a target as being "used" by a BranchStmt
76 Label useContExit() { assert( kind <= WhileDoStmtK ); return useTarget(secondTarget); }
77 Label useBreakExit() { assert( kind != CaseClauseK ); return useTarget(firstTarget); }
78 Label useFallExit() { assert( kind == CaseClauseK ); return useTarget(firstTarget); }
79 Label useFallDefaultExit() { assert( kind == SwitchStmtK ); return useTarget(secondTarget); }
80
81 // These routines check if a specific label for a statement is used by a BranchStmt
82 bool isContUsed() const { assert( kind <= WhileDoStmtK ); return secondTarget.used; }
83 bool isBreakUsed() const { assert( kind != CaseClauseK ); return firstTarget.used; }
84 bool isFallUsed() const { assert( kind == CaseClauseK ); return firstTarget.used; }
85 bool isFallDefaultUsed() const { assert( kind == SwitchStmtK ); return secondTarget.used; }
86 void seenDefault() { fallDefaultValid = false; }
87 bool isFallDefaultValid() const { return fallDefaultValid; }
88};
89
90// Helper predicates used in find_if calls (it doesn't take methods):
91bool isBreakTarget( const Entry & entry ) {
92 return entry.isBreakTarget();
93}
94
95bool isContinueTarget( const Entry & entry ) {
96 return entry.isContTarget();
97}
98
99bool isFallthroughTarget( const Entry & entry ) {
100 return entry.isFallTarget();
101}
102
103bool isFallthroughDefaultTarget( const Entry & entry ) {
104 return entry.isFallDefaultTarget();
105}
106
107struct MultiLevelExitCore final :
108 public WithVisitorRef<MultiLevelExitCore>,
109 public WithShortCircuiting, public WithGuards {
110 MultiLevelExitCore( const LabelToStmt & lt );
111
112 void previsit( const FunctionDecl * );
113
114 const CompoundStmt * previsit( const CompoundStmt * );
115 const BranchStmt * postvisit( const BranchStmt * );
116 void previsit( const WhileDoStmt * );
117 const WhileDoStmt * postvisit( const WhileDoStmt * );
118 void previsit( const ForStmt * );
119 const ForStmt * postvisit( const ForStmt * );
120 const CaseClause * previsit( const CaseClause * );
121 void previsit( const IfStmt * );
122 const IfStmt * postvisit( const IfStmt * );
123 void previsit( const SwitchStmt * );
124 const SwitchStmt * postvisit( const SwitchStmt * );
125 void previsit( const ReturnStmt * );
126 void previsit( const TryStmt * );
127 void postvisit( const TryStmt * );
128 void previsit( const FinallyClause * );
129
130 const Stmt * mutateLoop( const Stmt * body, Entry& );
131
132 const LabelToStmt & target_table;
133 set<Label> fallthrough_labels;
134 vector<Entry> enclosing_control_structures;
135 Label break_label;
136 bool inFinally;
137
138 template<typename LoopNode>
139 void prehandleLoopStmt( const LoopNode * loopStmt );
140 template<typename LoopNode>
141 const LoopNode * posthandleLoopStmt( const LoopNode * loopStmt );
142
143 list<ptr<Stmt>> fixBlock(
144 const list<ptr<Stmt>> & kids, bool caseClause );
145
146 template<typename UnaryPredicate>
147 auto findEnclosingControlStructure( UnaryPredicate pred ) {
148 return find_if( enclosing_control_structures.rbegin(),
149 enclosing_control_structures.rend(), pred );
150 }
151};
152
153NullStmt * labelledNullStmt( const CodeLocation & cl, const Label & label ) {
154 return new NullStmt( cl, vector<Label>{ label } );
155}
156
157MultiLevelExitCore::MultiLevelExitCore( const LabelToStmt & lt ) :
158 target_table( lt ), break_label( CodeLocation(), "" ),
159 inFinally( false )
160{}
161
162void MultiLevelExitCore::previsit( const FunctionDecl * ) {
163 visit_children = false;
164}
165
166const CompoundStmt * MultiLevelExitCore::previsit(
167 const CompoundStmt * stmt ) {
168 visit_children = false;
169
170 // if the stmt is labelled then generate a label to check in postvisit if the label is used
171 bool isLabeled = ! stmt->labels.empty();
172 if ( isLabeled ) {
173 Label breakLabel = newLabel( "blockBreak", stmt );
174 enclosing_control_structures.emplace_back( stmt, breakLabel );
175 GuardAction( [this]() { enclosing_control_structures.pop_back(); } );
176 }
177
178 auto mutStmt = mutate( stmt );
179 // A child statement may set the break label.
180 mutStmt->kids = fixBlock( stmt->kids, false );
181
182 if ( isLabeled ) {
183 assert( ! enclosing_control_structures.empty() );
184 Entry & entry = enclosing_control_structures.back();
185 if ( ! entry.useBreakExit().empty() ) {
186 break_label = entry.useBreakExit();
187 }
188 }
189 return mutStmt;
190}
191
192size_t getUnusedIndex( const Stmt * stmt, const Label & originalTarget ) {
193 const size_t size = stmt->labels.size();
194
195 // If the label is empty, do not add unused attribute.
196 if ( originalTarget.empty() ) return size;
197
198 // Search for a label that matches the originalTarget.
199 for ( size_t i = 0 ; i < size ; ++i ) {
200 const Label & label = stmt->labels[i];
201 if ( label == originalTarget ) {
202 for ( const Attribute * attr : label.attributes ) {
203 if ( attr->name == "unused" ) return size;
204 }
205 return i;
206 }
207 }
208 assertf( false, "CFA internal error: could not find label '%s' on statement %s",
209 originalTarget.name.c_str(), toString( stmt ).c_str() );
210}
211
212const Stmt * addUnused( const Stmt * stmt, const Label & originalTarget ) {
213 size_t i = getUnusedIndex( stmt, originalTarget );
214 if ( i == stmt->labels.size() ) {
215 return stmt;
216 }
217 Stmt * mutStmt = mutate( stmt );
218 mutStmt->labels[i].attributes.push_back( new Attribute( "unused" ) );
219 return mutStmt;
220}
221
222// This routine updates targets on enclosing control structures to indicate which
223// label is used by the BranchStmt that is passed
224const BranchStmt * MultiLevelExitCore::postvisit( const BranchStmt * stmt ) {
225 vector<Entry>::reverse_iterator targetEntry =
226 enclosing_control_structures.rend();
227
228 // Labels on different stmts require different approaches to access
229 switch ( stmt->kind ) {
230 case BranchStmt::Goto:
231 return stmt;
232 case BranchStmt::Continue:
233 case BranchStmt::Break: {
234 bool isContinue = stmt->kind == BranchStmt::Continue;
235 // Handle unlabeled break and continue.
236 if ( stmt->target.empty() ) {
237 if ( isContinue ) {
238 targetEntry = findEnclosingControlStructure( isContinueTarget );
239 } else {
240 if ( enclosing_control_structures.empty() ) {
241 SemanticError( stmt->location,
242 "'break' outside a loop, 'switch', or labelled block" );
243 }
244 targetEntry = findEnclosingControlStructure( isBreakTarget );
245 }
246 // Handle labeled break and continue.
247 } else {
248 // Lookup label in table to find attached control structure.
249 targetEntry = findEnclosingControlStructure(
250 [ targetStmt = target_table.at(stmt->target) ](auto entry){
251 return entry.stmt == targetStmt;
252 } );
253 }
254 // Ensure that selected target is valid.
255 if ( targetEntry == enclosing_control_structures.rend() || ( isContinue && ! isContinueTarget( *targetEntry ) ) ) {
256 SemanticError( stmt->location, toString( (isContinue ? "'continue'" : "'break'"),
257 " target must be an enclosing ", (isContinue ? "loop: " : "control structure: "),
258 stmt->originalTarget ) );
259 }
260 break;
261 }
262 // handle fallthrough in case/switch stmts
263 case BranchStmt::FallThrough: {
264 targetEntry = findEnclosingControlStructure( isFallthroughTarget );
265 // Check that target is valid.
266 if ( targetEntry == enclosing_control_structures.rend() ) {
267 SemanticError( stmt->location, "'fallthrough' must be enclosed in a 'switch' or 'choose'" );
268 }
269 if ( ! stmt->target.empty() ) {
270 // Labelled fallthrough: target must be a valid fallthough label.
271 if ( ! fallthrough_labels.count( stmt->target ) ) {
272 SemanticError( stmt->location, toString( "'fallthrough' target must be a later case statement: ",
273 stmt->originalTarget ) );
274 }
275 return new BranchStmt( stmt->location, BranchStmt::Goto, stmt->originalTarget );
276 }
277 break;
278 }
279 case BranchStmt::FallThroughDefault: {
280 targetEntry = findEnclosingControlStructure( isFallthroughDefaultTarget );
281
282 // Check if in switch or choose statement.
283 if ( targetEntry == enclosing_control_structures.rend() ) {
284 SemanticError( stmt->location, "'fallthrough' must be enclosed in a 'switch' or 'choose'" );
285 }
286
287 // Check if switch or choose has default clause.
288 auto switchStmt = strict_dynamic_cast< const SwitchStmt * >( targetEntry->stmt );
289 bool foundDefault = false;
290 for ( auto caseStmt : switchStmt->cases ) {
291 if ( caseStmt->isDefault() ) {
292 foundDefault = true;
293 break;
294 }
295 }
296 if ( ! foundDefault ) {
297 SemanticError( stmt->location, "'fallthrough default' must be enclosed in a 'switch' or 'choose'"
298 "control structure with a 'default' clause" );
299 }
300 break;
301 }
302 default:
303 assert( false );
304 }
305
306 // Branch error checks: get the appropriate label name, which is always replaced.
307 Label exitLabel( CodeLocation(), "" );
308 switch ( stmt->kind ) {
309 case BranchStmt::Break:
310 assert( ! targetEntry->useBreakExit().empty() );
311 exitLabel = targetEntry->useBreakExit();
312 break;
313 case BranchStmt::Continue:
314 assert( ! targetEntry->useContExit().empty() );
315 exitLabel = targetEntry->useContExit();
316 break;
317 case BranchStmt::FallThrough:
318 assert( ! targetEntry->useFallExit().empty() );
319 exitLabel = targetEntry->useFallExit();
320 break;
321 case BranchStmt::FallThroughDefault:
322 assert( ! targetEntry->useFallDefaultExit().empty() );
323 exitLabel = targetEntry->useFallDefaultExit();
324 // Check that fallthrough default comes before the default clause.
325 if ( ! targetEntry->isFallDefaultValid() ) {
326 SemanticError( stmt->location, "'fallthrough default' must precede the 'default' clause" );
327 }
328 break;
329 default:
330 assert(0);
331 }
332
333 // Add unused attribute to silence warnings.
334 targetEntry->stmt = addUnused( targetEntry->stmt, stmt->originalTarget );
335
336 // Replace with goto to make later passes more uniform.
337 return new BranchStmt( stmt->location, BranchStmt::Goto, exitLabel );
338}
339
340void MultiLevelExitCore::previsit( const WhileDoStmt * stmt ) {
341 return prehandleLoopStmt( stmt );
342}
343
344const WhileDoStmt * MultiLevelExitCore::postvisit( const WhileDoStmt * stmt ) {
345 return posthandleLoopStmt( stmt );
346}
347
348void MultiLevelExitCore::previsit( const ForStmt * stmt ) {
349 return prehandleLoopStmt( stmt );
350}
351
352const ForStmt * MultiLevelExitCore::postvisit( const ForStmt * stmt ) {
353 return posthandleLoopStmt( stmt );
354}
355
356// Mimic what the built-in push_front would do anyways. It is O(n).
357void push_front( vector<ptr<Stmt>> & vec, const Stmt * element ) {
358 vec.emplace_back( nullptr );
359 for ( size_t i = vec.size() - 1 ; 0 < i ; --i ) {
360 vec[ i ] = std::move( vec[ i - 1 ] );
361 }
362 vec[ 0 ] = element;
363}
364
365const CaseClause * MultiLevelExitCore::previsit( const CaseClause * stmt ) {
366 visit_children = false;
367
368 // If default, mark seen.
369 if ( stmt->isDefault() ) {
370 assert( ! enclosing_control_structures.empty() );
371 enclosing_control_structures.back().seenDefault();
372 }
373
374 // The cond may not exist, but if it does update it now.
375 visitor->maybe_accept( stmt, &CaseClause::cond );
376
377 // Just save the mutated node for simplicity.
378 CaseClause * mutStmt = mutate( stmt );
379
380 Label fallLabel = newLabel( "fallThrough", stmt->location );
381 if ( ! mutStmt->stmts.empty() ) {
382 // These should already be in a block.
383 auto first = mutStmt->stmts.front().get_and_mutate();
384 auto block = strict_dynamic_cast<CompoundStmt *>( first );
385
386 // Ensure that the stack isn't corrupted by exceptions in fixBlock.
387 auto guard = makeFuncGuard(
388 [&](){ enclosing_control_structures.emplace_back( mutStmt, block, fallLabel ); },
389 [this](){ enclosing_control_structures.pop_back(); }
390 );
391
392 block->kids = fixBlock( block->kids, true );
393
394 // Add fallthrough label if necessary.
395 assert( ! enclosing_control_structures.empty() );
396 Entry & entry = enclosing_control_structures.back();
397 if ( entry.isFallUsed() ) {
398 mutStmt->stmts.push_back( labelledNullStmt( block->location, entry.useFallExit() ) );
399 }
400 }
401 assert( ! enclosing_control_structures.empty() );
402 Entry & entry = enclosing_control_structures.back();
403 assertf( dynamic_cast< const SwitchStmt * >( entry.stmt ),
404 "CFA internal error: control structure enclosing a case clause must be a switch, but is: %s",
405 toString( entry.stmt ).c_str() );
406 if ( mutStmt->isDefault() ) {
407 if ( entry.isFallDefaultUsed() ) {
408 // Add fallthrough default label if necessary.
409 push_front( mutStmt->stmts, labelledNullStmt( stmt->location, entry.useFallDefaultExit() ) );
410 }
411 }
412 return mutStmt;
413}
414
415void MultiLevelExitCore::previsit( const IfStmt * stmt ) {
416 bool labeledBlock = ! stmt->labels.empty();
417 if ( labeledBlock ) {
418 Label breakLabel = newLabel( "blockBreak", stmt );
419 enclosing_control_structures.emplace_back( stmt, breakLabel );
420 GuardAction( [this](){ enclosing_control_structures.pop_back(); } );
421 }
422}
423
424const IfStmt * MultiLevelExitCore::postvisit( const IfStmt * stmt ) {
425 bool labeledBlock = ! stmt->labels.empty();
426 if ( labeledBlock ) {
427 auto this_label = enclosing_control_structures.back().useBreakExit();
428 if ( ! this_label.empty() ) {
429 break_label = this_label;
430 }
431 }
432 return stmt;
433}
434
435static bool isDefaultCase( const ptr<CaseClause> & caseClause ) {
436 return caseClause->isDefault();
437}
438
439void MultiLevelExitCore::previsit( const SwitchStmt * stmt ) {
440 Label label = newLabel( "switchBreak", stmt );
441 auto it = find_if( stmt->cases.rbegin(), stmt->cases.rend(), isDefaultCase );
442
443 const CaseClause * defaultCase = it != stmt->cases.rend() ? (*it) : nullptr;
444 Label defaultLabel = defaultCase ? newLabel( "fallThroughDefault", defaultCase->location ) : Label( stmt->location, "" );
445 enclosing_control_structures.emplace_back( stmt, label, defaultLabel );
446 GuardAction( [this]() { enclosing_control_structures.pop_back(); } );
447
448 // Collect valid labels for fallthrough. It starts with all labels at this level, then remove as each is seen during
449 // traversal.
450 for ( const CaseClause * caseStmt : stmt->cases ) {
451 if ( caseStmt->stmts.empty() ) continue;
452 auto block = caseStmt->stmts.front().strict_as<CompoundStmt>();
453 for ( const Stmt * stmt : block->kids ) {
454 for ( const Label & l : stmt->labels ) {
455 fallthrough_labels.insert( l );
456 }
457 }
458 }
459}
460
461const SwitchStmt * MultiLevelExitCore::postvisit( const SwitchStmt * stmt ) {
462 assert( ! enclosing_control_structures.empty() );
463 Entry & entry = enclosing_control_structures.back();
464 assert( entry.stmt == stmt );
465
466 // Only run to generate the break label.
467 if ( entry.isBreakUsed() ) {
468 // To keep the switch statements uniform (all direct children of a SwitchStmt should be CastStmts), append the
469 // exit label and break to the last case, create a default case if no cases.
470 SwitchStmt * mutStmt = mutate( stmt );
471 if ( mutStmt->cases.empty() ) {
472 mutStmt->cases.push_back( new CaseClause( mutStmt->location, nullptr, {} ) );
473 }
474
475 auto caseStmt = mutStmt->cases.back().get();
476 auto mutCase = mutate( caseStmt );
477 mutStmt->cases.back() = mutCase;
478
479 Label label( mutCase->location, "breakLabel" );
480 auto branch = new BranchStmt( mutCase->location, BranchStmt::Break, label );
481 branch->labels.push_back( entry.useBreakExit() );
482 mutCase->stmts.push_back( branch );
483
484 return mutStmt;
485 }
486 return stmt;
487}
488
489void MultiLevelExitCore::previsit( const ReturnStmt * stmt ) {
490 if ( inFinally ) {
491 SemanticError( stmt->location, "'return' may not appear in a finally clause" );
492 }
493}
494
495void MultiLevelExitCore::previsit( const TryStmt * stmt ) {
496 bool isLabeled = ! stmt->labels.empty();
497 if ( isLabeled ) {
498 Label breakLabel = newLabel( "blockBreak", stmt );
499 enclosing_control_structures.emplace_back( stmt, breakLabel );
500 GuardAction([this](){ enclosing_control_structures.pop_back(); } );
501 }
502}
503
504void MultiLevelExitCore::postvisit( const TryStmt * stmt ) {
505 bool isLabeled = ! stmt->labels.empty();
506 if ( isLabeled ) {
507 auto this_label = enclosing_control_structures.back().useBreakExit();
508 if ( ! this_label.empty() ) {
509 break_label = this_label;
510 }
511 }
512}
513
514void MultiLevelExitCore::previsit( const FinallyClause * ) {
515 GuardAction([this, old = std::move( enclosing_control_structures)](){ enclosing_control_structures = std::move(old); });
516 enclosing_control_structures = vector<Entry>();
517 GuardValue( inFinally ) = true;
518}
519
520const Stmt * MultiLevelExitCore::mutateLoop(
521 const Stmt * body, Entry & entry ) {
522 if ( entry.isBreakUsed() ) {
523 break_label = entry.useBreakExit();
524 }
525
526 // if continue is used insert a continue label into the back of the body of the loop
527 if ( entry.isContUsed() ) {
528 // {
529 // body
530 // ContinueLabel: ;
531 // }
532 return new CompoundStmt( body->location, {
533 body,
534 labelledNullStmt( body->location, entry.useContExit() ),
535 } );
536 }
537
538 return body;
539}
540
541template<typename LoopNode>
542void MultiLevelExitCore::prehandleLoopStmt( const LoopNode * loopStmt ) {
543 // Remember is loop before going onto mutate the body.
544 // The labels will be folded in if they are used.
545 Label breakLabel = newLabel( "loopBreak", loopStmt );
546 Label contLabel = newLabel( "loopContinue", loopStmt );
547 enclosing_control_structures.emplace_back( loopStmt, breakLabel, contLabel );
548 // labels are added temporarily to see if they are used and then added permanently in postvisit if ther are used
549 // children will tag labels as being used during their traversal which occurs before postvisit
550
551 // GuardAction calls the lambda after the node is done being visited
552 GuardAction( [this](){ enclosing_control_structures.pop_back(); } );
553}
554
555template<typename LoopNode>
556const LoopNode * MultiLevelExitCore::posthandleLoopStmt( const LoopNode * loopStmt ) {
557 assert( ! enclosing_control_structures.empty() );
558 Entry & entry = enclosing_control_structures.back();
559 assert( entry.stmt == loopStmt );
560
561 // Now check if the labels are used and add them if so.
562 return mutate_field( loopStmt, &LoopNode::body, mutateLoop( loopStmt->body, entry ) );
563 // this call to mutate_field compares loopStmt->body and the result of mutateLoop
564 // if they are the same the node isn't mutated, if they differ then the new mutated node is returned
565 // the stmts will only differ if a label is used
566}
567
568list<ptr<Stmt>> MultiLevelExitCore::fixBlock(
569 const list<ptr<Stmt>> & kids, bool is_case_clause ) {
570 // Unfortunately cannot use automatic error collection.
571 SemanticErrorException errors;
572
573 list<ptr<Stmt>> ret;
574
575 // Manually visit each child.
576 for ( const ptr<Stmt> & kid : kids ) {
577 if ( is_case_clause ) {
578 // Once a label is seen, it's no longer a valid for fallthrough.
579 for ( const Label & l : kid->labels ) {
580 fallthrough_labels.erase( l );
581 }
582 }
583
584 ptr<Stmt> else_stmt = nullptr;
585 const Stmt * loop_kid = nullptr;
586 // check if loop node and if so add else clause if it exists
587 const WhileDoStmt * whilePtr = kid.as<WhileDoStmt>();
588 if ( whilePtr && whilePtr->else_ ) {
589 else_stmt = whilePtr->else_;
590 loop_kid = mutate_field( whilePtr, &WhileDoStmt::else_, nullptr );
591 }
592 const ForStmt * forPtr = kid.as<ForStmt>();
593 if ( forPtr && forPtr->else_ ) {
594 else_stmt = forPtr->else_;
595 loop_kid = mutate_field( forPtr, &ForStmt::else_, nullptr );
596 }
597
598 try {
599 if (else_stmt) ret.push_back( loop_kid->accept( *visitor ) );
600 else ret.push_back( kid->accept( *visitor ) );
601 } catch ( SemanticErrorException & e ) {
602 errors.append( e );
603 }
604
605 if (else_stmt) ret.push_back(else_stmt);
606
607 if ( ! break_label.empty() ) {
608 ret.push_back( labelledNullStmt( ret.back()->location, break_label ) );
609 break_label = Label( CodeLocation(), "" );
610 }
611 }
612
613 if ( ! errors.isEmpty() ) {
614 throw errors;
615 }
616 return ret;
617}
618
619} // namespace
620
621const CompoundStmt * multiLevelExitUpdate(
622 const CompoundStmt * stmt, const LabelToStmt & labelTable ) {
623 // Must start in the body, so FunctionDecls can be a stopping point.
624 Pass<MultiLevelExitCore> visitor( labelTable );
625 return stmt->accept( visitor );
626}
627
628} // namespace ControlStruct
629
630// Local Variables: //
631// tab-width: 4 //
632// mode: c++ //
633// compile-command: "make install" //
634// End: //
Note: See TracBrowser for help on using the repository browser.