Changeset 1b6ec23


Ignore:
Timestamp:
Mar 11, 2026, 5:38:44 PM (21 hours ago)
Author:
Peter A. Buhr <pabuhr@…>
Branches:
master
Children:
4acd1f8
Parents:
54c01bb
Message:

rework member fixBlock to allow loop else-clause to access while/for conditional declarations

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/ControlStruct/MultiLevelExit.cpp

    r54c01bb r1b6ec23  
    1010// Created On       : Mon Nov  1 13:48:00 2021
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Dec 14 17:34:12 2023
    13 // Update Count     : 39
     12// Last Modified On : Wed Mar 11 17:35:25 2026
     13// Update Count     : 137
    1414//
    1515
     
    160160        const LoopNode * posthandleLoopStmt( const LoopNode * loopStmt );
    161161
    162         list<ptr<Stmt>> fixBlock(
    163                 const list<ptr<Stmt>> & kids, bool caseClause );
     162        list<ptr<Stmt>> fixBlock( const list<ptr<Stmt>> & kids, bool caseClause );
    164163
    165164        void enterSealedContext( ReturnContext );
     
    185184}
    186185
    187 const CompoundStmt * MultiLevelExitCore::previsit(
    188                 const CompoundStmt * stmt ) {
     186const CompoundStmt * MultiLevelExitCore::previsit( const CompoundStmt * stmt ) {
    189187        visit_children = false;
    190188
     
    244242//     label is used by the BranchStmt that is passed
    245243const BranchStmt * MultiLevelExitCore::postvisit( const BranchStmt * stmt ) {
    246         vector<Entry>::reverse_iterator targetEntry =
    247                 enclosing_control_structures.rend();
     244        vector<Entry>::reverse_iterator targetEntry = enclosing_control_structures.rend();
    248245
    249246        // Labels on different stmts require different approaches to access
     
    584581}
    585582
    586 const Stmt * MultiLevelExitCore::mutateLoop(
    587         const Stmt * body, Entry & entry ) {
     583const Stmt * MultiLevelExitCore::mutateLoop( const Stmt * body, Entry & entry ) {
    588584        if ( entry.isBreakUsed() ) {
    589585                break_label = entry.useBreakExit();
     
    608604void MultiLevelExitCore::prehandleLoopStmt( const LoopNode * loopStmt ) {
    609605        // Create temporary labels and mark the enclosing loop before traversal.
    610         // The labels will be folded in if they are used.
     606        // The labels are folded in if they are used.
    611607        Label breakLabel = newLabel( "loopBreak", loopStmt );
    612608        Label contLabel = newLabel( "loopContinue", loopStmt );
     
    614610
    615611        GuardAction( [this](){ enclosing_control_structures.pop_back(); } );
    616 
    617         // Because of fixBlock, this should be empty now (and must be).
    618         assert( nullptr == loopStmt->else_ );
    619612}
    620613
     
    629622}
    630623
    631 list<ptr<Stmt>> MultiLevelExitCore::fixBlock(
    632         const list<ptr<Stmt>> & kids, bool is_case_clause ) {
    633         // Unfortunately cannot use automatic error collection.
    634         SemanticErrorException errors;
    635 
    636         list<ptr<Stmt>> ret;
    637 
    638         // Manually visit each child.
     624list<ptr<Stmt>> MultiLevelExitCore::fixBlock( const list<ptr<Stmt>> & kids, bool is_case_clause ) {
     625        // SKULLDUGGERY: While the loop-else clause is not part of C, it is allowed to proceed to codegen via the else_
     626        // field and printed there as a compound statement. The alternative is another pass after hoist-control-declarations
     627        // to make the else clause a separate compound statement, which seems unnecessary.
     628
     629        SemanticErrorException errors;                                          // cannot use automatic error collection
     630        list<ptr<Stmt>> ret;                                                            // list of augmented statements
     631
     632        // Manually visit each each control structure in a block checking for untargeted break and continue statements.
    639633        for ( const ptr<Stmt> & kid : kids ) {
    640634                if ( is_case_clause ) {
    641                         // Once a label is seen, it's no longer a valid for fallthrough.
     635                        // Once a label is seen, it is no longer valid for fallthrough.
    642636                        for ( const Label & l : kid->labels ) {
    643637                                fallthrough_labels.erase( l );
    644                         }
    645                 }
     638                        } // for
     639                } // if
    646640
    647641                ptr<Stmt> else_stmt = nullptr;
    648                 const Stmt * to_visit;
    649                 // check if loop node and if so add else clause if it exists
     642                const Stmt * to_visit = nullptr;
     643                // SKULLDUGGERY: temporarily hide the else clause on a loop statement, by setting the else_ field to NULL.
    650644                if ( auto ptr = kid.as<WhileDoStmt>() ; ptr && ptr->else_ ) {
    651645                        else_stmt = ptr->else_;
     
    655649                        to_visit = mutate_field( ptr, &ForStmt::else_, nullptr );
    656650                } else {
    657                         to_visit = kid.get();
    658                 }
    659 
    660                 // This is the main (safe) visit of the child node.
    661                 try {
     651                        // Process all other statements as a whole rather than parts, as is done below for loop-else.
     652                        try {
     653                                ret.push_back( kid.get()->accept( *visitor ) );
     654                        } catch ( SemanticErrorException & e ) {
     655                                errors.append( e );
     656                        } // try
     657                } // if
     658
     659                // Process loops with hidden else clause.
     660                if ( else_stmt ) try {
     661                        // In both else_stmt cases, we already modified to_visit, so its location won't change
     662                        const Stmt * subvisit_rslt = to_visit->accept( *visitor );
     663                        assert( subvisit_rslt == to_visit );
     664                } catch ( SemanticErrorException & e ) {
     665                        errors.append( e );
     666                } // try
     667
     668                // Create untargeted break-label for all statements. (global) break_label must be copied here, as it is changed
     669                // by subsequent processing.
     670                Label local_break_label = std::move( break_label );
     671                break_label = Label( CodeLocation(), "" );
     672
     673                if ( else_stmt ) try {
     674                        // SKULLDUGGERY: now reconnect the else clause to the loop and process it seperately looking for untargeted
     675                        // breaks. These breaks apply to the containing switch or loop, not the connected loop-else.
     676                        if ( auto ptr = dynamic_cast<const WhileDoStmt *>(to_visit) ) {
     677                                assert( ptr->else_ == nullptr );
     678                                else_stmt->accept( *visitor );
     679                                mutate_field( ptr, &WhileDoStmt::else_, else_stmt );
     680                        } else if ( auto ptr = dynamic_cast<const ForStmt *>(to_visit) ) {
     681                                assert( ptr->else_ == nullptr );
     682                                else_stmt->accept( *visitor );
     683                                mutate_field( ptr, &ForStmt::else_, else_stmt );
     684                        } // if
     685                        // Now process the else clause so breaks apply to the containing scope.
    662686                        ret.push_back( to_visit->accept( *visitor ) );
    663687                } catch ( SemanticErrorException & e ) {
    664688                        errors.append( e );
    665                 }
    666 
    667                 // The following sections handle visiting loop else clause and makes
    668                 // sure breaking from a loop body does not execute that clause.
    669                 Label local_break_label = std::move( break_label );
    670                 break_label = Label( CodeLocation(), "" );
    671 
    672                 if ( else_stmt ) try {
    673                         ret.push_back( else_stmt->accept( *visitor ) );
    674                 } catch ( SemanticErrorException & e ) {
    675                         errors.append( e );
    676                 }
    677 
    678                 if ( !break_label.empty() ) {
    679                         ret.push_back( labelledNullStmt( ret.back()->location, break_label ) );
    680                         break_label = Label( CodeLocation(), "" );
    681                 }
    682 
    683                 // This handles a break from the body or non-loop statement.
    684                 if ( !local_break_label.empty() ) {
     689                } // try
     690
     691                // Generate untargeted break-label AFTER its containing control structure, including else clause, if present.
     692                if ( ! local_break_label.empty() ) {
    685693                        ret.push_back( labelledNullStmt( ret.back()->location, local_break_label ) );
    686                 }
    687         }
     694                } // if
     695        } // for
    688696
    689697        errors.throwIfNonEmpty();
     698        // Return a new list of augmented statements with untargeted breaks replaced by goto statements that transfer after
     699        // the containing switch/loop.
    690700        return ret;
    691701}
     
    699709} // namespace
    700710
    701 const CompoundStmt * multiLevelExitUpdate(
    702                 const CompoundStmt * stmt, const LabelToStmt & labelTable ) {
     711const CompoundStmt * multiLevelExitUpdate( const CompoundStmt * stmt, const LabelToStmt & labelTable ) {
    703712        // Must start in the body, so FunctionDecls can be a stopping point.
    704713        Pass<MultiLevelExitCore> visitor( labelTable );
Note: See TracChangeset for help on using the changeset viewer.