Goose  Check-in [9d81e31cfe]

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

Overview
Comment:
  • Fixed speculative compile-time execution causing compilation failures.
  • Fixed compile time execution budget underflowing, leading to the compiler going into an infinte loop while compiling an infinite loop (erm...)
  • Fixed more cases of missing propagation of poison states leading to spurious error messages.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 9d81e31cfebf9f9d6690200bb746d5f4c3b1fd7daf046bd264ca52584f4d2e36
User & Date: achavasse 2019-08-18 14:51:10.311
Context
2019-08-18
15:35
llr: added the break and continue terminators. check-in: b16e7c5933 user: achavasse tags: trunk
14:51
  • Fixed speculative compile-time execution causing compilation failures.
  • Fixed compile time execution budget underflowing, leading to the compiler going into an infinte loop while compiling an infinite loop (erm...)
  • Fixed more cases of missing propagation of poison states leading to spurious error messages.
check-in: 9d81e31cfe user: achavasse tags: trunk
00:54
  • Implemented the while statement.
  • Fixed a lexer issue that generated invalid locations at the very end of files.
check-in: 459ee84d6b user: achavasse tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to bs/builtins/api/compiler.cpp.
46
47
48
49
50
51
52



53
54
55
56
57
58
59
                parse::Parser p( r );

                auto cfg = make_shared< llr::CFG >();
                p.setCFG( cfg );
                p.setCurrentBB( cfg->entryBB() );

                p.parseSequence();




                if( !r->eos() )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                        r->getCurrentLocation(), "syntax error." );
                    return 0;
                }







>
>
>







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
                parse::Parser p( r );

                auto cfg = make_shared< llr::CFG >();
                p.setCFG( cfg );
                p.setCurrentBB( cfg->entryBB() );

                p.parseSequence();

                if( cfg->isPoisoned() )
                    return 0;

                if( !r->eos() )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                        r->getCurrentLocation(), "syntax error." );
                    return 0;
                }
121
122
123
124
125
126
127



128
129
130
131
132
133
134

                auto cfg = make_shared< llr::CFG >();
                p.setCFG( cfg );
                p.setCurrentBB( cfg->entryBB() );

                p.parseSequence();




                if( !r->eos() )
                {
                    pFuncLLR->setInvalid();
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        r->getCurrentLocation(), "syntax error." );
                    return ToValue( func ).setPoison();
                }







>
>
>







124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140

                auto cfg = make_shared< llr::CFG >();
                p.setCFG( cfg );
                p.setCurrentBB( cfg->entryBB() );

                p.parseSequence();

                if( cfg->isPoisoned() )
                    return PoisonValue();

                if( !r->eos() )
                {
                    pFuncLLR->setInvalid();
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        r->getCurrentLocation(), "syntax error." );
                    return ToValue( func ).setPoison();
                }
Changes to bs/builtins/statements/if.cpp.
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
    void SetupIfStmt( Env& e )
    {
        auto handleIf = []( Parser& p, uint32_t locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();

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

            auto pPrecBB = p.currentBB();








|







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
    void SetupIfStmt( Env& e )
    {
        auto handleIf = []( Parser& p, uint32_t locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();

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

            auto pPrecBB = p.currentBB();

52
53
54
55
56
57
58



59
60
61
62
63
64
65
                    case ValUnifyError::Ambiguous:
                        dm.emitSyntaxErrorMessage( condVal->locationId(), "ambiguous if condition bool conversion." );
                        break;
                }

                return false;
            }




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







>
>
>







52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
                    case ValUnifyError::Ambiguous:
                        dm.emitSyntaxErrorMessage( condVal->locationId(), "ambiguous if condition bool conversion." );
                        break;
                }

                return false;
            }

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

            auto pThenBB = ParseSubStatement( p, precedence::IfStmt );
            if( !pThenBB )
            {
                dm.emitSyntaxErrorMessage( p.resolver()->getCurrentLocation(), "expected a statement after the if condition.", 0 );
                return false;
            }
Changes to bs/builtins/statements/return.cpp.
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
    void SetupReturnStmt( Env& e )
    {
        auto handleReturn = []( Parser& p, uint32_t locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();

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

            const auto& context = p.resolver()->context();








|







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
    void SetupReturnStmt( Env& e )
    {
        auto handleReturn = []( Parser& p, uint32_t locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();

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

            const auto& context = p.resolver()->context();

58
59
60
61
62
63
64

65
66
67
68
69
70
71
72
73
74
75
76
77
                        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.
                p.emitTerminator( llr::Ret( PoisonValue() ) );

                return false;
            }

            p.emitTerminator( llr::Ret( get< Value >( converted ) ) );
            return true;
        };

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







>
|












58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
                        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.
                p.emitTerminator( llr::Ret( PoisonValue() ) );
                p.cfg()->poison();
                return true;
            }

            p.emitTerminator( llr::Ret( get< Value >( converted ) ) );
            return true;
        };

        Rule r( handleReturn );
        auto ruleVal = ToValue( move( r ) );
        auto ruleTerm = ValueToIRExpr( ruleVal );
        e.storeValue( AppendToVectorTerm( RootIdentity(), TSID( return ) ), ANYTERM( _ ), ruleTerm );
    }
}
Changes to bs/builtins/statements/while.cpp.
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
    void SetupWhileStmt( Env& e )
    {
        auto handleWhile = []( Parser& p, uint32_t locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();

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

            auto pPrecBB = p.currentBB();








|







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
    void SetupWhileStmt( Env& e )
    {
        auto handleWhile = []( Parser& p, uint32_t locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();

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

            auto pPrecBB = p.currentBB();

52
53
54
55
56
57
58



59
60
61
62
63
64
65
                    case ValUnifyError::Ambiguous:
                        dm.emitSyntaxErrorMessage( condVal->locationId(), "ambiguous while condition bool conversion." );
                        break;
                }

                return false;
            }




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

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







>
>
>







52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
                    case ValUnifyError::Ambiguous:
                        dm.emitSyntaxErrorMessage( condVal->locationId(), "ambiguous while condition bool conversion." );
                        break;
                }

                return false;
            }

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

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

            auto pBodyBB = ParseSubStatement( p, precedence::IfStmt );
            if( !pBodyBB )
            {
                dm.emitSyntaxErrorMessage( p.resolver()->getCurrentLocation(), "expected a statement after the while condition.", 0 );
Changes to bs/builtins/types/func/bfunc.inl.
103
104
105
106
107
108
109
110

111
112
113
114
115
116
117
    template< typename R, typename... T >
    template< typename F >
    auto Bridge< R ( T... ) >::WrapFunc( F&& func )
    {
        return [func]( const Term& argVec ) -> Value
        {
            auto params = Bridge< tuple< typename StripCustomPattern< T >::type... > >::FromVectorTerm( argVec );
            assert( params );


            if constexpr( is_void_v< builtins::remove_eager_t< R > > )
            {
                apply( func, *params );
                return Value( GetValueType< void >(), 0U );
            }
            else







|
>







103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
    template< typename R, typename... T >
    template< typename F >
    auto Bridge< R ( T... ) >::WrapFunc( F&& func )
    {
        return [func]( const Term& argVec ) -> Value
        {
            auto params = Bridge< tuple< typename StripCustomPattern< T >::type... > >::FromVectorTerm( argVec );
            if( !params )
                return PoisonValue();

            if constexpr( is_void_v< builtins::remove_eager_t< R > > )
            {
                apply( func, *params );
                return Value( GetValueType< void >(), 0U );
            }
            else
Changes to bs/builtins/types/func/func.cpp.
11
12
13
14
15
16
17













18
19
20
21
22
23
24
    {
        static auto pattern = ValueToIRExpr(
            Value( TypeType(), VEC( TSID( func ), MkHole( "llvmType"_sid ),
            MkHole( "_"_sid ), MkHole( "_"_sid ), MkHole( "_"_sid ) ) ) );

        return pattern;
    }














    bool IsFuncType( const Value& t )
    {
        auto result = Decompose( t.val(),
            Vec(
                Lit( "func"_sid ),
                Val< void* >(),







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







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
    {
        static auto pattern = ValueToIRExpr(
            Value( TypeType(), VEC( TSID( func ), MkHole( "llvmType"_sid ),
            MkHole( "_"_sid ), MkHole( "_"_sid ), MkHole( "_"_sid ) ) ) );

        return pattern;
    }

    bool IsExternalFunc( const Value& f )
    {
        if( !f.isConstant() )
            return false;

        // Try to decode it as an external func
        auto result = Decompose( f.val(),
            Val< string >()
        );

        return !!result;
    }

    bool IsFuncType( const Value& t )
    {
        auto result = Decompose( t.val(),
            Vec(
                Lit( "func"_sid ),
                Val< void* >(),
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
        auto cfg = make_shared< llr::CFG >();
        p.setCFG( cfg );
        p.setCurrentBB( cfg->entryBB() );

        bool success = p.parseBraceBlock();
        p.flushValue();

        if( !success )
        {
            // Fail silently here, because it may be a case of trying
            // to execute an invocation at compile time before falling
            // back to code generation.
            // It's up to our caller to catch the error here, if any.
            pFuncLLR->setInvalid();
            return false;







|







223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
        auto cfg = make_shared< llr::CFG >();
        p.setCFG( cfg );
        p.setCurrentBB( cfg->entryBB() );

        bool success = p.parseBraceBlock();
        p.flushValue();

        if( !success || cfg->isPoisoned() )
        {
            // Fail silently here, because it may be a case of trying
            // to execute an invocation at compile time before falling
            // back to code generation.
            // It's up to our caller to catch the error here, if any.
            pFuncLLR->setInvalid();
            return false;
Changes to bs/builtins/types/func/func.h.
55
56
57
58
59
60
61

62
63
64
65
66
67
68
        private:
            Term m_domain;
            Term m_returnType;
            Term m_params;
            llvm::FunctionType* m_pllvmType = nullptr;
    };


    extern bool IsFuncType( const Value& t );

    class Func
    {
        public:
            template< typename T, typename B >
            Func( T&& funcType, B&& toks, llr::Func* llr ) :







>







55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
        private:
            Term m_domain;
            Term m_returnType;
            Term m_params;
            llvm::FunctionType* m_pllvmType = nullptr;
    };

    extern bool IsExternalFunc( const Value& f );
    extern bool IsFuncType( const Value& t );

    class Func
    {
        public:
            template< typename T, typename B >
            Func( T&& funcType, B&& toks, llr::Func* llr ) :
Changes to bs/builtins/types/func/unify.cpp.
36
37
38
39
40
41
42



43
44
45
46
47
48
49
            auto wrapped = WrapWithPostprocFunc( rhs, [rhsVal]( const Term& t, const UnificationContext& uc )
                -> optional< Term >
            {
                DiagnosticsContext dc( 0, true );
                VerbosityContext vc( Verbosity::Normal, true );

                auto func = CompileFunc( uc.context(), rhsVal );



                return ValueToIRExpr( func );
            } );

            co_yield { move( wrapped ), uc };
        } );

        // tfunc type param / func arg







>
>
>







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
            auto wrapped = WrapWithPostprocFunc( rhs, [rhsVal]( const Term& t, const UnificationContext& uc )
                -> optional< Term >
            {
                DiagnosticsContext dc( 0, true );
                VerbosityContext vc( Verbosity::Normal, true );

                auto func = CompileFunc( uc.context(), rhsVal );
                if( func.isPoison() )
                    return nullopt;

                return ValueToIRExpr( func );
            } );

            co_yield { move( wrapped ), uc };
        } );

        // tfunc type param / func arg
89
90
91
92
93
94
95



96
97
98
99
100
101
102
103
                auto wrapped = WrapWithPostprocFunc( s, [rhsVal]( const Term& t, const UnificationContext& uc )
                    -> optional< Term >
                {
                    DiagnosticsContext dc( 0, true );
                    VerbosityContext vc( Verbosity::Normal, true );

                    auto func = CompileFunc( uc.context(), rhsVal );



                    return ValueToIRExpr( func );
                } );

                co_yield { move( wrapped ), uc };
            }
        } );
    }
}







>
>
>








92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
                auto wrapped = WrapWithPostprocFunc( s, [rhsVal]( const Term& t, const UnificationContext& uc )
                    -> optional< Term >
                {
                    DiagnosticsContext dc( 0, true );
                    VerbosityContext vc( Verbosity::Normal, true );

                    auto func = CompileFunc( uc.context(), rhsVal );
                    if( func.isPoison() )
                        return nullopt;

                    return ValueToIRExpr( func );
                } );

                co_yield { move( wrapped ), uc };
            }
        } );
    }
}
Changes to bs/builtins/types/overloadset/unify.cpp.
73
74
75
76
77
78
79




80
81
82
83
84
85
86
                auto overload = ovl;
                auto wrapped = WrapWithPostprocFunc( s,
                    [overload,localNSId,rhsLocId]( const Term& t, UnificationContext& uc ) -> optional< Term >
                    {
                        auto localC = uc;
                        localC.setRHSNamespaceIndex( localNSId );
                        auto func = overload.pInvRule->prepareFunc( uc.context(), rhsLocId, *overload.callee, t, localC.flip() );




                        return ValueToIRExpr( move( func ) );
                    } );

                co_yield { move( wrapped ), uc };
            }

            uc.flip();







>
>
>
>







73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
                auto overload = ovl;
                auto wrapped = WrapWithPostprocFunc( s,
                    [overload,localNSId,rhsLocId]( const Term& t, UnificationContext& uc ) -> optional< Term >
                    {
                        auto localC = uc;
                        localC.setRHSNamespaceIndex( localNSId );
                        auto func = overload.pInvRule->prepareFunc( uc.context(), rhsLocId, *overload.callee, t, localC.flip() );

                        if( func.isPoison() )
                            return nullopt;

                        return ValueToIRExpr( move( func ) );
                    } );

                co_yield { move( wrapped ), uc };
            }

            uc.flip();
147
148
149
150
151
152
153




154
155
156
157
158
159
160
161
162
163
164
                auto overload = ovl;
                auto wrapped = WrapWithPostprocFunc( s,
                    [overload,localNSId,rhsLocId]( const Term& t, UnificationContext& uc ) -> optional< Term >
                    {
                        auto localC = uc;
                        localC.setRHSNamespaceIndex( localNSId );
                        auto func = overload.pInvRule->prepareFunc( uc.context(), rhsLocId, *overload.callee, t, localC.flip() );




                        return ValueToIRExpr( move( func ) );
                    } );

                co_yield { move( wrapped ), uc };
            }

            uc.flip();
            uc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
        } );
    }
}







>
>
>
>











151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
                auto overload = ovl;
                auto wrapped = WrapWithPostprocFunc( s,
                    [overload,localNSId,rhsLocId]( const Term& t, UnificationContext& uc ) -> optional< Term >
                    {
                        auto localC = uc;
                        localC.setRHSNamespaceIndex( localNSId );
                        auto func = overload.pInvRule->prepareFunc( uc.context(), rhsLocId, *overload.callee, t, localC.flip() );

                        if( func.isPoison() )
                            return nullopt;

                        return ValueToIRExpr( move( func ) );
                    } );

                co_yield { move( wrapped ), uc };
            }

            uc.flip();
            uc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
        } );
    }
}
Changes to bs/builtins/types/template/instantiate.cpp.
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
                } );

                instanceFunc = ToValue( func );

                // TODO: better description including the function's name
                DiagnosticsContext dc( callee.locationId(), "In the template function declared here.", false );

                auto instFunc = CompileFunc( c, instanceFunc );
                c.env()->storeValue( instanceIdentity, ANYTERM( _ ), ValueToIRExpr( instFunc ) );
                break;
            }

            case Env::Status::AmbiguousMatch:
                DiagnosticsManager::GetInstance().emitErrorMessage( callee.locationId(),
                    "unexpected ambiguous match when looking up a template function instance." );
                return PoisonValue();







|
|







63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
                } );

                instanceFunc = ToValue( func );

                // TODO: better description including the function's name
                DiagnosticsContext dc( callee.locationId(), "In the template function declared here.", false );

                instanceFunc = CompileFunc( c, instanceFunc );
                c.env()->storeValue( instanceIdentity, ANYTERM( _ ), ValueToIRExpr( instanceFunc ) );
                break;
            }

            case Env::Status::AmbiguousMatch:
                DiagnosticsManager::GetInstance().emitErrorMessage( callee.locationId(),
                    "unexpected ambiguous match when looking up a template function instance." );
                return PoisonValue();
Changes to bs/builtins/types/template/unify.cpp.
58
59
60
61
62
63
64




65
66
67
68
69
70
71
                {
                    auto localC = uc;
                    localC.setRHSNamespaceIndex( localNSId );

                    DiagnosticsContext dc( rhsVal.locationId(), rhsVal.locationId() ?  "Instantiated here." : "", false );

                    auto ifunc = InstantiateTFunc( uc.context(), rhsVal, t, localC.flip() );




                    return ValueToIRExpr( ifunc );
                } );

                co_yield { move( wrapped ), uc };
            }

            uc.setRHSNamespaceIndex( savedRHSNamespaceIndex );







>
>
>
>







58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
                {
                    auto localC = uc;
                    localC.setRHSNamespaceIndex( localNSId );

                    DiagnosticsContext dc( rhsVal.locationId(), rhsVal.locationId() ?  "Instantiated here." : "", false );

                    auto ifunc = InstantiateTFunc( uc.context(), rhsVal, t, localC.flip() );

                    if( ifunc.isPoison() )
                        return nullopt;

                    return ValueToIRExpr( ifunc );
                } );

                co_yield { move( wrapped ), uc };
            }

            uc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
117
118
119
120
121
122
123




124
125
126
127
128
129
130
131
132
133
                {
                    auto localC = uc;
                    localC.setRHSNamespaceIndex( localNSId );

                    DiagnosticsContext dc( rhsVal.locationId(), rhsVal.locationId() ?  "Instantiated here." : "", false );

                    auto ifunc = InstantiateTFunc( uc.context(), rhsVal, t, localC.flip() );




                    return ValueToIRExpr( ifunc );
                } );

                co_yield { move( wrapped ), uc };
            }

            uc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
        } );
    }
}







>
>
>
>










121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
                {
                    auto localC = uc;
                    localC.setRHSNamespaceIndex( localNSId );

                    DiagnosticsContext dc( rhsVal.locationId(), rhsVal.locationId() ?  "Instantiated here." : "", false );

                    auto ifunc = InstantiateTFunc( uc.context(), rhsVal, t, localC.flip() );

                    if( ifunc.isPoison() )
                        return nullopt;

                    return ValueToIRExpr( ifunc );
                } );

                co_yield { move( wrapped ), uc };
            }

            uc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
        } );
    }
}
Changes to bs/builtins/types/tuple/tuple.inl.
76
77
78
79
80
81
82
83
84
85
86
87





88





89
90
91
92
93
94
95
    Value Bridge< tuple< T... > >::ToValue( const tuple< T... >& x )
    {
        auto val = BuildTupleValue( x, index_sequence_for< T... >() );
        return Value( Type(), move( val ) );
    }

    template< typename TT, size_t... I >
    auto BuildTupleFromValueVec( const Vector& vec, index_sequence< I... > )
    {
        if constexpr( sizeof... ( I ) == 0 )
            return TT();
        else





            return TT( move( *FromValue< tuple_element_t< I, TT > >( *ValueFromIRExpr( vec[I] ) ) )... );





    }

    template< typename... T >
    optional< tuple< T... > > Bridge< tuple< T... > >::FromVectorTerm( const Term& v )
    {
        const auto& vec = *get< pvec >( v );
        return BuildTupleFromValueVec< tuple< T... > >( vec, index_sequence_for< T... >() );







|




>
>
>
>
>
|
>
>
>
>
>







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
    Value Bridge< tuple< T... > >::ToValue( const tuple< T... >& x )
    {
        auto val = BuildTupleValue( x, index_sequence_for< T... >() );
        return Value( Type(), move( val ) );
    }

    template< typename TT, size_t... I >
    optional< TT > BuildTupleFromValueVec( const Vector& vec, index_sequence< I... > )
    {
        if constexpr( sizeof... ( I ) == 0 )
            return TT();
        else
        {
            auto values = make_tuple( ValueFromIRExpr( vec[I] )... );
            if( ( !get< I >( values ) || ... ) )
                return nullopt;

            auto items = make_tuple( FromValue< tuple_element_t< I, TT > >( *get< I >( values ) )... );
            if( ( !get< I >( items ) || ... ) )
                return nullopt;

            return TT( move( *get< I >( items ) )... );
        }
    }

    template< typename... T >
    optional< tuple< T... > > Bridge< tuple< T... > >::FromVectorTerm( const Term& v )
    {
        const auto& vec = *get< pvec >( v );
        return BuildTupleFromValueVec< tuple< T... > >( vec, index_sequence_for< T... >() );
Changes to bs/compiler.cpp.
57
58
59
60
61
62
63



64
65
66
67
68
69
70
    auto cfg = make_shared< llr::CFG >();
    p.setCFG( cfg );
    p.setCurrentBB( cfg->entryBB() );

    VerbosityContext vc( Verbosity::Normal, true );

    p.parseSequence();




    if( !r->eos() )
    {
        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
            r->getCurrentLocation(), "syntax error." );
        return 0;
    }







>
>
>







57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
    auto cfg = make_shared< llr::CFG >();
    p.setCFG( cfg );
    p.setCurrentBB( cfg->entryBB() );

    VerbosityContext vc( Verbosity::Normal, true );

    p.parseSequence();

    if( cfg->isPoisoned() )
        return 0;

    if( !r->eos() )
    {
        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
            r->getCurrentLocation(), "syntax error." );
        return 0;
    }
Changes to bs/diagnostics/renderer.cpp.
176
177
178
179
180
181
182

183
184
185
186
187
188
189

        // If we flush a context that had no location,
        // just print it as is.
        if( m_pendingContextFilename.empty() )
        {
            applyColor( m_pendingContextMessageColor );
            m_output << m_pendingContextMessage << '\n';


            m_pendingContextFilename.clear();
            m_pendingContextMessage.clear();
            m_pendingContextMessageColor = nullptr;
            m_pendingCaret = false;
            m_pendingHighlightSpans.clear();
            return;







>







176
177
178
179
180
181
182
183
184
185
186
187
188
189
190

        // If we flush a context that had no location,
        // just print it as is.
        if( m_pendingContextFilename.empty() )
        {
            applyColor( m_pendingContextMessageColor );
            m_output << m_pendingContextMessage << '\n';
            applyColor( Colors::Reset );

            m_pendingContextFilename.clear();
            m_pendingContextMessage.clear();
            m_pendingContextMessageColor = nullptr;
            m_pendingCaret = false;
            m_pendingHighlightSpans.clear();
            return;
Changes to bs/execute/eval.cpp.
14
15
16
17
18
19
20



21
22
23
24
25
26
27
28


29
30
31
32
33
34
35

        if( !v.isConstant() )
        {
            const auto& llr = val.llr();
            if( !llr )
                return v.setPoison();




            auto result = vm.execute( *llr );

            // Execution may fail: there are some cases when we can't really
            // be sure that eager evaluation is possible until we actually try.
            // In this case we forget about the eager evaluation and return the
            //value as is.
            if( !result )
                return v;



            v = move( *result );
            v.setLocationId( val.locationId() );
        }

        return v;
    }







>
>
>





|
|

>
>







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

        if( !v.isConstant() )
        {
            const auto& llr = val.llr();
            if( !llr )
                return v.setPoison();

            VerbosityContext vc( Verbosity::Silent );
            execute::ExecutionBudgetGuard ebg;

            auto result = vm.execute( *llr );

            // Execution may fail: there are some cases when we can't really
            // be sure that eager evaluation is possible until we actually try.
            // In this case we forget about the eager evaluation and return the
            // value as is.
            if( !result || result->isPoison() )
                return v;

            ebg.commit();

            v = move( *result );
            v.setLocationId( val.locationId() );
        }

        return v;
    }
Changes to bs/execute/vm.cpp.
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
optional< Value > VM::execute( const ptr< CFG >& ic )
{
    return execute( *ic );
}

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



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

    auto func = Evaluate( call.func(), *this );
    if( func.isPoison() || !func.isConstant() )



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








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

    if( IsBuiltinFunc( func ) )
    {
        auto newVec = vec.transform( [&]( auto&& x ) -> optional< Term >







|






>
>




|
>
>
>





>
>
>
>
>
>
>







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
optional< Value > VM::execute( const ptr< CFG >& ic )
{
    return execute( *ic );
}

optional< Value > VM::execute( const llr::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::SilentLocally );

    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 ) )
    {
        auto newVec = vec.transform( [&]( auto&& x ) -> optional< Term >
88
89
90
91
92
93
94



95
96
97
98
99
100
101
102

            if( !newVal.isConstant() )
                return ValueToIRExpr( PoisonValue() );

            return ValueToIRExpr( newVal );
        } );




        if( !newVec || poisoned )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( 0,
                "Execute: args evaluation failed." );
            return PoisonValue();
        }

        return ExecuteBuiltinFuncCall( func, TERM( newVec ) );







>
>
>
|







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

            if( !newVal.isConstant() )
                return ValueToIRExpr( PoisonValue() );

            return ValueToIRExpr( newVal );
        } );

        if( poisoned )
            return PoisonValue();

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

        return ExecuteBuiltinFuncCall( func, TERM( newVec ) );
114
115
116
117
118
119
120
121



122
123
124
125
126
127
128

    for( auto&& a : vec.terms() )
    {
        auto val = ValueFromIRExpr( a );
        assert( val );

        auto newVal = Evaluate( *val, *this );
        if( newVal.isPoison() || !newVal.isConstant() )



        {
            DiagnosticsManager::GetInstance().emitErrorMessage( 0,
                "Execute: args evaluation failed." );
            return PoisonValue();
        }

        args.emplace_back( move( newVal ) );







|
>
>
>







129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146

    for( auto&& a : vec.terms() )
    {
        auto val = ValueFromIRExpr( a );
        assert( val );

        auto newVal = Evaluate( *val, *this );
        if( newVal.isPoison()  )
            return PoisonValue();

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

        args.emplace_back( move( newVal ) );
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
optional< Value > VM::execute( const llr::LoadConstInt& lci )
{
    return ToValue( lci.value() );
}

ptr< BasicBlock > VM::execute( const llr::Branch& b )
{
    if( !( --ms_remainingBranchInstExecutions ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "Execute: compilation time execution budget exceeded." );
        m_frame.setRetVal( PoisonValue() );
        return nullptr;
    }


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

ptr< BasicBlock > VM::execute( const llr::CondBranch& cb )
{
    if( !( --ms_remainingBranchInstExecutions ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "Execute: compilation time execution budget exceeded." );
        m_frame.setRetVal( PoisonValue() );
        return nullptr;
    }



    auto cond = Evaluate( cb.cond(), *this );
    if( cond.isPoison() || !cond.isConstant() )



    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "Execute: branch condition evaluation failed." );
        m_frame.setRetVal( PoisonValue() );
        return nullptr;
    }

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

    return cb.falseDest().lock();
}







|







>





|







>
>

|
>
>
>












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
268
269
optional< Value > VM::execute( const llr::LoadConstInt& lci )
{
    return ToValue( lci.value() );
}

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

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

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

    --ms_remainingBranchInstExecutions;

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

    if( !cond.isConstant() )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "Execute: branch condition evaluation failed." );
        m_frame.setRetVal( PoisonValue() );
        return nullptr;
    }

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

    return cb.falseDest().lock();
}
Changes to bs/execute/vm.h.
1
2
3
4
5
6
7


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

namespace empathy::execute
{
    class VM
    {


        public:
            optional< Value > execute( CFG& cfg );
            optional< Value > execute( ptr< BasicBlock > bb );

            optional< Value > execute( const llr::Instruction& instr );
            optional< Value > execute( const ptr< CFG >& ic );
            optional< Value > execute( const llr::Call& call );







>
>







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

namespace empathy::execute
{
    class VM
    {
        friend class ExecutionBudgetGuard;

        public:
            optional< Value > execute( CFG& cfg );
            optional< Value > execute( ptr< BasicBlock > bb );

            optional< Value > execute( const llr::Instruction& instr );
            optional< Value > execute( const ptr< CFG >& ic );
            optional< Value > execute( const llr::Call& call );
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
            optional< Value > execute( const llr::SGE& bo );
            optional< Value > execute( const llr::SLT& bo );
            optional< Value > execute( const llr::SLE& bo );

            template< typename T >
            optional< Value > execute( const T& )
            {
                return nullopt;
            }

            ptr< BasicBlock > execute( const llr::Terminator& terminator );
            ptr< BasicBlock > execute( const llr::Ret& r );
            ptr< BasicBlock > execute( const llr::Branch& b );
            ptr< BasicBlock > execute( const llr::CondBranch& cb );








|







48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
            optional< Value > execute( const llr::SGE& bo );
            optional< Value > execute( const llr::SLT& bo );
            optional< Value > execute( const llr::SLE& bo );

            template< typename T >
            optional< Value > execute( const T& )
            {
                return PoisonValue();
            }

            ptr< BasicBlock > execute( const llr::Terminator& terminator );
            ptr< BasicBlock > execute( const llr::Ret& r );
            ptr< BasicBlock > execute( const llr::Branch& b );
            ptr< BasicBlock > execute( const llr::CondBranch& cb );

79
80
81
82
83
84
85























86
87
88
            ptr< BasicBlock > m_pPreviousBB;

            // To avoid compile time code to get stuck, we have a limitation on the
            // total number of call and loop instructions that we are willing to execute
            // before giving up.
            static uint32_t ms_remainingBranchInstExecutions;
    };























}

#endif







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



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
            ptr< BasicBlock > m_pPreviousBB;

            // To avoid compile time code to get stuck, we have a limitation on the
            // total number of call and loop instructions that we are willing to execute
            // before giving up.
            static uint32_t ms_remainingBranchInstExecutions;
    };

    // To avoid using up the execution budget on failed non mandatory compile time execution attempts
    class ExecutionBudgetGuard
    {
        public:
            ExecutionBudgetGuard() :
                m_savedBudget( VM::ms_remainingBranchInstExecutions )
            {}

            ~ExecutionBudgetGuard()
            {
                VM::ms_remainingBranchInstExecutions = m_savedBudget;
            }

            // If the budget use should be kept, call this before destruction.
            void commit()
            {
                m_savedBudget = VM::ms_remainingBranchInstExecutions;
            }

        private:
            uint32_t m_savedBudget = 0;
    };
}

#endif
Changes to bs/llr/call.cpp.
1
2
3
4
5
6
7
8
9
10
11



12
13
14
15
16
17
18
#include "llr/llr.h"
#include "builtins/builtins.h"

using namespace empathy::builtins;

namespace empathy::llr
{
    bool Call::canBeExecuted() const
    {
        if( !IsValueConstantOrExecutable( m_func ) )
            return false;




        bool argsCanBeExecuted = true;
        ForEachInVectorTerm( m_args, [&]( auto&& arg )
        {
            if( !IsValueConstantOrExecutable( *ValueFromIRExpr( arg ) ) )
            {
                argsCanBeExecuted = false;











>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include "llr/llr.h"
#include "builtins/builtins.h"

using namespace empathy::builtins;

namespace empathy::llr
{
    bool Call::canBeExecuted() const
    {
        if( !IsValueConstantOrExecutable( m_func ) )
            return false;

        if( IsExternalFunc( m_func ) )
            return false;

        bool argsCanBeExecuted = true;
        ForEachInVectorTerm( m_args, [&]( auto&& arg )
        {
            if( !IsValueConstantOrExecutable( *ValueFromIRExpr( arg ) ) )
            {
                argsCanBeExecuted = false;
Changes to bs/llr/cfg.h.
1
2
3
4
5
6
7
8
9
10
11



12
13
14
15
16
17
18
#ifndef EMPATHY_LLR_CFG_H
#define EMPATHY_LLR_CFG_H

namespace empathy::llr
{
    class BasicBlock;

    class CFG
    {
        public:
            CFG();




            const auto& entryBB() const { return m_basicBlocks.front(); }
            const auto& lastBB() const { return m_basicBlocks.back(); }

            auto count() const { return m_basicBlocks.size(); }
            auto uniqueId() const { return m_uniqueId; }












>
>
>







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

namespace empathy::llr
{
    class BasicBlock;

    class CFG
    {
        public:
            CFG();

            bool isPoisoned() const { return m_poisoned; }
            void poison() { m_poisoned = true; }

            const auto& entryBB() const { return m_basicBlocks.front(); }
            const auto& lastBB() const { return m_basicBlocks.back(); }

            auto count() const { return m_basicBlocks.size(); }
            auto uniqueId() const { return m_uniqueId; }

47
48
49
50
51
52
53


54
55
56
57
58
59
            vector< ptr< llr::BasicBlock > > m_basicBlocks;

            // The unique identifier of this CFG.
            uint32_t m_uniqueId = ms_nextUniqueId++;

            // The number of temporary indices used by this CFG.
            uint32_t m_temporariesCount = 0;



            static uint32_t ms_nextUniqueId;
    };
}

#endif







>
>






50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
            vector< ptr< llr::BasicBlock > > m_basicBlocks;

            // The unique identifier of this CFG.
            uint32_t m_uniqueId = ms_nextUniqueId++;

            // The number of temporary indices used by this CFG.
            uint32_t m_temporariesCount = 0;

            bool m_poisoned = false;

            static uint32_t ms_nextUniqueId;
    };
}

#endif
Changes to bs/parse/parser.inl.
33
34
35
36
37
38
39

40



41
42
43
44
45
46
47
        m_currentBB->setTerminator( forward< T >( terminator ) );
    }

    template< typename V >
    void Parser::pushValue( V&& val )
    {
        if( val.isPoison() )

            DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::SilentLocally );




        flushValue();

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







>

>
>
>







33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
        m_currentBB->setTerminator( forward< T >( terminator ) );
    }

    template< typename V >
    void Parser::pushValue( V&& val )
    {
        if( val.isPoison() )
        {
            DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::SilentLocally );
            if( cfg() )
                cfg()->poison();
        }

        flushValue();

        if( val.isConstant() || !llr::CanValueBeEagerlyEvaluated( val ) )
        {
            m_lastValue = forward< V >( val );
            return;
Changes to bs/parse/rule-helpers.inl.
23
24
25
26
27
28
29






30
31
32
33
34
35
36
                if( !rightVal )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                        "expected an expression.", 0 );
                    return false;
                }
            }







            DiagnosticsContext dc( locationId, false );

            auto result = func( p, *rightVal );
            p.pushValue( move( result ) );
            return true;
        } );







>
>
>
>
>
>







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
                if( !rightVal )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                        "expected an expression.", 0 );
                    return false;
                }
            }

            if( rightVal->isPoison() )
            {
                p.pushValue( PoisonValue() );
                return true;
            }

            DiagnosticsContext dc( locationId, false );

            auto result = func( p, *rightVal );
            p.pushValue( move( result ) );
            return true;
        } );
46
47
48
49
50
51
52






53
54
55
56
57
58
59
                auto leftVal = p.popValue();
                if( !leftVal )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                        "expected a value operand.", 0 );
                    return false;
                }







                DiagnosticsContext dc( locationId, false );

                auto result = func( p, *leftVal );
                p.pushValue( move( result ) );
                return true;
            } );







>
>
>
>
>
>







52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
                auto leftVal = p.popValue();
                if( !leftVal )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                        "expected a value operand.", 0 );
                    return false;
                }

                if( leftVal->isPoison() )
                {
                    p.pushValue( PoisonValue() );
                    return true;
                }

                DiagnosticsContext dc( locationId, false );

                auto result = func( p, *leftVal );
                p.pushValue( move( result ) );
                return true;
            } );
88
89
90
91
92
93
94






95
96
97
98
99
100
101
                    if( !rightVal )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                            "expected an expression.", 0 );
                        return false;
                    }
                }







                DiagnosticsContext dcl( leftVal->locationId(), false );
                DiagnosticsContext dcr( rightVal->locationId(), false );

                DiagnosticsContext dc( locationId, false );

                auto result = func( p, *leftVal, *rightVal );







>
>
>
>
>
>







100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
                    if( !rightVal )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                            "expected an expression.", 0 );
                        return false;
                    }
                }

                if( leftVal->isPoison() || rightVal->isPoison() )
                {
                    p.pushValue( PoisonValue() );
                    return true;
                }

                DiagnosticsContext dcl( leftVal->locationId(), false );
                DiagnosticsContext dcr( rightVal->locationId(), false );

                DiagnosticsContext dc( locationId, false );

                auto result = func( p, *leftVal, *rightVal );
134
135
136
137
138
139
140






141
142
143
144
145
146
147
                    if( !rightVal )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                            "expected an expression.", 0 );
                        return false;
                    }
                }







                DiagnosticsContext dcl( leftVal->locationId(), false );
                DiagnosticsContext dcr( rightVal->locationId(), false );

                DiagnosticsContext dc( locationId, false );

                auto result = func( p, *leftVal, *rightVal );







>
>
>
>
>
>







152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
                    if( !rightVal )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                            "expected an expression.", 0 );
                        return false;
                    }
                }

                if( leftVal->isPoison() || rightVal->isPoison() )
                {
                    p.pushValue( PoisonValue() );
                    return true;
                }

                DiagnosticsContext dcl( leftVal->locationId(), false );
                DiagnosticsContext dcr( rightVal->locationId(), false );

                DiagnosticsContext dc( locationId, false );

                auto result = func( p, *leftVal, *rightVal );
Changes to bs/sema/invocation.cpp.
1
2

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


namespace empathy::sema
{
    ptr< InvocationRule > GetInvocationRule( const Env& e, const Value& callee )
    {
        const auto& rules = e.invocationRuleSet()->rules();



>







1
2
3
4
5
6
7
8
9
10
#include "sema.h"
#include "builtins/builtins.h"
#include "execute/execute.h"

namespace empathy::sema
{
    ptr< InvocationRule > GetInvocationRule( const Env& e, const Value& callee )
    {
        const auto& rules = e.invocationRuleSet()->rules();

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
    bool CanBeInvoked( const Context& c, const Value& callee )
    {
        return !!GetInvocationRule( *c.env(), callee );
    }

    Value ResolveInvocation( const Context& c, const ptr< InvocationRule >& pInvRule, const Value& callee, const Value& args )
    {



        auto loc = Location::CreateSpanningLocation( callee.locationId(), args.locationId() );

        // If the current domain isn't compile time and the args are constant, attempt to resolve it as a compile time invocation
        // first.
        if( args.isConstant() && c.domain() != DomainCompileTime() && builtins::IsTupleConstant( args ) )
        {
            // This is a speculative attempt at compile time execution. It it doesn't work, we'll
            // just try to resolve the invocation in its originally intended domain.
            // So don't display any error message during the attempt.
            VerbosityContext vc( Verbosity::Silent );



            Context localC( c.env(), InjectDomainIntoIdentity( c.identity(), DomainCompileTime() ) );
            auto result = pInvRule->resolveInvocation( localC, loc, callee, args ).setLocationId( loc );
            if( !result.isPoison() )




                return result;

        }

        if( loc == ~0 )
            DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::SilentLocally );

        return pInvRule->resolveInvocation( c, loc, callee, args ).setLocationId( loc );
    }
}







>
>
>










>
>




>
>
>
>

>








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
    bool CanBeInvoked( const Context& c, const Value& callee )
    {
        return !!GetInvocationRule( *c.env(), callee );
    }

    Value ResolveInvocation( const Context& c, const ptr< InvocationRule >& pInvRule, const Value& callee, const Value& args )
    {
        if( callee.isPoison() || args.isPoison() )
            return PoisonValue();

        auto loc = Location::CreateSpanningLocation( callee.locationId(), args.locationId() );

        // If the current domain isn't compile time and the args are constant, attempt to resolve it as a compile time invocation
        // first.
        if( args.isConstant() && c.domain() != DomainCompileTime() && builtins::IsTupleConstant( args ) )
        {
            // This is a speculative attempt at compile time execution. It it doesn't work, we'll
            // just try to resolve the invocation in its originally intended domain.
            // So don't display any error message during the attempt.
            VerbosityContext vc( Verbosity::Silent );

            execute::ExecutionBudgetGuard ebg;

            Context localC( c.env(), InjectDomainIntoIdentity( c.identity(), DomainCompileTime() ) );
            auto result = pInvRule->resolveInvocation( localC, loc, callee, args ).setLocationId( loc );
            if( !result.isPoison() )
            {
                execute::VM vm;
                result = execute::Evaluate( result, vm );
                ebg.commit();
                return result;
            }
        }

        if( loc == ~0 )
            DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::SilentLocally );

        return pInvRule->resolveInvocation( c, loc, callee, args ).setLocationId( loc );
    }
}