source: src/ControlStruct/MultiLevelExit.cpp @ b8ab91a

ADTast-experimentalenumforall-pointer-decaypthread-emulationqualifiedEnum
Last change on this file since b8ab91a was b8ab91a, checked in by Andrew Beach <ajbeach@…>, 2 years ago

Fix Labels pass translated. This is fix label, mult-level exit and label generator.

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