Goose  Check-in [1ad61a2717]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Refactored the code builder: it is now carried around as a Value, and accessed through a bunch of extension points, so we can have different builders (and even user defined ones) later to make classes etc.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 1ad61a27179e88785c36e73529f99fcb5ee401c1dd8027a48631b8c4275c5990
User & Date: zlodo 2021-11-11 20:05:58.905
Context
2021-11-11
20:19
Removed a file that was not properly deleted in the previous commit check-in: b808c4a5f5 user: zlodo tags: trunk
20:05
Refactored the code builder: it is now carried around as a Value, and accessed through a bunch of extension points, so we can have different builders (and even user defined ones) later to make classes etc. check-in: 1ad61a2717 user: zlodo tags: trunk
01:00
  • refactored the ref syntax again: "ref" is now an infix operator that takes an "AccessLevel" compile time value (mut/const/temp) on the lhs, or a TVar to capture/constrain it
  • used the above feature to fix the constref verification _ConvertArg overload which was silently dereferencing the arg, causing it to get converted to a temp ref in all cases
check-in: d679acf746 user: zlodo tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Added bs/builtins/builders/codebuilder.cpp.










































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
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
123
124
125
126
127
128
129
130
131
132
133
#include "builtins.h"

using namespace goose;
using namespace goose::sema;
using namespace goose::builtins;

bool CodeBuilder::destroyAndPopCurrentLifetimeScopeValues( const Context& c )
{
    auto it = m_liveValuesList.begin();
    while( it != m_liveValuesList.end() )
    {
        if( it->m_lifeTimeScopeLevel < m_lifeTimeScopeLevels )
            break;

        if( !destroyLiveValue( c, it->m_value ) )
        {
            m_liveValuesList.clear();

            if( cfg() )
                cfg()->poison();
            return false;
        }

        it = m_liveValuesList.erase( it );
    }

    return true;
}

bool CodeBuilder::destroyAllLiveValues( const Context& c )
{
    for( auto it = m_liveValuesList.begin(); it != m_liveValuesList.end(); ++it )
    {
        if( !destroyLiveValue( c, it->m_value ) )
        {
            m_liveValuesList.clear();

            if( cfg() )
                cfg()->poison();
            return false;
        }
    }

    return true;
}

bool CodeBuilder::destroyAllLiveValuesFromBreakScope( const Context& c, uint32_t breakScopeLevel )
{
    for( auto it = m_liveValuesList.begin(); it != m_liveValuesList.end(); ++it )
    {
        if( it->m_breakableScopeLevel < breakScopeLevel )
            return true;

        if( !destroyLiveValue( c, it->m_value ) )
        {
            m_liveValuesList.clear();

            if( cfg() )
                cfg()->poison();
            return false;
        }
    }

    return true;
}

bool CodeBuilder::destroyAllLiveValuesFromContinueScope( const Context& c, uint32_t continueScopeLevel )
{
    for( auto it = m_liveValuesList.begin(); it != m_liveValuesList.end(); ++it )
    {
        if( it->m_continuableScopeLevel < continueScopeLevel )
            return true;

        if( !destroyLiveValue( c, it->m_value ) )
        {
            m_liveValuesList.clear();

            if( cfg() )
                cfg()->poison();
            return false;
        }
    }

    return true;
}

bool CodeBuilder::destroyLiveValue( const Context& c, const Value& v )
{
    if( v.isPoison() )
        return false;

    DiagnosticsContext dc( v.locationId(), "When invoking DestroyValue." );
    auto result = InvokeOverloadSet( c, c.env()->extDestroyValue(),
        MakeTuple( v ) );

    if( result.isPoison() )
        return false;

    if( !result.isConstant() )
        cfg()->currentBB()->emplace_back( *result.cir() );

    return true;
}

void CodeBuilder::extendValueLifetime( uint32_t index )
{
    // Find the live value.
    auto it = find_if( m_liveValuesList.begin(), m_liveValuesList.end(),
        [&]( auto&& lv )
        {
            return lv.m_index == index;
        } );

    if( it == m_liveValuesList.end() )
        return;

    --it->m_lifeTimeScopeLevel;

    // Look for the last value that belongs to the parent lifetime scope, if any.
    auto last = find_if( it, m_liveValuesList.end(),
        [&]( auto&& lv )
        {
            return lv.m_lifeTimeScopeLevel < it->m_lifeTimeScopeLevel;
        } );

    // Reinsert the value just after the last value of the parent scope (the one we
    // just found), like it would be if it was inserted in that lifetime scope in the first
    // place.
    auto lvToExtend = move( *it );
    m_liveValuesList.erase( it );

    m_liveValuesList.emplace( last, move( lvToExtend ) );
}
Added bs/builtins/builders/codebuilder.h.






































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
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
#ifndef GOOSE_BUILTINS_BUILDER_CODEBUILDER_H
#define GOOSE_BUILTINS_BUILDER_CODEBUILDER_H

namespace goose::builtins
{
    class CodeBuilder
    {
        public:
            template< typename C >
            CodeBuilder( C&& cfg ) :
                m_cfg( forward< C >( cfg ) )
            {}

            const auto& cfg() const { return m_cfg; }

            void poison()
            {
                if( m_cfg )
                    m_cfg->poison();
            }

            void pushLifeTimeScope() { ++m_lifeTimeScopeLevels; }
            void popLifeTimeScope() { --m_lifeTimeScopeLevels; }

            void pushBreakableScope() { ++m_breakableScopeLevels; }
            void popBreakableScope() { --m_breakableScopeLevels; }

            void pushContinuableScope() { ++m_continuableScopeLevels; }
            void popContinuableScope() { --m_continuableScopeLevels; }

            uint32_t lifeTimeScopeLevels() const { return m_lifeTimeScopeLevels; }
            uint32_t breakableScopeLevels() const { return m_breakableScopeLevels; }
            uint32_t continuableScopeLevels() const { return m_continuableScopeLevels; }

            template< typename T >
            void pushLiveValue( T&& v, uint32_t index )
            {
                m_liveValuesList.emplace_front(
                    forward< T >( v ), index,
                    m_lifeTimeScopeLevels,
                    m_breakableScopeLevels,
                    m_continuableScopeLevels
                );
            }

            // Extend the life duration of a live value to the current lifetime scope.
            void extendValueLifetime( uint32_t index );

            // Invoke the destroy extension point for every live variable from the current
            // lifetime scope, and pops off the stack. To be called when exiting a lifetime scope.
            // Returns false if something goes wrong, in which case the cfg will also be marked
            // as poisoned.
            // Note: it is called automatically by LifetimeScopeGuard.
            bool destroyAndPopCurrentLifetimeScopeValues( const Context& c );

            // Invoke the destroy extension point for every live variable, but leave them on the stack.
            // To be called before emitting a return terminator.
            bool destroyAllLiveValues( const Context& c );

            // Invoke the destroy extension point for every live variable that belong in the specified
            // break scope and every child scope, but leave them on the stack.
            // To be called before emitting a break terminator.
            bool destroyAllLiveValuesFromBreakScope( const Context& c, uint32_t breakScopeLevel );

            // Invoke the destroy extension point for every live variable that belong in the specified
            // continue scope and every child scope, but leave them on the stack.
            // To be called before emitting a continue terminator.
            bool destroyAllLiveValuesFromContinueScope( const Context& c, uint32_t continueScopeLevel );

        private:
            // Destruction
            bool destroyLiveValue( const Context& c, const Value& v );

            ptr< cir::CFG > m_cfg;

            // The number of nested lifetime scopes.
            uint32_t m_lifeTimeScopeLevels = 0;

            // The number of nested breakable scopes.
            uint32_t m_breakableScopeLevels = 0;

            // The number of nested continuable scopes.
            uint32_t m_continuableScopeLevels = 0;

            // The live values stack, used to track when
            // to destroy values (such as local variables going out of scope)
            struct LiveValue
            {
                LiveValue() {}

                template< typename T >
                LiveValue( T&& v, uint32_t index, uint32_t lt, uint32_t b, uint32_t c ) :
                    m_value( forward< T >( v ) ),
                    m_index( index ),
                    m_lifeTimeScopeLevel( lt ),
                    m_breakableScopeLevel( b ),
                    m_continuableScopeLevel( c )
                {}

                Value m_value;
                uint32_t m_index = 0;

                // We keep track of those to determine which values to
                // destroy when exiting a scope, emitting a break terminator,
                // and emitting a continue terminator, respectively.
                uint32_t m_lifeTimeScopeLevel = 0;
                uint32_t m_breakableScopeLevel = 0;
                uint32_t m_continuableScopeLevel = 0;
            };

            list< LiveValue > m_liveValuesList;
    };
}

#endif
Added bs/builtins/builders/interfaces.cpp.












































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
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
#include "builtins/builtins.h"
#include "parse/parse.h"

using namespace goose::parse;
using namespace goose::cir;

namespace goose::builtins
{
    void SetupBuilderInterfaces( Env& e )
    {
        RegisterBuiltinFunc< Eager< void > ( TypeWrapper< ptr< CodeBuilder > > ) >( e, e.extPoisonBuilder(),
            []( const TypeWrapper< ptr< CodeBuilder > >& cb )
            {
                cb->poison();
            } );

        RegisterBuiltinFunc< Eager< bool > ( TypeWrapper< ptr< CodeBuilder > > ) >( e, e.extBuilderAllowsVarDecl(),
            []( const TypeWrapper< ptr< CodeBuilder > >& cb )
            {
                return true;
            } );

        RegisterBuiltinFunc< Eager< TypeWrapper< ptr< cir::CFG > > > ( TypeWrapper< ptr< CodeBuilder > > ) >( e, e.extGetCFG(),
            []( const TypeWrapper< ptr< CodeBuilder > >& cb )
            {
                return cb->cfg();
            } );

        RegisterBuiltinFunc< Eager< uint32_t > ( TypeWrapper< ptr< CodeBuilder > > ) >( e, e.extGetBreakableScopeLevels(),
            []( const TypeWrapper< ptr< CodeBuilder > >& cb )
            {
                return cb->breakableScopeLevels();
            } );

        RegisterBuiltinFunc< Eager< uint32_t > ( TypeWrapper< ptr< CodeBuilder > > ) >( e, e.extGetContinuableScopeLevels(),
            []( const TypeWrapper< ptr< CodeBuilder > >& cb )
            {
                return cb->continuableScopeLevels();
            } );

        RegisterBuiltinFunc< Eager< void > ( TypeWrapper< ptr< CodeBuilder > > ) >( e, e.extPushLifetimeScope(),
            []( const TypeWrapper< ptr< CodeBuilder > >& cb )
            {
                cb->pushLifeTimeScope();
            } );

        RegisterBuiltinFunc< Eager< void > ( TypeWrapper< ptr< CodeBuilder > > ) >( e, e.extPopLifetimeScope(),
            []( const TypeWrapper< ptr< CodeBuilder > >& cb )
            {
                cb->popLifeTimeScope();
            } );

        RegisterBuiltinFunc< Eager< void > ( TypeWrapper< ptr< CodeBuilder > > ) >( e, e.extPushBreakableScope(),
            []( const TypeWrapper< ptr< CodeBuilder > >& cb )
            {
                cb->pushBreakableScope();
            } );

        RegisterBuiltinFunc< Eager< void > ( TypeWrapper< ptr< CodeBuilder > > ) >( e, e.extPopBreakableScope(),
            []( const TypeWrapper< ptr< CodeBuilder > >& cb )
            {
                cb->popBreakableScope();
            } );

        RegisterBuiltinFunc< Eager< void > ( TypeWrapper< ptr< CodeBuilder > > ) >( e, e.extPushContinuableScope(),
            []( const TypeWrapper< ptr< CodeBuilder > >& cb )
            {
                cb->pushContinuableScope();
            } );

        RegisterBuiltinFunc< Eager< void > ( TypeWrapper< ptr< CodeBuilder > > ) >( e, e.extPopContinuableScope(),
            []( const TypeWrapper< ptr< CodeBuilder > >& cb )
            {
                cb->popContinuableScope();
            } );

        RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< CodeBuilder > >, Value, uint32_t ) > >( e, e.extPushLiveValue(),
            []( auto&& c, const Value& cbv, const Value& v, const Value& index )
            {
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( cbv );
                cb->pushLiveValue( v, *FromValue< uint32_t >( index ) );
            } );

        RegisterBuiltinFunc< Eager< void > ( TypeWrapper< ptr< CodeBuilder > >, uint32_t ) >( e, e.extExtendValueLifetime(),
            []( const TypeWrapper< ptr< CodeBuilder > >& cb, uint32_t index )
            {
                cb->extendValueLifetime( index );
            } );

        RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< CodeBuilder > > ) > >( e, e.extDestroyAndPopCurrentLifetimeScopeValues(),
            []( auto&& c, const Value& cbv )
            {
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( cbv );
                return ToValue( cb->destroyAndPopCurrentLifetimeScopeValues( c ) );
            } );

        RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< CodeBuilder > > ) > >( e, e.extDestroyAllLiveValues(),
            []( auto&& c, const Value& cbv )
            {
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( cbv );
                return ToValue( cb->destroyAllLiveValues( c ) );
            } );

        RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< CodeBuilder > >, uint32_t ) > >( e, e.extDestroyAllLiveValuesFromBreakScope(),
            []( auto&& c, const Value& cbv, const Value& index )
            {
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( cbv );
                return ToValue( cb->destroyAllLiveValuesFromBreakScope( c, *FromValue< uint32_t >( index ) ) );
            } );

        RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< CodeBuilder > >, uint32_t ) > >( e, e.extDestroyAllLiveValuesFromContinueScope(),
            []( auto&& c, const Value& cbv, const Value& index )
            {
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( cbv );
                return ToValue( cb->destroyAllLiveValuesFromContinueScope( c, *FromValue< uint32_t >( index ) ) );
            } );
    }
}
Changes to bs/builtins/builtins.cpp.
15
16
17
18
19
20
21























22
23
24

25
26
27
28
29
30
31
        e.extLowerConstantForRuntime() = CreateOverloadSet( e, "_LowerConstantForRuntime"_sid );
        e.extLowerTypeForVerification() = CreateOverloadSet( e, "_LowerTypeForVerification"_sid );
        e.extLowerConstantForVerification() = CreateOverloadSet( e, "_LowerConstantForVerification"_sid );

        e.extConvertFuncParam() = CreateOverloadSet( e, "_ConvertFuncParam"_sid );
        e.extConvertFuncArg() = CreateOverloadSet( e, "_ConvertFuncArg"_sid );
























        SetupBuiltinTypes( e );
        SetupBuiltinOperators( e );
        SetupBuiltinStatements( e );

    }

    const Term& RootG0Identity()
    {
        static auto identity = VEC( TSID( g0 ) );
        return identity;
    }







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



>







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
        e.extLowerConstantForRuntime() = CreateOverloadSet( e, "_LowerConstantForRuntime"_sid );
        e.extLowerTypeForVerification() = CreateOverloadSet( e, "_LowerTypeForVerification"_sid );
        e.extLowerConstantForVerification() = CreateOverloadSet( e, "_LowerConstantForVerification"_sid );

        e.extConvertFuncParam() = CreateOverloadSet( e, "_ConvertFuncParam"_sid );
        e.extConvertFuncArg() = CreateOverloadSet( e, "_ConvertFuncArg"_sid );

        e.extPoisonBuilder() = CreateOverloadSet( e, "_PoisonBuilder"_sid );

        e.extBuilderAllowsVarDecl() = CreateOverloadSet( e, "_BuilderAllowsVarDecl"_sid );
        e.extGetCFG() = CreateOverloadSet( e, "_GetCFG"_sid );

        e.extGetBreakableScopeLevels() = CreateOverloadSet( e, "_GetBreakableScopeLevels"_sid );
        e.extGetContinuableScopeLevels() = CreateOverloadSet( e, "_GetContinuableScopeLevels"_sid );

        e.extPushLifetimeScope() = CreateOverloadSet( e, "_PushLifetimeScope"_sid );
        e.extPopLifetimeScope() = CreateOverloadSet( e, "_PopLifetimeScope"_sid );
        e.extPushBreakableScope() = CreateOverloadSet( e, "_PushBreakableScope"_sid );
        e.extPopBreakableScope() = CreateOverloadSet( e, "_PopBreakableScope"_sid );
        e.extPushContinuableScope() = CreateOverloadSet( e, "_PushContinuableScope"_sid );
        e.extPopContinuableScope() = CreateOverloadSet( e, "_PopContinuableScope"_sid );

        e.extPushLiveValue() = CreateOverloadSet( e, "_PushLiveValue"_sid );
        e.extExtendValueLifetime() = CreateOverloadSet( e, "_ExtendValueLifetime"_sid );

        e.extDestroyAndPopCurrentLifetimeScopeValues() = CreateOverloadSet( e, "_DestroyAndPopCurrentLifetimeScopeValues"_sid );
        e.extDestroyAllLiveValues() = CreateOverloadSet( e, "_DestroyAllLiveValues"_sid );
        e.extDestroyAllLiveValuesFromBreakScope() = CreateOverloadSet( e, "_DestroyAllLiveValuesFromBreakScope"_sid );
        e.extDestroyAllLiveValuesFromContinueScope() = CreateOverloadSet( e, "_DestroyAllLiveValuesFromContinueScope"_sid );

        SetupBuiltinTypes( e );
        SetupBuiltinOperators( e );
        SetupBuiltinStatements( e );
        SetupBuilderInterfaces( e );
    }

    const Term& RootG0Identity()
    {
        static auto identity = VEC( TSID( g0 ) );
        return identity;
    }
Changes to bs/builtins/builtins.h.
13
14
15
16
17
18
19


20
21
22
23
24
25
26
27
28
29
30
31
    using namespace diagnostics;

    extern const Term& RootG0Identity();
}

#include "codegen/codegen.h"



#include "types/types.h"
#include "operators/operators.h"
#include "statements/statements.h"
#include "exprbuilder.h"

namespace goose::builtins
{
    extern void SetupBuiltins( Env& e );
    extern void SetupExtensionPoints( Env& e );
}

#endif







>
>



|



|
|



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
    using namespace diagnostics;

    extern const Term& RootG0Identity();
}

#include "codegen/codegen.h"

#include "helpers.h"
#include "builders/codebuilder.h"
#include "types/types.h"
#include "operators/operators.h"
#include "statements/statements.h"
#include "exprhelpers.h"

namespace goose::builtins
{
    extern void SetupBuilderInterfaces( Env& e );
    extern void SetupBuiltins( Env& e );
}

#endif
Added bs/builtins/exprhelpers.h.
































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
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
#ifndef GOOSE_BUILTINS_EXPRHELPERS_H
#define GOOSE_BUILTINS_EXPRHELPERS_H

#include "parse/parse.h"

namespace goose::builtins::exprhelpers
{
    template< typename V >
    Value Not( V&& val )
    {
        return BuildComputedValue( GetValueType< bool >(),
            cir::Not( forward< V >( val ) ) ).setLocationId( val.locationId() );
    }

    template< typename L, typename R >
    Value Or( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::Or( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value And( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::And( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value Eq( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::Eq( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value Neq( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::Neq( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value UGT( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::UGT( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value UGE( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::UGE( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value ULT( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::ULT( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value ULE( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::ULE( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value SGT( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::SGT( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value SGE( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::SGE( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value SLT( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::SLT( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }

    template< typename L, typename R >
    Value SLE( L&& lhs, R&& rhs )
    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue( GetValueType< bool >(),
            cir::SLE( forward< L >( lhs ), forward< R >( rhs ) ) ).setLocationId( locId );
    }
}

#endif
Changes to bs/builtins/helpers.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

23
24
25
26
27
28
29
30
31
32
#include "builtins/builtins.h"
#include "builtins/helpers.h"

using namespace goose::parse;

namespace goose::builtins
{
    void RegisterRule( sema::Env& env, StringId name, parse::Rule&& rule )
    {
        parse::RegisterRule( env, AppendToVectorTerm( RootG0Identity(), TERM( name ) ), move( rule ) );
    }

    ptr< cir::BasicBlock > ParseSubStatement( Parser& p, uint32_t precedence )
    {
        auto next = p.resolver()->lookAheadUnresolved();
        if( !next )
            return nullptr;

        auto np = p.makeNestedParser();

        const auto& cb = np.context().codeBuilder();
        assert( cb );


        auto bb = cb->cfg()->createBB();
        cb->cfg()->setCurrentBB( bb );

        auto decomp = Decompose( next->first, Val< Delimiter >() );
        if( !decomp || *decomp != Delimiter::OpenBrace )
        {
            // This is not a braced statement: create a new identity and visibility rules
            // so that anything defined by the statement isn't visible outside of it.
            // If it is a braced statement, the block will be parsed as a nested brace block

<


















|
|
>

|
|







1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "builtins/builtins.h"


using namespace goose::parse;

namespace goose::builtins
{
    void RegisterRule( sema::Env& env, StringId name, parse::Rule&& rule )
    {
        parse::RegisterRule( env, AppendToVectorTerm( RootG0Identity(), TERM( name ) ), move( rule ) );
    }

    ptr< cir::BasicBlock > ParseSubStatement( Parser& p, uint32_t precedence )
    {
        auto next = p.resolver()->lookAheadUnresolved();
        if( !next )
            return nullptr;

        auto np = p.makeNestedParser();

        auto cfg = GetCFG( np.context() );
        if( !cfg )
            return nullptr;

        auto bb = cfg->createBB();
        cfg->setCurrentBB( bb );

        auto decomp = Decompose( next->first, Val< Delimiter >() );
        if( !decomp || *decomp != Delimiter::OpenBrace )
        {
            // This is not a braced statement: create a new identity and visibility rules
            // so that anything defined by the statement isn't visible outside of it.
            // If it is a braced statement, the block will be parsed as a nested brace block
69
70
71
72
73
74
75
76































































































































































































                return false;

            vec.emplace_back( *val );
        }

        return true;
    }
}






































































































































































































|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
                return false;

            vec.emplace_back( *val );
        }

        return true;
    }

    void PoisonBuilder( const Context& c )
    {
        DiagnosticsContext dc( 0, "When invoking _PoisonBuilder." );
        InvokeOverloadSet( c, c.env()->extPoisonBuilder(),
            MakeTuple( c.builder() ) );
    }

    bool BuilderAllowsVarDecl( const Context& c )
    {
        DiagnosticsContext dc( 0, "When invoking _BuilderAllowsVarDecl." );
        auto result = InvokeOverloadSet( c, c.env()->extBuilderAllowsVarDecl(),
            MakeTuple( c.builder() ) );

        if( result.isPoison() )
            return {};

        auto success = FromValue< bool >( result );
        if( !success )
            return {};

        return *success;
    }

    ptr< cir::CFG > GetCFG( const Context& c )
    {
        DiagnosticsContext dc( 0, "When invoking _GetCFG." );
        auto result = InvokeOverloadSet( c, c.env()->extGetCFG(),
            MakeTuple( c.builder() ) );

        if( result.isPoison() )
            return {};

        auto cfg = FromValue< TypeWrapper< ptr< cir::CFG > > >( result );
        if( !cfg )
            return {};

        return *cfg;
    }

    uint32_t GetBreakableScopeLevels( const Context& c )
    {
        DiagnosticsContext dc( 0, "When invoking _GetBreakableScopeLevels." );
        auto result = InvokeOverloadSet( c, c.env()->extGetBreakableScopeLevels(),
            MakeTuple( c.builder() ) );

        if( result.isPoison() )
            return {};

        auto levels = FromValue< uint32_t >( result );
        if( !levels )
            return {};

        return *levels;
    }

    uint32_t GetContinuableScopeLevels( const Context& c )
    {
        DiagnosticsContext dc( 0, "When invoking _GetContinuableScopeLevels." );
        auto result = InvokeOverloadSet( c, c.env()->extGetContinuableScopeLevels(),
            MakeTuple( c.builder() ) );

        if( result.isPoison() )
            return {};

        auto levels = FromValue< uint32_t >( result );
        if( !levels )
            return {};

        return *levels;
    }

    void PushLifetimeScope( const Context& c )
    {
        DiagnosticsContext dc( 0, "When invoking _PushLifetimeScope." );
        InvokeOverloadSet( c, c.env()->extPushLifetimeScope(),
            MakeTuple( c.builder() ) );
    }

    void PopLifetimeScope( const Context& c )
    {
        DiagnosticsContext dc( 0, "When invoking _PopLifetimeScope." );
        InvokeOverloadSet( c, c.env()->extPopLifetimeScope(),
            MakeTuple( c.builder() ) );
    }

    void PushBreakableScope( const Context& c )
    {
        DiagnosticsContext dc( 0, "When invoking _PushBreakableScope." );
        InvokeOverloadSet( c, c.env()->extPushBreakableScope(),
            MakeTuple( c.builder() ) );
    }

    void PopBreakableScope( const Context& c )
    {
        DiagnosticsContext dc( 0, "When invoking _PopBreakableScope." );
        InvokeOverloadSet( c, c.env()->extPopBreakableScope(),
            MakeTuple( c.builder() ) );
    }

    void PushContinuableScope( const Context& c )
    {
        DiagnosticsContext dc( 0, "When invoking _PushContinuableScope." );
        InvokeOverloadSet( c, c.env()->extPushContinuableScope(),
            MakeTuple( c.builder() ) );
    }

    void PopContinuableScope( const Context& c )
    {
        DiagnosticsContext dc( 0, "When invoking _PopContinuableScope." );
        InvokeOverloadSet( c, c.env()->extPopContinuableScope(),
            MakeTuple( c.builder() ) );
    }

    void PushLiveValue( const Context& c, const Value& val, uint32_t index )
    {
        DiagnosticsContext dc( val.locationId(), "When invoking _PushLiveValue." );
        InvokeOverloadSet( c, c.env()->extPushLiveValue(),
            MakeTuple( c.builder(), val, ToValue( index ) ) );
    }

    void ExtendValueLifetime( const Context& c, uint32_t index )
    {
        DiagnosticsContext dc( 0, "When invoking _ExtendValueLifetime." );
        InvokeOverloadSet( c, c.env()->extExtendValueLifetime(),
            MakeTuple( c.builder(), ToValue( index ) ) );
    }

    bool DestroyAndPopCurrentLifetimeScopeValues( const Context& c )
    {
        DiagnosticsContext dc( 0, "When invoking _DestroyAndPopCurrentLifetimeScopeValues." );
        auto result = InvokeOverloadSet( c, c.env()->extDestroyAndPopCurrentLifetimeScopeValues(),
            MakeTuple( c.builder() ) );

        if( result.isPoison() )
            return {};

        auto success = FromValue< bool >( result );
        if( !success )
            return {};

        return *success;
    }

    bool DestroyAllLiveValues( const Context& c )
    {
        DiagnosticsContext dc( 0, "When invoking _DestroyAllLiveValues." );
        auto result = InvokeOverloadSet( c, c.env()->extDestroyAllLiveValues(),
            MakeTuple( c.builder() ) );

        if( result.isPoison() )
            return {};

        auto success = FromValue< bool >( result );
        if( !success )
            return {};

        return *success;
    }

    bool DestroyAllLiveValuesFromBreakScope( const Context& c, uint32_t breakScopeLevel )
    {
        DiagnosticsContext dc( 0, "When invoking _DestroyAllLiveValuesFromBreakScope." );
        auto result = InvokeOverloadSet( c, c.env()->extDestroyAllLiveValuesFromBreakScope(),
            MakeTuple( c.builder(), ToValue( breakScopeLevel ) ) );

        if( result.isPoison() )
            return {};

        auto success = FromValue< bool >( result );
        if( !success )
            return {};

        return *success;
    }

    bool DestroyAllLiveValuesFromContinueScope( const Context& c, uint32_t continueScopeLevel )
    {
        DiagnosticsContext dc( 0, "When invoking _DestroyAllLiveValuesFromContinueScope." );
        auto result = InvokeOverloadSet( c, c.env()->extDestroyAllLiveValuesFromContinueScope(),
            MakeTuple( c.builder(), ToValue( continueScopeLevel ) ) );

        if( result.isPoison() )
            return {};

        auto success = FromValue< bool >( result );
        if( !success )
            return {};

        return *success;
    }
}
Changes to bs/builtins/helpers.h.
1
2
3
4




5
6
7


8
9
10
11
12
13
14
#ifndef GOOSE_BUILTINS_HELPERS_H
#define GOOSE_BUILTINS_HELPERS_H

#include "parse/parse.h"





namespace goose::builtins
{


    template< typename T >
    void DefineConstant( sema::Env& env, StringId name, T&& val )
    {
        env.storeValue( AppendToVectorTerm( RootG0Identity(), TERM( name ) ), ANYTERM( _ ), forward< T >( val ) );
    }

    extern void RegisterRule( sema::Env& env, StringId name, parse::Rule&& rule );



|
>
>
>
>



>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef GOOSE_BUILTINS_HELPERS_H
#define GOOSE_BUILTINS_HELPERS_H

namespace goose::parse
{
    class Parser;
    class Rule;
}

namespace goose::builtins
{
    extern const Term& EmptyPredicates();

    template< typename T >
    void DefineConstant( sema::Env& env, StringId name, T&& val )
    {
        env.storeValue( AppendToVectorTerm( RootG0Identity(), TERM( name ) ), ANYTERM( _ ), forward< T >( val ) );
    }

    extern void RegisterRule( sema::Env& env, StringId name, parse::Rule&& rule );
55
56
57
58
59
60
61























62
63
64
65
66
67
68
    auto MkStdRTType( I&& identity, L&& llvmType )
    {
        return VEC( forward< I >( identity ), EmptyPredicates(), forward< L >( llvmType ) );
    }

    extern bool ParseExpressionList( parse::Parser& p, uint32_t precedence, ValueVec& vec );
























    struct CTTypePattern
    {
        static const Term& GetPattern()
        {
            static auto pat = ValueToEIR( Value( TypeType(), VEC( TSID( ct_type ), REPEAT( HOLE( "_"_sid ) ) ) ) );
            return pat;
        }







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
    auto MkStdRTType( I&& identity, L&& llvmType )
    {
        return VEC( forward< I >( identity ), EmptyPredicates(), forward< L >( llvmType ) );
    }

    extern bool ParseExpressionList( parse::Parser& p, uint32_t precedence, ValueVec& vec );

    extern void PoisonBuilder( const Context& c );

    extern bool BuilderAllowsVarDecl( const Context& c );
    extern ptr< cir::CFG > GetCFG( const Context& c );

    extern uint32_t GetBreakableScopeLevels( const Context& c );
    extern uint32_t GetContinuableScopeLevels( const Context& c );

    extern void PushLifetimeScope( const Context& c );
    extern void PopLifetimeScope( const Context& c );
    extern void PushBreakableScope( const Context& c );
    extern void PopBreakableScope( const Context& c );
    extern void PushContinuableScope( const Context& c );
    extern void PopContinuableScope( const Context& c );

    extern void PushLiveValue( const Context& c, const Value& val, uint32_t index );
    extern void ExtendValueLifetime( const Context& c, uint32_t index );

    extern bool DestroyAndPopCurrentLifetimeScopeValues( const Context& c );
    extern bool DestroyAllLiveValues( const Context& c );
    extern bool DestroyAllLiveValuesFromBreakScope( const Context& c, uint32_t breakScopeLevel );
    extern bool DestroyAllLiveValuesFromContinueScope( const Context& c, uint32_t continueScopeLevel );

    struct CTTypePattern
    {
        static const Term& GetPattern()
        {
            static auto pat = ValueToEIR( Value( TypeType(), VEC( TSID( ct_type ), REPEAT( HOLE( "_"_sid ) ) ) ) );
            return pat;
        }
Changes to bs/builtins/meson.build.
98
99
100
101
102
103
104



105
106
107
108
    'statements/return.cpp',
    'statements/if.cpp',
    'statements/hif.cpp',
    'statements/while.cpp',
    'statements/break.cpp',
    'statements/continue.cpp',
    'statements/verification.cpp',




    include_directories: bsinc,
    dependencies: [fmt_dep, tracy_dep]
)







>
>
>




98
99
100
101
102
103
104
105
106
107
108
109
110
111
    'statements/return.cpp',
    'statements/if.cpp',
    'statements/hif.cpp',
    'statements/while.cpp',
    'statements/break.cpp',
    'statements/continue.cpp',
    'statements/verification.cpp',

    'builders/codebuilder.cpp',
    'builders/interfaces.cpp',

    include_directories: bsinc,
    dependencies: [fmt_dep, tracy_dep]
)
Changes to bs/builtins/operators/arith.cpp.
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
123
124
        BuildParseRule( e, "/"_sid,
            LeftAssInfixOp( "operator_divide"_sid, precedence::MulDivOp,
                BuildGenericTupleOperator(),
                ForType< BigInt, SDiv >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprbuilder;

                    auto cb = c.codeBuilder();
                    auto cfg = cb->cfg();
                    assert( cfg );

                    // Build a zero constant of the same type as the denominator
                    // to construct the assertion expression.
                    auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
                    auto cond = Neq( rhs, zeroValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(
                        cond.locationId(), "assert"_sid, "the divisor may be 0." );

                    cfg->currentBB()->emplace_back(
                        cir::Assert( move( cond ) )
                    );

                    return BuildComputedValue( lhs.type(),
                        SDiv( lhs, rhs ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprbuilder;

                    auto cb = c.codeBuilder();
                    auto cfg = cb->cfg();
                    assert( cfg );

                    // Build a zero constant of the same type as the denominator
                    // to construct the assertion expression.
                    auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
                    auto cond = Neq( rhs, zeroValue );








|

<
|




















|

<
|







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
        BuildParseRule( e, "/"_sid,
            LeftAssInfixOp( "operator_divide"_sid, precedence::MulDivOp,
                BuildGenericTupleOperator(),
                ForType< BigInt, SDiv >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprhelpers;


                    auto cfg = GetCFG( c );
                    assert( cfg );

                    // Build a zero constant of the same type as the denominator
                    // to construct the assertion expression.
                    auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
                    auto cond = Neq( rhs, zeroValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(
                        cond.locationId(), "assert"_sid, "the divisor may be 0." );

                    cfg->currentBB()->emplace_back(
                        cir::Assert( move( cond ) )
                    );

                    return BuildComputedValue( lhs.type(),
                        SDiv( lhs, rhs ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprhelpers;


                    auto cfg = GetCFG( c );
                    assert( cfg );

                    // Build a zero constant of the same type as the denominator
                    // to construct the assertion expression.
                    auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
                    auto cond = Neq( rhs, zeroValue );

138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
        BuildParseRule( e, "%"_sid,
            LeftAssInfixOp( "operator_modulo"_sid, precedence::MulDivOp,
                BuildGenericTupleOperator(),
                ForType< BigInt, SRem >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprbuilder;

                    auto cb = c.codeBuilder();
                    auto cfg = cb->cfg();
                    assert( cfg );

                    // Build a zero constant of the same type as the denominator
                    // to construct the assertion expression.
                    auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
                    auto cond = Neq( rhs, zeroValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(
                        cond.locationId(), "assert"_sid, "the divisor may be 0." );

                    cfg->currentBB()->emplace_back(
                        cir::Assert( move( cond ) )
                    );

                    return BuildComputedValue( lhs.type(),
                        SRem( lhs, rhs ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprbuilder;

                    auto cb = c.codeBuilder();
                    auto cfg = cb->cfg();
                    assert( cfg );

                    // Build a zero constant of the same type as the denominator
                    // to construct the assertion expression.
                    auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
                    auto cond = Neq( rhs, zeroValue );








|

<
|




















|

<
|







136
137
138
139
140
141
142
143
144

145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

168
169
170
171
172
173
174
175
        BuildParseRule( e, "%"_sid,
            LeftAssInfixOp( "operator_modulo"_sid, precedence::MulDivOp,
                BuildGenericTupleOperator(),
                ForType< BigInt, SRem >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprhelpers;


                    auto cfg = GetCFG( c );
                    assert( cfg );

                    // Build a zero constant of the same type as the denominator
                    // to construct the assertion expression.
                    auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
                    auto cond = Neq( rhs, zeroValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(
                        cond.locationId(), "assert"_sid, "the divisor may be 0." );

                    cfg->currentBB()->emplace_back(
                        cir::Assert( move( cond ) )
                    );

                    return BuildComputedValue( lhs.type(),
                        SRem( lhs, rhs ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprhelpers;


                    auto cfg = GetCFG( c );
                    assert( cfg );

                    // Build a zero constant of the same type as the denominator
                    // to construct the assertion expression.
                    auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
                    auto cond = Neq( rhs, zeroValue );

Changes to bs/builtins/operators/assignment.cpp.
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
            G_VAL_ASSERT( lhs, !lhs.isConstant() );

            auto refType = *FromValue< ReferenceType >( *EIRToValue( lhs.type() ) );

            if( !ParseTypePredicates( c, *EIRToValue( refType.type() ) ) )
                return PoisonValue();

            const auto& cb = c.codeBuilder();
            if( !cb )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "assignments are not allowed here." );
                return PoisonValue();
            }

            if( auto bb = cb->cfg()->currentBB() )
                bb->emplace_back( Store( lhs.cir(), refType.type(), rhs, lhs.locationId() ) );

            // Return the ref to support chained assignments, like in c++.
            return Value( lhs );
        };

        // Generic function to assign a value to a decl (local variable declaration)
        auto DeclAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
        {
            if( !c.codeBuilder() )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "variable declarations are not allowed here." );
                return PoisonValue();
            }

            auto decl = *FromValue< Decl >( lhs );
            return DeclareLocalVar( c, decl.type(), decl.name(), rhs, lhs.locationId() );
        };

        // Generic function to assign a value to a template named decl (local variable declaration with local type inference)
        auto TNamedDeclAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
        {
            if( !c.codeBuilder() )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "variable declarations are not allowed here." );
                return PoisonValue();
            }

            auto tndecl = *FromValue< TNamedDecl >( lhs );
            return DeclareLocalVarWithTypeInference( c, tndecl.type(), tndecl.name(), rhs, lhs.locationId() );







|
|





|









|












|







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
            G_VAL_ASSERT( lhs, !lhs.isConstant() );

            auto refType = *FromValue< ReferenceType >( *EIRToValue( lhs.type() ) );

            if( !ParseTypePredicates( c, *EIRToValue( refType.type() ) ) )
                return PoisonValue();

            auto cfg = GetCFG( c );
            if( !cfg )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "assignments are not allowed here." );
                return PoisonValue();
            }

            if( auto bb = cfg->currentBB() )
                bb->emplace_back( Store( lhs.cir(), refType.type(), rhs, lhs.locationId() ) );

            // Return the ref to support chained assignments, like in c++.
            return Value( lhs );
        };

        // Generic function to assign a value to a decl (local variable declaration)
        auto DeclAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
        {
            if( !BuilderAllowsVarDecl( c ) )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "variable declarations are not allowed here." );
                return PoisonValue();
            }

            auto decl = *FromValue< Decl >( lhs );
            return DeclareLocalVar( c, decl.type(), decl.name(), rhs, lhs.locationId() );
        };

        // Generic function to assign a value to a template named decl (local variable declaration with local type inference)
        auto TNamedDeclAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
        {
            if( !BuilderAllowsVarDecl( c ) )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "variable declarations are not allowed here." );
                return PoisonValue();
            }

            auto tndecl = *FromValue< TNamedDecl >( lhs );
            return DeclareLocalVarWithTypeInference( c, tndecl.type(), tndecl.name(), rhs, lhs.locationId() );
Changes to bs/builtins/operators/dollar.cpp.
1
2
3
4
5
6
7
8
9
10
#include "builtins/builtins.h"
#include "precedence.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::eir;
using namespace goose::parse;
using namespace goose::builtins;

// Dollar is a low level operator: it doesn't expect a value rhs operand,


<







1
2

3
4
5
6
7
8
9
#include "builtins/builtins.h"
#include "precedence.h"


using namespace goose;
using namespace goose::eir;
using namespace goose::parse;
using namespace goose::builtins;

// Dollar is a low level operator: it doesn't expect a value rhs operand,
Changes to bs/builtins/operators/dot.cpp.
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
                // returns a reference to the specified member.
                ForTypes< CustomPattern< Value, ReferenceType::PatternAnyOf< TuplePattern > >,
                    CustomPattern< IntegerType, IntegerType::PatternUnsigned32 > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    G_VAL_ASSERT( lhs, !lhs.isConstant() );

                    auto cb = c.codeBuilder();
                    auto cfg = cb->cfg();
                    assert( cfg );

                    if( !rhs.isConstant() )
                    {
                        DiagnosticsManager::GetInstance().emitErrorMessage( rhs.locationId(),
                            "the right operand for the dot operator needs to be a constant." );
                        return PoisonValue();
                    }








<
<
<
<







18
19
20
21
22
23
24




25
26
27
28
29
30
31
                // returns a reference to the specified member.
                ForTypes< CustomPattern< Value, ReferenceType::PatternAnyOf< TuplePattern > >,
                    CustomPattern< IntegerType, IntegerType::PatternUnsigned32 > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    G_VAL_ASSERT( lhs, !lhs.isConstant() );





                    if( !rhs.isConstant() )
                    {
                        DiagnosticsManager::GetInstance().emitErrorMessage( rhs.locationId(),
                            "the right operand for the dot operator needs to be a constant." );
                        return PoisonValue();
                    }

Changes to bs/builtins/operators/helpers.h.
1
2
3
4
5
6
7
8
9
10
11
12
#ifndef GOOSE_BUILTINS_OPERATORS_HELPERS_H
#define GOOSE_BUILTINS_OPERATORS_HELPERS_H

#include "builtins/helpers.h"

namespace goose::builtins
{
    using namespace goose::parse;

    template< typename... R >
    void BuildParseRule( sema::Env& env, StringId name, R&&... ruleBuilders )
    {



<
<







1
2
3


4
5
6
7
8
9
10
#ifndef GOOSE_BUILTINS_OPERATORS_HELPERS_H
#define GOOSE_BUILTINS_OPERATORS_HELPERS_H



namespace goose::builtins
{
    using namespace goose::parse;

    template< typename... R >
    void BuildParseRule( sema::Env& env, StringId name, R&&... ruleBuilders )
    {
Changes to bs/builtins/operators/logic.cpp.
89
90
91
92
93
94
95

96
97
98
99
100
101
102
103
104
105
106
107
108
109
                            return forward< L >( lhs );

                        return forward< R >( rhs );
                    }

                    // When building verification clauses we have no code builder and no cfg,
                    // and we just want to generate a plain or operation.

                    if( !c.codeBuilder() )
                        return BuildComputedValue( GetValueType< bool >(), Or( lhs, rhs ) );

                    // Build the control flow for shortcut evaluation.
                    auto& cb = c.codeBuilder();

                    const auto& cfg = cb->cfg();
                    const auto& predBB = cfg->currentBB();

                    auto pRhsBB = cfg->createBB();
                    auto pSuccBB = cfg->createBB();

                    // If the lhs is true, skip to the end directly.
                    // Otherwise, jump to the BB that computes rhs.







>
|



<
<
<







89
90
91
92
93
94
95
96
97
98
99
100



101
102
103
104
105
106
107
                            return forward< L >( lhs );

                        return forward< R >( rhs );
                    }

                    // When building verification clauses we have no code builder and no cfg,
                    // and we just want to generate a plain or operation.
                    auto cfg = GetCFG( c );
                    if( !cfg )
                        return BuildComputedValue( GetValueType< bool >(), Or( lhs, rhs ) );

                    // Build the control flow for shortcut evaluation.



                    const auto& predBB = cfg->currentBB();

                    auto pRhsBB = cfg->createBB();
                    auto pSuccBB = cfg->createBB();

                    // If the lhs is true, skip to the end directly.
                    // Otherwise, jump to the BB that computes rhs.
164
165
166
167
168
169
170

171
172
173
174
175
176
177
178
179
180
181
182
183
184
                            return forward< R >( rhs );

                        return forward< L >( lhs );
                    }

                    // When building verification clauses we have bo code builder and no cfg,
                    // and we just want to generate a plain and operation.

                    if( !c.codeBuilder() )
                        return BuildComputedValue( GetValueType< bool >(), And( lhs, rhs ) );

                    // Build the control flow for shortcut evaluation.
                    auto& cb = c.codeBuilder();

                    const auto& cfg = cb->cfg();
                    const auto& predBB = cfg->currentBB();

                    auto pRhsBB = cfg->createBB();
                    auto pSuccBB = cfg->createBB();

                    // If the lhs is false, skip to the end directly.
                    // Otherwise, jump to the BB that computes rhs.







>
|



<
<
<







162
163
164
165
166
167
168
169
170
171
172
173



174
175
176
177
178
179
180
                            return forward< R >( rhs );

                        return forward< L >( lhs );
                    }

                    // When building verification clauses we have bo code builder and no cfg,
                    // and we just want to generate a plain and operation.
                    auto cfg = GetCFG( c );
                    if( !cfg )
                        return BuildComputedValue( GetValueType< bool >(), And( lhs, rhs ) );

                    // Build the control flow for shortcut evaluation.



                    const auto& predBB = cfg->currentBB();

                    auto pRhsBB = cfg->createBB();
                    auto pSuccBB = cfg->createBB();

                    // If the lhs is false, skip to the end directly.
                    // Otherwise, jump to the BB that computes rhs.
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
                } ),

                // runtime integer left shift.
                ForTypes< CustomPattern< IntegerType, IntegerType::Pattern >,
                    CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprbuilder;

                    auto cb = c.codeBuilder();
                    auto cfg = cb->cfg();
                    assert( cfg );

                    // Shifting for a number of bits equal or larger than the bitsize
                    // of lhs is an undefined behavior, so we require verification that
                    // it won't happen.
                    // Extract the integer type of lhs to retreieve its bit size.
                    auto lhsType = *FromValue< IntegerType >( *EIRToValue( lhs.type() ) );







|

<
|







220
221
222
223
224
225
226
227
228

229
230
231
232
233
234
235
236
                } ),

                // runtime integer left shift.
                ForTypes< CustomPattern< IntegerType, IntegerType::Pattern >,
                    CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprhelpers;


                    auto cfg = GetCFG( c );
                    assert( cfg );

                    // Shifting for a number of bits equal or larger than the bitsize
                    // of lhs is an undefined behavior, so we require verification that
                    // it won't happen.
                    // Extract the integer type of lhs to retreieve its bit size.
                    auto lhsType = *FromValue< IntegerType >( *EIRToValue( lhs.type() ) );
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287

                // runtime signed integer right shift, defined to work for any two integers of same
                // bit size.
                ForTypes< CustomPattern< IntegerType, IntegerType::PatternSigned >,
                    CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprbuilder;

                    auto cb = c.codeBuilder();
                    auto cfg = cb->cfg();
                    assert( cfg );

                    // Shifting for a number of bits equal or larger than the bitsize
                    // of lhs is an undefined behavior, so we require verification that
                    // it won't happen.
                    // Extract the integer type of lhs to retreieve its bit size.
                    auto lhsType = *FromValue< IntegerType >( *EIRToValue( lhs.type() ) );







|

<
|







265
266
267
268
269
270
271
272
273

274
275
276
277
278
279
280
281

                // runtime signed integer right shift, defined to work for any two integers of same
                // bit size.
                ForTypes< CustomPattern< IntegerType, IntegerType::PatternSigned >,
                    CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprhelpers;


                    auto cfg = GetCFG( c );
                    assert( cfg );

                    // Shifting for a number of bits equal or larger than the bitsize
                    // of lhs is an undefined behavior, so we require verification that
                    // it won't happen.
                    // Extract the integer type of lhs to retreieve its bit size.
                    auto lhsType = *FromValue< IntegerType >( *EIRToValue( lhs.type() ) );
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
                } ),

                // runtime unsigned integer right shift, defined to work for any two integers of same
                // bit size.
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprbuilder;

                    auto cb = c.codeBuilder();
                    auto cfg = cb->cfg();
                    assert( cfg );

                    // Shifting for a number of bits equal or larger than the bitsize
                    // of lhs is an undefined behavior, so we require verification that
                    // it won't happen.
                    // Extract the integer type of lhs to retreieve its bit size.
                    auto lhsType = *FromValue< IntegerType >( *EIRToValue( lhs.type() ) );







|

<
|







295
296
297
298
299
300
301
302
303

304
305
306
307
308
309
310
311
                } ),

                // runtime unsigned integer right shift, defined to work for any two integers of same
                // bit size.
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    using namespace goose::builtins::exprhelpers;


                    auto cfg = GetCFG( c );
                    assert( cfg );

                    // Shifting for a number of bits equal or larger than the bitsize
                    // of lhs is an undefined behavior, so we require verification that
                    // it won't happen.
                    // Extract the integer type of lhs to retreieve its bit size.
                    auto lhsType = *FromValue< IntegerType >( *EIRToValue( lhs.type() ) );
Changes to bs/builtins/operators/semicolon.cpp.
1
2
3
4
5
6
7
8
9
10
#include "builtins/builtins.h"
#include "precedence.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::eir;
using namespace goose::parse;

namespace
{


<







1
2

3
4
5
6
7
8
9
#include "builtins/builtins.h"
#include "precedence.h"


using namespace goose;
using namespace goose::eir;
using namespace goose::parse;

namespace
{
Changes to bs/builtins/operators/tuple.h.
1
2
3
4
5
6
7
8
9
10
11
12
#ifndef GOOSE_BUILTINS_OPERATORS_TUPLE_H
#define GOOSE_BUILTINS_OPERATORS_TUPLE_H

#include "builtins/helpers.h"

namespace goose::builtins
{
    using namespace goose::parse;

    static inline auto BuildGenericTupleOperator()
    {
        return []< typename tag >( auto&& e, auto&& pOvlSet, tag t )



<
<







1
2
3


4
5
6
7
8
9
10
#ifndef GOOSE_BUILTINS_OPERATORS_TUPLE_H
#define GOOSE_BUILTINS_OPERATORS_TUPLE_H



namespace goose::builtins
{
    using namespace goose::parse;

    static inline auto BuildGenericTupleOperator()
    {
        return []< typename tag >( auto&& e, auto&& pOvlSet, tag t )
Changes to bs/builtins/operators/where.cpp.
1
2
3
4
5
6
7
8
9
10
#include "builtins/builtins.h"
#include "precedence.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::eir;
using namespace goose::parse;
using namespace goose::builtins;

namespace goose::builtins


<







1
2

3
4
5
6
7
8
9
#include "builtins/builtins.h"
#include "precedence.h"


using namespace goose;
using namespace goose::eir;
using namespace goose::parse;
using namespace goose::builtins;

namespace goose::builtins
Changes to bs/builtins/statements/break.cpp.
1
2
3
4
5
6
7
8
9
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
#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::eir;
using namespace goose::parse;

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


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

            const auto& cfg = cb->cfg();

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

            // Emit cleanups for all live variables in the scopes that we are breaking through.
            cb->destroyAllLiveValuesFromBreakScope( p.context(), cb->breakableScopeLevels() );

            cfg->currentBB()->setTerminator( cir::Break( cb->breakableScopeLevels() ) );
            return true;
        };

        RegisterRule( e, "break"_sid, Rule( handleBreak ) );
    }
}



<












|
>

|





|





|




|

|






1
2
3

4
5
6
7
8
9
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
#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"


using namespace goose;
using namespace goose::eir;
using namespace goose::parse;

namespace goose::builtins
{
    void SetupBreakStmt( Env& e )
    {
        auto handleBreak = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();

            auto level = GetBreakableScopeLevels( p.context() );

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

            auto cfg = GetCFG( p.context() );

            if( !cfg->currentBB() || cfg->currentBB()->terminator() )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                    locationId, "unreachable code.", 0 );
                PoisonBuilder( p.context() );
                return true;
            }

            // Emit cleanups for all live variables in the scopes that we are breaking through.
            DestroyAllLiveValuesFromBreakScope( p.context(), level );

            cfg->currentBB()->setTerminator( cir::Break( level ) );
            return true;
        };

        RegisterRule( e, "break"_sid, Rule( handleBreak ) );
    }
}
Changes to bs/builtins/statements/continue.cpp.
1
2
3
4
5
6
7
8
9
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
#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::eir;
using namespace goose::parse;

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


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

            const auto& cfg = cb->cfg();

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

            // Emit cleanups for all live variables in the scopes that we are continuing through.
            cb->destroyAllLiveValuesFromBreakScope( p.context(), cb->continuableScopeLevels() );

            cfg->currentBB()->setTerminator( cir::Continue( cb->continuableScopeLevels() ) );
            return true;
        };

        RegisterRule( e, "continue"_sid, Rule( handleContinue ) );
    }
}



<












|
>

|





|





|




|

|






1
2
3

4
5
6
7
8
9
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
#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"


using namespace goose;
using namespace goose::eir;
using namespace goose::parse;

namespace goose::builtins
{
    void SetupContinueStmt( Env& e )
    {
        auto handleContinue = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();

            auto level = GetContinuableScopeLevels( p.context() );

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

            auto cfg = GetCFG( p.context() );

            if( !cfg->currentBB() || cfg->currentBB()->terminator() )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                    locationId, "unreachable code.", 0 );
                PoisonBuilder( p.context() );
                return true;
            }

            // Emit cleanups for all live variables in the scopes that we are continuing through.
            DestroyAllLiveValuesFromContinueScope( p.context(), level );

            cfg->currentBB()->setTerminator( cir::Continue( level ) );
            return true;
        };

        RegisterRule( e, "continue"_sid, Rule( handleContinue ) );
    }
}
Changes to bs/builtins/statements/hif.cpp.
1
2
3
4
5
6
7
8
9
10
11
#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::eir;
using namespace goose::parse;

namespace goose::builtins
{



<







1
2
3

4
5
6
7
8
9
10
#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"


using namespace goose;
using namespace goose::eir;
using namespace goose::parse;

namespace goose::builtins
{
Changes to bs/builtins/statements/if.cpp.
1
2
3
4
5
6
7
8
9
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
#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::eir;
using namespace goose::parse;

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

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

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

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

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

            auto np = p.makeNestedParser();



<












|

|





<






|







1
2
3

4
5
6
7
8
9
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
#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"


using namespace goose;
using namespace goose::eir;
using namespace goose::parse;

namespace goose::builtins
{
    void SetupIfStmt( Env& e )
    {
        auto handleIf = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();
            auto cfg = GetCFG( p.context() );

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


            auto pPrecBB = cfg->currentBB();

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

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

            auto np = p.makeNestedParser();
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
                        break;
                }

                return false;
            }

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

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

            auto pThenBB = ParseSubStatement( p, precedence::IfStmt );
            if( !pThenBB )
            {







|







65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
                        break;
                }

                return false;
            }

            if( get< Value >( converted ).isPoison() )
                PoisonBuilder( p.context() );

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

            auto pThenBB = ParseSubStatement( p, precedence::IfStmt );
            if( !pThenBB )
            {
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126

            ptr< cir::BasicBlock > pSuccBB;

            // If both the then and the else blocks successors exist and are terminated,
            // we don't need a successor block.
            if( !pElseBB || ( pThenSuccBB && !pThenSuccBB->terminator() )
                || ( pElseSuccBB && !pElseSuccBB->terminator() ) )
                pSuccBB = cb->cfg()->createBB();

            pPrecBB->setTerminator( cir::CondBranch(
                get< Value >( converted ),
                pThenBB,
                pElseBB ? pElseBB : pSuccBB ) );

            if( pThenSuccBB && !pThenSuccBB->terminator() )







|







110
111
112
113
114
115
116
117
118
119
120
121
122
123
124

            ptr< cir::BasicBlock > pSuccBB;

            // If both the then and the else blocks successors exist and are terminated,
            // we don't need a successor block.
            if( !pElseBB || ( pThenSuccBB && !pThenSuccBB->terminator() )
                || ( pElseSuccBB && !pElseSuccBB->terminator() ) )
                pSuccBB = cfg->createBB();

            pPrecBB->setTerminator( cir::CondBranch(
                get< Value >( converted ),
                pThenBB,
                pElseBB ? pElseBB : pSuccBB ) );

            if( pThenSuccBB && !pThenSuccBB->terminator() )
Changes to bs/builtins/statements/return.cpp.
1
2
3
4
5
6
7
8
9
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
#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::eir;
using namespace goose::parse;

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

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

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

            p.flushValue();

            // Emit cleanups (destructor calls) for all currently live values in the function.
            cb->destroyAllLiveValues( p.context() );

            const auto& context = p.context();

            if( context.returnType() == GetValueType< void >() )
            {
                cb->emitTerminator( p.resolver()->currentLocation(), cir::Ret() );
                return true;
            }

            auto np = p.makeNestedParser();
            if( !np.parseExpression( precedence::ReturnStmt + 1 ) )
            {
                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the return statement.", 0 );
                cb->emitTerminator( p.resolver()->currentLocation(), cir::Ret( PoisonValue() ) );
                return false;
            }

            auto retVal = np.popValue();
            if( !retVal )
            {
                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the return statement.", 0 );
                cb->emitTerminator( p.resolver()->currentLocation(), cir::Ret( PoisonValue() ) );
                return false;
            }

            auto converted = ConvertValueToType( context, *retVal, *context.returnType() );
            if( holds_alternative< ValUnifyError >( converted ) )
            {
                switch( get< ValUnifyError >( converted ) )
                {
                    case ValUnifyError::NoSolution:
                        dm.emitErrorMessage( retVal->locationId(), "return value type mismatch." );
                        break;

                    case ValUnifyError::Ambiguous:
                        dm.emitErrorMessage( retVal->locationId(), "ambiguous return value conversion." );
                        break;
                }

                // Emit a terminator with a poison value to avoid the function compilation
                // code to complain about a missing return.
                cb->emitTerminator( p.resolver()->currentLocation(), cir::Ret( PoisonValue() ) );
                cb->poison();
                return true;
            }

            cb->emitTerminator( p.resolver()->currentLocation(), cir::Ret( get< Value >( converted ) ) );
            return true;
        };

        RegisterRule( e, "return"_sid, Rule( handleReturn ) );
    }
}



<












|

|





|



|





|





|







|







|



















|
|



|






1
2
3

4
5
6
7
8
9
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
#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"


using namespace goose;
using namespace goose::eir;
using namespace goose::parse;

namespace goose::builtins
{
    void SetupReturnStmt( Env& e )
    {
        auto handleReturn = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();
            auto cfg = GetCFG( p.context() );

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

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

            p.flushValue();

            // Emit cleanups (destructor calls) for all currently live values in the function.
            DestroyAllLiveValues( p.context() );

            const auto& context = p.context();

            if( context.returnType() == GetValueType< void >() )
            {
                cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret() );
                return true;
            }

            auto np = p.makeNestedParser();
            if( !np.parseExpression( precedence::ReturnStmt + 1 ) )
            {
                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the return statement.", 0 );
                cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret( PoisonValue() ) );
                return false;
            }

            auto retVal = np.popValue();
            if( !retVal )
            {
                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the return statement.", 0 );
                cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret( PoisonValue() ) );
                return false;
            }

            auto converted = ConvertValueToType( context, *retVal, *context.returnType() );
            if( holds_alternative< ValUnifyError >( converted ) )
            {
                switch( get< ValUnifyError >( converted ) )
                {
                    case ValUnifyError::NoSolution:
                        dm.emitErrorMessage( retVal->locationId(), "return value type mismatch." );
                        break;

                    case ValUnifyError::Ambiguous:
                        dm.emitErrorMessage( retVal->locationId(), "ambiguous return value conversion." );
                        break;
                }

                // Emit a terminator with a poison value to avoid the function compilation
                // code to complain about a missing return.
                cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret( PoisonValue() ) );
                PoisonBuilder( p.context() );
                return true;
            }

            cfg->emitTerminator( p.resolver()->currentLocation(), cir::Ret( get< Value >( converted ) ) );
            return true;
        };

        RegisterRule( e, "return"_sid, Rule( handleReturn ) );
    }
}
Changes to bs/builtins/statements/using.cpp.
1
2
3
4
5
6
7
8
9
10
11
#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::eir;
using namespace goose::parse;

namespace goose::builtins
{



<







1
2
3

4
5
6
7
8
9
10
#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"


using namespace goose;
using namespace goose::eir;
using namespace goose::parse;

namespace goose::builtins
{
Changes to bs/builtins/statements/verification.cpp.
1
2
3
4
5
6
7
8
9
10
#include "builtins/builtins.h"
#include "precedence.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::eir;
using namespace goose::parse;
using namespace goose::builtins;

namespace


<







1
2

3
4
5
6
7
8
9
#include "builtins/builtins.h"
#include "precedence.h"


using namespace goose;
using namespace goose::eir;
using namespace goose::parse;
using namespace goose::builtins;

namespace
Changes to bs/builtins/statements/while.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::eir;
using namespace goose::parse;

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

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

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

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



<












|

|





<







1
2
3

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"


using namespace goose;
using namespace goose::eir;
using namespace goose::parse;

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

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


            auto pPrecBB = cfg->currentBB();

            if( !pPrecBB || pPrecBB->terminator() )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                    locationId, "unreachable code.", 0 );
                cfg->poison();
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
                        break;
                }

                return false;
            }

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

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

            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() ) );

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

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







|









|
|







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
                        break;
                }

                return false;
            }

            if( get< Value >( converted ).isPoison() )
                PoisonBuilder( p.context() );

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

            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() ) );

            BreakableScopeGuard bsg( p.context() );
            ContinuableScopeGuard csg( p.context() );

            auto pBodyBB = ParseSubStatement( p, precedence::IfStmt );
            if( !pBodyBB )
            {
                dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), "expected a statement after the while condition.", 0 );
                return false;
            }
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
            }

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

            auto pSuccBB = cfg->createBB();

            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.
            cfg->forEachBB( [&]( auto&& bb )
            {
                const auto& t = bb->terminator();







|
|







100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
            }

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

            auto pSuccBB = cfg->createBB();

            auto breakLevel = GetBreakableScopeLevels( p.context() );
            auto continueLevel = GetContinuableScopeLevels( p.context() );

            // 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.
            cfg->forEachBB( [&]( auto&& bb )
            {
                const auto& t = bb->terminator();
Changes to bs/builtins/types/basic.cpp.
1
2
3
4
5
6
7
8
9
#include "builtins/builtins.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::sema;
using namespace goose::builtins;

namespace goose::builtins
{

<







1

2
3
4
5
6
7
8
#include "builtins/builtins.h"


using namespace goose;
using namespace goose::sema;
using namespace goose::builtins;

namespace goose::builtins
{
Changes to bs/builtins/types/drop.cpp.
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
    {
        // Default implementation of DropValue().
        // Constant values are discarded, and the cir of computed values
        // is appended to the current BB of the current parser.
        RegisterBuiltinFunc< Intrinsic< void ( Value ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& v )
            {
                if( v.isConstant() || !c.codeBuilder() )
                    return;

                // Reference use a load instruction to store their address,
                // so don't emit them. There is never any point in emitting
                // a load whose result isn't used anyway.
                if( holds_alternative< Load >( v.cir()->content() ) )
                    return;





                auto bb = c.codeBuilder()->cfg()->currentBB();
                bb->emplace_back( move( *v.cir() ) );
            } );

        using AnyDeclType = CustomPattern< Decl, Decl::Pattern >;

        // DropValue for Decls: declare a local variable with default initialization.
        // TODO: if the invocation to InitializeValue fails, we should have a way to
        // replace the generic "function arguments mismatch" error message with something
        // more specific such as "can't default-initialize a variable of type XXX"
        RegisterBuiltinFunc< Intrinsic< void ( AnyDeclType ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& v )
            {
                if( !c.codeBuilder() )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "variable declarations are not allowed here." );
                    return PoisonValue();
                }

                auto decl = *FromValue< Decl >( v );
                return DeclareLocalVar( c, decl.type(), decl.name(), nullopt, v.locationId() );







|








>
>
>
>
|












|







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
    {
        // Default implementation of DropValue().
        // Constant values are discarded, and the cir of computed values
        // is appended to the current BB of the current parser.
        RegisterBuiltinFunc< Intrinsic< void ( Value ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& v )
            {
                if( v.isConstant() )
                    return;

                // Reference use a load instruction to store their address,
                // so don't emit them. There is never any point in emitting
                // a load whose result isn't used anyway.
                if( holds_alternative< Load >( v.cir()->content() ) )
                    return;

                auto cfg = GetCFG( c );
                if( !cfg )
                    return;

                auto bb = cfg->currentBB();
                bb->emplace_back( move( *v.cir() ) );
            } );

        using AnyDeclType = CustomPattern< Decl, Decl::Pattern >;

        // DropValue for Decls: declare a local variable with default initialization.
        // TODO: if the invocation to InitializeValue fails, we should have a way to
        // replace the generic "function arguments mismatch" error message with something
        // more specific such as "can't default-initialize a variable of type XXX"
        RegisterBuiltinFunc< Intrinsic< void ( AnyDeclType ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& v )
            {
                if( !GetCFG( c ) )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "variable declarations are not allowed here." );
                    return PoisonValue();
                }

                auto decl = *FromValue< Decl >( v );
                return DeclareLocalVar( c, decl.type(), decl.name(), nullopt, v.locationId() );
Changes to bs/builtins/types/func/compile.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
#include "builtins/builtins.h"
#include "lex/lex.h"
#include "parse/parse.h"
#include "verify/verify.h"
#include "builtins/helpers.h"

using namespace goose::builtins;
using namespace goose::parse;

namespace goose::builtins
{
    Value CompileFunc( const Context& c, const Value& f )




<







1
2
3
4

5
6
7
8
9
10
11
#include "builtins/builtins.h"
#include "lex/lex.h"
#include "parse/parse.h"
#include "verify/verify.h"


using namespace goose::builtins;
using namespace goose::parse;

namespace goose::builtins
{
    Value CompileFunc( const Context& c, const Value& f )
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
            f.type().intrinsic() ? GetValueType< TypeWrapper< Value > >() : f.type().returnType() );

        auto cfg = make_shared< cir::CFG >( VecSize( f.type().params() ) );
        cfg->createBB();
        cfg->setCurrentBB( cfg->entryBB() );

        auto cb = make_shared< CodeBuilder >( cfg );
        localContext.setBuilder( cb );

        // Create the local variable bindings to access the function params
        // from inside the body, as well as the signature.
        uint32_t varId = 0;

        // For intrinsic functions, expose the context, which was
        // pushed as en extra, implicit first arg







|







148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
            f.type().intrinsic() ? GetValueType< TypeWrapper< Value > >() : f.type().returnType() );

        auto cfg = make_shared< cir::CFG >( VecSize( f.type().params() ) );
        cfg->createBB();
        cfg->setCurrentBB( cfg->entryBB() );

        auto cb = make_shared< CodeBuilder >( cfg );
        localContext.setBuilder( ToValue( TypeWrapper< ptr< CodeBuilder > >( cb ) ) );

        // Create the local variable bindings to access the function params
        // from inside the body, as well as the signature.
        uint32_t varId = 0;

        // For intrinsic functions, expose the context, which was
        // pushed as en extra, implicit first arg
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
            // TODO: at some point we'll want to check for reachability in the static verifier,
            // and either emit the implicit return or declare the code unreachable depending on the result.
            // The reachability analysis will have to be done before contract validation, as the
            // calls to DestroyValue() may also have requirements to enforce, so we'll need to emit
            // the eventual implicit return first.
            p.flushValue();
            cb->destroyAllLiveValues( localContext );
            cb->emitTerminator( r->currentLocation(), cir::Ret() );
        }

        pFuncCIR->body() = cfg;
        verify::Func fv( localContext, f );
        return fv.verify();
    }
}







|







223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
            // TODO: at some point we'll want to check for reachability in the static verifier,
            // and either emit the implicit return or declare the code unreachable depending on the result.
            // The reachability analysis will have to be done before contract validation, as the
            // calls to DestroyValue() may also have requirements to enforce, so we'll need to emit
            // the eventual implicit return first.
            p.flushValue();
            cb->destroyAllLiveValues( localContext );
            cfg->emitTerminator( r->currentLocation(), cir::Ret() );
        }

        pFuncCIR->body() = cfg;
        verify::Func fv( localContext, f );
        return fv.verify();
    }
}
Changes to bs/builtins/types/func/func.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
#include "builtins/builtins.h"
#include "lex/lex.h"
#include "parse/parse.h"
#include "verify/verify.h"
#include "builtins/helpers.h"

using namespace goose::builtins;
using namespace goose::parse;

namespace goose::builtins
{
    const Term& FuncPattern::GetPattern()




<







1
2
3
4

5
6
7
8
9
10
11
#include "builtins/builtins.h"
#include "lex/lex.h"
#include "parse/parse.h"
#include "verify/verify.h"


using namespace goose::builtins;
using namespace goose::parse;

namespace goose::builtins
{
    const Term& FuncPattern::GetPattern()
Changes to bs/builtins/types/func/invoke.cpp.
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
                auto argList = BuildArgListForCall( c, ft, typeCheckedArgs );
                if( !argList )
                    return PoisonValue();

                auto result = BuildComputedValue( typeCheckedRType, cir::Call( preparedCallee, move( *argList ) ) );

                // If the result is non-void, register it for destruction.
                if( c.codeBuilder() && result.type() != GetValueType< void >() )
                {
                    if( auto cfg = c.codeBuilder()->cfg() )
                        c.codeBuilder()->pushLiveValue( result, cfg->getNewTemporaryIndex() );
                }

                return result;
            }

            optional< Term > getSignature( const Value& callee ) const final
            {







|

|
|







85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
                auto argList = BuildArgListForCall( c, ft, typeCheckedArgs );
                if( !argList )
                    return PoisonValue();

                auto result = BuildComputedValue( typeCheckedRType, cir::Call( preparedCallee, move( *argList ) ) );

                // If the result is non-void, register it for destruction.
                if( result.type() != GetValueType< void >() )
                {
                    if( auto cfg = GetCFG( c ) )
                        PushLiveValue( c, result, cfg->getNewTemporaryIndex() );
                }

                return result;
            }

            optional< Term > getSignature( const Value& callee ) const final
            {
Changes to bs/builtins/types/localvar/drop.cpp.
24
25
26
27
28
29
30
31
32
33
34
35
                auto identity = AppendToVectorTerm( c.identity(), lv.name() );

                c.env()->storeValue( identity, ANYTERM( _ ),
                    ValueToEIR( v ) );

                // Extend the variable's lifetime, so that it won't be destroyed as the statement's lifetime scope ends,
                // but instead when its parent lifetime scope ends.
                if( c.codeBuilder() )
                    c.codeBuilder()->extendValueLifetime( lv.index() );
            } );
    }
}







<
|



24
25
26
27
28
29
30

31
32
33
34
                auto identity = AppendToVectorTerm( c.identity(), lv.name() );

                c.env()->storeValue( identity, ANYTERM( _ ),
                    ValueToEIR( v ) );

                // Extend the variable's lifetime, so that it won't be destroyed as the statement's lifetime scope ends,
                // but instead when its parent lifetime scope ends.

                ExtendValueLifetime( c, lv.index() );
            } );
    }
}
Changes to bs/builtins/types/localvar/localvar.cpp.
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
    {
        static auto pattern = GetValueType< LocalVar >( HOLE( "T"_sid ) );
        return pattern;
    }

    Value DeclareLocalVar( const Context& c, const Term& type, StringId name, const optional< Value >& initializer, LocationId locId )
    {
        const auto& cb = c.codeBuilder();
        assert( cb );
        const auto& cfg = cb->cfg();

        auto index = cfg->getNewTemporaryIndex();

        auto bb = cfg->currentBB();
        if( !bb )
            return PoisonValue();








|
|
|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
    {
        static auto pattern = GetValueType< LocalVar >( HOLE( "T"_sid ) );
        return pattern;
    }

    Value DeclareLocalVar( const Context& c, const Term& type, StringId name, const optional< Value >& initializer, LocationId locId )
    {
        auto cfg = GetCFG( c );
        if( !cfg )
            return PoisonValue();

        auto index = cfg->getNewTemporaryIndex();

        auto bb = cfg->currentBB();
        if( !bb )
            return PoisonValue();

69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

        auto locVar = ToValue( lv );
        auto identity = AppendToVectorTerm( c.identity(), name );

        c.env()->storeValue( identity, ANYTERM( _ ),
            ValueToEIR( locVar ) );

        cb->pushLiveValue( locVar, lv.index() );
        return locVar;
    }

    Value DeclareLocalVarWithTypeInference( Context& c, const Term& typeTExpr, StringId name, const Value& initVal, LocationId locId )
    {
        const auto& cb = c.codeBuilder();
        assert( cb );
        const auto& cfg = cb->cfg();

        auto bb = cfg->currentBB();
        if( !bb )
            return PoisonValue();

        // To infer the type and obtain a suitable initialization function, we typecheck
        //          ( $$T, _ ( MutRef[$$T], initValType ) initFunc )







|





|
|
|







69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

        auto locVar = ToValue( lv );
        auto identity = AppendToVectorTerm( c.identity(), name );

        c.env()->storeValue( identity, ANYTERM( _ ),
            ValueToEIR( locVar ) );

        PushLiveValue( c, locVar, lv.index() );
        return locVar;
    }

    Value DeclareLocalVarWithTypeInference( Context& c, const Term& typeTExpr, StringId name, const Value& initVal, LocationId locId )
    {
        auto cfg = GetCFG( c );
        if( !cfg )
            return PoisonValue();

        auto bb = cfg->currentBB();
        if( !bb )
            return PoisonValue();

        // To infer the type and obtain a suitable initialization function, we typecheck
        //          ( $$T, _ ( MutRef[$$T], initValType ) initFunc )
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
        if( name != ""_sid )
        {
            auto identity = AppendToVectorTerm( c.identity(), name );
            c.env()->storeValue( identity, ANYTERM( _ ),
                ValueToEIR( locVar ) );
        }

        cb->pushLiveValue( locVar, lv.index() );
        return locVar;
    }
}

namespace goose::eir
{
    const Term& Bridge< LocalVarType >::Type()







|







204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
        if( name != ""_sid )
        {
            auto identity = AppendToVectorTerm( c.identity(), name );
            c.env()->storeValue( identity, ANYTERM( _ ),
                ValueToEIR( locVar ) );
        }

        PushLiveValue( c, locVar, lv.index() );
        return locVar;
    }
}

namespace goose::eir
{
    const Term& Bridge< LocalVarType >::Type()
Changes to bs/builtins/types/overloadset/helpers.cpp.
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
    }

    Value InvokeOverloadSet( const Context& c, const ptr< OverloadSet >& pOvlSet, Value args )
    {
        assert( pOvlSet );

        Context localC( c.env(), c.identity(), GetValueType< uint32_t >() );
        localC.setBuilder( c.codeBuilder() );

        execute::VM vm;

        if( !args.isConstant() && cir::CanValueBeEagerlyEvaluated( args ) )
            args = execute::Evaluate( args, vm );
        if( args.isPoison() )
            return PoisonValue();







|







58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
    }

    Value InvokeOverloadSet( const Context& c, const ptr< OverloadSet >& pOvlSet, Value args )
    {
        assert( pOvlSet );

        Context localC( c.env(), c.identity(), GetValueType< uint32_t >() );
        localC.setBuilder( c.builder() );

        execute::VM vm;

        if( !args.isConstant() && cir::CanValueBeEagerlyEvaluated( args ) )
            args = execute::Evaluate( args, vm );
        if( args.isPoison() )
            return PoisonValue();
Changes to bs/builtins/types/predicates/predicates.cpp.
1
2
3
4
5
6
7
8
9
10
11
#include "builtins/builtins.h"
#include "lex/lex.h"
#include "parse/parse.h"
#include "builtins/helpers.h"

using namespace goose::parse;

namespace goose::builtins
{
    const Term& EmptyPredicates()
    {



<







1
2
3

4
5
6
7
8
9
10
#include "builtins/builtins.h"
#include "lex/lex.h"
#include "parse/parse.h"


using namespace goose::parse;

namespace goose::builtins
{
    const Term& EmptyPredicates()
    {
Changes to bs/builtins/types/pretty.cpp.
1
2
3
4
5
6
7
8
9
#include "builtins/builtins.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::sema;
using namespace goose::builtins;

namespace goose::builtins
{

<







1

2
3
4
5
6
7
8
#include "builtins/builtins.h"


using namespace goose;
using namespace goose::sema;
using namespace goose::builtins;

namespace goose::builtins
{
Changes to bs/builtins/types/reference/parse.cpp.
1
2
3
4
5
6
7
8
9
10
11
#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::eir;
using namespace goose::parse;
using namespace goose::builtins;

namespace



<







1
2
3

4
5
6
7
8
9
10
#include "builtins/builtins.h"
#include "parse/parse.h"
#include "precedence.h"


using namespace goose;
using namespace goose::eir;
using namespace goose::parse;
using namespace goose::builtins;

namespace
Changes to bs/builtins/types/reference/typecheck.cpp.
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

        // TypeCheck the param with the ref's content
        co_yield TypeCheck( lhs, content, tcc );
    }

    TCGen TypeCheckingBuildTempRef( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc )
    {
        if( !tcc.context().codeBuilder() )
            co_return;

        if( !tcc.context().codeBuilder()->cfg() )
            co_return;

        auto lRefType = *FromValue< ReferenceType >( *EIRToValue( ValuePatternFromEIR( lhs )->type() ) );

        if( lRefType.behavior() == MutAccessLevel() )
            co_return;

        auto lhsPat = ValueToEIR( ValuePattern( TSID( param ), lRefType.type(), HOLE( "_"_sid ) ) );








<
<
<
<
<
<







27
28
29
30
31
32
33






34
35
36
37
38
39
40

        // TypeCheck the param with the ref's content
        co_yield TypeCheck( lhs, content, tcc );
    }

    TCGen TypeCheckingBuildTempRef( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc )
    {






        auto lRefType = *FromValue< ReferenceType >( *EIRToValue( ValuePatternFromEIR( lhs )->type() ) );

        if( lRefType.behavior() == MutAccessLevel() )
            co_return;

        auto lhsPat = ValueToEIR( ValuePattern( TSID( param ), lRefType.type(), HOLE( "_"_sid ) ) );

60
61
62
63
64
65
66





67
68
69
70
71
72
73
74
                auto&& [arg, rhs] = *result;

                auto val = *EIRToValue( arg );
                ReferenceType rt( val.type(), TempAccessLevel() );

                auto rhsVal = *EIRToValue( rhs );






                auto tempIndex = tcc.context().codeBuilder()->cfg()->getNewTemporaryIndex();
                return ValueToEIR( BuildComputedValue( ValueToEIR( ToValue( rt ) ), TempAddr( tempIndex, rhsVal ) )
                    .setLocationId( rhsVal.locationId() ) );
            } );

            // Override the weight because we don't want
            // this solution to count more than directly using
            // the value without wrapping it into a tempref







>
>
>
>
>
|







54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
                auto&& [arg, rhs] = *result;

                auto val = *EIRToValue( arg );
                ReferenceType rt( val.type(), TempAccessLevel() );

                auto rhsVal = *EIRToValue( rhs );

                auto cfg = GetCFG( tcc.context() );
                if( !cfg )
                    return nullopt;

                // TODO create an ext point for this
                auto tempIndex = cfg->getNewTemporaryIndex();
                return ValueToEIR( BuildComputedValue( ValueToEIR( ToValue( rt ) ), TempAddr( tempIndex, rhsVal ) )
                    .setLocationId( rhsVal.locationId() ) );
            } );

            // Override the weight because we don't want
            // this solution to count more than directly using
            // the value without wrapping it into a tempref
Changes to bs/builtins/types/runtime/array.cpp.
1
2
3
4
5
6
7
8
9
#include "builtins/builtins.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::builtins;

namespace goose::builtins
{
    void SetupRuntimeArrayType( Env& e )

<







1

2
3
4
5
6
7
8
#include "builtins/builtins.h"


using namespace goose;
using namespace goose::builtins;

namespace goose::builtins
{
    void SetupRuntimeArrayType( Env& e )
Changes to bs/builtins/types/runtime/basic.cpp.
1
2
3
4
5
6
7
8
9
10
#include "builtins/builtins.h"
#include "codegen/codegen.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::builtins;
using namespace goose::codegen;

namespace goose::builtins
{


<







1
2

3
4
5
6
7
8
9
#include "builtins/builtins.h"
#include "codegen/codegen.h"


using namespace goose;
using namespace goose::builtins;
using namespace goose::codegen;

namespace goose::builtins
{
Changes to bs/builtins/types/runtime/pointer.cpp.
1
2
3
4
5
6
7
8
9
#include "builtins/builtins.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::builtins;

namespace goose::builtins
{
    void SetupRuntimePointerType( Env& e )

<







1

2
3
4
5
6
7
8
#include "builtins/builtins.h"


using namespace goose;
using namespace goose::builtins;

namespace goose::builtins
{
    void SetupRuntimePointerType( Env& e )
Changes to bs/builtins/types/runtime/record.cpp.
1
2
3
4
5
6
7
8
9
10
#include "builtins/builtins.h"
#include "codegen/codegen.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::builtins;
using namespace goose::codegen;

namespace goose::builtins
{


<







1
2

3
4
5
6
7
8
9
#include "builtins/builtins.h"
#include "codegen/codegen.h"


using namespace goose;
using namespace goose::builtins;
using namespace goose::codegen;

namespace goose::builtins
{
Changes to bs/builtins/types/runtime/typecheck.cpp.
1
2
3
4
5
6
7
8
9
#include "builtins/builtins.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::eir;

namespace goose::builtins
{
    void SetupRuntimeTypesChecking( Env& e )

<







1

2
3
4
5
6
7
8
#include "builtins/builtins.h"


using namespace goose;
using namespace goose::eir;

namespace goose::builtins
{
    void SetupRuntimeTypesChecking( Env& e )
Changes to bs/builtins/types/template/pretty.cpp.
1
2
3
4
5
6
7
8
9
#include "builtins/builtins.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::sema;
using namespace goose::builtins;

namespace goose::builtins
{

<







1

2
3
4
5
6
7
8
#include "builtins/builtins.h"


using namespace goose;
using namespace goose::sema;
using namespace goose::builtins;

namespace goose::builtins
{
Changes to bs/builtins/types/tuple/typecheck.cpp.
96
97
98
99
100
101
102
103




104
105
106
107
108

109
110
111
112
113
114
115
116
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            auto ltup = ValuePatternFromEIR( lhs );
            auto rtup = EIRToValue( rhs );

            if( !ltup || !rtup || !tcc.context().codeBuilder() )




                co_return;

            auto tupType = *EIRToValue( ltup->type() );
            assert( TupleTypeSize( tupType ) == TupleTypeSize( *EIRToValue( rtup->type() ) ) );


            auto tempIndex = tcc.context().codeBuilder()->cfg()->getNewTemporaryIndex();

            auto rtupref = BuildComputedValue( ValueToEIR( ToValue( ReferenceType{ rtup->type(), ConstAccessLevel() } ) ),
                cir::TempAddr( tempIndex, *rtup ) );

            co_yield TypeCheckComputedTuple( tcc, tupType, *rtup, rtupref, 0, EmptyTuple() );
        } );








|
>
>
>
>





>
|







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
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            auto ltup = ValuePatternFromEIR( lhs );
            auto rtup = EIRToValue( rhs );

            if( !ltup || !rtup )
                co_return;

            auto cfg = GetCFG( tcc.context() );
            if( !cfg )
                co_return;

            auto tupType = *EIRToValue( ltup->type() );
            assert( TupleTypeSize( tupType ) == TupleTypeSize( *EIRToValue( rtup->type() ) ) );

            // TODO create an ext point for this instead of going directly thru cfg
            auto tempIndex = cfg->getNewTemporaryIndex();

            auto rtupref = BuildComputedValue( ValueToEIR( ToValue( ReferenceType{ rtup->type(), ConstAccessLevel() } ) ),
                cir::TempAddr( tempIndex, *rtup ) );

            co_yield TypeCheckComputedTuple( tcc, tupType, *rtup, rtupref, 0, EmptyTuple() );
        } );

Changes to bs/builtins/types/types.cpp.
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
123
124
125
126
127
128
129
130
131
132
133
134

        auto result = InvokeOverloadSet( c,
            c.env()->extIsType(),
            MakeTuple( v ) );

        if( result.isPoison() )
        {
            if( c.codeBuilder() )
                c.codeBuilder()->poison();
            return false;
        }

        auto boolRes = FromValue< bool >( result );
        if( !boolRes )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( v.locationId(),
                "the IsType extension point returned a non boolean value." );

            if( c.codeBuilder() )
                c.codeBuilder()->poison();
            return false;
        }

        return *boolRes;
    }

    Value ToType( const Context& c, const Value& v )
    {
        auto result = InvokeOverloadSet( c,
            c.env()->extToType(),
            MakeTuple( v ) );

        if( result.isPoison() )
        {
            if( c.codeBuilder() )
                c.codeBuilder()->poison();
            return PoisonType();
        }

        if( !result.isType() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( v.locationId(),
                "the ToType extension point returned a non type value." );

            if( c.codeBuilder() )
                c.codeBuilder()->poison();
            return PoisonType();
        }

        return result;
    }
}







|
<









|
<














|
<








|
<






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
123
124

125
126
127
128
129
130

        auto result = InvokeOverloadSet( c,
            c.env()->extIsType(),
            MakeTuple( v ) );

        if( result.isPoison() )
        {
            PoisonBuilder( c );

            return false;
        }

        auto boolRes = FromValue< bool >( result );
        if( !boolRes )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( v.locationId(),
                "the IsType extension point returned a non boolean value." );

            PoisonBuilder( c );

            return false;
        }

        return *boolRes;
    }

    Value ToType( const Context& c, const Value& v )
    {
        auto result = InvokeOverloadSet( c,
            c.env()->extToType(),
            MakeTuple( v ) );

        if( result.isPoison() )
        {
            PoisonBuilder( c );

            return PoisonType();
        }

        if( !result.isType() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( v.locationId(),
                "the ToType extension point returned a non type value." );

            PoisonBuilder( c );

            return PoisonType();
        }

        return result;
    }
}
Changes to bs/builtins/types/wrapper.h.
174
175
176
177
178
179
180






181
182
183
184
185
186
187
    };

    template<>
    struct TypeWrapperTraits< ptr< sema::Context > >
    {
        static auto typeId() { return "Context"_sid; }
    };







    template<>
    struct TypeWrapperTraits< ptr< CodeBuilder > >
    {
        static auto typeId() { return "CodeBuilder"_sid; }
    };








>
>
>
>
>
>







174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
    };

    template<>
    struct TypeWrapperTraits< ptr< sema::Context > >
    {
        static auto typeId() { return "Context"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< cir::CFG > >
    {
        static auto typeId() { return "CFG"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< CodeBuilder > >
    {
        static auto typeId() { return "CodeBuilder"_sid; }
    };

Changes to bs/cir/call.cpp.
39
40
41
42
43
44
45
46
47

48
49
50
51
52
53
54
55
56
        // executing it in the first place in most cases.
        return !pFunc || pFunc->canBeExecuted();
    }

    bool Call::canBeEagerlyEvaluated() const
    {
        // Functions with void return type are assumed to have side effects
        // and therefore that they should never be eagerly evaluated.
        // It's not like they would need to be anyway.

        if( GetFuncRType( m_func ) == GetValueType< void >() )
            return false;

        if( IsNonEagerBuiltinFunc( m_func ) )
            return false;

        if( IsExternalFunc( m_func ) )
            return false;








|

>

|







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
        // executing it in the first place in most cases.
        return !pFunc || pFunc->canBeExecuted();
    }

    bool Call::canBeEagerlyEvaluated() const
    {
        // Functions with void return type are assumed to have side effects
        // and therefore that they should never be eagerly evaluated,
        // It's not like they would need to be anyway.
        // An exception is builtin functions that have been explicitely marked to be eagerly evaluated.
        if( GetFuncRType( m_func ) == GetValueType< void >() )
            return IsEagerBuiltinFunc( m_func );

        if( IsNonEagerBuiltinFunc( m_func ) )
            return false;

        if( IsExternalFunc( m_func ) )
            return false;

Changes to bs/cir/cfg.h.
20
21
22
23
24
25
26













27
28
29
30
31
32
33

            const auto& currentBB() const { return m_currentBB; }
            template< typename T >
            void setCurrentBB( T&& pBB )
            {
                m_currentBB = forward<  T >( pBB );
            }














            void addEdge( uint32_t srcIndex, uint32_t destIndex );
            const auto& edges() const { return m_edges; }

            template< typename V >
            void setIdoms( V&& idoms )
            {







>
>
>
>
>
>
>
>
>
>
>
>
>







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

            const auto& currentBB() const { return m_currentBB; }
            template< typename T >
            void setCurrentBB( T&& pBB )
            {
                m_currentBB = forward<  T >( pBB );
            }

            template< typename T >
            void emitTerminator( eir::LocationId locId, T&& terminator )
            {
                if( !currentBB() || currentBB()->terminator() )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                        locId, "unreachable code.", 0 );
                    return;
                }

                currentBB()->setTerminator( forward< T >( terminator ) );
            }

            void addEdge( uint32_t srcIndex, uint32_t destIndex );
            const auto& edges() const { return m_edges; }

            template< typename V >
            void setIdoms( V&& idoms )
            {
Changes to bs/cir/cir.h.
1
2
3
4
5

6
7
8
9

10
11
12
13
14
15
16
#ifndef GOOSE_CIR_H
#define GOOSE_CIR_H

#include "util/util.h"
#include "eir/eir.h"


namespace goose::cir
{
    using namespace util;


    static constexpr uint32_t InvalidVarId = numeric_limits< uint32_t >::max();

    class CFG;
}

#include "helpers.h"





>




>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef GOOSE_CIR_H
#define GOOSE_CIR_H

#include "util/util.h"
#include "eir/eir.h"
#include "diagnostics/diagnostics.h"

namespace goose::cir
{
    using namespace util;
    using namespace diagnostics;

    static constexpr uint32_t InvalidVarId = numeric_limits< uint32_t >::max();

    class CFG;
}

#include "helpers.h"
Changes to bs/compile/compiler.cpp.
1
2
3
4
5
6
7
8
9
10
#include "compiler.h"
#include "builtins/builtins.h"
#include "builtins/helpers.h"
#include "g0api/g0api.h"
#include "sema/sema.h"
#include "verify/verify.h"
#include "execute/execute.h"
#include "diagnostics/diagnostics.h"

#include "llvm/InitializePasses.h"


<







1
2

3
4
5
6
7
8
9
#include "compiler.h"
#include "builtins/builtins.h"

#include "g0api/g0api.h"
#include "sema/sema.h"
#include "verify/verify.h"
#include "execute/execute.h"
#include "diagnostics/diagnostics.h"

#include "llvm/InitializePasses.h"
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
        VerbosityContext vc( Verbosity::Normal, true );

        auto cfg = make_shared< cir::CFG >( 0 );
        cfg->createBB();
        cfg->setCurrentBB( cfg->entryBB() );

        auto cb = make_shared< CodeBuilder >( cfg );
        c.setBuilder( cb );

        auto r = make_shared< parse::Resolver >(
            make_shared< lex::Lexer >( sourcefile, filename ), c );
        parse::Parser p( r );

        p.parseSequence();








|







123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
        VerbosityContext vc( Verbosity::Normal, true );

        auto cfg = make_shared< cir::CFG >( 0 );
        cfg->createBB();
        cfg->setCurrentBB( cfg->entryBB() );

        auto cb = make_shared< CodeBuilder >( cfg );
        c.setBuilder( ToValue( TypeWrapper< ptr< CodeBuilder > >( cb ) ) );

        auto r = make_shared< parse::Resolver >(
            make_shared< lex::Lexer >( sourcefile, filename ), c );
        parse::Parser p( r );

        p.parseSequence();

147
148
149
150
151
152
153
154
155
156
157
158
159
160
161

        if( cfg->currentBB() && !cfg->currentBB()->terminator() )
        {
            p.flushValue();
            cb->destroyAllLiveValues( c );

            if( returnType == GetValueType< void >() )
               cb->emitTerminator( r->currentLocation(), cir::Ret() );
            else if( !defRetVal )
            {
                dm.emitSyntaxErrorMessage( r->currentLocation(), "missing return statement." );
                return nullptr;
            }
            else
            {







|







146
147
148
149
150
151
152
153
154
155
156
157
158
159
160

        if( cfg->currentBB() && !cfg->currentBB()->terminator() )
        {
            p.flushValue();
            cb->destroyAllLiveValues( c );

            if( returnType == GetValueType< void >() )
               cfg->emitTerminator( r->currentLocation(), cir::Ret() );
            else if( !defRetVal )
            {
                dm.emitSyntaxErrorMessage( r->currentLocation(), "missing return statement." );
                return nullptr;
            }
            else
            {
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
                            dm.emitErrorMessage( defRetVal->locationId(), "ambiguous default return value conversion." );
                            break;
                    }

                    return nullptr;
                }

                cb->emitTerminator( r->currentLocation(), cir::Ret( get< Value >( converted ) ) );
            }
        }

        verify::Func fv( c, cfg, returnType );
        if( !fv.verify() )
            return nullptr;

        return cfg;
    }
}







|










171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
                            dm.emitErrorMessage( defRetVal->locationId(), "ambiguous default return value conversion." );
                            break;
                    }

                    return nullptr;
                }

                cfg->emitTerminator( r->currentLocation(), cir::Ret( get< Value >( converted ) ) );
            }
        }

        verify::Func fv( c, cfg, returnType );
        if( !fv.verify() )
            return nullptr;

        return cfg;
    }
}
Changes to bs/execute/termaddr.cpp.
1
2
3
4
5
6
7
8
9
10
#include "execute.h"
#include "builtins/builtins.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::execute;
using namespace goose::builtins;

const Term& Bridge< eir::Term* >::Type()
{


<







1
2

3
4
5
6
7
8
9
#include "execute.h"
#include "builtins/builtins.h"


using namespace goose;
using namespace goose::execute;
using namespace goose::builtins;

const Term& Bridge< eir::Term* >::Type()
{
Changes to bs/execute/vm.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "execute.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::execute;
using namespace goose::builtins;

uint32_t VM::ms_remainingBranchInstExecutions = 1024;

optional< Value > VM::execute( CFG& cfg )
{
    return execute( cfg.entryBB() );
}

optional< Value > VM::execute( ptr< BasicBlock > bb )







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "execute.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::execute;
using namespace goose::builtins;

uint32_t VM::ms_remainingBranchInstExecutions = 1 << 24;

optional< Value > VM::execute( CFG& cfg )
{
    return execute( cfg.entryBB() );
}

optional< Value > VM::execute( ptr< BasicBlock > bb )
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
}

optional< Value > VM::execute( const cir::Call& call )
{
    if( !( ms_remainingBranchInstExecutions ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "Execute: compilation time execution budget exceeded." );
        return PoisonValue();
    }

    --ms_remainingBranchInstExecutions;

    if( call.func().isPoison() )
        DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );

    auto func = Evaluate( call.func(), *this );
    if( func.isPoison() )
        return PoisonValue();

    if( !func.isConstant() )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "Execute: function evaluation failed." );
        return PoisonValue();
    }

    if( IsExternalFunc( func ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "Execute: can't call external functions." );
        return PoisonValue();
    }

    bool poisoned = false;
    const auto& vec = *get< pvec >( call.args() );

    if( IsBuiltinFunc( func ) )







|















|






|







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
}

optional< Value > VM::execute( const cir::Call& call )
{
    if( !( ms_remainingBranchInstExecutions ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "execute: compilation time execution budget exceeded." );
        return PoisonValue();
    }

    --ms_remainingBranchInstExecutions;

    if( call.func().isPoison() )
        DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );

    auto func = Evaluate( call.func(), *this );
    if( func.isPoison() )
        return PoisonValue();

    if( !func.isConstant() )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "execute: function evaluation failed." );
        return PoisonValue();
    }

    if( IsExternalFunc( func ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "execute: can't call external functions." );
        return PoisonValue();
    }

    bool poisoned = false;
    const auto& vec = *get< pvec >( call.args() );

    if( IsBuiltinFunc( func ) )
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
}

ptr< BasicBlock > VM::executeTerminator( const cir::Branch& b )
{
    if( !( ms_remainingBranchInstExecutions ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "Execute: compilation time execution budget exceeded." );
        m_retVal = PoisonValue();
        return nullptr;
    }

    --ms_remainingBranchInstExecutions;
    return b.dest().lock();
}

ptr< BasicBlock > VM::executeTerminator( const cir::CondBranch& cb )
{
    if( !( ms_remainingBranchInstExecutions ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "Execute: compilation time execution budget exceeded." );
        m_retVal = PoisonValue();
        return nullptr;
    }

    --ms_remainingBranchInstExecutions;

    auto cond = Evaluate( cb.cond(), *this );
    if( cond.isPoison() )
        return nullptr;

    if( !cond.isConstant() )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( cb.cond().locationId(),
            "Execute: branch condition evaluation failed." );
        m_retVal = PoisonValue();
        return nullptr;
    }

    if( *FromValue< bool >( cond ) )
        return cb.trueDest().lock();








|













|













|







318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
}

ptr< BasicBlock > VM::executeTerminator( const cir::Branch& b )
{
    if( !( ms_remainingBranchInstExecutions ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "execute: compilation time execution budget exceeded." );
        m_retVal = PoisonValue();
        return nullptr;
    }

    --ms_remainingBranchInstExecutions;
    return b.dest().lock();
}

ptr< BasicBlock > VM::executeTerminator( const cir::CondBranch& cb )
{
    if( !( ms_remainingBranchInstExecutions ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "execute: compilation time execution budget exceeded." );
        m_retVal = PoisonValue();
        return nullptr;
    }

    --ms_remainingBranchInstExecutions;

    auto cond = Evaluate( cb.cond(), *this );
    if( cond.isPoison() )
        return nullptr;

    if( !cond.isConstant() )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( cb.cond().locationId(),
            "execute: branch condition evaluation failed." );
        m_retVal = PoisonValue();
        return nullptr;
    }

    if( *FromValue< bool >( cond ) )
        return cb.trueDest().lock();

Changes to bs/g0api/compiler.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "g0api/g0api.h"
#include "parse/parse.h"
#include "verify/verify.h"
#include "execute/execute.h"
#include "compile/compiler.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::compile;

namespace goose::g0api
{
    void SetupApiCompiler( Env& e )





<







1
2
3
4
5

6
7
8
9
10
11
12
#include "g0api/g0api.h"
#include "parse/parse.h"
#include "verify/verify.h"
#include "execute/execute.h"
#include "compile/compiler.h"


using namespace goose;
using namespace goose::compile;

namespace goose::g0api
{
    void SetupApiCompiler( Env& e )
Changes to bs/g0api/extensibility/cir.cpp.
1
2
3
4
5
6
7
8
9
10
11
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace
{



<







1
2
3

4
5
6
7
8
9
10
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"


using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace
{
Changes to bs/g0api/extensibility/diagnostics.cpp.
1
2
3
4
5
6
7
8
9
10
11
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace goose::g0api
{



<







1
2
3

4
5
6
7
8
9
10
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"


using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace goose::g0api
{
Changes to bs/g0api/extensibility/eir.cpp.
1
2
3
4
5
6
7
8
9
10
11
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace
{



<







1
2
3

4
5
6
7
8
9
10
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"


using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace
{
Changes to bs/g0api/extensibility/env.cpp.
1
2
3
4
5
6
7
8
9
10
11
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace goose::g0api
{



<







1
2
3

4
5
6
7
8
9
10
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"


using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace goose::g0api
{
Changes to bs/g0api/extensibility/value.cpp.
1
2
3
4
5
6
7
8
9
10
11
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace goose::g0api
{



<







1
2
3

4
5
6
7
8
9
10
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"


using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace goose::g0api
{
Changes to bs/g0api/types.cpp.
1
2
3
4
5
6
7
8
9
10
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "builtins/helpers.h"

using namespace goose;
using namespace goose::g0api;

namespace
{
    template< typename T >


<







1
2

3
4
5
6
7
8
9
#include "g0api/g0api.h"
#include "eir/eir.h"


using namespace goose;
using namespace goose::g0api;

namespace
{
    template< typename T >
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
123
124
125
126
127
        SetupWrapperForType< ptr< Terminator > >( e, "Terminator"_sid, pEquals, pNotEquals );

        ////////////////////////////
        // Context
        ////////////////////////////
        SetupWrapperForType< ptr< Context > >( e, "Context"_sid, pEquals, pNotEquals );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< Context > >, TermRef< TypeWrapper< ptr< CodeBuilder > > > ) >( e, "ContextGetCodeBuilder"_sid,
            []( const TypeWrapper< ptr< Context > >& c, TermRef< TypeWrapper< ptr< CodeBuilder > > >& out )
            {
                if( !c.get() )
                    return false;

                const auto& pCB = c->codeBuilder();
                if( !pCB )
                    return false;

                out = pCB;
                return true;
            } );

        ////////////////////////////
        // CodeBuilder
        ////////////////////////////
        SetupWrapperForType< ptr< CodeBuilder > >( e, "CodeBuilder"_sid, pEquals, pNotEquals );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CodeBuilder > >, TermRef< TypeWrapper< ptr< CFG > > > ) >( e, "CodeBuilderGetCFG"_sid,
            []( const TypeWrapper< ptr< CodeBuilder > >& cb, TermRef< TypeWrapper< ptr< CFG > > >& out )
            {
                if( !cb.get() )
                    return false;

                const auto& pCFG = cb->cfg();
                if( !pCFG )
                    return false;

                out = pCFG;
                return true;
            } );

        ////////////////////////////
        // ReferenceType
        ////////////////////////////
        SetupWrapperForType< ptr< ReferenceType > >( e, "RefTypeDesc"_sid, pEquals, pNotEquals );







|
|

|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|


<
<
<
<
|







83
84
85
86
87
88
89
90
91
92
93


















94
95
96




97
98
99
100
101
102
103
104
        SetupWrapperForType< ptr< Terminator > >( e, "Terminator"_sid, pEquals, pNotEquals );

        ////////////////////////////
        // Context
        ////////////////////////////
        SetupWrapperForType< ptr< Context > >( e, "Context"_sid, pEquals, pNotEquals );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< Context > >, TermRef< TypeWrapper< ptr< CFG > > > ) >( e, "ContextGetCFG"_sid,
            []( const TypeWrapper< ptr< Context > >& c, TermRef< TypeWrapper< ptr< CFG > > >& out )
            {
                auto cfg = GetCFG( *c.get() );


















                if( !cfg )
                    return false;





                out = cfg;
                return true;
            } );

        ////////////////////////////
        // ReferenceType
        ////////////////////////////
        SetupWrapperForType< ptr< ReferenceType > >( e, "RefTypeDesc"_sid, pEquals, pNotEquals );
Changes to bs/g0api/types.h.
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

    template<>
    struct TypeWrapperTraits< ptr< TypePredicates > >
    {
        static auto typeId() { return "TypePredicates"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< cir::CFG > >
    {
        static auto typeId() { return "CFG"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< cir::BasicBlock > >
    {
        static auto typeId() { return "BasicBlock"_sid; }
    };

    template<>







<
<
<
<
<
<







58
59
60
61
62
63
64






65
66
67
68
69
70
71

    template<>
    struct TypeWrapperTraits< ptr< TypePredicates > >
    {
        static auto typeId() { return "TypePredicates"_sid; }
    };







    template<>
    struct TypeWrapperTraits< ptr< cir::BasicBlock > >
    {
        static auto typeId() { return "BasicBlock"_sid; }
    };

    template<>
Changes to bs/parse/blocks.cpp.
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
{
    // Create a nested verbosity context to restrict the scope of
    // error silencing caused by syntax errors to the block.
    VerbosityContext vc( Verbosity::Normal );

    m_resolver->consume();

    const auto& cb = context().codeBuilder();
    CodeBuilder::LifetimeScopeGuard lsg( context() );

    auto p = makeNestedParser( Delimiter::OpenBrace );
    p.parseSequence();

    auto next = m_resolver->consumeUnresolved();
    if( !next )
    {
        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
            resolver()->currentLocation(), "'}' expected.", 0 );
        if( cb )
            cb->poison();
        return true;
    }

    auto decomp = Decompose( next->first, Val< Delimiter >() );
    if( !decomp || *decomp != Delimiter::CloseBrace )
    {
        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
            next->second, "'}' expected.", 0 );
        if( cb )
            cb->poison();
        return true;
    }

    return true;
}

bool Parser::parseNestedBraceBlock()







<
|









<
|








<
|







230
231
232
233
234
235
236

237
238
239
240
241
242
243
244
245
246

247
248
249
250
251
252
253
254
255

256
257
258
259
260
261
262
263
{
    // Create a nested verbosity context to restrict the scope of
    // error silencing caused by syntax errors to the block.
    VerbosityContext vc( Verbosity::Normal );

    m_resolver->consume();


    LifetimeScopeGuard lsg( context() );

    auto p = makeNestedParser( Delimiter::OpenBrace );
    p.parseSequence();

    auto next = m_resolver->consumeUnresolved();
    if( !next )
    {
        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
            resolver()->currentLocation(), "'}' expected.", 0 );

        PoisonBuilder( context() );
        return true;
    }

    auto decomp = Decompose( next->first, Val< Delimiter >() );
    if( !decomp || *decomp != Delimiter::CloseBrace )
    {
        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
            next->second, "'}' expected.", 0 );

        PoisonBuilder( context() );
        return true;
    }

    return true;
}

bool Parser::parseNestedBraceBlock()
Changes to bs/parse/parser.cpp.
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

// Parse a sequence of expression. Each expression is
// a statement.
void Parser::parseSequence()
{
    for(;;)
    {
        CodeBuilder::LifetimeScopeGuard lsg( context() );

        {
            // Each statement gets its own visibility scope,
            // so that vars defined inside of the statement are only
            // visible from within it.
            // However, they also have a lifetime scope, which
            // is separate because we need to flush the value







|







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

// Parse a sequence of expression. Each expression is
// a statement.
void Parser::parseSequence()
{
    for(;;)
    {
        LifetimeScopeGuard lsg( context() );

        {
            // Each statement gets its own visibility scope,
            // so that vars defined inside of the statement are only
            // visible from within it.
            // However, they also have a lifetime scope, which
            // is separate because we need to flush the value
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
        if( !parseInfix( next->first, *prec ) )
            break;
    }
}

void Parser::flushValue()
{
    const auto& cb = context().codeBuilder();
    if( m_lastValue && cb && cb->cfg() && ( !cb->cfg()->currentBB() || cb->cfg()->currentBB()->terminator() )  )
    {
        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
            m_lastValue->locationId(), "unreachable code.", 0 );
    }

    // Flush the pending value, by invoking the DropValue
    // extension point, where an overload will decide







|
|







74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
        if( !parseInfix( next->first, *prec ) )
            break;
    }
}

void Parser::flushValue()
{
    auto cfg = GetCFG( context() );
    if( m_lastValue && cfg && ( !cfg->currentBB() || cfg->currentBB()->terminator() )  )
    {
        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
            m_lastValue->locationId(), "unreachable code.", 0 );
    }

    // Flush the pending value, by invoking the DropValue
    // extension point, where an overload will decide
Changes to bs/parse/parser.inl.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef GOOSE_PARSE_PARSER_INL
#define GOOSE_PARSE_PARSER_INL

namespace goose::parse
{
    template< typename V >
    void Parser::pushValue( V&& val )
    {
        if( val.isPoison() )
        {
            DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );
            if( context().codeBuilder() )
                context().codeBuilder()->poison();
        }

        flushValue();

        if( val.isConstant() || !cir::CanValueBeEagerlyEvaluated( val ) )
        {
            m_lastValue = forward< V >( val );











|
<







1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
#ifndef GOOSE_PARSE_PARSER_INL
#define GOOSE_PARSE_PARSER_INL

namespace goose::parse
{
    template< typename V >
    void Parser::pushValue( V&& val )
    {
        if( val.isPoison() )
        {
            DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );
            builtins::PoisonBuilder( context() );

        }

        flushValue();

        if( val.isConstant() || !cir::CanValueBeEagerlyEvaluated( val ) )
        {
            m_lastValue = forward< V >( val );
Changes to bs/parse/scope.cpp.
1
2
3
4
5

































6
7
8
9
10
11
12
#include "parse.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::builtins;


































VisibilityScope::VisibilityScope( Parser& p ) :
    m_identityGuard( p )
{
    auto parentBaseIdentity = p.context().identity();
    parentBaseIdentity = TakeVectorTerm( parentBaseIdentity, VecSize( parentBaseIdentity ) - 1 );






>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
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
#include "parse.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::builtins;

LifetimeScopeGuard::LifetimeScopeGuard( const sema::Context& c ) :
    m_context( c )
{
    PushLifetimeScope( m_context );
}

LifetimeScopeGuard::~LifetimeScopeGuard()
{
    PopLifetimeScope( m_context );
}

BreakableScopeGuard::BreakableScopeGuard( const sema::Context& c ) :
    m_context( c )
{
    PushBreakableScope( m_context );
}

BreakableScopeGuard::~BreakableScopeGuard()
{
    PopBreakableScope( m_context );
}

ContinuableScopeGuard::ContinuableScopeGuard( const sema::Context& c ) :
    m_context( c )
{
    PushContinuableScope( m_context );
}

ContinuableScopeGuard::~ContinuableScopeGuard()
{
    PopContinuableScope( m_context );
}

VisibilityScope::VisibilityScope( Parser& p ) :
    m_identityGuard( p )
{
    auto parentBaseIdentity = p.context().identity();
    parentBaseIdentity = TakeVectorTerm( parentBaseIdentity, VecSize( parentBaseIdentity ) - 1 );

Changes to bs/parse/scope.h.
1
2
3
4
5






























6
7
8
9
10
11
12
#ifndef GOOSE_PARSE_SCOPE_H
#define GOOSE_PARSE_SCOPE_H

namespace goose::parse
{






























    // Helper object that creates a nested visibility scope.
    class VisibilityScope
    {
        public:
            VisibilityScope( Parser& p );

        private:





>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
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
#ifndef GOOSE_PARSE_SCOPE_H
#define GOOSE_PARSE_SCOPE_H

namespace goose::parse
{
    class LifetimeScopeGuard
    {
        public:
            LifetimeScopeGuard( const sema::Context& c );
            ~LifetimeScopeGuard();

        private:
            const sema::Context& m_context;
    };

    class BreakableScopeGuard
    {
        public:
            BreakableScopeGuard( const sema::Context& c );
            ~BreakableScopeGuard();

        private:
            const sema::Context& m_context;
    };

    class ContinuableScopeGuard
    {
        public:
            ContinuableScopeGuard( const sema::Context& c );
            ~ContinuableScopeGuard();

        private:
            const sema::Context& m_context;
    };

    // Helper object that creates a nested visibility scope.
    class VisibilityScope
    {
        public:
            VisibilityScope( Parser& p );

        private:
20
21
22
23
24
25
26
27
28
29
30
31
        public:
            Scope( Parser& p ) :
                VisibilityScope( p ),
                m_lifeTimeScopeGuard( p.context() )
            {}

        private:
            sema::CodeBuilder::LifetimeScopeGuard m_lifeTimeScopeGuard;
    };
}

#endif







|




50
51
52
53
54
55
56
57
58
59
60
61
        public:
            Scope( Parser& p ) :
                VisibilityScope( p ),
                m_lifeTimeScopeGuard( p.context() )
            {}

        private:
            LifetimeScopeGuard m_lifeTimeScopeGuard;
    };
}

#endif
Changes to bs/sema/context.h.
1
2
3
4
5
6
7
8
9
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
#ifndef GOOSE_SEMA_CONTEXT_H
#define GOOSE_SEMA_CONTEXT_H

namespace goose::sema
{
    class Env;
    class CodeBuilder;

    class Context
    {
        public:
            template< typename E, typename I >
            Context( E&& pEnv, I&& identity ) :
                m_pEnv( forward< E >( pEnv ) ),
                m_identity( forward< I >( identity ) )
            {}

            template< typename E, typename I, typename R >
            Context( E&& pEnv, I&& identity, R&& returnType ) :
                m_pEnv( forward< E >( pEnv ) ),
                m_identity( forward< I >( identity ) ),
                m_returnType( forward< R >( returnType ) )
            {}

            template< typename B >
            void setBuilder( B&& b )
            {
                m_codeBuilder = forward< B >( b );
            }

            const auto& env() const { return m_pEnv; }
            const auto& identity() const { return m_identity; }
            const auto& returnType() const { return m_returnType; }
            const auto& codeBuilder() const { return m_codeBuilder; }




            void setEnv( const ptr< Env >& pEnv )
            {
                m_pEnv = pEnv;
            }

            void setIdentity( const Term& identity )
            {
                m_identity = identity;
            }

        private:
            ptr< Env >          m_pEnv;
            Term                m_identity;
            optional< Term >    m_returnType;

            ptr< CodeBuilder >  m_codeBuilder;
    };
}

#endif






<




















|





|
>
>
>
















|




1
2
3
4
5
6

7
8
9
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
#ifndef GOOSE_SEMA_CONTEXT_H
#define GOOSE_SEMA_CONTEXT_H

namespace goose::sema
{
    class Env;


    class Context
    {
        public:
            template< typename E, typename I >
            Context( E&& pEnv, I&& identity ) :
                m_pEnv( forward< E >( pEnv ) ),
                m_identity( forward< I >( identity ) )
            {}

            template< typename E, typename I, typename R >
            Context( E&& pEnv, I&& identity, R&& returnType ) :
                m_pEnv( forward< E >( pEnv ) ),
                m_identity( forward< I >( identity ) ),
                m_returnType( forward< R >( returnType ) )
            {}

            template< typename B >
            void setBuilder( B&& b )
            {
                m_builder = forward< B >( b );
            }

            const auto& env() const { return m_pEnv; }
            const auto& identity() const { return m_identity; }
            const auto& returnType() const { return m_returnType; }
            const auto& builder() const { return m_builder; }

            template< typename T >
            T builder() const { return FromValue< T >( m_builder ); }

            void setEnv( const ptr< Env >& pEnv )
            {
                m_pEnv = pEnv;
            }

            void setIdentity( const Term& identity )
            {
                m_identity = identity;
            }

        private:
            ptr< Env >          m_pEnv;
            Term                m_identity;
            optional< Term >    m_returnType;

            Value               m_builder;
    };
}

#endif
Changes to bs/sema/env.h.
75
76
77
78
79
80
81



















































82
83
84
85
86
87
88

            auto& extConvertFuncParam() { return m_extConvertFuncParam; }
            const auto& extConvertFuncParam() const { return m_extConvertFuncParam; }

            auto& extConvertFuncArg() { return m_extConvertFuncArg; }
            const auto& extConvertFuncArg() const { return m_extConvertFuncArg; }




















































            template< typename... T >
            auto createCIRFunc( T&&... args )
            {
                ms_cirFuncs.emplace_back( make_shared< cir::Func >( forward< T >( args )... ) );
                return ms_cirFuncs.back();
            }








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139

            auto& extConvertFuncParam() { return m_extConvertFuncParam; }
            const auto& extConvertFuncParam() const { return m_extConvertFuncParam; }

            auto& extConvertFuncArg() { return m_extConvertFuncArg; }
            const auto& extConvertFuncArg() const { return m_extConvertFuncArg; }

            auto& extPoisonBuilder() { return m_extPoisonBuilder; }
            const auto& extPoisonBuilder() const { return m_extPoisonBuilder; }

            auto& extBuilderAllowsVarDecl() { return m_extBuilderAllowsVarDecl; }
            const auto& extBuilderAllowsVarDecl() const { return m_extBuilderAllowsVarDecl; }

            auto& extGetCFG() { return m_extGetCFG; }
            const auto& extGetCFG() const { return m_extGetCFG; }

            auto& extGetBreakableScopeLevels() { return m_extGetBreakableScopeLevels; }
            const auto& extGetBreakableScopeLevels() const { return m_extGetBreakableScopeLevels; }

            auto& extGetContinuableScopeLevels() { return m_extGetContinuableScopeLevels; }
            const auto& extGetContinuableScopeLevels() const { return m_extGetContinuableScopeLevels; }

            auto& extPushLifetimeScope() { return m_extPushLifetimeScope; }
            const auto& extPushLifetimeScope() const { return m_extPushLifetimeScope; }

            auto& extPopLifetimeScope() { return m_extPopLifetimeScope; }
            const auto& extPopLifetimeScope() const { return m_extPopLifetimeScope; }

            auto& extPushBreakableScope() { return m_extPushBreakableScope; }
            const auto& extPushBreakableScope() const { return m_extPushBreakableScope; }

            auto& extPopBreakableScope() { return m_extPopBreakableScope; }
            const auto& extPopBreakableScope() const { return m_extPopBreakableScope; }

            auto& extPushContinuableScope() { return m_extPushContinuableScope; }
            const auto& extPushContinuableScope() const { return m_extPushContinuableScope; }

            auto& extPopContinuableScope() { return m_extPopContinuableScope; }
            const auto& extPopContinuableScope() const { return m_extPopContinuableScope; }

            auto& extPushLiveValue() { return m_extPushLiveValue; }
            const auto& extPushLiveValue() const { return m_extPushLiveValue; }

            auto& extExtendValueLifetime() { return m_extExtendValueLifetime; }
            const auto& extExtendValueLifetime() const { return m_extExtendValueLifetime; }

            auto& extDestroyAndPopCurrentLifetimeScopeValues() { return m_extDestroyAndPopCurrentLifetimeScopeValues; }
            const auto& extDestroyAndPopCurrentLifetimeScopeValues() const { return m_extDestroyAndPopCurrentLifetimeScopeValues; }

            auto& extDestroyAllLiveValues() { return m_extDestroyAllLiveValues; }
            const auto& extDestroyAllLiveValues() const { return m_extDestroyAllLiveValues; }

            auto& extDestroyAllLiveValuesFromBreakScope() { return m_extDestroyAllLiveValuesFromBreakScope; }
            const auto& extDestroyAllLiveValuesFromBreakScope() const { return m_extDestroyAllLiveValuesFromBreakScope; }

            auto& extDestroyAllLiveValuesFromContinueScope() { return m_extDestroyAllLiveValuesFromContinueScope; }
            const auto& extDestroyAllLiveValuesFromContinueScope() const { return m_extDestroyAllLiveValuesFromContinueScope; }

            template< typename... T >
            auto createCIRFunc( T&&... args )
            {
                ms_cirFuncs.emplace_back( make_shared< cir::Func >( forward< T >( args )... ) );
                return ms_cirFuncs.back();
            }

103
104
105
106
107
108
109























110
111
112
113
114
115
116
            ptr< OverloadSet >                  m_extLowerConstantForRuntime;
            ptr< OverloadSet >                  m_extLowerTypeForVerification;
            ptr< OverloadSet >                  m_extLowerConstantForVerification;

            ptr< OverloadSet >                  m_extConvertFuncParam;
            ptr< OverloadSet >                  m_extConvertFuncArg;
























            uint64_t                            m_valueStoreVersion = 0;

            ptr< CTMemoryManager >              m_memManager;

            // CIR funcs form a cyclic graph, since functions can
            // be recursive or mutually recursive.
            // Since they end up being stored in cir::Call in the form of values,







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
            ptr< OverloadSet >                  m_extLowerConstantForRuntime;
            ptr< OverloadSet >                  m_extLowerTypeForVerification;
            ptr< OverloadSet >                  m_extLowerConstantForVerification;

            ptr< OverloadSet >                  m_extConvertFuncParam;
            ptr< OverloadSet >                  m_extConvertFuncArg;

            ptr< OverloadSet >                  m_extPoisonBuilder;

            ptr< OverloadSet >                  m_extBuilderAllowsVarDecl;
            ptr< OverloadSet >                  m_extGetCFG;

            ptr< OverloadSet >                  m_extGetBreakableScopeLevels;
            ptr< OverloadSet >                  m_extGetContinuableScopeLevels;

            ptr< OverloadSet >                  m_extPushLifetimeScope;
            ptr< OverloadSet >                  m_extPopLifetimeScope;
            ptr< OverloadSet >                  m_extPushBreakableScope;
            ptr< OverloadSet >                  m_extPopBreakableScope;
            ptr< OverloadSet >                  m_extPushContinuableScope;
            ptr< OverloadSet >                  m_extPopContinuableScope;

            ptr< OverloadSet >                  m_extPushLiveValue;
            ptr< OverloadSet >                  m_extExtendValueLifetime;

            ptr< OverloadSet >                  m_extDestroyAndPopCurrentLifetimeScopeValues;
            ptr< OverloadSet >                  m_extDestroyAllLiveValues;
            ptr< OverloadSet >                  m_extDestroyAllLiveValuesFromBreakScope;
            ptr< OverloadSet >                  m_extDestroyAllLiveValuesFromContinueScope;

            uint64_t                            m_valueStoreVersion = 0;

            ptr< CTMemoryManager >              m_memManager;

            // CIR funcs form a cyclic graph, since functions can
            // be recursive or mutually recursive.
            // Since they end up being stored in cir::Call in the form of values,
Changes to bs/sema/meson.build.
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    'inv-ruleset.cpp',
    'invocation.cpp',

    'tpl-ruleset.cpp',
    'template.cpp',

    'overloadset.cpp',
    'codebuilder.cpp',

    'lower.cpp',

    include_directories: bsinc,
    dependencies: [fmt_dep, tracy_dep]
)








<







16
17
18
19
20
21
22

23
24
25
26
27
28
29
    'inv-ruleset.cpp',
    'invocation.cpp',

    'tpl-ruleset.cpp',
    'template.cpp',

    'overloadset.cpp',


    'lower.cpp',

    include_directories: bsinc,
    dependencies: [fmt_dep, tracy_dep]
)

Changes to bs/sema/sema.h.
40
41
42
43
44
45
46
47
48
49
50
51
52
#include "inv-ruleset.h"
#include "invocation.h"

#include "tpl-ruleset.h"
#include "template.h"

#include "overloadset.h"
#include "codebuilder.h"

#include "tctrie.inl"
#include "tctrie-typecheck.inl"

#endif







<





40
41
42
43
44
45
46

47
48
49
50
51
#include "inv-ruleset.h"
#include "invocation.h"

#include "tpl-ruleset.h"
#include "template.h"

#include "overloadset.h"


#include "tctrie.inl"
#include "tctrie-typecheck.inl"

#endif
Changes to bs/verify/helpers.inl.
1
2
3
4
5
6
7
8
9
10
11
12
#ifndef GOOSE_HELPERS_INL
#define GOOSE_HELPERS_INL

#include "builtins/helpers.h"

namespace goose::verify
{
    template< typename F >
    void ForEachPredicate( Builder& b, const Term& t, const z3::expr& valExpr, F&& func )
    {
        auto type = *EIRToValue( t );




<
<







1
2
3


4
5
6
7
8
9
10
#ifndef GOOSE_HELPERS_INL
#define GOOSE_HELPERS_INL



namespace goose::verify
{
    template< typename F >
    void ForEachPredicate( Builder& b, const Term& t, const z3::expr& valExpr, F&& func )
    {
        auto type = *EIRToValue( t );

Changes to lib/prelude/ref_verification.g0.
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

    Value rhsExpr
    if p2ok
        rhsExpr = BuildConjunction( pred2 )
    else
        rhsExpr = WrapValue( true )

    CodeBuilder cb
    if !ContextGetCodeBuilder( c, cb )
        return

    CFG cfg
    if !CodeBuilderGetCFG( cb, cfg )
        return

    BasicBlock bb
    if !GetCFGCurrentBasicBlock( cfg, bb )
        return

    var cond = MkValue( ValueToEIR( WrapValue( bool ) ), MkInstr( InstrOpCodeEq, lhsExpr, rhsExpr ) )







<
<
<
<

|







39
40
41
42
43
44
45




46
47
48
49
50
51
52
53
54

    Value rhsExpr
    if p2ok
        rhsExpr = BuildConjunction( pred2 )
    else
        rhsExpr = WrapValue( true )





    CFG cfg
    if !ContextGetCFG( c, cfg )
        return

    BasicBlock bb
    if !GetCFGCurrentBasicBlock( cfg, bb )
        return

    var cond = MkValue( ValueToEIR( WrapValue( bool ) ), MkInstr( InstrOpCodeEq, lhsExpr, rhsExpr ) )
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

void EmitPredicatesCheck( Context c, Value arg, Value refType )
{
    TypePredicates pred
    if !GetTypePredicates( c, refType, pred )
        return

    CodeBuilder cb
    if !ContextGetCodeBuilder( c, cb )
        return

    CFG cfg
    if !CodeBuilderGetCFG( cb, cfg )
        return

    BasicBlock bb
    if !GetCFGCurrentBasicBlock( cfg, bb )
        return

    RefTypeDesc rt







<
<
<
<

|







66
67
68
69
70
71
72




73
74
75
76
77
78
79
80
81

void EmitPredicatesCheck( Context c, Value arg, Value refType )
{
    TypePredicates pred
    if !GetTypePredicates( c, refType, pred )
        return





    CFG cfg
    if !ContextGetCFG( c, cfg )
        return

    BasicBlock bb
    if !GetCFGCurrentBasicBlock( cfg, bb )
        return

    RefTypeDesc rt