Goose  Diff

Differences From Artifact [90de2eb743]:

  • File bs/builtins/statements/while.cpp — part of check-in [5f20eec65e] at 2019-11-19 20:49:10 on branch trunk — Store a location in loop headers, and use it to provide more context when diagnosing loop verification failures. (user: achavasse size: 5720)

To Artifact [d7ab488717]:

  • File bs/builtins/statements/while.cpp — part of check-in [b3aeaae2df] at 2020-01-11 18:28:15 on branch trunk —
    • Moved the cfg and lifetime management stuff into a CodeBuilder object owned by sema::Context. This is in preparation to allow alternative implementations of the builder, for instance to build classes.
    • Pass the context to intrinsic functions, which removes their dependency to the parser the need for the ugly "GetCurrentParser" static function.
    (user: achavasse size: 5776)

10
11
12
13
14
15
16

17
18

19
20
21
22
23

24

25
26
27
28
29
30

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

73
74
75

76
77

78
79
80
81
82
83


84
85
86
87
88

89
90
91
92
93
94
95
96

97
98
99
100
101
102
103
104
105

106
107
108


109
110
111
112
113

114
115
116
117
118
119
120
10
11
12
13
14
15
16
17
18

19
20
21
22
23
24
25

26
27
28
29
30
31

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

74
75
76

77
78

79
80
81
82
83


84
85
86
87
88
89

90
91
92
93
94
95
96
97

98
99
100
101
102
103
104
105
106

107
108


109
110
111
112
113
114

115
116
117
118
119
120
121
122







+

-
+





+
-
+





-
+




















-
+




















-
+


-
+

-
+




-
-
+
+




-
+







-
+








-
+

-
-
+
+




-
+







namespace goose::builtins
{
    void SetupWhileStmt( Env& e )
    {
        auto handleWhile = []( Parser& p, uint32_t locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();
            const auto& cb = p.context().codeBuilder();

            if( p.isInParenExpr() || !p.cfg() )
            if( p.isInParenExpr() || !cb )
            {
                dm.emitSyntaxErrorMessage( locationId, "the while statement is not allowed here.", 0 );
                return false;
            }

            const auto& cfg = cb->cfg();
            auto pPrecBB = p.cfg()->currentBB();
            auto pPrecBB = cfg->currentBB();

            if( !pPrecBB || pPrecBB->terminator() )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                    locationId, "unreachable code.", 0 );
                p.cfg()->poison();
                cfg->poison();
            }

            // Create a scope for the entire while, so that any var declared
            // inside of the condition is alive and visible throughout the while.
            Scope s( p );

            auto np = p.makeNestedParser();
            if( !np.parseExpression( precedence::IfStmt ) )
            {
                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the while statement.", 0 );
                return false;
            }

            auto condVal = np.popValue();
            if( !condVal )
            {
                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the while statement.", 0 );
                return false;
            }

            const auto& context = p.resolver()->context();
            const auto& context = p.context();
            auto converted = ConvertValueToType( context, *condVal, GetValueType< bool >() );
            if( holds_alternative< ValUnifyError >( converted ) )
            {
                switch( get< ValUnifyError >( converted ) )
                {
                    // If the condition is invalid, bail out and mark the current
                    // diagnostics context as bust to avoid spamming cascading errors.
                    case ValUnifyError::NoSolution:
                        dm.emitSyntaxErrorMessage( condVal->locationId(), "the while condition can't be converted to a bool." );
                        break;

                    case ValUnifyError::Ambiguous:
                        dm.emitSyntaxErrorMessage( condVal->locationId(), "ambiguous while condition bool conversion." );
                        break;
                }

                return false;
            }

            if( get< Value >( converted ).isPoison() )
                p.cfg()->poison();
                cb->poison();

            // The condition may have emitted additional basic blocks, so get the current block again.
            pPrecBB = p.cfg()->currentBB();
            pPrecBB = cfg->currentBB();

            auto pHeaderBB = p.cfg()->createBB();
            auto pHeaderBB = cfg->createBB();

            // Set the loop header's location to a span including the while keyword and the condition
            pHeaderBB->setLocationId( Location::CreateSpanningLocation( locationId, condVal->locationId() ) );

            Parser::BreakableScopeGuard bsg( p );
            Parser::ContinuableScopeGuard csg( p );
            CodeBuilder::BreakableScopeGuard bsg( cb );
            CodeBuilder::ContinuableScopeGuard csg( cb );

            auto pBodyBB = ParseSubStatement( p, precedence::IfStmt );
            if( !pBodyBB )
            {
                dm.emitSyntaxErrorMessage( p.resolver()->getCurrentLocation(), "expected a statement after the while condition.", 0 );
                dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), "expected a statement after the while condition.", 0 );
                return false;
            }

            if( !pPrecBB )
                return true;

            // Retrieve the final block of the loop body, if any
            if( auto pBodySuccBB = p.cfg()->currentBB(); pBodySuccBB && !pBodySuccBB->terminator() )
            if( auto pBodySuccBB = cfg->currentBB(); pBodySuccBB && !pBodySuccBB->terminator() )
            {
                // Jump from the end of the body BB back to the loop header
                pBodySuccBB->setTerminator( llr::Branch( pHeaderBB ) );
            }

            // Jump unconditionally from the pred block to the loop header.
            pPrecBB->setTerminator( llr::Branch( pHeaderBB ) );

            auto pSuccBB = p.cfg()->createBB();
            auto pSuccBB = cfg->createBB();

            auto breakLevel = p.breakableScopeLevels();
            auto continueLevel = p.continuableScopeLevels();
            auto breakLevel = cb->breakableScopeLevels();
            auto continueLevel = cb->continuableScopeLevels();

            // Go through all basic blocks, find all break and continue terminators
            // for our scope level and replace them with branches respectively to
            // the successor BB or to the loop header BB.
            p.cfg()->forEachBB( [&]( auto&& bb )
            cfg->forEachBB( [&]( auto&& bb )
            {
                const auto& t = bb->terminator();
                if( !t )
                    return;

                if( const auto* pBreak = get_if< llr::Break >( &t->content() ) )
                {
132
133
134
135
136
137
138
139

140
141
142
143
144
145
146
147
148
134
135
136
137
138
139
140

141
142
143
144
145
146
147
148
149
150







-
+









            } );

            // Emit the conditional branch that will either run an iteration of the loop or exit to the succ bb.
            pHeaderBB->setTerminator( llr::CondBranch(
                get< Value >( converted ),
                pBodyBB, pSuccBB ) );

            p.cfg()->setCurrentBB( pSuccBB );
            cfg->setCurrentBB( pSuccBB );
            return true;
        };

        Rule r( handleWhile );
        auto ruleVal = ToValue( move( r ) );
        auto ruleTerm = ValueToIRExpr( ruleVal );
        e.storeValue( AppendToVectorTerm( RootIdentity(), TSID( while ) ), ANYTERM( _ ), ruleTerm );
    }
}