Changeset 553f032f


Ignore:
Timestamp:
Sep 8, 2023, 5:15:41 PM (6 months ago)
Author:
Andrew Beach <ajbeach@…>
Branches:
master
Children:
5cfb8b1
Parents:
2fa0237
Message:

Insert additional checks so that impossible, or just unimplemented, local control flow raises an error in CFA.

Files:
1 added
1 deleted
1 edited
1 moved

Legend:

Unmodified
Added
Removed
  • src/ControlStruct/MultiLevelExit.cpp

    r2fa0237 r553f032f  
    1010// Created On       : Mon Nov  1 13:48:00 2021
    1111// Last Modified By : Andrew Beach
    12 // Last Modified On : Wed Sep  6 12:00:00 2023
    13 // Update Count     : 35
     12// Last Modified On : Fri Sep  8 17:04:00 2023
     13// Update Count     : 36
    1414//
    1515
     
    2727
    2828namespace {
     29
     30/// The return context is used to remember if returns are allowed and if
     31/// not, why not. It is the nearest local control flow blocking construct.
     32enum ReturnContext {
     33        MayReturn,
     34        InTryWithHandler,
     35        InResumeHandler,
     36        InTerminateHandler,
     37        InFinally,
     38};
    2939
    3040class Entry {
     
    126136        void previsit( const TryStmt * );
    127137        void postvisit( const TryStmt * );
     138        void previsit( const CatchClause * );
    128139        void previsit( const FinallyClause * );
    129140
     
    134145        vector<Entry> enclosing_control_structures;
    135146        Label break_label;
    136         bool inFinally;
     147        ReturnContext ret_context;
    137148
    138149        template<typename LoopNode>
     
    144155                const list<ptr<Stmt>> & kids, bool caseClause );
    145156
     157        void enterSealedContext( ReturnContext );
     158
    146159        template<typename UnaryPredicate>
    147160        auto findEnclosingControlStructure( UnaryPredicate pred ) {
     
    157170MultiLevelExitCore::MultiLevelExitCore( const LabelToStmt & lt ) :
    158171        target_table( lt ), break_label( CodeLocation(), "" ),
    159         inFinally( false )
     172        ret_context( ReturnContext::MayReturn )
    160173{}
    161174
     
    488501
    489502void MultiLevelExitCore::previsit( const ReturnStmt * stmt ) {
    490         if ( inFinally ) {
    491                 SemanticError( stmt->location, "'return' may not appear in a finally clause" );
    492         }
     503        char const * context;
     504        switch ( ret_context ) {
     505        case ReturnContext::MayReturn:
     506                return;
     507        case ReturnContext::InTryWithHandler:
     508                context = "try statement with a catch clause";
     509                break;
     510        case ReturnContext::InResumeHandler:
     511                context = "catchResume clause";
     512                break;
     513        case ReturnContext::InTerminateHandler:
     514                context = "catch clause";
     515                break;
     516        case ReturnContext::InFinally:
     517                context = "finally clause";
     518                break;
     519        default:
     520                assert(0);
     521        }
     522        SemanticError( stmt->location, toString( "'return' may not appear in a ", context ) );
    493523}
    494524
     
    500530                GuardAction([this](){ enclosing_control_structures.pop_back(); } );
    501531        }
     532
     533        // Try statements/try blocks are only sealed with a termination handler.
     534        for ( auto clause : stmt->handlers ) {
     535                if ( ast::Terminate == clause->kind ) {
     536                        return enterSealedContext( ReturnContext::InTryWithHandler );
     537                }
     538        }
    502539}
    503540
     
    512549}
    513550
     551void MultiLevelExitCore::previsit( const CatchClause * clause ) {
     552        ReturnContext context = ( ast::Terminate == clause->kind )
     553                ? ReturnContext::InTerminateHandler : ReturnContext::InResumeHandler;
     554        enterSealedContext( context );
     555}
     556
    514557void 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;
     558        enterSealedContext( ReturnContext::InFinally );
    518559}
    519560
     
    617658}
    618659
     660void MultiLevelExitCore::enterSealedContext( ReturnContext enter_context ) {
     661        GuardAction([this, old = std::move(enclosing_control_structures)](){ enclosing_control_structures = std::move(old); });
     662        enclosing_control_structures = vector<Entry>();
     663        GuardValue( ret_context ) = enter_context;
     664}
     665
    619666} // namespace
    620667
  • tests/exceptions/try-ctrl-flow.cfa

    r2fa0237 r553f032f  
    1 // All of these should be caught as long as the check remains in the same
    2 // pass. (Although not even all of the checks are in place yet.)
     1// Check all the local control flow structures that are "sealed" by some the
     2// try statement clauses; where structured programming is stricter.
    33
    44void break_in_finally() {
     
    151151}
    152152
     153// Now just use return to test the other try control flow interactions.
     154
     155exception nil_exception {};
     156
     157void return_in_try_with_catch() {
     158        try {
     159                return;
     160        } catch (nil_exception *) {
     161                ;
     162        }
     163}
     164
     165// Allowed.
     166void return_in_try_with_catchReturn() {
     167        try {
     168                return;
     169        } catchResume (nil_exception *) {
     170                ;
     171        }
     172}
     173
     174// Allowed.
     175void return_in_try_with_finally() {
     176        try {
     177                return;
     178        } finally {
     179                ;
     180        }
     181}
     182
     183void return_in_catch() {
     184        try {
     185                ;
     186        } catch (nil_exception *) {
     187                return;
     188        }
     189}
     190
     191void return_in_catchResume() {
     192        try {
     193                ;
     194        } catchResume (nil_exception *) {
     195                return;
     196        }
     197}
     198
    153199void main() {
    154200        // Should not compile.
Note: See TracChangeset for help on using the changeset viewer.