Goose  Check-in [b64ea47f6b]

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

Overview
Comment:Clearly separate the type checking rules and the unification rules, instead of lumping them all together in a single set of patterns which is increasingly confusing.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: b64ea47f6b7383e3e195fa34c3f4361296ae990cd7394730a1ff14a0e82b3dce
User & Date: achavasse 2020-06-27 22:05:05.101
Context
2020-07-02
00:47
  • Added overloads to the comma operator to be able to construct tuples of references.
  • Fixed reference typechecking rules so that overloads taking values directly have priority over overloads taking references.
  • Fixed a template function expression parsing bug.
check-in: 04aea08600 user: achavasse tags: trunk
2020-06-27
22:05
Clearly separate the type checking rules and the unification rules, instead of lumping them all together in a single set of patterns which is increasingly confusing. check-in: b64ea47f6b user: achavasse tags: trunk
2020-06-26
23:34
Cleanup:
  • Removed the poorly thought out "domain" system that was intended to allow for different implementations of functions for runtime and for compilation time, which was adding an absurd amount of crap everywhere and should be unnecessary with the current planned approach for implementing data structures.
  • The using statement doesn't do lazy parsing anymore. Lazy parsing is better left to specific constructs that require them (such as function bodies and later on class/structs). This removes the only case of significant newline character in the language.
check-in: 568c366a36 user: achavasse tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to bs/builtins/helpers.cpp.
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
    }

    variant< Value, ValUnifyError > ConvertValueToType( const Context& c, const Value& val, const Term& type )
    {
        auto valTerm = ValueToIRExpr( val );
        auto paramPat = ParamPat( type );

        auto us = FindBestUnification( paramPat, valTerm, c );

        if( holds_alternative< NoUnification >( us ) )
            return ValUnifyError::NoSolution;

        if( holds_alternative< AmbiguousUnification >( us ) )
            return ValUnifyError::Ambiguous;

        auto&& [s,uc] = get< UniSol >( us );
        auto finalVal = ValueFromIRExpr( s );
        assert( finalVal );

        return *finalVal;
    }

    pvec ParseExpressionList( Parser& p, uint32_t precedence, const pvec& pVec )







|




|


|







44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
    }

    variant< Value, ValUnifyError > ConvertValueToType( const Context& c, const Value& val, const Term& type )
    {
        auto valTerm = ValueToIRExpr( val );
        auto paramPat = ParamPat( type );

        auto us = FindBestTyping( paramPat, valTerm, c );

        if( holds_alternative< NoUnification >( us ) )
            return ValUnifyError::NoSolution;

        if( holds_alternative< AmbiguousTypeCheck >( us ) )
            return ValUnifyError::Ambiguous;

        auto&& [s,tcc] = get< TCSol >( us );
        auto finalVal = ValueFromIRExpr( s );
        assert( finalVal );

        return *finalVal;
    }

    pvec ParseExpressionList( Parser& p, uint32_t precedence, const pvec& pVec )
Changes to bs/builtins/meson.build.
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
    'types/lower.cpp',

    'types/runtime/runtime.cpp',
    'types/runtime/basic.cpp',
    'types/runtime/pointer.cpp',
    'types/runtime/array.cpp',
    'types/runtime/record.cpp',
    'types/runtime/unify.cpp',
    'types/runtime/init.cpp',

    'types/tuple/tuple.cpp',
    'types/tuple/unify.cpp',
    'types/tuple/init.cpp',
    'types/tuple/destroy.cpp',
    'types/tuple/drop.cpp',
    'types/tuple/lower.cpp',

    'types/func/bfunc.cpp',
    'types/func/func.cpp',
    'types/func/build.cpp',
    'types/func/compile.cpp',
    'types/func/invoke.cpp',
    'types/func/unify.cpp',
    'types/func/lower.cpp',

    'types/intrinsic/intrinsic.cpp',

    'types/template/tvec.cpp',
    'types/template/tvar.cpp',
    'types/template/tdecl.cpp',
    'types/template/tnameddecl.cpp',
    'types/template/tfunctype.cpp',
    'types/template/tfunc.cpp',
    'types/template/build.cpp',
    'types/template/rules.cpp',
    'types/template/invoke.cpp',
    'types/template/instantiate.cpp',
    'types/template/unify.cpp',
    'types/template/uni-tdecl.cpp',

    'types/overloadset/overloadset.cpp',
    'types/overloadset/invoke.cpp',
    'types/overloadset/unify.cpp',
    'types/overloadset/helpers.cpp',

    'types/constrainedfunc/constrainedfunc.cpp',
    'types/constrainedfunc/invoke.cpp',
    'types/constrainedfunc/unify.cpp',

    'types/localvar/localvar.cpp',
    'types/localvar/unify.cpp',
    'types/localvar/drop.cpp',
    'types/localvar/invoke.cpp',

    'types/reference/reference.cpp',
    'types/reference/unify.cpp',

    'operators/semicolon.cpp',
    'operators/comma.cpp',
    'operators/dollar.cpp',
    'operators/logic.cpp',
    'operators/arith.cpp',
    'operators/comparison.cpp',







|



|










|














|
|



|




|


|




|







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
    'types/lower.cpp',

    'types/runtime/runtime.cpp',
    'types/runtime/basic.cpp',
    'types/runtime/pointer.cpp',
    'types/runtime/array.cpp',
    'types/runtime/record.cpp',
    'types/runtime/typecheck.cpp',
    'types/runtime/init.cpp',

    'types/tuple/tuple.cpp',
    'types/tuple/typecheck.cpp',
    'types/tuple/init.cpp',
    'types/tuple/destroy.cpp',
    'types/tuple/drop.cpp',
    'types/tuple/lower.cpp',

    'types/func/bfunc.cpp',
    'types/func/func.cpp',
    'types/func/build.cpp',
    'types/func/compile.cpp',
    'types/func/invoke.cpp',
    'types/func/typecheck.cpp',
    'types/func/lower.cpp',

    'types/intrinsic/intrinsic.cpp',

    'types/template/tvec.cpp',
    'types/template/tvar.cpp',
    'types/template/tdecl.cpp',
    'types/template/tnameddecl.cpp',
    'types/template/tfunctype.cpp',
    'types/template/tfunc.cpp',
    'types/template/build.cpp',
    'types/template/rules.cpp',
    'types/template/invoke.cpp',
    'types/template/instantiate.cpp',
    'types/template/typecheck.cpp',
    'types/template/tc-tdecl.cpp',

    'types/overloadset/overloadset.cpp',
    'types/overloadset/invoke.cpp',
    'types/overloadset/typecheck.cpp',
    'types/overloadset/helpers.cpp',

    'types/constrainedfunc/constrainedfunc.cpp',
    'types/constrainedfunc/invoke.cpp',
    'types/constrainedfunc/typecheck.cpp',

    'types/localvar/localvar.cpp',
    'types/localvar/typecheck.cpp',
    'types/localvar/drop.cpp',
    'types/localvar/invoke.cpp',

    'types/reference/reference.cpp',
    'types/reference/typecheck.cpp',

    'operators/semicolon.cpp',
    'operators/comma.cpp',
    'operators/dollar.cpp',
    'operators/logic.cpp',
    'operators/arith.cpp',
    'operators/comparison.cpp',
Changes to bs/builtins/types/constrainedfunc/constrainedfunc.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "builtins/builtins.h"

using namespace goose::builtins;

bool goose::builtins::IsConstrainedFunc( const Value& uc )
{
    return uc.type() == GetValueType< ConstrainedFunc >();
}

namespace goose::ir
{
    const Term& Bridge< builtins::ConstrainedFunc >::Type()
    {
        static auto type = ValueToIRExpr( Value( TypeType(), VEC( TSID( ct_type ), TSID( constrainedfunc ) ) ) );




|

|







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

using namespace goose::builtins;

bool goose::builtins::IsConstrainedFunc( const Value& tcc )
{
    return tcc.type() == GetValueType< ConstrainedFunc >();
}

namespace goose::ir
{
    const Term& Bridge< builtins::ConstrainedFunc >::Type()
    {
        static auto type = ValueToIRExpr( Value( TypeType(), VEC( TSID( ct_type ), TSID( constrainedfunc ) ) ) );
Changes to bs/builtins/types/constrainedfunc/constrainedfunc.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef GOOSE_BUILTINS_TYPES_CONSTRAINEDFUNC_H
#define GOOSE_BUILTINS_TYPES_CONSTRAINEDFUNC_H

namespace goose::builtins
{
    extern void SetupConstrainedFuncInvocationRule( Env& e );
    extern void SetupConstrainedFuncUnification( Env& e );

    class ConstrainedFunc
    {
        public:
            template< typename P, typename IR, typename F >
            ConstrainedFunc( P&& constraintPat, IR&& pInvRule, F&& func ) :
                m_constraintPat( forward< P >( constraintPat ) ),






|







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

namespace goose::builtins
{
    extern void SetupConstrainedFuncInvocationRule( Env& e );
    extern void SetupConstrainedFuncTypeChecking( Env& e );

    class ConstrainedFunc
    {
        public:
            template< typename P, typename IR, typename F >
            ConstrainedFunc( P&& constraintPat, IR&& pInvRule, F&& func ) :
                m_constraintPat( forward< P >( constraintPat ) ),
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

        private:
            Term                    m_constraintPat;
            ptr< InvocationRule >   m_pInvRule;
            Value                   m_func;
    };

    extern bool IsConstrainedFunc( const Value& uc );
}

namespace goose::ir
{
    template<>
    struct Bridge< builtins::ConstrainedFunc >
    {







|







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

        private:
            Term                    m_constraintPat;
            ptr< InvocationRule >   m_pInvRule;
            Value                   m_func;
    };

    extern bool IsConstrainedFunc( const Value& tcc );
}

namespace goose::ir
{
    template<>
    struct Bridge< builtins::ConstrainedFunc >
    {
Changes to bs/builtins/types/constrainedfunc/invoke.cpp.
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

                // Unify with the constraint pattern. We only care that there is at least one
                // solution, and if so, we defer the actual call resolution to the contained
                // function.
                auto cfunc = FromValue< ConstrainedFunc >( callee );
                assert( cfunc );

                UnificationContext uc( c );
                uc.setComplexity( GetComplexity( cfunc->constraintPat() ) + GetComplexity( callPat ) );

                for( auto&& [s, uc] : Unify( cfunc->constraintPat(), callPat, uc ) )
                {
                    if( Postprocess( s, uc ) )
                        return cfunc->invRule()->resolveInvocation( c, loc, cfunc->func(), args );
                }

                // TODO display details
                DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                    "function arguments mismatch." );
                return PoisonValue();







|
|

|

|







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

                // Unify with the constraint pattern. We only care that there is at least one
                // solution, and if so, we defer the actual call resolution to the contained
                // function.
                auto cfunc = FromValue< ConstrainedFunc >( callee );
                assert( cfunc );

                TypeCheckingContext tcc( c );
                tcc.setComplexity( GetComplexity( cfunc->constraintPat() ) + GetComplexity( callPat ) );

                for( auto&& [s, tcc] : TypeCheck( cfunc->constraintPat(), callPat, tcc ) )
                {
                    if( Postprocess( s, tcc ) )
                        return cfunc->invRule()->resolveInvocation( c, loc, cfunc->func(), args );
                }

                // TODO display details
                DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                    "function arguments mismatch." );
                return PoisonValue();
Name change from bs/builtins/types/constrainedfunc/unify.cpp to bs/builtins/types/constrainedfunc/typecheck.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
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::ir;

namespace goose::builtins
{
    void SetupConstrainedFuncUnification( Env& e )
    {
        auto funcTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( func ),
            ANYTERM( _ ), ANYTERM( _ ) ) ) );

        auto tFuncTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( texpr ), TSID( tfunc ),
            ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );

        // func type param / constrainedfunc arg
        e.unificationRuleSet()->addSymRule( URINFOS,

            ParamPat( move( funcTypePat ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                GetValueType< builtins::ConstrainedFunc >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
        {
            auto ldecomp = Decompose( lhs,
                Vec(
                    Lit( "value"_sid ),
                    SubTerm(),
                    SubTerm(),
                    SubTerm(),
                    Val< LocationId >()
                )
            );
            assert( ldecomp );

            auto&& [sort, type, val, locId] = *ldecomp;
            auto callPat = BuildCallPatternFromFuncType( *ValueFromIRExpr( type ) );

            auto rhsVal = *ValueFromIRExpr( rhs );
            auto cfunc = FromValue< ConstrainedFunc >( rhsVal );
            assert( cfunc );

            auto localC = uc;
            for( auto&& [s, uc] : Unify( callPat, cfunc->constraintPat(), localC ) )
            {
                if( Postprocess( s, uc ) )
                {
                    auto func = ValueToIRExpr( cfunc->func() );
                    co_yield Unify( lhs, func, uc );
                    co_return;
                }
            }
        } );

        // tfunc type param / constrainedfunc arg
        e.unificationRuleSet()->addAsymRule( URINFOS,

            ValueToIRExpr( ValuePattern( HOLE( "_"_sid ), move( tFuncTypePat ), HOLE( "_"_sid ) ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                GetValueType< builtins::ConstrainedFunc >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
        {
            auto ldecomp = Decompose( lhs,
                Vec(
                    Lit( "value"_sid ),
                    SubTerm(),
                    SubTerm(),
                    SubTerm(),
                    Val< LocationId >()
                )
            );
            assert( ldecomp );

            auto&& [sort, type, val, locId] = *ldecomp;
            auto callPat = BuildArgPatternFromTFuncType( uc.context(), *ValueFromIRExpr( type ) );
            assert( callPat );

            auto rhsVal = *ValueFromIRExpr( rhs );
            auto cfunc = FromValue< ConstrainedFunc >( rhsVal );
            assert( cfunc );

            auto localC = uc;
            auto g = Unify( *callPat, cfunc->constraintPat(), localC );
            auto it = g.begin();

            if( it != g.end() )
            {
                auto func = ValueToIRExpr( cfunc->func() );
                co_yield Unify( lhs, func, uc );


            }
        } );

        // constrainedfunc param / any arg:
        // Just yield the constrained func.
        //
        // This is because when we monomorphize a function that takes
        // a polymorphic function type, we turn the later into a
        // compile time constant. So it isn't really a parameter
        // in the resulting monomorphic function, and we just want
        // to ignore whatever parameter is being passed there
        e.unificationRuleSet()->addSymRule( URINFOS,

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                GetValueType< builtins::ConstrainedFunc >(),
                ANYTERM( _ ) ) ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                ANYTERM( _ ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
        {
            co_yield { lhs, uc };
        } );
    }
}







|








|








|



















|
|

|


|






|








|













|






|
|
<
>
|
|
|
|
>
>











|











|

|



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
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::ir;

namespace goose::builtins
{
    void SetupConstrainedFuncTypeChecking( Env& e )
    {
        auto funcTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( func ),
            ANYTERM( _ ), ANYTERM( _ ) ) ) );

        auto tFuncTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( texpr ), TSID( tfunc ),
            ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );

        // func type param / constrainedfunc arg
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ParamPat( move( funcTypePat ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                GetValueType< builtins::ConstrainedFunc >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            auto ldecomp = Decompose( lhs,
                Vec(
                    Lit( "value"_sid ),
                    SubTerm(),
                    SubTerm(),
                    SubTerm(),
                    Val< LocationId >()
                )
            );
            assert( ldecomp );

            auto&& [sort, type, val, locId] = *ldecomp;
            auto callPat = BuildCallPatternFromFuncType( *ValueFromIRExpr( type ) );

            auto rhsVal = *ValueFromIRExpr( rhs );
            auto cfunc = FromValue< ConstrainedFunc >( rhsVal );
            assert( cfunc );

            auto localC = tcc;
            for( auto&& [s, tcc] : TypeCheck( callPat, cfunc->constraintPat(), localC ) )
            {
                if( Postprocess( s, tcc ) )
                {
                    auto func = ValueToIRExpr( cfunc->func() );
                    co_yield TypeCheck( lhs, func, tcc );
                    co_return;
                }
            }
        } );

        // tfunc type param / constrainedfunc arg
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ValueToIRExpr( ValuePattern( HOLE( "_"_sid ), move( tFuncTypePat ), HOLE( "_"_sid ) ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                GetValueType< builtins::ConstrainedFunc >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            auto ldecomp = Decompose( lhs,
                Vec(
                    Lit( "value"_sid ),
                    SubTerm(),
                    SubTerm(),
                    SubTerm(),
                    Val< LocationId >()
                )
            );
            assert( ldecomp );

            auto&& [sort, type, val, locId] = *ldecomp;
            auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *ValueFromIRExpr( type ) );
            assert( callPat );

            auto rhsVal = *ValueFromIRExpr( rhs );
            auto cfunc = FromValue< ConstrainedFunc >( rhsVal );
            assert( cfunc );

            auto localC = tcc;
            for( auto&& [s, tcc] : TypeCheck( *callPat, cfunc->constraintPat(), localC ) )

            {
                if( Postprocess( s, tcc ) )
                {
                    auto func = ValueToIRExpr( cfunc->func() );
                    co_yield TypeCheck( lhs, func, tcc );
                    co_return;
                }
            }
        } );

        // constrainedfunc param / any arg:
        // Just yield the constrained func.
        //
        // This is because when we monomorphize a function that takes
        // a polymorphic function type, we turn the later into a
        // compile time constant. So it isn't really a parameter
        // in the resulting monomorphic function, and we just want
        // to ignore whatever parameter is being passed there
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                GetValueType< builtins::ConstrainedFunc >(),
                ANYTERM( _ ) ) ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                ANYTERM( _ ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            co_yield { lhs, tcc };
        } );
    }
}
Changes to bs/builtins/types/func/bfunc.inl.
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
    };

    template< typename T >
    struct BuildBuiltinFuncParamPat< builtins::TypeParam< T > >
    {
        static const auto& GetParamPattern()
        {
            static auto pat = builtins::ParamPatFromTerm( GetValueType< T >() );
            return pat;
        }
    };

    template< typename PP >
    struct BuildBuiltinFuncParamPat< builtins::TypePatternParam< PP > >
    {
        static const auto& GetParamPattern()
        {
            static auto pat = builtins::ParamPatFromTerm( PP::GetPattern() );
            return pat;
        }
    };

    template< typename... T >
    Term BuildBuiltinFuncParamTypeList()
    {







|









|







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

    template< typename T >
    struct BuildBuiltinFuncParamPat< builtins::TypeParam< T > >
    {
        static const auto& GetParamPattern()
        {
            static auto pat = GetValueType< T >();
            return pat;
        }
    };

    template< typename PP >
    struct BuildBuiltinFuncParamPat< builtins::TypePatternParam< PP > >
    {
        static const auto& GetParamPattern()
        {
            static auto pat = PP::GetPattern();
            return pat;
        }
    };

    template< typename... T >
    Term BuildBuiltinFuncParamTypeList()
    {
Changes to bs/builtins/types/func/build.cpp.
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
                    TERM( decl.name() ) );

                c.env()->storeValue( paramVerificationIdentity, ANYTERM( _ ),
                    ValueToIRExpr( BuildComputedValue( decl.type(),
                    llr::Load( llr::VarAddress( varId++ ), decl.type() ) ) ) );
            }
            else if( param.isConstant() )
                tv->append( ParamPatFromTerm( ValueToIRExpr( param ) ) );

            return true;
        } );

        // If the return type is non-void, expose @result under the verification identity as a computed value whose type
        // is the function's return type, and the value is a placeholder llr instruction. This will allow verification
        // expressions to refer to the current function's return value as a value of the correct type.







|







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
                    TERM( decl.name() ) );

                c.env()->storeValue( paramVerificationIdentity, ANYTERM( _ ),
                    ValueToIRExpr( BuildComputedValue( decl.type(),
                    llr::Load( llr::VarAddress( varId++ ), decl.type() ) ) ) );
            }
            else if( param.isConstant() )
                tv->append( ValueToIRExpr( param ) );

            return true;
        } );

        // If the return type is non-void, expose @result under the verification identity as a computed value whose type
        // is the function's return type, and the value is a placeholder llr instruction. This will allow verification
        // expressions to refer to the current function's return value as a value of the correct type.
Changes to bs/builtins/types/func/func.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef GOOSE_BUILTINS_TYPES_FUNC_H
#define GOOSE_BUILTINS_TYPES_FUNC_H

namespace goose::builtins
{
    extern ptr< InvocationRule >& GetFuncInvocationRule();
    extern void SetupFunctionInvocationRule( Env& e );
    extern void SetupFunctionUnification( Env& e );
    extern void SetupFunctionLowering( Env& e );

    extern optional< FuncType > LowerFunctionTypeForRuntime( const Context& c, const FuncType& ft );

    // Helper to provide generic param patterns for functions.
    struct FuncPattern
    {







|







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

namespace goose::builtins
{
    extern ptr< InvocationRule >& GetFuncInvocationRule();
    extern void SetupFunctionInvocationRule( Env& e );
    extern void SetupFunctionTypeChecking( Env& e );
    extern void SetupFunctionLowering( Env& e );

    extern optional< FuncType > LowerFunctionTypeForRuntime( const Context& c, const FuncType& ft );

    // Helper to provide generic param patterns for functions.
    struct FuncPattern
    {
Changes to bs/builtins/types/func/invoke.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
#include "builtins/builtins.h"

using namespace goose::sema;

namespace goose::builtins
{
    class FunctionInvocationRule : public InvocationRule
    {
        public:
            Value resolveInvocation( const Context& c, uint32_t loc, const Value& callee, const Term& args ) const final
            {
                optional< UnificationContext > bestUC;
                optional< Term > bestSol;
                bool ambiguous = false;
                auto sig = GetFuncSig( callee );


                auto callPat = VEC( args, HOLE( "_"_sid ) );

                auto us = FindBestUnification( sig, callPat, c );

                if( holds_alternative< NoUnification >( us ) )
                {
                    // TODO display details
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                        "function arguments mismatch." );
                    return PoisonValue();
                }

                if( holds_alternative< AmbiguousUnification >( us ) )
                {
                    // TODO display details
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                        "ambiguous function call." );
                    return PoisonValue();
                }

                auto&& [s,uc] = get< UniSol >( us );

                return invoke( c, loc, callee, args, s, uc );
            }

            Value invoke( const Context& c, uint32_t loc, const Value& callee, const Term& args, const Term& unifiedCallPat, UnificationContext& uc ) const final
            {
                auto newCallee = prepareFunc( c, 0, callee, unifiedCallPat, uc );
                if( newCallee.isPoison() )
                    return PoisonValue();

                auto callDecomp = Decompose( unifiedCallPat,
                    Vec(
                        SubTerm(),  // args
                        SubTerm()   // return type











|


<

>


|









|







|

|


|

|







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
#include "builtins/builtins.h"

using namespace goose::sema;

namespace goose::builtins
{
    class FunctionInvocationRule : public InvocationRule
    {
        public:
            Value resolveInvocation( const Context& c, uint32_t loc, const Value& callee, const Term& args ) const final
            {
                optional< TypeCheckingContext > bestTCC;
                optional< Term > bestSol;
                bool ambiguous = false;


                auto sig = GetFuncSig( callee );
                auto callPat = VEC( args, HOLE( "_"_sid ) );

                auto us = FindBestTyping( sig, callPat, c );

                if( holds_alternative< NoUnification >( us ) )
                {
                    // TODO display details
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                        "function arguments mismatch." );
                    return PoisonValue();
                }

                if( holds_alternative< AmbiguousTypeCheck >( us ) )
                {
                    // TODO display details
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                        "ambiguous function call." );
                    return PoisonValue();
                }

                auto&& [s,tcc] = get< TCSol >( us );

                return invoke( c, loc, callee, args, s, tcc );
            }

            Value invoke( const Context& c, uint32_t loc, const Value& callee, const Term& args, const Term& unifiedCallPat, TypeCheckingContext& tcc ) const final
            {
                auto newCallee = prepareFunc( c, 0, callee, unifiedCallPat, tcc );
                if( newCallee.isPoison() )
                    return PoisonValue();

                auto callDecomp = Decompose( unifiedCallPat,
                    Vec(
                        SubTerm(),  // args
                        SubTerm()   // return type
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
            }

            optional< Term > getSignature( const Value& callee ) const final
            {
                return GetFuncSig( callee );
            }

            Value prepareFunc( const Context& c, uint32_t funcValLocation, const Value& callee, const Term& unifiedCallPat, UnificationContext& uc ) const final
            {
                if( IsBuiltinFunc( callee ) || IsIntrinsicFunc( callee ) )
                    return callee;

                // TODO better description with the function's name if possible (we may need to explicitely store it in the func)
                DiagnosticsContext dc( 0, true );
                VerbosityContext vc( Verbosity::Normal, true );







|







69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
            }

            optional< Term > getSignature( const Value& callee ) const final
            {
                return GetFuncSig( callee );
            }

            Value prepareFunc( const Context& c, uint32_t funcValLocation, const Value& callee, const Term& unifiedCallPat, TypeCheckingContext& tcc ) const final
            {
                if( IsBuiltinFunc( callee ) || IsIntrinsicFunc( callee ) )
                    return callee;

                // TODO better description with the function's name if possible (we may need to explicitely store it in the func)
                DiagnosticsContext dc( 0, true );
                VerbosityContext vc( Verbosity::Normal, true );
Name change from bs/builtins/types/func/unify.cpp to bs/builtins/types/func/typecheck.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
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::ir;

namespace goose::builtins
{
    void SetupFunctionUnification( Env& e )
    {
        // Default param rule: we basically treat it like a regular value.
        // Things that need a more specific rule for params can override this with
        // more specific pattern.
        e.unificationRuleSet()->addSymRule( URINFOS,

            ValueToIRExpr( ValuePattern(
                TSID( param ),
                ANYTERM( _ ),
                ANYTERM( _ ) ) ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                ANYTERM( _ ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
        {
            auto lhsVal = *ValuePatternFromIRExpr( lhs );
            lhsVal.sort() = HOLE( "_"_sid );
            co_yield Unify( ValueToIRExpr( lhsVal ), rhs, uc );






























        } );

        auto funcTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( func ),
            ANYTERM( _ ), ANYTERM( RT ), ANYTERM( P ), ANYTERM( _ ), ANYTERM( VA ) ) ) );

        auto tFuncTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( texpr ), TSID( tfunc ),
            ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );

        // func type param / func arg
        e.unificationRuleSet()->addSymRule( URINFOS,

            ParamPat( funcTypePat ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                funcTypePat,
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
        {
            auto rhsVal = *ValueFromIRExpr( rhs );

            if( IsBuiltinFunc( rhsVal ) )
            {
                co_yield { rhs, uc };
                co_return;
            }

            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
        e.unificationRuleSet()->addSymRule( URINFOS,

            ParamPat( move( tFuncTypePat ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                move( funcTypePat ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
        {
            auto ldecomp = Decompose( lhs,
                Vec(
                    Lit( "value"_sid ),
                    SubTerm(),
                    SubTerm(),
                    SubTerm(),
                    Val< LocationId >()
                )
            );
            assert( ldecomp );

            auto&& [sort, type, val, locId] = *ldecomp;

            auto callPat = BuildArgPatternFromTFuncType( uc.context(), *ValueFromIRExpr( type ) );
            assert( callPat );

            auto rhsVal = *ValueFromIRExpr( rhs );

            auto sig = GetFuncSig( rhsVal );

            for( auto&& [s, uc] : Unify( *callPat, sig, uc ) )
            {
                if( IsBuiltinFunc( rhsVal ) )
                {
                    co_yield { ValueToIRExpr( rhsVal ), uc };
                    continue;
                }

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







|




|











|



|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>









|








|





|



|





|






|



|








|














|



<


|



|



|





|






|




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
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
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::ir;

namespace goose::builtins
{
    void SetupFunctionTypeChecking( Env& e )
    {
        // Default param rule: we basically treat it like a regular value.
        // Things that need a more specific rule for params can override this with
        // more specific pattern.
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ValueToIRExpr( ValuePattern(
                TSID( param ),
                ANYTERM( _ ),
                ANYTERM( _ ) ) ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                ANYTERM( _ ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            auto lhsVal = *ValuePatternFromIRExpr( lhs );
            lhsVal.sort() = HOLE( "_"_sid );
            co_yield Unify( ValueToIRExpr( lhsVal ), rhs, tcc );
        } );

        // Constant param rule
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                ANYTERM( _ ),
                ANYTERM( _ ) ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                ANYTERM( _ ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            auto lhsVal = *ValuePatternFromIRExpr( lhs );
            auto rhsVal = *ValuePatternFromIRExpr( rhs );

            // Unify the types
            for( auto&& [ut,tcc] : Unify( lhsVal.type(), rhsVal.type(), tcc ) )
            {
                // Unify the contents
                for( auto&& [uv,tcc] : Unify( lhsVal.val(), rhsVal.val(), tcc ) )
                {
                    ValuePattern result( move( ut ), move( uv ), rhsVal.locationId() );
                    co_yield { ValueToIRExpr( move( result ) ), tcc };
                }
            }
        } );

        auto funcTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( func ),
            ANYTERM( _ ), ANYTERM( RT ), ANYTERM( P ), ANYTERM( _ ), ANYTERM( VA ) ) ) );

        auto tFuncTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( texpr ), TSID( tfunc ),
            ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );

        // func type param / func arg
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ParamPat( funcTypePat ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                funcTypePat,
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            auto rhsVal = *ValueFromIRExpr( rhs );

            if( IsBuiltinFunc( rhsVal ) )
            {
                co_yield { rhs, tcc };
                co_return;
            }

            auto wrapped = WrapWithPostprocFunc( rhs, [rhsVal]( const Term& t, const TypeCheckingContext& tcc )
                -> optional< Term >
            {
                DiagnosticsContext dc( 0, true );
                VerbosityContext vc( Verbosity::Normal, true );

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

                return ValueToIRExpr( func );
            } );

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

        // tfunc type param / func arg
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ParamPat( move( tFuncTypePat ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                move( funcTypePat ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            auto ldecomp = Decompose( lhs,
                Vec(
                    Lit( "value"_sid ),
                    SubTerm(),
                    SubTerm(),
                    SubTerm(),
                    Val< LocationId >()
                )
            );
            assert( ldecomp );

            auto&& [sort, type, val, locId] = *ldecomp;

            auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *ValueFromIRExpr( type ) );
            assert( callPat );

            auto rhsVal = *ValueFromIRExpr( rhs );

            auto sig = GetFuncSig( rhsVal );

            for( auto&& [s, tcc] : TypeCheck( sig, *callPat, tcc ) )
            {
                if( IsBuiltinFunc( rhsVal ) )
                {
                    co_yield { ValueToIRExpr( rhsVal ), tcc };
                    continue;
                }

                auto wrapped = WrapWithPostprocFunc( s, [rhsVal]( const Term& t, const TypeCheckingContext& tcc )
                    -> optional< Term >
                {
                    DiagnosticsContext dc( 0, true );
                    VerbosityContext vc( Verbosity::Normal, true );

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

                    return ValueToIRExpr( func );
                } );

                co_yield { move( wrapped ), tcc };
            }
        } );
    }
}
Changes to bs/builtins/types/localvar/localvar.cpp.
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

        // Create our arg list pattern.
        auto args = VEC(
            *BuildTemplateArgPattern( c, typeTExpr ),
            ValueToIRExpr( ToValue( c.env()->extInitialize() ) )
        );

        auto us = FindBestUnification( paramPat, args, c );

        if( holds_alternative< NoUnification >( us ) )
        {
            // TODO display details
            DiagnosticsManager::GetInstance().emitErrorMessage( 0,
                "variable initialization type mismatch." );
            return PoisonValue();
        }

        if( holds_alternative< AmbiguousUnification >( us ) )
        {
            // TODO display details
            DiagnosticsManager::GetInstance().emitErrorMessage( 0,
                "ambiguous variable type inference." );
            return PoisonValue();
        }

        auto&& [s,uc] = get< UniSol >( us );


        // Perform the setup of the type template expression. This will create local bindings
        // for tvars ($whatever), if any, making them available for further use.

        // We need to create a temporary context and adjust the identity so that the tvar
        // bindings will be injected to our parent scope, rather than in our local statement scope.
        auto parentIdentity = make_shared< Vector >( *get< pvec >( c.identity() ) );
        parentIdentity->terms().resize( parentIdentity->terms().size() - 1 );
        Context ctxt( c.env(), parentIdentity );
        TemplateSetup( ctxt, uc, typeTExpr );

        auto callDecomp = Decompose( s,
            Vec(
                SubTerm(),  // locvar
                SubTerm()   // initializer
            )
        );







|









|







|










|







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

        // Create our arg list pattern.
        auto args = VEC(
            *BuildTemplateArgPattern( c, typeTExpr ),
            ValueToIRExpr( ToValue( c.env()->extInitialize() ) )
        );

        auto us = FindBestTyping( paramPat, args, c );

        if( holds_alternative< NoUnification >( us ) )
        {
            // TODO display details
            DiagnosticsManager::GetInstance().emitErrorMessage( 0,
                "variable initialization type mismatch." );
            return PoisonValue();
        }

        if( holds_alternative< AmbiguousTypeCheck >( us ) )
        {
            // TODO display details
            DiagnosticsManager::GetInstance().emitErrorMessage( 0,
                "ambiguous variable type inference." );
            return PoisonValue();
        }

        auto&& [s,tcc] = get< TCSol >( us );


        // Perform the setup of the type template expression. This will create local bindings
        // for tvars ($whatever), if any, making them available for further use.

        // We need to create a temporary context and adjust the identity so that the tvar
        // bindings will be injected to our parent scope, rather than in our local statement scope.
        auto parentIdentity = make_shared< Vector >( *get< pvec >( c.identity() ) );
        parentIdentity->terms().resize( parentIdentity->terms().size() - 1 );
        Context ctxt( c.env(), parentIdentity );
        TemplateSetup( ctxt, tcc, typeTExpr );

        auto callDecomp = Decompose( s,
            Vec(
                SubTerm(),  // locvar
                SubTerm()   // initializer
            )
        );
Changes to bs/builtins/types/localvar/localvar.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef GOOSE_BUILTINS_TYPES_LOCALVAR_H
#define GOOSE_BUILTINS_TYPES_LOCALVAR_H

namespace goose::parse
{
    class Parser;
}

namespace goose::builtins
{
    extern void SetupLocalVarInvocationRule( Env& e );
    extern void SetupLocalVarUnification( Env& e );
    extern void SetupLocalVarInitialize( Env& e );
    extern void SetupLocalVarDropValue( Env& e );

    Value DeclareLocalVar( const Context& c, const Term& type, StringId name, const optional< Value >& initializer );
    Value DeclareLocalVarWithTypeInference( Context& c, const Term& typeTExpr, StringId name, const Value& initVal );

    class LocalVarType











|







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

namespace goose::parse
{
    class Parser;
}

namespace goose::builtins
{
    extern void SetupLocalVarInvocationRule( Env& e );
    extern void SetupLocalVarTypeChecking( Env& e );
    extern void SetupLocalVarInitialize( Env& e );
    extern void SetupLocalVarDropValue( Env& e );

    Value DeclareLocalVar( const Context& c, const Term& type, StringId name, const optional< Value >& initializer );
    Value DeclareLocalVarWithTypeInference( Context& c, const Term& typeTExpr, StringId name, const Value& initVal );

    class LocalVarType
Name change from bs/builtins/types/localvar/unify.cpp to bs/builtins/types/localvar/typecheck.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
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::ir;
using namespace goose::llr;

namespace goose::builtins
{
    void SetupLocalVarUnification( Env& e )
    {
        auto localVarPattern = GetValueType< LocalVar >( ANYTERM( _ ) );

        auto refTypePattern = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), ANYTERM( _ ), ANYTERM( _ ) ) ) );

        // LocalVar unification against another LocalVar: unify their types.
        e.unificationRuleSet()->addSymRule( URINFOS,

            ParamPat( localVarPattern ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                localVarPattern,
                ANYTERM( _ ) ) ),

            []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
            {
                auto lvarType = *FromValue< LocalVarType >( *ValueFromIRExpr( ValuePatternFromIRExpr( lhs )->type() ) );

                auto rhsVal = *ValuePatternFromIRExpr( rhs );
                auto rvarType = *FromValue< LocalVarType >( *ValueFromIRExpr( rhsVal.type() ) );

                for( auto&& [s, uc] : Unify( lvarType.type(), rvarType.type(), uc ) )
                {
                    co_yield { ValueToIRExpr( Value( ValueToIRExpr( ToValue( LocalVarType( s ) ) ),
                        rhsVal.val() ) ), uc };
                }
            } );
    }
}








|







|








|






|


|




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
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::ir;
using namespace goose::llr;

namespace goose::builtins
{
    void SetupLocalVarTypeChecking( Env& e )
    {
        auto localVarPattern = GetValueType< LocalVar >( ANYTERM( _ ) );

        auto refTypePattern = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), ANYTERM( _ ), ANYTERM( _ ) ) ) );

        // LocalVar unification against another LocalVar: unify their types.
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ParamPat( localVarPattern ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                localVarPattern,
                ANYTERM( _ ) ) ),

            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                auto lvarType = *FromValue< LocalVarType >( *ValueFromIRExpr( ValuePatternFromIRExpr( lhs )->type() ) );

                auto rhsVal = *ValuePatternFromIRExpr( rhs );
                auto rvarType = *FromValue< LocalVarType >( *ValueFromIRExpr( rhsVal.type() ) );

                for( auto&& [s, tcc] : Unify( lvarType.type(), rvarType.type(), tcc ) )
                {
                    co_yield { ValueToIRExpr( Value( ValueToIRExpr( ToValue( LocalVarType( s ) ) ),
                        rhsVal.val() ) ), tcc };
                }
            } );
    }
}
Changes to bs/builtins/types/overloadset/invoke.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
#include "builtins/builtins.h"

using namespace goose::sema;

namespace goose::builtins
{
    class OverloadSetInvocationRule : public InvocationRule
    {
        public:
            Value resolveInvocation( const Context& c, uint32_t loc, const Value& callee, const Term& args ) const final
            {
                auto pOvlSet = *FromValue< ptr< OverloadSet > >( callee );

                optional< UnificationContext > bestUC;
                optional< Term > bestSol;
                const OverloadSet::Overload* bestOvl = nullptr;
                bool ambiguous = false;

                auto rtPat = HOLE( "_"_sid );
                UnificationContext uc( c );

                for( auto&& [s,ovl,uc] : pOvlSet->unify( args, rtPat, uc ) )
                {
                    if( uc.numUnknownValues() )
                       continue;

                    if( bestUC && uc.score() < bestUC->score() )
                        continue;

                    auto pps = Postprocess( s, uc );
                    if( !pps )
                        continue;

                    if( bestUC && uc.score() == bestUC->score() )
                    {
                        ambiguous = true;
                        continue;
                    }

                    bestUC = uc;
                    bestSol = move( *pps );
                    bestOvl = &ovl;
                    ambiguous = false;
                }

                if( ambiguous )
                {













|





<
|
|

|


|


|



|





|







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
#include "builtins/builtins.h"

using namespace goose::sema;

namespace goose::builtins
{
    class OverloadSetInvocationRule : public InvocationRule
    {
        public:
            Value resolveInvocation( const Context& c, uint32_t loc, const Value& callee, const Term& args ) const final
            {
                auto pOvlSet = *FromValue< ptr< OverloadSet > >( callee );

                optional< TypeCheckingContext > bestTCC;
                optional< Term > bestSol;
                const OverloadSet::Overload* bestOvl = nullptr;
                bool ambiguous = false;

                auto rtPat = HOLE( "_"_sid );

                TypeCheckingContext tcc( c );
                for( auto&& [s,ovl,tcc] : pOvlSet->typeCheck( args, rtPat, tcc ) )
                {
                    if( tcc.numUnknownValues() )
                       continue;

                    if( bestTCC && tcc.score() < bestTCC->score() )
                        continue;

                    auto pps = Postprocess( s, tcc );
                    if( !pps )
                        continue;

                    if( bestTCC && tcc.score() == bestTCC->score() )
                    {
                        ambiguous = true;
                        continue;
                    }

                    bestTCC = tcc;
                    bestSol = move( *pps );
                    bestOvl = &ovl;
                    ambiguous = false;
                }

                if( ambiguous )
                {
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
                {
                    // TODO display details
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                        "function arguments mismatch." );
                    return PoisonValue();
                }

                return bestOvl->pInvRule->invoke( c, loc, *bestOvl->callee, args, *bestSol, *bestUC );
            }
    };

    ptr< InvocationRule >& GetOverloadSetInvocationRule()
    {
        static ptr< InvocationRule > pRule = make_shared< OverloadSetInvocationRule >();
        return pRule;







|







54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
                {
                    // TODO display details
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                        "function arguments mismatch." );
                    return PoisonValue();
                }

                return bestOvl->pInvRule->invoke( c, loc, *bestOvl->callee, args, *bestSol, *bestTCC );
            }
    };

    ptr< InvocationRule >& GetOverloadSetInvocationRule()
    {
        static ptr< InvocationRule > pRule = make_shared< OverloadSetInvocationRule >();
        return pRule;
Changes to bs/builtins/types/overloadset/overloadset.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef GOOSE_BUILTINS_TYPES_OVERLOADSET_H
#define GOOSE_BUILTINS_TYPES_OVERLOADSET_H

#include "helpers.h"

namespace goose::builtins
{
    extern ptr< InvocationRule >& GetOverloadSetInvocationRule();
    extern void SetupOverloadSetInvocationRule( Env& e );
    extern void SetupOverloadSetUnification( Env& e );

    extern bool IsOverloadSet( const Value& os );
}

namespace goose::ir
{
    template<>









|







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

#include "helpers.h"

namespace goose::builtins
{
    extern ptr< InvocationRule >& GetOverloadSetInvocationRule();
    extern void SetupOverloadSetInvocationRule( Env& e );
    extern void SetupOverloadSetTypeChecking( Env& e );

    extern bool IsOverloadSet( const Value& os );
}

namespace goose::ir
{
    template<>
Name change from bs/builtins/types/overloadset/unify.cpp to bs/builtins/types/overloadset/typecheck.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
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::ir;

namespace goose::builtins
{
    void SetupOverloadSetUnification( Env& e )
    {
        auto funcTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( func ),
            ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( VA ) ) ) );

        auto tFuncTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( texpr ), TSID( tfunc ),
            ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );

        // func type param / overloadset arg
        e.unificationRuleSet()->addSymRule( URINFOS,

            ParamPat( move( funcTypePat ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                GetValueType< ptr< builtins::OverloadSet > >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, UnificationContext uc ) -> UniGen
        {
            auto ldecomp = Decompose( lhs,
                Vec(
                    Lit( "value"_sid ),
                    SubTerm(),
                    SubTerm(),
                    SubTerm(),







|








|








|







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
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::ir;

namespace goose::builtins
{
    void SetupOverloadSetTypeChecking( Env& e )
    {
        auto funcTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( func ),
            ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( VA ) ) ) );

        auto tFuncTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( texpr ), TSID( tfunc ),
            ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );

        // func type param / overloadset arg
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ParamPat( move( funcTypePat ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                GetValueType< ptr< builtins::OverloadSet > >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
        {
            auto ldecomp = Decompose( lhs,
                Vec(
                    Lit( "value"_sid ),
                    SubTerm(),
                    SubTerm(),
                    SubTerm(),
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
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
            auto rhsVal = *ValueFromIRExpr( rhs );
            auto os = *FromValue< ptr< OverloadSet > >( rhsVal );

            auto rhsLocId = rhsVal.locationId();

            // Create a new named hole namespace to isolate holes from the passed function from those in
            // the called function.
            auto savedRHSNamespaceIndex = uc.RHSNamespaceIndex();
            auto localNSId = uc.newNamespaceIndex();
            uc.setRHSNamespaceIndex( localNSId );

            for( auto&& [s,ovl,uc] : os->unify( argPat, rtPat, uc.flip() ) )
            {
                if( !ovl.callee )
                    continue;

                // Restore the namespace
                uc.flip();
                uc.setRHSNamespaceIndex( savedRHSNamespaceIndex );

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

                        if( func.isPoison() )
                            return nullopt;

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

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

        // tfunc type param / overloadset arg
        e.unificationRuleSet()->addSymRule( URINFOS,

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                move( tFuncTypePat ),
                ANYTERM( _ ) ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                GetValueType< ptr< builtins::OverloadSet > >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, UnificationContext uc ) -> UniGen
        {
            auto ldecomp = Decompose( lhs,
                Vec(
                    Lit( "value"_sid ),
                    SubTerm(),
                    SubTerm(),
                    SubTerm(),
                    Val< LocationId >()
                )
            );
            assert( ldecomp );

            auto&& [sort, type, val, locId] = *ldecomp;
            auto callPat = BuildArgPatternFromTFuncType( uc.context(), *ValueFromIRExpr( type ) );
            assert( callPat );

            auto cpdecomp = Decompose( *callPat,
                Vec(
                    SubTerm(),
                    SubTerm()
                )
            );
            assert( cpdecomp );

            auto&& [argPat, rtPat] = *cpdecomp;

            auto rhsVal = *ValueFromIRExpr( rhs );
            auto os = *FromValue< ptr< OverloadSet > >( rhsVal );

            auto rhsLocId = rhsVal.locationId();

            // Create a new named hole namespace to isolate holes from the called functions from those
            // in the call expression.
            auto savedRHSNamespaceIndex = uc.RHSNamespaceIndex();
            auto localNSId = uc.newNamespaceIndex();
            uc.setRHSNamespaceIndex( localNSId );

            for( auto&& [s,ovl,uc] : os->unify( argPat, rtPat, uc.flip() ) )
            {
                if( !ovl.callee )
                    continue;

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

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

                        if( func.isPoison() )
                            return nullopt;

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

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







|
|
|

|





|
|



|

|
|







|




|











|













|



















|
|
|

|




|
|



|

|
|







|




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
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
            auto rhsVal = *ValueFromIRExpr( rhs );
            auto os = *FromValue< ptr< OverloadSet > >( rhsVal );

            auto rhsLocId = rhsVal.locationId();

            // Create a new named hole namespace to isolate holes from the passed function from those in
            // the called function.
            auto savedRHSNamespaceIndex = tcc.RHSNamespaceIndex();
            auto localNSId = tcc.newNamespaceIndex();
            tcc.setRHSNamespaceIndex( localNSId );

            for( auto&& [s,ovl,tcc] : os->typeCheck( argPat, rtPat, tcc.flip() ) )
            {
                if( !ovl.callee )
                    continue;

                // Restore the namespace
                tcc.flip();
                tcc.setRHSNamespaceIndex( savedRHSNamespaceIndex );

                auto overload = ovl;
                auto wrapped = WrapWithPostprocFunc( s,
                    [overload,localNSId,rhsLocId]( const Term& t, TypeCheckingContext tcc ) -> optional< Term >
                    {
                        tcc.setRHSNamespaceIndex( localNSId );
                        auto func = overload.pInvRule->prepareFunc( tcc.context(), rhsLocId, *overload.callee, t, tcc.flip() );

                        if( func.isPoison() )
                            return nullopt;

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

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

        // tfunc type param / overloadset arg
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                move( tFuncTypePat ),
                ANYTERM( _ ) ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                GetValueType< ptr< builtins::OverloadSet > >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
        {
            auto ldecomp = Decompose( lhs,
                Vec(
                    Lit( "value"_sid ),
                    SubTerm(),
                    SubTerm(),
                    SubTerm(),
                    Val< LocationId >()
                )
            );
            assert( ldecomp );

            auto&& [sort, type, val, locId] = *ldecomp;
            auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *ValueFromIRExpr( type ) );
            assert( callPat );

            auto cpdecomp = Decompose( *callPat,
                Vec(
                    SubTerm(),
                    SubTerm()
                )
            );
            assert( cpdecomp );

            auto&& [argPat, rtPat] = *cpdecomp;

            auto rhsVal = *ValueFromIRExpr( rhs );
            auto os = *FromValue< ptr< OverloadSet > >( rhsVal );

            auto rhsLocId = rhsVal.locationId();

            // Create a new named hole namespace to isolate holes from the called functions from those
            // in the call expression.
            auto savedRHSNamespaceIndex = tcc.RHSNamespaceIndex();
            auto localNSId = tcc.newNamespaceIndex();
            tcc.setRHSNamespaceIndex( localNSId );

            for( auto&& [s,ovl,tcc] : os->typeCheck( argPat, rtPat, tcc.flip() ) )
            {
                if( !ovl.callee )
                    continue;

                tcc.flip();
                tcc.setRHSNamespaceIndex( savedRHSNamespaceIndex );

                auto overload = ovl;
                auto wrapped = WrapWithPostprocFunc( s,
                    [overload,localNSId,rhsLocId]( const Term& t, TypeCheckingContext tcc ) -> optional< Term >
                    {
                        tcc.setRHSNamespaceIndex( localNSId );
                        auto func = overload.pInvRule->prepareFunc( tcc.context(), rhsLocId, *overload.callee, t, tcc.flip() );

                        if( func.isPoison() )
                            return nullopt;

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

                co_yield { move( wrapped ), tcc };
            }
        } );
    }
}
Changes to bs/builtins/types/param.h.
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

    template< typename T >
    auto ParamPat( T&& type )
    {
        // If the type is a tuple of type, convert it to the type of a tuple.
        // (the type of a tuple is just a list of values, so just extract the value part of the tuple)
        if( auto typeVal = ValueFromIRExpr( type ); typeVal && IsTuple( *typeVal ) )
            return ValueToIRExpr( ValuePattern( TSID( param ), ValueToIRExpr( TupleOfTypesToTupleType( *typeVal ) ), HOLE( "_"_sid ) ) );

        return ValueToIRExpr( ValuePattern( TSID( param ), forward< T >( type ), HOLE( "_"_sid ) ) );
    }

    template< typename T >
    auto ParamPatFromTerm( T&& t )
    {
        return forward< T >( t );
    }
}

#endif







|



<
<
<
<
<
<



16
17
18
19
20
21
22
23
24
25
26






27
28
29

    template< typename T >
    auto ParamPat( T&& type )
    {
        // If the type is a tuple of type, convert it to the type of a tuple.
        // (the type of a tuple is just a list of values, so just extract the value part of the tuple)
        if( auto typeVal = ValueFromIRExpr( type ); typeVal && IsTuple( *typeVal ) )
            return ValueToIRExpr( ValuePattern( HOLE( "_"_sid ), ValueToIRExpr( TupleOfTypesToTupleType( *typeVal ) ), HOLE( "_"_sid ) ) );

        return ValueToIRExpr( ValuePattern( TSID( param ), forward< T >( type ), HOLE( "_"_sid ) ) );
    }






}

#endif
Changes to bs/builtins/types/reference/reference.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef GOOSE_BUILTINS_TYPES_REFERENCE_H
#define GOOSE_BUILTINS_TYPES_REFERENCE_H

namespace goose::builtins
{
    extern void SetupReferenceUnification( Env& e );

    class ReferenceType
    {
        public:
            template< typename T, typename B >
            ReferenceType( T&& type, B&& bhv ) :
                m_type( forward< T >( type ) ),





|







1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef GOOSE_BUILTINS_TYPES_REFERENCE_H
#define GOOSE_BUILTINS_TYPES_REFERENCE_H

namespace goose::builtins
{
    extern void SetupReferenceTypeChecking( Env& e );

    class ReferenceType
    {
        public:
            template< typename T, typename B >
            ReferenceType( T&& type, B&& bhv ) :
                m_type( forward< T >( type ) ),
Name change from bs/builtins/types/reference/unify.cpp to bs/builtins/types/reference/typecheck.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
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
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::ir;
using namespace goose::llr;

namespace goose::builtins
{
    void SetupReferenceUnification( Env& e )
    {
        auto localVarPattern = GetValueType< LocalVar >( ANYTERM( _ ) );

        auto refTypePattern = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), ANYTERM( _ ), ANYTERM( _ ) ) ) );

        auto refTypePatternConstant = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TSID( const ), ANYTERM( _ ) ) ) );

        auto refTypePatternMutable = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TSID( mut ), ANYTERM( _ ) ) ) );

        auto refTypePatternTemporary = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TSID( temporary ), ANYTERM( _ ) ) ) );

        // Reference unification rule.
        e.unificationRuleSet()->addSymRule( URINFOS,

            ParamPat( refTypePattern ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                refTypePattern,
                ANYTERM( _ ) ) ),

            []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
            {
                auto lRefType = *FromValue< ReferenceType >( *ValueFromIRExpr( ValuePatternFromIRExpr( lhs )->type() ) );

                auto rhsVal = *ValueFromIRExpr( rhs );
                auto rRefType = *FromValue< ReferenceType >( *ValueFromIRExpr( rhsVal.type() ) );

                // Unify the behaviors
                for( auto&& [b, uc] : Unify( lRefType.behavior(), rRefType.behavior(), uc ) )
                {
                    // Unify the types
                    for( auto&& [t, uc] : Unify( lRefType.type(), rRefType.type(), uc ) )
                    {
                        co_yield { ValueToIRExpr( Value( ValueToIRExpr( ToValue( ReferenceType( t, b ) ) ),
                            rhsVal.llr() ) ), uc };
                    }
                }
            }
        );

        // mut -> const reference unification rule.
        e.unificationRuleSet()->addAsymRule( URINFOS,
            TSID( const ),
            TSID( mut ),

            []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
            {
                co_yield { TSID( const ), uc };
            }
        );

        // temp -> const reference unification rule.
        e.unificationRuleSet()->addAsymRule( URINFOS,
            TSID( const ),
            TSID( temp ),

            []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
            {
                co_yield { TSID( const ), uc };
            }
        );

        // temp -> mut reference unification rule.
        e.unificationRuleSet()->addAsymRule( URINFOS,
            TSID( mut ),
            TSID( temp ),

            []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
            {
                co_yield { TSID( mut ), uc };
            }
        );

        // Reference unification against a param (implicit dereferencing):
        // Unify the referenced value with the param.
        e.unificationRuleSet()->addSymRule( URINFOS,

            ParamPat( ANYTERM( _ ) ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                refTypePattern,
                ANYTERM( _ ) ) ),

            []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
            {
                auto refval = *ValueFromIRExpr( rhs );
                auto ref = FromValue< Reference >( refval );
                if( !ref )
                    co_return;

                auto content = ValueToIRExpr( BuildComputedValue( ref->type().type(),
                    llr::Load( ref->address(), ref->type().type() ) )
                    .setLocationId( refval.locationId() ) );

                // Unify the param with the ref's content
                co_yield Unify( lhs, content, uc );
            } );

        // LocalVar unification against a param (implicit referencing):
        // Build a mutable ref value.
        e.unificationRuleSet()->addSymRule( URINFOS,

            ParamPat( ANYTERM( _ ) ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                localVarPattern,
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
        {
            auto ltype = ValuePatternFromIRExpr( lhs )->type();

            auto lvval = *ValueFromIRExpr( rhs );
            auto locvar = FromValue< LocalVar >( lvval );
            if( !locvar )
                co_return;

            ReferenceType rt( locvar->type(), TSID( mut ) );
            auto ref = ValueToIRExpr( BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),
                Load( VarAddress( locvar->index() ), rt.type() ) )
                .setLocationId( lvval.locationId() ) );

            co_yield Unify( lhs, ref, uc );
        } );

        // Implicit referencing of non-variables: build a tempref
        e.unificationRuleSet()->addSymRule( URINFOS,

            ParamPat( refTypePattern ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                ANYTERM( _ ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
        {
            auto rval = *ValueFromIRExpr( rhs );

            ReferenceType rt( rval.type(), TSID( temp ) );
            auto tempIndex = uc.context().codeBuilder()->cfg()->getNewTemporaryIndex();
            auto ref = ValueToIRExpr( BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),
                Load( TemporaryAddress( tempIndex, rval ), rt.type() ) )
                .setLocationId( rval.locationId() ) );

            // Unify the param with the ref
            co_yield Unify( lhs, ref, uc );
        } );
    }
}








|
















|








|







|


|


|






|



|

|




|



|

|




|



|

|





|








|










|
|




|








|













|



|








|




|




|
|



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
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
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::ir;
using namespace goose::llr;

namespace goose::builtins
{
    void SetupReferenceTypeChecking( Env& e )
    {
        auto localVarPattern = GetValueType< LocalVar >( ANYTERM( _ ) );

        auto refTypePattern = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), ANYTERM( _ ), ANYTERM( _ ) ) ) );

        auto refTypePatternConstant = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TSID( const ), ANYTERM( _ ) ) ) );

        auto refTypePatternMutable = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TSID( mut ), ANYTERM( _ ) ) ) );

        auto refTypePatternTemporary = ValueToIRExpr(
            Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TSID( temporary ), ANYTERM( _ ) ) ) );

        // Reference unification rule.
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ParamPat( refTypePattern ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                refTypePattern,
                ANYTERM( _ ) ) ),

            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                auto lRefType = *FromValue< ReferenceType >( *ValueFromIRExpr( ValuePatternFromIRExpr( lhs )->type() ) );

                auto rhsVal = *ValueFromIRExpr( rhs );
                auto rRefType = *FromValue< ReferenceType >( *ValueFromIRExpr( rhsVal.type() ) );

                // Unify the behaviors
                for( auto&& [b, tcc] : Unify( lRefType.behavior(), rRefType.behavior(), tcc ) )
                {
                    // Unify the types
                    for( auto&& [t, tcc] : Unify( lRefType.type(), rRefType.type(), tcc ) )
                    {
                        co_yield { ValueToIRExpr( Value( ValueToIRExpr( ToValue( ReferenceType( t, b ) ) ),
                            rhsVal.llr() ) ), tcc };
                    }
                }
            }
        );

        // mut -> const reference unification rule.
        e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,
            TSID( const ),
            TSID( mut ),

            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                co_yield { TSID( const ), tcc };
            }
        );

        // temp -> const reference unification rule.
        e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,
            TSID( const ),
            TSID( temp ),

            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                co_yield { TSID( const ), tcc };
            }
        );

        // temp -> mut reference unification rule.
        e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,
            TSID( mut ),
            TSID( temp ),

            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                co_yield { TSID( mut ), tcc };
            }
        );

        // Reference unification against a param (implicit dereferencing):
        // Unify the referenced value with the param.
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ParamPat( ANYTERM( _ ) ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                refTypePattern,
                ANYTERM( _ ) ) ),

            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                auto refval = *ValueFromIRExpr( rhs );
                auto ref = FromValue< Reference >( refval );
                if( !ref )
                    co_return;

                auto content = ValueToIRExpr( BuildComputedValue( ref->type().type(),
                    llr::Load( ref->address(), ref->type().type() ) )
                    .setLocationId( refval.locationId() ) );

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

        // LocalVar unification against a param (implicit referencing):
        // Build a mutable ref value.
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ParamPat( ANYTERM( _ ) ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                localVarPattern,
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            auto ltype = ValuePatternFromIRExpr( lhs )->type();

            auto lvval = *ValueFromIRExpr( rhs );
            auto locvar = FromValue< LocalVar >( lvval );
            if( !locvar )
                co_return;

            ReferenceType rt( locvar->type(), TSID( mut ) );
            auto ref = ValueToIRExpr( BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),
                Load( VarAddress( locvar->index() ), rt.type() ) )
                .setLocationId( lvval.locationId() ) );

            co_yield TypeCheck( lhs, ref, tcc );
        } );

        // Implicit referencing of non-variables: build a tempref
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ParamPat( refTypePattern ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                ANYTERM( _ ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            auto rval = *ValueFromIRExpr( rhs );

            ReferenceType rt( rval.type(), TSID( temp ) );
            auto tempIndex = tcc.context().codeBuilder()->cfg()->getNewTemporaryIndex();
            auto ref = ValueToIRExpr( BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),
                Load( TemporaryAddress( tempIndex, rval ), rt.type() ) )
                .setLocationId( rval.locationId() ) );

            // TypeCheck the param with the ref
            co_yield TypeCheck( lhs, ref, tcc );
        } );
    }
}
Name change from bs/builtins/types/runtime/unify.cpp to bs/builtins/types/runtime/typecheck.cpp.
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
        auto rtIntTypePattern = Value( TypeType(), MkStdType( TSID( integer ),
            VEC( ANYTERM( _ ), ANYTERM( _ ) ) ) );

        // ct_int type against a IntegerType type:
        // return the IntegerType type. We don't care if the
        // ct_int fits at this point, this will be dealt with by
        // the ct_int value unification rule below.
        e.unificationRuleSet()->addSymRule( URINFOS,
            ValueToIRExpr( rtIntTypePattern ),
            GetValueType< BigInt >(),
        []( const Term& lhs, const Term& rhs, const UnificationContext& c ) -> UniGen
        {
            return HalfUnify( lhs, c );
        } );

        // Reject the ct_int param and IntegerType arg pattern,
        // so that it doesn't fall into the rule above which would be incorrect
        // in that case.
        e.unificationRuleSet()->addSymRule( URINFOS,

            ParamPat( GetValueType< BigInt >() ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                ValueToIRExpr( rtIntTypePattern ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const UnificationContext& c ) -> UniGen
        {
            co_return;
        } );

        // ct_integer constant unification against a IntegerType:
        // Check if the IntegerType is big enough for the constant,
        // and emit a LoadConstantInt llr instruction if so.
        e.unificationRuleSet()->addSymRule( URINFOS,

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                ValueToIRExpr( rtIntTypePattern ),
                ANYTERM( _ ) ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                GetValueType< BigInt >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
        {
            // The rhs might not be constant. It can happen even with ct_int if the rhs is a
            // fake argument pattern generated to unify a higher-order function param.
            // It means that we are unifying a function parameter against any ct_int value,
            // which we can't (because we have to know the value to safely convert it),
            // so we reject it.
            auto rhsVal = *ValueFromIRExpr( rhs );
            if( !rhsVal.isConstant() )
                co_return;

            auto lhsVal = ValuePatternFromIRExpr( lhs );
            if( !lhsVal )
                co_return;

            for( auto&& [s,uc] : HalfUnify( lhsVal->type(), uc ) )
            {
                // We need to defer the calculation of the init value until after the rest of the unification is complete,
                // because we need to make sure that any hole present in the integer type is resolved before we can do
                // the sign/bitsize check. However, since we still need to be able to manipulate the result as a value in
                // the mean time, we construct a value with the correct (but possibly not complete yet type), and a payload
                // that contains the type + the ct_int value wrapped with a post process callback that finalizes the job.
                auto wrapped = WrapWithPostprocFunc( VEC( lhsVal->type(), rhs ),
                []( const Term& t, UnificationContext uc ) -> optional< Term >
                {
                    auto result = Decompose( t,
                        Vec(
                            SubTerm(),
                            SubTerm()
                        )
                    );







|


|







|








|







|











|














|







|







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
        auto rtIntTypePattern = Value( TypeType(), MkStdType( TSID( integer ),
            VEC( ANYTERM( _ ), ANYTERM( _ ) ) ) );

        // ct_int type against a IntegerType type:
        // return the IntegerType type. We don't care if the
        // ct_int fits at this point, this will be dealt with by
        // the ct_int value unification rule below.
        e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,
            ValueToIRExpr( rtIntTypePattern ),
            GetValueType< BigInt >(),
        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& c ) -> TCGen
        {
            return HalfUnify( lhs, c );
        } );

        // Reject the ct_int param and IntegerType arg pattern,
        // so that it doesn't fall into the rule above which would be incorrect
        // in that case.
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ParamPat( GetValueType< BigInt >() ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                ValueToIRExpr( rtIntTypePattern ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& c ) -> TCGen
        {
            co_return;
        } );

        // ct_integer constant unification against a IntegerType:
        // Check if the IntegerType is big enough for the constant,
        // and emit a LoadConstantInt llr instruction if so.
        e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                ValueToIRExpr( rtIntTypePattern ),
                ANYTERM( _ ) ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                GetValueType< BigInt >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            // The rhs might not be constant. It can happen even with ct_int if the rhs is a
            // fake argument pattern generated to unify a higher-order function param.
            // It means that we are unifying a function parameter against any ct_int value,
            // which we can't (because we have to know the value to safely convert it),
            // so we reject it.
            auto rhsVal = *ValueFromIRExpr( rhs );
            if( !rhsVal.isConstant() )
                co_return;

            auto lhsVal = ValuePatternFromIRExpr( lhs );
            if( !lhsVal )
                co_return;

            for( auto&& [s,tcc] : HalfUnify( lhsVal->type(), tcc ) )
            {
                // We need to defer the calculation of the init value until after the rest of the unification is complete,
                // because we need to make sure that any hole present in the integer type is resolved before we can do
                // the sign/bitsize check. However, since we still need to be able to manipulate the result as a value in
                // the mean time, we construct a value with the correct (but possibly not complete yet type), and a payload
                // that contains the type + the ct_int value wrapped with a post process callback that finalizes the job.
                auto wrapped = WrapWithPostprocFunc( VEC( lhsVal->type(), rhs ),
                []( const Term& t, TypeCheckingContext tcc ) -> optional< Term >
                {
                    auto result = Decompose( t,
                        Vec(
                            SubTerm(),
                            SubTerm()
                        )
                    );
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
                        valToLoad = ct.zext( rttype->m_numBits );
                        valToLoad.setIsSigned( false );
                    }

                    return TERM( valToLoad );
                } );

                co_yield { ValueToIRExpr( Value( s, move( wrapped ) ) ), uc };
            }
        } );

        auto rtInt8TypePattern = Value( TypeType(), MkStdType( TSID( integer ),
            VEC( TERM( 8U ), ANYTERM( _ ) ) ) );

        auto rtInt8PtrTypePattern = Value( TypeType(), MkStdType( TSID( pointer ),
            ValueToIRExpr( rtInt8TypePattern ) ) );

        // ct_string type against a char*:
        // return the char* type.
        e.unificationRuleSet()->addSymRule( URINFOS,
            ValueToIRExpr( rtInt8PtrTypePattern ),
            GetValueType< string >(),
        []( const Term& lhs, const Term& rhs, const UnificationContext& c ) -> UniGen
        {
            co_yield HalfUnify( lhs, c );
        } );

        // Reject the ct_string param and char* arg pattern,
        // so that it doesn't fall into the rule above which would be incorrect
        // in that case.
        e.unificationRuleSet()->addSymRule( URINFOS,

            ParamPat( GetValueType< string >() ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                ValueToIRExpr( rtInt8PtrTypePattern ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const UnificationContext& c ) -> UniGen
        {
            co_return;
        } );

        // ct_string constant unification against a pointer to a integer( 8 ):
        // Emit a LoadConstantStr llr instruction.
        e.unificationRuleSet()->addSymRule( URINFOS,

            ParamPat( GetValueType< string >(), ANYTERM( _ ) ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                ValueToIRExpr( rtInt8PtrTypePattern ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const UnificationContext& c ) -> UniGen
        {
            auto str = *FromValue< string >( *ValueFromIRExpr( lhs ) );
            auto rhsVal = *ValuePatternFromIRExpr( rhs );

            co_yield { ValueToIRExpr(
                BuildComputedValue( rhsVal.type(), llr::LoadConstStr( str ) ) ), c };
        } );

        auto ptrTypePattern = Value( TypeType(), MkStdType( TSID( pointer ),
            ANYTERM( _ ) ) );

        // nullptr constant unification against a pointer of any type;
        // Yield a value of the given pointer type, with a 0 integer as its content.
        // This'll be recognized by codegen to emit a null pointer value of the right type.
        e.unificationRuleSet()->addSymRule( URINFOS,

            ParamPat( ValueToIRExpr( ptrTypePattern ) ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                GetValueType< NullPointer >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const UnificationContext& c ) -> UniGen
        {
            auto lVal = *ValuePatternFromIRExpr( lhs );
            co_yield { ValueToIRExpr( Value( lVal.type(), 0U ) ), c };
        } );
    }
}







|











|


|




|
<
|
<
|
<






<
<
<
<
<
<
<
<
<
<
<


|


|

|
|


|








|








|






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
                        valToLoad = ct.zext( rttype->m_numBits );
                        valToLoad.setIsSigned( false );
                    }

                    return TERM( valToLoad );
                } );

                co_yield { ValueToIRExpr( Value( s, move( wrapped ) ) ), tcc };
            }
        } );

        auto rtInt8TypePattern = Value( TypeType(), MkStdType( TSID( integer ),
            VEC( TERM( 8U ), ANYTERM( _ ) ) ) );

        auto rtInt8PtrTypePattern = Value( TypeType(), MkStdType( TSID( pointer ),
            ValueToIRExpr( rtInt8TypePattern ) ) );

        // ct_string type against a char*:
        // return the char* type.
        e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,
            ValueToIRExpr( rtInt8PtrTypePattern ),
            GetValueType< string >(),
        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& c ) -> TCGen
        {
            co_yield HalfUnify( lhs, c );
        } );

        // ct_string constant unification against a pointer to a integer( 8 ):

        // Emit a LoadConstantStr llr instruction.

        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,


            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                ValueToIRExpr( rtInt8PtrTypePattern ),
                ANYTERM( _ ) ) ),












            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                GetValueType< string >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& c ) -> TCGen
        {
            auto str = *FromValue< string >( *ValueFromIRExpr( rhs ) );
            auto lhsVal = *ValuePatternFromIRExpr( lhs );

            co_yield { ValueToIRExpr(
                BuildComputedValue( lhsVal.type(), llr::LoadConstStr( str ) ) ), c };
        } );

        auto ptrTypePattern = Value( TypeType(), MkStdType( TSID( pointer ),
            ANYTERM( _ ) ) );

        // nullptr constant unification against a pointer of any type;
        // Yield a value of the given pointer type, with a 0 integer as its content.
        // This'll be recognized by codegen to emit a null pointer value of the right type.
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ParamPat( ValueToIRExpr( ptrTypePattern ) ),

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                GetValueType< NullPointer >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& c ) -> TCGen
        {
            auto lVal = *ValuePatternFromIRExpr( lhs );
            co_yield { ValueToIRExpr( Value( lVal.type(), 0U ) ), c };
        } );
    }
}
Changes to bs/builtins/types/template/instantiate.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "builtins/builtins.h"
#include "parse/parse.h"

using namespace goose::sema;
using namespace goose::parse;

namespace goose::builtins
{
    Value InstantiateTFunc( const Context& c, const Value& callee, const Term& unifiedCallPat, const UnificationContext& uc )
    {
        auto tf = FromValue< TFunc >( callee );
        assert( tf );

        auto callDecomp = Decompose( unifiedCallPat,
            Vec(
                SubTerm(),  // args








|







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

using namespace goose::sema;
using namespace goose::parse;

namespace goose::builtins
{
    Value InstantiateTFunc( const Context& c, const Value& callee, const Term& unifiedCallPat, const TypeCheckingContext& tcc )
    {
        auto tf = FromValue< TFunc >( callee );
        assert( tf );

        auto callDecomp = Decompose( unifiedCallPat,
            Vec(
                SubTerm(),  // args
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

                auto verifContext = c;
                verifContext.setIdentity( instanceType.verifInfos()->identity() );

                // Setup the template TVars binding in the verification context.
                ForEachInVectorTerm( tf->type().params(), [&]( auto&& param )
                {
                    TemplateSetup( verifContext, uc, param );
                    return true;
                } );

                auto bodyContext = c;
                auto func = BuildFunc( c, instanceType, instanceIdentity, instanceParams, tf->toks(), bodyContext );

                ForEachInVectorTerm( tf->type().params(), [&]( auto&& param )
                {
                    TemplateSetup( bodyContext, uc, param );
                    return true;
                } );

                instanceFunc = ToValue( func );

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







|








|







56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

                auto verifContext = c;
                verifContext.setIdentity( instanceType.verifInfos()->identity() );

                // Setup the template TVars binding in the verification context.
                ForEachInVectorTerm( tf->type().params(), [&]( auto&& param )
                {
                    TemplateSetup( verifContext, tcc, param );
                    return true;
                } );

                auto bodyContext = c;
                auto func = BuildFunc( c, instanceType, instanceIdentity, instanceParams, tf->toks(), bodyContext );

                ForEachInVectorTerm( tf->type().params(), [&]( auto&& param )
                {
                    TemplateSetup( bodyContext, tcc, param );
                    return true;
                } );

                instanceFunc = ToValue( func );

                // TODO: better description including the function's name
                DiagnosticsContext dc( callee.locationId(), "In the template function declared here.", false );
Changes to bs/builtins/types/template/invoke.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
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
    {
        public:
            Value resolveInvocation( const Context& c, uint32_t loc, const Value& callee, const Term& args ) const final
            {
                auto tf = FromValue< TFunc >( callee );
                assert( tf );

                optional< UnificationContext > bestUC;
                optional< Term > bestSol;
                bool ambiguous = false;

                auto callPat = VEC( args, HOLE( "_"_sid ) );
                auto us = FindBestUnification( tf->signature(), callPat, c );

                if( holds_alternative< NoUnification >( us ) )
                {
                    // TODO display details
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                        "template function arguments mismatch." );
                    return PoisonValue();
                }

                if( holds_alternative< AmbiguousUnification >( us ) )
                {
                    // TODO display details
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                        "ambiguous function call." );
                    return PoisonValue();
                }

                auto&& [s,uc] = get< UniSol >( us );

                return invoke( c, loc, callee, args, s, uc );
            }

            Value invoke( const Context& c, uint32_t loc, const Value& callee, const Term& args, const Term& unifiedCallPat, UnificationContext& uc ) const final
            {
                auto callDecomp = Decompose( unifiedCallPat,
                    Vec(
                        SubTerm(),  // args
                        SubTerm()   // return type
                    )
                );

                auto&& [unifiedArgs, unifiedRType] = *callDecomp;

                DiagnosticsContext dc( loc, loc ?  "Called here." : "", false );

                auto instanceFunc = InstantiateTFunc( c, callee, unifiedCallPat, uc );
                if( instanceFunc.isPoison() )
                    return PoisonValue();

                return GetFuncInvocationRule()->resolveInvocation( c, loc, instanceFunc, args );
            }

            optional< Term > getSignature( const Value& callee ) const final
            {
                auto tfuncVal = FromValue< TFunc >( callee );
                assert( tfuncVal );
                return tfuncVal->signature();
            }

            Value prepareFunc( const Context& c, uint32_t funcValLocation, const Value& callee, const Term& unifiedCallPat, UnificationContext& uc ) const final
            {
                DiagnosticsContext dc( funcValLocation, funcValLocation ? "Instantiated here." : "", false );
                return InstantiateTFunc( c, callee, unifiedCallPat, uc );
            }
    };

    ptr< InvocationRule >& GetTFuncInvocationRule()
    {
        static ptr< InvocationRule > pRule = make_shared< TemplateFunctionInvocationRule >();
        return pRule;







|




|









|







|

|


|












|













|


|







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
    {
        public:
            Value resolveInvocation( const Context& c, uint32_t loc, const Value& callee, const Term& args ) const final
            {
                auto tf = FromValue< TFunc >( callee );
                assert( tf );

                optional< TypeCheckingContext > bestTCC;
                optional< Term > bestSol;
                bool ambiguous = false;

                auto callPat = VEC( args, HOLE( "_"_sid ) );
                auto us = FindBestTyping( tf->signature(), callPat, c );

                if( holds_alternative< NoUnification >( us ) )
                {
                    // TODO display details
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                        "template function arguments mismatch." );
                    return PoisonValue();
                }

                if( holds_alternative< AmbiguousTypeCheck >( us ) )
                {
                    // TODO display details
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                        "ambiguous function call." );
                    return PoisonValue();
                }

                auto&& [s,tcc] = get< TCSol >( us );

                return invoke( c, loc, callee, args, s, tcc );
            }

            Value invoke( const Context& c, uint32_t loc, const Value& callee, const Term& args, const Term& unifiedCallPat, TypeCheckingContext& tcc ) const final
            {
                auto callDecomp = Decompose( unifiedCallPat,
                    Vec(
                        SubTerm(),  // args
                        SubTerm()   // return type
                    )
                );

                auto&& [unifiedArgs, unifiedRType] = *callDecomp;

                DiagnosticsContext dc( loc, loc ?  "Called here." : "", false );

                auto instanceFunc = InstantiateTFunc( c, callee, unifiedCallPat, tcc );
                if( instanceFunc.isPoison() )
                    return PoisonValue();

                return GetFuncInvocationRule()->resolveInvocation( c, loc, instanceFunc, args );
            }

            optional< Term > getSignature( const Value& callee ) const final
            {
                auto tfuncVal = FromValue< TFunc >( callee );
                assert( tfuncVal );
                return tfuncVal->signature();
            }

            Value prepareFunc( const Context& c, uint32_t funcValLocation, const Value& callee, const Term& unifiedCallPat, TypeCheckingContext& tcc ) const final
            {
                DiagnosticsContext dc( funcValLocation, funcValLocation ? "Instantiated here." : "", false );
                return InstantiateTFunc( c, callee, unifiedCallPat, tcc );
            }
    };

    ptr< InvocationRule >& GetTFuncInvocationRule()
    {
        static ptr< InvocationRule > pRule = make_shared< TemplateFunctionInvocationRule >();
        return pRule;
Changes to bs/builtins/types/template/rules.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
#include "builtins/builtins.h"

namespace goose::builtins
{
    static void setupTVar( const Context& c, UnificationContext& uc, const StringId& name )
    {
        // By convention, the callee's pattern will always be on the lhs of the unification,
        // which means that the captures TVars will always be in the "left hand side" namespace
        // of the unification context.
        auto varIndex = uc.getLHSHoleIndex( name );
        if( varIndex == UnificationContext::InvalidIndex )
            return;

        // The captured value may be missing: unresolved TVars make sense when second order polymorphism
        // is involved.
        // For instance, if we have a function whose signature is this:
        // void( void( $T foo ) $F ), $T can't be resolved, as the function that we pass
        // may be polymorphic over this parameter. In this case, it doesn't make sense to use $T inside of the
        // function anyway.
        // TODO: in this case setup a special value provider for $T that will emit a specific diagnostic
        // to explain why $T doesn't have a value.
        auto capturedValue = uc.getValue( varIndex );
        if( !capturedValue )
            return;

        auto valueToStore = Substitute( *capturedValue, uc );
        auto captureIdentity = AppendToVectorTerm( c.identity(),
            TERM( "$$"_sid ), TERM( name ) );

        c.env()->storeValue( captureIdentity, ANYTERM( _ ), valueToStore );

        // If the same TVar was present multiple time in the
        // function's signature, its setup will be called multiple time, so
        // to make sure we only store it once, erase the variable's name from
        // the unification context.
        uc.eraseLHSName( name );
    }

    class DeclTemplateRule : public TemplateRule
    {
        optional< Term > buildSignature( const Context& c, const Value& val ) const final
        {
            auto decl = FromValue< Decl >( val );




|




|
|










|



|









|







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
#include "builtins/builtins.h"

namespace goose::builtins
{
    static void setupTVar( const Context& c, TypeCheckingContext& tcc, const StringId& name )
    {
        // By convention, the callee's pattern will always be on the lhs of the unification,
        // which means that the captures TVars will always be in the "left hand side" namespace
        // of the unification context.
        auto varIndex = tcc.getLHSHoleIndex( name );
        if( varIndex == TypeCheckingContext::InvalidIndex )
            return;

        // The captured value may be missing: unresolved TVars make sense when second order polymorphism
        // is involved.
        // For instance, if we have a function whose signature is this:
        // void( void( $T foo ) $F ), $T can't be resolved, as the function that we pass
        // may be polymorphic over this parameter. In this case, it doesn't make sense to use $T inside of the
        // function anyway.
        // TODO: in this case setup a special value provider for $T that will emit a specific diagnostic
        // to explain why $T doesn't have a value.
        auto capturedValue = tcc.getValue( varIndex );
        if( !capturedValue )
            return;

        auto valueToStore = Substitute( *capturedValue, tcc );
        auto captureIdentity = AppendToVectorTerm( c.identity(),
            TERM( "$$"_sid ), TERM( name ) );

        c.env()->storeValue( captureIdentity, ANYTERM( _ ), valueToStore );

        // If the same TVar was present multiple time in the
        // function's signature, its setup will be called multiple time, so
        // to make sure we only store it once, erase the variable's name from
        // the unification context.
        tcc.eraseLHSName( name );
    }

    class DeclTemplateRule : public TemplateRule
    {
        optional< Term > buildSignature( const Context& c, const Value& val ) const final
        {
            auto decl = FromValue< Decl >( val );
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
        {
            auto tnd = FromValue< TNamedDecl >( param );
            assert( tnd );

            return ToValue( Decl( arg.type(), tnd->name() ) );
        }

        void setup( const Context& c, UnificationContext& uc, const Value& val ) const final
        {
            auto tnd = FromValue< TNamedDecl >( val );
            assert( tnd );
            TemplateSetup( c, uc, tnd->type() );
        }

        optional< Term > buildArgPattern( const Context& c, const Value& val ) const final
        {
            auto tnd = FromValue< TNamedDecl >( val );
            assert( tnd );

            auto typeSig = BuildTemplateArgPattern( c, tnd->type() );
            if( !typeSig )
                return nullopt;

            return ValueToIRExpr( ValuePattern( TSID( computed ), *typeSig, TERM( ptr< void >() ) ) );
        }
    };

    class TDeclTemplateRule : public TemplateRule
    {
        optional< Term > buildSignature( const Context& c, const Value& val ) const final
        {
            return ParamPatFromTerm( ValueToIRExpr( val ) );
        }

        Value buildParamDecl( const Context& c, const Value& param, const Value& arg ) const final
        {
            return arg;
        }

        void setup( const Context& c, UnificationContext& uc, const Value& val ) const final
        {
            auto tdecl = FromValue< TDecl >( val );
            assert( tdecl );

            TemplateSetup( c, uc, tdecl->type() );
            setupTVar( c, uc, tdecl->name() );
        }

        optional< Term > buildArgPattern( const Context& c, const Value& val ) const final
        {
            return buildSignature( c, val );
        }
    };

    class TVarTemplateRule : public TemplateRule
    {
        optional< Term > buildSignature( const Context& c, const Value& val ) const final
        {
            auto tvar = FromValue< TVar >( val );
            assert( tvar );
            return ParamPatFromTerm( HOLE( tvar->name() ) );
        }

        Value buildParamDecl( const Context& c, const Value& param, const Value& arg ) const final
        {
            return arg;
        }

        void setup( const Context& c, UnificationContext& uc, const Value& val ) const final
        {
            auto tvar = FromValue< TVar >( val );
            assert( tvar );
            setupTVar( c, uc, tvar->name() );
        }

        optional< Term > buildArgPattern( const Context& c, const Value& val ) const final
        {
            return buildSignature( c, val );
        }
    };







|



|



















|







|




|
|














|







|



|







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
        {
            auto tnd = FromValue< TNamedDecl >( param );
            assert( tnd );

            return ToValue( Decl( arg.type(), tnd->name() ) );
        }

        void setup( const Context& c, TypeCheckingContext& tcc, const Value& val ) const final
        {
            auto tnd = FromValue< TNamedDecl >( val );
            assert( tnd );
            TemplateSetup( c, tcc, tnd->type() );
        }

        optional< Term > buildArgPattern( const Context& c, const Value& val ) const final
        {
            auto tnd = FromValue< TNamedDecl >( val );
            assert( tnd );

            auto typeSig = BuildTemplateArgPattern( c, tnd->type() );
            if( !typeSig )
                return nullopt;

            return ValueToIRExpr( ValuePattern( TSID( computed ), *typeSig, TERM( ptr< void >() ) ) );
        }
    };

    class TDeclTemplateRule : public TemplateRule
    {
        optional< Term > buildSignature( const Context& c, const Value& val ) const final
        {
            return ValueToIRExpr( val );
        }

        Value buildParamDecl( const Context& c, const Value& param, const Value& arg ) const final
        {
            return arg;
        }

        void setup( const Context& c, TypeCheckingContext& tcc, const Value& val ) const final
        {
            auto tdecl = FromValue< TDecl >( val );
            assert( tdecl );

            TemplateSetup( c, tcc, tdecl->type() );
            setupTVar( c, tcc, tdecl->name() );
        }

        optional< Term > buildArgPattern( const Context& c, const Value& val ) const final
        {
            return buildSignature( c, val );
        }
    };

    class TVarTemplateRule : public TemplateRule
    {
        optional< Term > buildSignature( const Context& c, const Value& val ) const final
        {
            auto tvar = FromValue< TVar >( val );
            assert( tvar );
            return HOLE( tvar->name() );
        }

        Value buildParamDecl( const Context& c, const Value& param, const Value& arg ) const final
        {
            return arg;
        }

        void setup( const Context& c, TypeCheckingContext& tcc, const Value& val ) const final
        {
            auto tvar = FromValue< TVar >( val );
            assert( tvar );
            setupTVar( c, tcc, tvar->name() );
        }

        optional< Term > buildArgPattern( const Context& c, const Value& val ) const final
        {
            return buildSignature( c, val );
        }
    };
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
            {
                if( auto s = BuildTemplateSignature( c, x ) )
                    v->append( move( *s ) );
                else
                    v->append( x );
            }

            return ParamPatFromTerm( v );
        }

        Value buildParamDecl( const Context& c, const Value& param, const Value& arg ) const final
        {
            return arg;
        }

        void setup( const Context& c, UnificationContext& uc, const Value& val ) const final
        {
            auto tvec = FromValue< TVec >( val );
            assert( tvec );

            for( auto&& x : tvec->content()->terms() )
                TemplateSetup( c, uc, x );
        }

        optional< Term > buildArgPattern( const Context& c, const Value& val ) const final
        {
            auto tvec = FromValue< TVec >( val );
            assert( tvec );








|







|





|







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
            {
                if( auto s = BuildTemplateSignature( c, x ) )
                    v->append( move( *s ) );
                else
                    v->append( x );
            }

            return v;
        }

        Value buildParamDecl( const Context& c, const Value& param, const Value& arg ) const final
        {
            return arg;
        }

        void setup( const Context& c, TypeCheckingContext& tcc, const Value& val ) const final
        {
            auto tvec = FromValue< TVec >( val );
            assert( tvec );

            for( auto&& x : tvec->content()->terms() )
                TemplateSetup( c, tcc, x );
        }

        optional< Term > buildArgPattern( const Context& c, const Value& val ) const final
        {
            auto tvec = FromValue< TVec >( val );
            assert( tvec );

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
270
271
        }

        Value buildParamDecl( const Context& c, const Value& val, const Value& arg ) const final
        {
            return val;
        }

        void setup( const Context& c, UnificationContext& uc, const Value& val ) const final
        {
            TemplateSetup( c, uc, val.type() );
            TemplateSetup( c, uc, val.val() );
        }

        optional< Term > buildArgPattern( const Context& c, const Value& val ) const final
        {
            return buildSignature( c, val );
        }
    };

    class TFuncTypeTemplateRule : public TemplateRule
    {
        optional< Term > buildSignature( const Context& c, const Value& val ) const final
        {
            return ParamPatFromTerm( ValueToIRExpr( val ) );
        }

        Value buildParamDecl( const Context& c, const Value& param, const Value& arg ) const final
        {
            return arg;
        }

        void setup( const Context& c, UnificationContext& uc, const Value& val ) const final
        {
            auto tft = FromValue< TFuncType >( val );
            assert( tft );

            ForEachInVectorTerm( tft->params(), [&]( auto&& param )
            {
                 TemplateSetup( c, uc, param );
                 return true;
            } );

            TemplateSetup( c, uc, tft->returnType() );
        }

        optional< Term > buildArgPattern( const Context& c, const Value& val ) const final
        {
            return buildSignature( c, val );
        }
    };







|

|
|












|







|






|



|







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
270
271
        }

        Value buildParamDecl( const Context& c, const Value& val, const Value& arg ) const final
        {
            return val;
        }

        void setup( const Context& c, TypeCheckingContext& tcc, const Value& val ) const final
        {
            TemplateSetup( c, tcc, val.type() );
            TemplateSetup( c, tcc, val.val() );
        }

        optional< Term > buildArgPattern( const Context& c, const Value& val ) const final
        {
            return buildSignature( c, val );
        }
    };

    class TFuncTypeTemplateRule : public TemplateRule
    {
        optional< Term > buildSignature( const Context& c, const Value& val ) const final
        {
            return ValueToIRExpr( val );
        }

        Value buildParamDecl( const Context& c, const Value& param, const Value& arg ) const final
        {
            return arg;
        }

        void setup( const Context& c, TypeCheckingContext& tcc, const Value& val ) const final
        {
            auto tft = FromValue< TFuncType >( val );
            assert( tft );

            ForEachInVectorTerm( tft->params(), [&]( auto&& param )
            {
                 TemplateSetup( c, tcc, param );
                 return true;
            } );

            TemplateSetup( c, tcc, tft->returnType() );
        }

        optional< Term > buildArgPattern( const Context& c, const Value& val ) const final
        {
            return buildSignature( c, val );
        }
    };
Name change from bs/builtins/types/template/uni-tdecl.cpp to bs/builtins/types/template/tc-tdecl.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
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
#include "builtins/builtins.h"

namespace goose::builtins
{
    Term BuildArgPatternFromTDecl( const TDecl& td )
    {
        return ValueToIRExpr( ValuePattern( HOLE( "_"_sid ), td.type(), HOLE( "_"_sid ) ) );
    }

    UniGen UnifyTDecl( const Term& lhs, const Term& rhs, UnificationContext uc )
    {
        auto tdecl = FromValue< TDecl >( *ValueFromIRExpr( lhs ) );
        assert( tdecl );

        auto tdeclHole = HOLE( tdecl->name() );

        auto pat = ValueToIRExpr( Value( tdecl->type(), HOLE( "_"_sid ) ) );

        // We are replacing lhs with a different terms and re-unifying,
        // so update the complexity accordingly. The structure of the tdecl
        // shouldn't count, only its pattern.
        uc.subComplexity( GetComplexity( lhs ) );
        uc.addComplexity( GetComplexity( pat ) );

        for( auto&& [s,uc] : Unify( pat, rhs, uc ) )
        {
            // We need to unify the result with a hole named after the decl. However, since both sides of
            // this unification orignally appeared on the LHS, we need to setup RHS to alias the LHS namespace for this.
            auto savedRHSNamespaceIndex = uc.RHSNamespaceIndex();
            uc.setRHSNamespaceIndex( uc.LHSNamespaceIndex() );

            uc.subComplexity( GetComplexity( pat ) + GetComplexity( rhs ) );
            uc.addComplexity( GetComplexity( s ) );

            for( auto&& [s,uc] : Unify( s, tdeclHole, uc ) )
            {
                uc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
                co_yield { s, uc };
            }
        }
    }

    void SetupTDeclUnification( Env& e )
    {
        auto tDeclPat = ValueToIRExpr( Value( GetValueType< TDecl >(), VEC( ANYTERM( _ ), ANYTERM( _ ) ) ) );

        e.unificationRuleSet()->addHalfUnificationRule( URINFOS, tDeclPat,
            []( const Term& lhs, const UnificationContext& c ) -> UniGen
            {
                auto tdecl = FromValue< TDecl >( *ValueFromIRExpr( lhs ) );
                assert( tdecl );
                return HalfUnify( tdecl->type(), c );
            } );

        e.unificationRuleSet()->addSymRule( URINFOS, tDeclPat, ANYTERM( _ ), UnifyTDecl );
        e.unificationRuleSet()->addSymRule( URINFOS, tDeclPat, UnifyTDecl );

        // tfunc tdecl param / tfunc arg
        auto tFuncTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( texpr ), TSID( tfunc ),
            ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );

        auto tDeclTFuncPat = ParamPat( GetValueType< TDecl >(), VEC( tFuncTypePat, ANYTERM( _ ) ) );

        e.unificationRuleSet()->addSymRule( URINFOS,

            tDeclTFuncPat,

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                move( tFuncTypePat ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, UnificationContext uc ) -> UniGen
        {
            auto tdecl = FromValue< TDecl >( *ValueFromIRExpr( lhs ) );
            assert( tdecl );

            auto tfuncType = FromValue< TFuncType >( *ValueFromIRExpr( tdecl->type() ) );
            assert( tfuncType );

            auto callPat = BuildArgPatternFromTDecl( *tdecl );
            auto tdeclHole = HOLE( tdecl->name() );

            auto rhsVal = *ValueFromIRExpr( rhs );

            auto constraintPat = BuildTFuncSignature( uc.context(), *tfuncType );
            assert( constraintPat );

            ConstrainedFunc cfunc( *constraintPat, GetTFuncInvocationRule(), rhsVal );
            auto cFuncTerm = ValueToIRExpr( ToValue( move( cfunc ) ) );

            // Create a new named hole namespace to isolate holes from the passed function from those in
            // the called function.
            auto savedRHSNamespaceIndex = uc.RHSNamespaceIndex();
            uc.setRHSNamespaceIndex( uc.newNamespaceIndex() );

            auto oldValueRequired = uc.isValueResolutionRequired();
            uc.setValueResolutionRequired( false );

            // We are replacing lhs with a different terms and re-unifying,
            // so update the complexity accordingly. The structure of the tdecl
            // shouldn't count, only its pattern.
            uc.subComplexity( GetComplexity( lhs ) );
            uc.addComplexity( GetComplexity( callPat ) );

            for( auto&& [s, uc] : Unify( callPat, rhs, uc ) )
            {
                // Restore the namespace
                uc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
                uc.setValueResolutionRequired( oldValueRequired );

                // We need to unify the result with a hole named after the decl. However, since both sides of
                // this unification orignally appeared on the LHS, we need to setup RHS to alias the LHS namespace for this.
                uc.setRHSNamespaceIndex( uc.LHSNamespaceIndex() );

                for( auto&& [s,uc] : Unify( cFuncTerm, tdeclHole, uc ) )
                {
                    uc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
                    co_yield { s, uc };
                }
            }
        } );

        // tfunc tdecl param / overloadset arg
        e.unificationRuleSet()->addSymRule( URINFOS,

            move( tDeclTFuncPat ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                GetValueType< ptr< builtins::OverloadSet > >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, UnificationContext uc ) -> UniGen
        {
            auto tdecl = FromValue< TDecl >( *ValueFromIRExpr( lhs ) );
            assert( tdecl );

            auto tfuncType = FromValue< TFuncType >( *ValueFromIRExpr( tdecl->type() ) );
            assert( tfuncType );

            auto callPat = BuildArgPatternFromTDecl( *tdecl );
            auto tdeclHole = HOLE( tdecl->name() );

            auto rhsVal = *ValueFromIRExpr( rhs );

            auto constraintPat = BuildTFuncSignature( uc.context(), *tfuncType );
            assert( constraintPat );

            ConstrainedFunc cfunc( *constraintPat, GetOverloadSetInvocationRule(), rhsVal );
            auto cFuncTerm = ValueToIRExpr( ToValue( move( cfunc ) ) );

            // Create a new named hole namespace to isolate holes from the passed function from those in
            // the called function.
            auto savedRHSNamespaceIndex = uc.RHSNamespaceIndex();
            uc.setRHSNamespaceIndex( uc.newNamespaceIndex() );

            auto oldValueRequired = uc.isValueResolutionRequired();
            uc.setValueResolutionRequired( false );

            // We are replacing lhs with a different terms and re-unifying,
            // so update the complexity accordingly. The structure of the tdecl
            // shouldn't count, only its pattern.
            uc.subComplexity( GetComplexity( lhs ) );
            uc.addComplexity( GetComplexity( callPat ) );

            for( auto&& [s, uc] : Unify( callPat, rhs, uc ) )
            {
                // Restore the namespace
                uc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
                uc.setValueResolutionRequired( oldValueRequired );

                // We need to unify the result with a hole named after the decl. However, since both sides of
                // this unification orignally appeared on the LHS, we need to setup RHS to alias the LHS namespace for this.
                uc.setRHSNamespaceIndex( uc.LHSNamespaceIndex() );

                for( auto&& [s,uc] : Unify( cFuncTerm, tdeclHole, uc ) )
                {
                    uc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
                    co_yield { s, uc };
                }
            }
        } );
    }
}









|











|
|

|



|
|

|
|

|

|
|




|



|
|






|
|







|








|












|







|
|

|
|




|
|

|


|
|



|

|

|
|





|








|












|







|
|

|
|




|
|

|


|
|



|

|

|
|





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
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
#include "builtins/builtins.h"

namespace goose::builtins
{
    Term BuildArgPatternFromTDecl( const TDecl& td )
    {
        return ValueToIRExpr( ValuePattern( HOLE( "_"_sid ), td.type(), HOLE( "_"_sid ) ) );
    }

    TCGen UnifyTDecl( const Term& lhs, const Term& rhs, TypeCheckingContext tcc )
    {
        auto tdecl = FromValue< TDecl >( *ValueFromIRExpr( lhs ) );
        assert( tdecl );

        auto tdeclHole = HOLE( tdecl->name() );

        auto pat = ValueToIRExpr( Value( tdecl->type(), HOLE( "_"_sid ) ) );

        // We are replacing lhs with a different terms and re-unifying,
        // so update the complexity accordingly. The structure of the tdecl
        // shouldn't count, only its pattern.
        tcc.subComplexity( GetComplexity( lhs ) );
        tcc.addComplexity( GetComplexity( pat ) );

        for( auto&& [s,tcc] : Unify( pat, rhs, tcc ) )
        {
            // We need to unify the result with a hole named after the decl. However, since both sides of
            // this unification orignally appeared on the LHS, we need to setup RHS to alias the LHS namespace for this.
            auto savedRHSNamespaceIndex = tcc.RHSNamespaceIndex();
            tcc.setRHSNamespaceIndex( tcc.LHSNamespaceIndex() );

            tcc.subComplexity( GetComplexity( pat ) + GetComplexity( rhs ) );
            tcc.addComplexity( GetComplexity( s ) );

            for( auto&& [s,tcc] : TypeCheck( s, tdeclHole, tcc ) )
            {
                tcc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
                co_yield { s, tcc };
            }
        }
    }

    void SetupTDeclTypeChecking( Env& e )
    {
        auto tDeclPat = ValueToIRExpr( Value( GetValueType< TDecl >(), VEC( ANYTERM( _ ), ANYTERM( _ ) ) ) );

        e.typeCheckingRuleSet()->addHalfUnificationRule( TCRINFOS, tDeclPat,
            []( const Term& lhs, const TypeCheckingContext& c ) -> TCGen
            {
                auto tdecl = FromValue< TDecl >( *ValueFromIRExpr( lhs ) );
                assert( tdecl );
                return HalfUnify( tdecl->type(), c );
            } );

        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS, tDeclPat, ANYTERM( _ ), UnifyTDecl );
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS, tDeclPat, tDeclPat, UnifyTDecl );

        // tfunc tdecl param / tfunc arg
        auto tFuncTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( texpr ), TSID( tfunc ),
            ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );

        auto tDeclTFuncPat = ParamPat( GetValueType< TDecl >(), VEC( tFuncTypePat, ANYTERM( _ ) ) );

        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            tDeclTFuncPat,

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                move( tFuncTypePat ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
        {
            auto tdecl = FromValue< TDecl >( *ValueFromIRExpr( lhs ) );
            assert( tdecl );

            auto tfuncType = FromValue< TFuncType >( *ValueFromIRExpr( tdecl->type() ) );
            assert( tfuncType );

            auto callPat = BuildArgPatternFromTDecl( *tdecl );
            auto tdeclHole = HOLE( tdecl->name() );

            auto rhsVal = *ValueFromIRExpr( rhs );

            auto constraintPat = BuildTFuncSignature( tcc.context(), *tfuncType );
            assert( constraintPat );

            ConstrainedFunc cfunc( *constraintPat, GetTFuncInvocationRule(), rhsVal );
            auto cFuncTerm = ValueToIRExpr( ToValue( move( cfunc ) ) );

            // Create a new named hole namespace to isolate holes from the passed function from those in
            // the called function.
            auto savedRHSNamespaceIndex = tcc.RHSNamespaceIndex();
            tcc.setRHSNamespaceIndex( tcc.newNamespaceIndex() );

            auto oldValueRequired = tcc.isValueResolutionRequired();
            tcc.setValueResolutionRequired( false );

            // We are replacing lhs with a different terms and re-unifying,
            // so update the complexity accordingly. The structure of the tdecl
            // shouldn't count, only its pattern.
            tcc.subComplexity( GetComplexity( lhs ) );
            tcc.addComplexity( GetComplexity( callPat ) );

            for( auto&& [s, tcc] : TypeCheck( callPat, rhs, tcc ) )
            {
                // Restore the namespace
                tcc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
                tcc.setValueResolutionRequired( oldValueRequired );

                // We need to unify the result with a hole named after the decl. However, since both sides of
                // this unification orignally appeared on the LHS, we need to setup RHS to alias the LHS namespace for this.
                tcc.setRHSNamespaceIndex( tcc.LHSNamespaceIndex() );

                for( auto&& [s,tcc] : Unify( cFuncTerm, tdeclHole, tcc ) )
                {
                    tcc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
                    co_yield { s, tcc };
                }
            }
        } );

        // tfunc tdecl param / overloadset arg
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            move( tDeclTFuncPat ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                GetValueType< ptr< builtins::OverloadSet > >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
        {
            auto tdecl = FromValue< TDecl >( *ValueFromIRExpr( lhs ) );
            assert( tdecl );

            auto tfuncType = FromValue< TFuncType >( *ValueFromIRExpr( tdecl->type() ) );
            assert( tfuncType );

            auto callPat = BuildArgPatternFromTDecl( *tdecl );
            auto tdeclHole = HOLE( tdecl->name() );

            auto rhsVal = *ValueFromIRExpr( rhs );

            auto constraintPat = BuildTFuncSignature( tcc.context(), *tfuncType );
            assert( constraintPat );

            ConstrainedFunc cfunc( *constraintPat, GetOverloadSetInvocationRule(), rhsVal );
            auto cFuncTerm = ValueToIRExpr( ToValue( move( cfunc ) ) );

            // Create a new named hole namespace to isolate holes from the passed function from those in
            // the called function.
            auto savedRHSNamespaceIndex = tcc.RHSNamespaceIndex();
            tcc.setRHSNamespaceIndex( tcc.newNamespaceIndex() );

            auto oldValueRequired = tcc.isValueResolutionRequired();
            tcc.setValueResolutionRequired( false );

            // We are replacing lhs with a different terms and re-unifying,
            // so update the complexity accordingly. The structure of the tdecl
            // shouldn't count, only its pattern.
            tcc.subComplexity( GetComplexity( lhs ) );
            tcc.addComplexity( GetComplexity( callPat ) );

            for( auto&& [s, tcc] : TypeCheck( callPat, rhs, tcc ) )
            {
                // Restore the namespace
                tcc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
                tcc.setValueResolutionRequired( oldValueRequired );

                // We need to unify the result with a hole named after the decl. However, since both sides of
                // this unification orignally appeared on the LHS, we need to setup RHS to alias the LHS namespace for this.
                tcc.setRHSNamespaceIndex( tcc.LHSNamespaceIndex() );

                for( auto&& [s,tcc] : Unify( cFuncTerm, tdeclHole, tcc ) )
                {
                    tcc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
                    co_yield { s, tcc };
                }
            }
        } );
    }
}
Changes to bs/builtins/types/template/tdecl.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TDECL_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TDECL_H

namespace goose::builtins
{
    extern void SetupTDeclUnification( Env& e );
    extern UniGen UnifyTDecl( const Term& lhs, const Term& rhs, UnificationContext c );
    extern Term BuildArgPatternFromTDecl( const Term& td );

    class TDecl
    {
        public:
            template< typename T >
            TDecl( T&& type, const StringId& name ) :





|
|







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

namespace goose::builtins
{
    extern void SetupTDeclTypeChecking( Env& e );
    extern TCGen UnifyTDecl( const Term& lhs, const Term& rhs, TypeCheckingContext c );
    extern Term BuildArgPatternFromTDecl( const Term& td );

    class TDecl
    {
        public:
            template< typename T >
            TDecl( T&& type, const StringId& name ) :
Changes to bs/builtins/types/template/tfunc.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TFUNC_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TFUNC_H

namespace goose::builtins
{
    extern ptr< InvocationRule >& GetTFuncInvocationRule();
    extern void SetupTemplateFunctionInvocationRule( Env& e );
    extern void SetupTemplateFunctionUnification( Env& e );

    class TFunc
    {
        public:
            template< typename T, typename S, typename I, typename V >
            TFunc( T&& type, S&& signature, I&& identity, V&& toks ) :
                m_type( forward< T >( type ) ),







|







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

namespace goose::builtins
{
    extern ptr< InvocationRule >& GetTFuncInvocationRule();
    extern void SetupTemplateFunctionInvocationRule( Env& e );
    extern void SetupTemplateFunctionTypeChecking( Env& e );

    class TFunc
    {
        public:
            template< typename T, typename S, typename I, typename V >
            TFunc( T&& type, S&& signature, I&& identity, V&& toks ) :
                m_type( forward< T >( type ) ),
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
        private:
            TFuncType   m_type;
            Term        m_signature;
            Term        m_identity;
            ptr< void > m_toks;
    };

    extern Value InstantiateTFunc( const Context& c, const Value& callee, const Term& unifiedCallPat, const UnificationContext& uc );
}

namespace goose::ir
{
    template<>
    struct Bridge< builtins::TFunc >
    {







|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
        private:
            TFuncType   m_type;
            Term        m_signature;
            Term        m_identity;
            ptr< void > m_toks;
    };

    extern Value InstantiateTFunc( const Context& c, const Value& callee, const Term& unifiedCallPat, const TypeCheckingContext& tcc );
}

namespace goose::ir
{
    template<>
    struct Bridge< builtins::TFunc >
    {
Name change from bs/builtins/types/template/unify.cpp to bs/builtins/types/template/typecheck.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/builtins.h"

using namespace goose;
using namespace goose::ir;

namespace goose::builtins
{
    void SetupTemplateFunctionUnification( Env& e )
    {
        auto funcTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( func ),
            ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( VA ) ) ) );

        auto tFuncTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( texpr ), TSID( tfunc ),
            ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );

        // func type param / tfunc arg
        e.unificationRuleSet()->addSymRule( URINFOS,

            ParamPat( move( funcTypePat ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                tFuncTypePat,
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, UnificationContext uc ) -> UniGen
        {
            auto ldecomp = Decompose( lhs,
                Vec(
                    Lit( "value"_sid ),
                    SubTerm(),
                    SubTerm(),
                    SubTerm(),
                    Val< LocationId >()
                )
            );
            assert( ldecomp );

            auto&& [sort, type, val, locId] = *ldecomp;
            auto callPat = BuildCallPatternFromFuncType( *ValueFromIRExpr( type ) );

            auto rhsVal = *ValueFromIRExpr( rhs );
            auto tf = *FromValue< TFunc >( rhsVal );

            // Create a new named hole namespace to isolate holes from the passed function from those in
            // the called function.
            auto savedRHSNamespaceIndex = uc.RHSNamespaceIndex();
            auto localNSId = uc.newNamespaceIndex();
            uc.setRHSNamespaceIndex( localNSId );

            for( auto&& [s, uc] : Unify( callPat, tf.signature(), uc ) )
            {
                // Restore the namespace
                uc.setRHSNamespaceIndex( savedRHSNamespaceIndex );

                auto wrapped = WrapWithPostprocFunc( s, [rhsVal,localNSId]( const Term& t, UnificationContext uc )
                    -> optional< Term >
                {
                    uc.setRHSNamespaceIndex( localNSId );

                    DiagnosticsContext dc( rhsVal.locationId(), rhsVal.locationId() ?  "Instantiated here." : "", false );
                    auto ifunc = InstantiateTFunc( uc.context(), rhsVal, t, uc.flip() );

                    if( ifunc.isPoison() )
                        return nullopt;

                    return ValueToIRExpr( ifunc );
                } );

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

        // tfunc type param / tfunc arg
        e.unificationRuleSet()->addSymRule( URINFOS,

            ParamPat( tFuncTypePat ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                move( tFuncTypePat ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, UnificationContext uc ) -> UniGen
        {
            auto ldecomp = Decompose( lhs,
                Vec(
                    Lit( "value"_sid ),
                    SubTerm(),
                    SubTerm(),
                    SubTerm(),
                    Val< LocationId >()
                )
            );
            assert( ldecomp );

            auto&& [sort, type, val, locId] = *ldecomp;
            auto callPat = BuildArgPatternFromTFuncType( uc.context(), *ValueFromIRExpr( type ) );
            assert( callPat );

            auto rhsVal = *ValueFromIRExpr( rhs );
            auto tf = *FromValue< TFunc >( rhsVal );

            // Create a new named hole namespace to isolate holes from the passed function from those in
            // the called function.
            auto savedRHSNamespaceIndex = uc.RHSNamespaceIndex();
            auto localNSId = uc.newNamespaceIndex();
            uc.setRHSNamespaceIndex( localNSId );

            for( auto&& [s, uc] : Unify( *callPat, tf.signature(), uc ) )
            {
                // Restore the namespace
                uc.setRHSNamespaceIndex( savedRHSNamespaceIndex );

                auto wrapped = WrapWithPostprocFunc( s, [rhsVal,localNSId]( const Term& t, UnificationContext uc )
                    -> optional< Term >
                {
                    uc.setRHSNamespaceIndex( localNSId );

                    DiagnosticsContext dc( rhsVal.locationId(), rhsVal.locationId() ?  "Instantiated here." : "", false );
                    auto ifunc = InstantiateTFunc( uc.context(), rhsVal, t, uc.flip() );

                    if( ifunc.isPoison() )
                        return nullopt;

                    return ValueToIRExpr( ifunc );
                } );

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







|








|








|




















|
|
|

|


|

|


|


|







|




|








|













|







|
|
|

|


|

|


|


|







|




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/builtins.h"

using namespace goose;
using namespace goose::ir;

namespace goose::builtins
{
    void SetupTemplateFunctionTypeChecking( Env& e )
    {
        auto funcTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( func ),
            ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( VA ) ) ) );

        auto tFuncTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( texpr ), TSID( tfunc ),
            ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );

        // func type param / tfunc arg
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ParamPat( move( funcTypePat ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                tFuncTypePat,
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
        {
            auto ldecomp = Decompose( lhs,
                Vec(
                    Lit( "value"_sid ),
                    SubTerm(),
                    SubTerm(),
                    SubTerm(),
                    Val< LocationId >()
                )
            );
            assert( ldecomp );

            auto&& [sort, type, val, locId] = *ldecomp;
            auto callPat = BuildCallPatternFromFuncType( *ValueFromIRExpr( type ) );

            auto rhsVal = *ValueFromIRExpr( rhs );
            auto tf = *FromValue< TFunc >( rhsVal );

            // Create a new named hole namespace to isolate holes from the passed function from those in
            // the called function.
            auto savedRHSNamespaceIndex = tcc.RHSNamespaceIndex();
            auto localNSId = tcc.newNamespaceIndex();
            tcc.setRHSNamespaceIndex( localNSId );

            for( auto&& [s, tcc] : TypeCheck( tf.signature(), callPat, tcc ) )
            {
                // Restore the namespace
                tcc.setRHSNamespaceIndex( savedRHSNamespaceIndex );

                auto wrapped = WrapWithPostprocFunc( s, [rhsVal,localNSId]( const Term& t, TypeCheckingContext tcc )
                    -> optional< Term >
                {
                    tcc.setRHSNamespaceIndex( localNSId );

                    DiagnosticsContext dc( rhsVal.locationId(), rhsVal.locationId() ?  "Instantiated here." : "", false );
                    auto ifunc = InstantiateTFunc( tcc.context(), rhsVal, t, tcc.flip() );

                    if( ifunc.isPoison() )
                        return nullopt;

                    return ValueToIRExpr( ifunc );
                } );

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

        // tfunc type param / tfunc arg
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ParamPat( tFuncTypePat ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                move( tFuncTypePat ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
        {
            auto ldecomp = Decompose( lhs,
                Vec(
                    Lit( "value"_sid ),
                    SubTerm(),
                    SubTerm(),
                    SubTerm(),
                    Val< LocationId >()
                )
            );
            assert( ldecomp );

            auto&& [sort, type, val, locId] = *ldecomp;
            auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *ValueFromIRExpr( type ) );
            assert( callPat );

            auto rhsVal = *ValueFromIRExpr( rhs );
            auto tf = *FromValue< TFunc >( rhsVal );

            // Create a new named hole namespace to isolate holes from the passed function from those in
            // the called function.
            auto savedRHSNamespaceIndex = tcc.RHSNamespaceIndex();
            auto localNSId = tcc.newNamespaceIndex();
            tcc.setRHSNamespaceIndex( localNSId );

            for( auto&& [s, tcc] : TypeCheck( tf.signature(), *callPat,  tcc ) )
            {
                // Restore the namespace
                tcc.setRHSNamespaceIndex( savedRHSNamespaceIndex );

                auto wrapped = WrapWithPostprocFunc( s, [rhsVal,localNSId]( const Term& t, TypeCheckingContext tcc )
                    -> optional< Term >
                {
                    tcc.setRHSNamespaceIndex( localNSId );

                    DiagnosticsContext dc( rhsVal.locationId(), rhsVal.locationId() ?  "Instantiated here." : "", false );
                    auto ifunc = InstantiateTFunc( tcc.context(), rhsVal, t, tcc.flip() );

                    if( ifunc.isPoison() )
                        return nullopt;

                    return ValueToIRExpr( ifunc );
                } );

                co_yield { move( wrapped ), tcc };
            }
        } );
    }
}
Changes to bs/builtins/types/tuple/tuple.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef GOOSE_BUILTINS_TYPES_TUPLE_H
#define GOOSE_BUILTINS_TYPES_TUPLE_H

namespace goose::builtins
{
    extern void SetupTupleUnification( Env& e );
    extern void SetupTupleInitialize( Env& e );
    extern void SetupTupleDestroyValue( Env& e );
    extern void SetupTupleDropValue( Env& e );
    extern void SetupTupleLowering( Env& e );

    extern Value MkTupleType( const Term& state, const Term& types );
    extern Value TupleOfTypesToTupleType( const Value& tup );





|







1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef GOOSE_BUILTINS_TYPES_TUPLE_H
#define GOOSE_BUILTINS_TYPES_TUPLE_H

namespace goose::builtins
{
    extern void SetupTupleTypeChecking( Env& e );
    extern void SetupTupleInitialize( Env& e );
    extern void SetupTupleDestroyValue( Env& e );
    extern void SetupTupleDropValue( Env& e );
    extern void SetupTupleLowering( Env& e );

    extern Value MkTupleType( const Term& state, const Term& types );
    extern Value TupleOfTypesToTupleType( const Value& tup );
Name change from bs/builtins/types/tuple/unify.cpp to bs/builtins/types/tuple/typecheck.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
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
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::ir;

namespace goose::builtins
{
    UniGen UnifyConstantTuple( const UnificationContext& uc, const Value& tupType, const Value& tupArg, uint32_t index, const Value& out )
    {
        auto param = ParamPat( GetTupleTypeElement( tupType, index ) );

        auto argType = GetTupleElementType( tupArg, index );
        auto arg = GetTupleElement( tupArg, index );

        auto tupSize = TupleSize( tupArg );

        for( auto&& [s,uc] : Unify( param, arg, uc ) )
        {
            auto val = ValuePatternFromIRExpr( s );
            assert( val );
            auto newOut = AppendToTuple( out, *val );

            if( index == ( tupSize - 1 ) )
                co_yield { ValueToIRExpr( newOut ), uc };
            else
                co_yield UnifyConstantTuple( uc, tupType, tupArg, index + 1, newOut );
        }
    }

    UniGen UnifyComputedTuple( const UnificationContext& uc, const Value& tupType, const Value& tupArg, const llr::Address& tupAddr, uint32_t index, const Value& out )
    {
        auto param = ParamPat( GetTupleTypeElement( tupType, index ) );

        auto argType = GetTupleElementType( tupArg, index );
        ReferenceType rt( argType, TSID( const ) );
        auto argAddr = tupAddr;
        argAddr.appendToPath( index );

        auto argRef = ValueToIRExpr( BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),
            llr::Load( move( argAddr ), argType ) ) );

        auto tupSize = TupleTypeSize( tupType );

        for( auto&& [s,uc] : Unify( param, argRef, uc ) )
        {
            auto val = ValuePatternFromIRExpr( s );
            assert( val );
            auto newOut = AppendToTuple( out, *val );

            if( index == ( tupSize - 1 ) )
                co_yield { ValueToIRExpr( newOut ), uc };
            else
                co_yield UnifyComputedTuple( uc, tupType, tupArg, tupAddr, index + 1, newOut );
        }
    }

    void SetupTupleUnification( Env& e )
    {
        e.unificationRuleSet()->addSymRule( URINFOS,

            ValueToIRExpr( ValuePattern(
                TSID( param ),
                ValueToIRExpr( MkTupleType( ANYTERM( _ ), VECOFLENGTH( L ) ) ),
                ANYTERM( _ ) ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                ValueToIRExpr( MkTupleType( ANYTERM( _ ), VECOFLENGTH( L ) ) ),
                VECOFLENGTH( L ) ) ),

        []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
        {
            auto ltup = ValuePatternFromIRExpr( lhs );
            auto rtup = ValueFromIRExpr( rhs );

            if( !ltup || !rtup )
                co_return;

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

            co_yield UnifyConstantTuple( uc, tupType, *rtup, 0, EmptyTuple() );
        } );

        e.unificationRuleSet()->addSymRule( URINFOS,

            ValueToIRExpr( ValuePattern(
                TSID( param ),
                ValueToIRExpr( MkTupleType( ANYTERM( _ ), VECOFLENGTH( L ) ) ),
                ANYTERM( _ ) ) ),

            ValueToIRExpr( ValuePattern(
                TSID( computed ),
                ValueToIRExpr( MkTupleType( ANYTERM( _ ), VECOFLENGTH( L ) ) ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
        {
            auto ltup = ValuePatternFromIRExpr( lhs );
            auto rtup = ValueFromIRExpr( rhs );

            if( !ltup || !rtup || !uc.context().codeBuilder() )
                co_return;

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

            auto tempIndex = uc.context().codeBuilder()->cfg()->getNewTemporaryIndex();
            llr::Address addr( llr::TemporaryAddress( tempIndex, *rtup ) );

            co_yield UnifyComputedTuple( uc, tupType, *rtup, addr, 0, EmptyTuple() );
        } );

        // Single element tuple unwrapping rules: if we encounter such a tuple, attempt to unify
        // its contained value with whatever's on the other side.
        e.unificationRuleSet()->addAsymRule( URINFOS,

            ValueToIRExpr( ValuePattern(
                TSID( param ),
                ANYTERM( _ ),
                ANYTERM( _ ) ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                ValueToIRExpr( MkTupleType( ANYTERM( _ ), VEC( ANYTERM( _ ) ) ) ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
        {
            auto tup = ValueFromIRExpr( rhs );
            if( !tup )
                co_return;

            const auto& vec1 = *get< pvec >( tup->val() );
            co_yield Unify( lhs, vec1[0], uc );
        } );

        e.unificationRuleSet()->addAsymRule( URINFOS,

            ValueToIRExpr( ValuePattern(
                TSID( param ),
                ValueToIRExpr( MkTupleType( ANYTERM( _ ), VEC( ANYTERM( _ ) ) ) ),
                ANYTERM( _ ) ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                ANYTERM( _ ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
        {
            auto tup = ValueFromIRExpr( lhs );
            if( !tup )
                co_return;

            const auto& vec1 = *get< pvec >( tup->val() );

            // In some cases, the tuple may not be an actual tuple value but merely
            // a pattern for a tuple value, with a hole instead of its content vector.
            // If it happens, since that hole is represented as a vector, we'll have a longer than
            // 1 vector here. But this means we can't extract the first element anyway (since it
            // doesn't really exist yet), so just yield the tuple as is.
            if( vec1.length().minLength() != 1 )
            {
                co_yield { lhs, uc };
                co_return;
            }

            co_yield Unify( vec1[0], rhs, uc );
        } );

        // When unifying a tuple against a value whose type is a hole, we don't want
        // the above rule to apply (ie we don't want 1 element tuples bound to generic
        // template parameters to be peeled off). So here's a rule for this case which
        // leaves the tuple unchanged. It will take priority over the above rule in
        // those cases because it matches a more specific pattern.
        e.unificationRuleSet()->addSymRule( URINFOS,

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                ValueToIRExpr( MkTupleType( ANYTERM( _ ), VEC( ANYTERM( _ ) ) ) ),
                ANYTERM( _ ) ) ),

            ValueToIRExpr( ValuePattern( ANYTERM( _ ), HolePattern(), ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
        {
            co_yield { lhs, uc };
        } );
    }
}







|








|






|

|



|













|






|

|



|

|


|








|










|


|


|








|




|





|


|


|

|


|








|






|


|


|








|














|



|







|








|

|



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
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
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::ir;

namespace goose::builtins
{
    TCGen UnifyConstantTuple( const TypeCheckingContext& tcc, const Value& tupType, const Value& tupArg, uint32_t index, const Value& out )
    {
        auto param = ParamPat( GetTupleTypeElement( tupType, index ) );

        auto argType = GetTupleElementType( tupArg, index );
        auto arg = GetTupleElement( tupArg, index );

        auto tupSize = TupleSize( tupArg );

        for( auto&& [s,tcc] : TypeCheck( param, arg, tcc ) )
        {
            auto val = ValuePatternFromIRExpr( s );
            assert( val );
            auto newOut = AppendToTuple( out, *val );

            if( index == ( tupSize - 1 ) )
                co_yield { ValueToIRExpr( newOut ), tcc };
            else
                co_yield UnifyConstantTuple( tcc, tupType, tupArg, index + 1, newOut );
        }
    }

    TCGen UnifyComputedTuple( const TypeCheckingContext& tcc, const Value& tupType, const Value& tupArg, const llr::Address& tupAddr, uint32_t index, const Value& out )
    {
        auto param = ParamPat( GetTupleTypeElement( tupType, index ) );

        auto argType = GetTupleElementType( tupArg, index );
        ReferenceType rt( argType, TSID( const ) );
        auto argAddr = tupAddr;
        argAddr.appendToPath( index );

        auto argRef = ValueToIRExpr( BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),
            llr::Load( move( argAddr ), argType ) ) );

        auto tupSize = TupleTypeSize( tupType );

        for( auto&& [s,tcc] : TypeCheck( param, argRef, tcc ) )
        {
            auto val = ValuePatternFromIRExpr( s );
            assert( val );
            auto newOut = AppendToTuple( out, *val );

            if( index == ( tupSize - 1 ) )
                co_yield { ValueToIRExpr( newOut ), tcc };
            else
                co_yield UnifyComputedTuple( tcc, tupType, tupArg, tupAddr, index + 1, newOut );
        }
    }

    void SetupTupleTypeChecking( Env& e )
    {
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                ValueToIRExpr( MkTupleType( ANYTERM( _ ), VECOFLENGTH( L ) ) ),
                ANYTERM( _ ) ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                ValueToIRExpr( MkTupleType( ANYTERM( _ ), VECOFLENGTH( L ) ) ),
                VECOFLENGTH( L ) ) ),

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

            if( !ltup || !rtup )
                co_return;

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

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

        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                ValueToIRExpr( MkTupleType( ANYTERM( _ ), VECOFLENGTH( L ) ) ),
                ANYTERM( _ ) ) ),

            ValueToIRExpr( ValuePattern(
                TSID( computed ),
                ValueToIRExpr( MkTupleType( ANYTERM( _ ), VECOFLENGTH( L ) ) ),
                ANYTERM( _ ) ) ),

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

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

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

            auto tempIndex = tcc.context().codeBuilder()->cfg()->getNewTemporaryIndex();
            llr::Address addr( llr::TemporaryAddress( tempIndex, *rtup ) );

            co_yield UnifyComputedTuple( tcc, tupType, *rtup, addr, 0, EmptyTuple() );
        } );

        // Single element tuple unwrapping rules: if we encounter such a tuple, attempt to typecheck
        // its contained value with whatever's on the other side.
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                ANYTERM( _ ),
                ANYTERM( _ ) ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                ValueToIRExpr( MkTupleType( ANYTERM( _ ), VEC( ANYTERM( _ ) ) ) ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            auto tup = ValueFromIRExpr( rhs );
            if( !tup )
                co_return;

            const auto& vec1 = *get< pvec >( tup->val() );
            co_yield TypeCheck( lhs, vec1[0], tcc );
        } );

        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                ValueToIRExpr( MkTupleType( ANYTERM( _ ), VEC( ANYTERM( _ ) ) ) ),
                ANYTERM( _ ) ) ),

            ValueToIRExpr( ValuePattern(
                TSID( constant ),
                ANYTERM( _ ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            auto tup = ValueFromIRExpr( lhs );
            if( !tup )
                co_return;

            const auto& vec1 = *get< pvec >( tup->val() );

            // In some cases, the tuple may not be an actual tuple value but merely
            // a pattern for a tuple value, with a hole instead of its content vector.
            // If it happens, since that hole is represented as a vector, we'll have a longer than
            // 1 vector here. But this means we can't extract the first element anyway (since it
            // doesn't really exist yet), so just yield the tuple as is.
            if( vec1.length().minLength() != 1 )
            {
                co_yield { lhs, tcc };
                co_return;
            }

            co_yield TypeCheck( vec1[0], rhs, tcc );
        } );

        // When unifying a tuple against a value whose type is a hole, we don't want
        // the above rule to apply (ie we don't want 1 element tuples bound to generic
        // template parameters to be peeled off). So here's a rule for this case which
        // leaves the tuple unchanged. It will take priority over the above rule in
        // those cases because it matches a more specific pattern.
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ValueToIRExpr( ValuePattern(
                ANYTERM( _ ),
                ValueToIRExpr( MkTupleType( ANYTERM( _ ), VEC( ANYTERM( _ ) ) ) ),
                ANYTERM( _ ) ) ),

            ValueToIRExpr( ValuePattern( ANYTERM( _ ), HolePattern(), ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            co_yield { lhs, tcc };
        } );
    }
}
Changes to bs/builtins/types/types.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
#include "builtins/builtins.h"

namespace goose::builtins
{
    void SetupBuiltinTypes( Env& e )
    {
        SetupBasicTypes( e );

        SetupTupleUnification( e );

        SetupFunctionInvocationRule( e );
        SetupFunctionUnification( e );
        SetupFunctionLowering( e );

        SetupTemplateRules( e );
        SetupTemplateFunctionInvocationRule( e );
        SetupTemplateFunctionUnification( e );
        SetupTDeclUnification( e );

        SetupOverloadSetInvocationRule( e );
        SetupOverloadSetUnification( e );

        SetupConstrainedFuncInvocationRule( e );
        SetupConstrainedFuncUnification( e );

        SetupLocalVarDropValue( e );
        SetupLocalVarUnification( e );
        SetupLocalVarInvocationRule( e );

        SetupReferenceUnification( e );

        SetupRuntimeTypes( e );

        SetupInitialize( e );
        SetupDestroyValue( e );
        SetupDropValue( e );
        SetupLower( e );








|


|




|
|


|


|


|


|







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"

namespace goose::builtins
{
    void SetupBuiltinTypes( Env& e )
    {
        SetupBasicTypes( e );

        SetupTupleTypeChecking( e );

        SetupFunctionInvocationRule( e );
        SetupFunctionTypeChecking( e );
        SetupFunctionLowering( e );

        SetupTemplateRules( e );
        SetupTemplateFunctionInvocationRule( e );
        SetupTemplateFunctionTypeChecking( e );
        SetupTDeclTypeChecking( e );

        SetupOverloadSetInvocationRule( e );
        SetupOverloadSetTypeChecking( e );

        SetupConstrainedFuncInvocationRule( e );
        SetupConstrainedFuncTypeChecking( e );

        SetupLocalVarDropValue( e );
        SetupLocalVarTypeChecking( e );
        SetupLocalVarInvocationRule( e );

        SetupReferenceTypeChecking( e );

        SetupRuntimeTypes( e );

        SetupInitialize( e );
        SetupDestroyValue( e );
        SetupDropValue( e );
        SetupLower( e );
Changes to bs/sema/env.cpp.
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
uint32_t Env::ms_nextUniqueId = 0;

// Env just instantiates the builtin unification rule set for now. The rule set will
// need to be extendable by compile time code later on.
Env::Env() :
    m_templateRuleSet( make_shared< TemplateRuleSet >() ),
    m_invocationRuleSet( make_shared< InvocationRuleSet >() ),
    m_unificationRuleSet( make_shared< UnificationRuleSet >() ),
    m_memManager( make_shared< CTMemoryManager >() )
{}

Env::~Env() {}

void Env::storeValue( const Term& idPat, const Term& contextIdPat, const Term& val )
{







|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
uint32_t Env::ms_nextUniqueId = 0;

// Env just instantiates the builtin unification rule set for now. The rule set will
// need to be extendable by compile time code later on.
Env::Env() :
    m_templateRuleSet( make_shared< TemplateRuleSet >() ),
    m_invocationRuleSet( make_shared< InvocationRuleSet >() ),
    m_unificationRuleSet( make_shared< TypeCheckingRuleSet >() ),
    m_memManager( make_shared< CTMemoryManager >() )
{}

Env::~Env() {}

void Env::storeValue( const Term& idPat, const Term& contextIdPat, const Term& val )
{
Changes to bs/sema/env.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef GOOSE_SEMA_ENV_H
#define GOOSE_SEMA_ENV_H

namespace llvm
{
    class LLVMContext;
}

namespace goose::sema
{
    class TemplateRuleSet;
    class InvocationRuleSet;
    class UnificationRuleSet;
    class OverloadSet;

    class Env : public enable_shared_from_this< Env >
    {
        public:
            Env();
            ~Env();












|







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

namespace llvm
{
    class LLVMContext;
}

namespace goose::sema
{
    class TemplateRuleSet;
    class InvocationRuleSet;
    class TypeCheckingRuleSet;
    class OverloadSet;

    class Env : public enable_shared_from_this< Env >
    {
        public:
            Env();
            ~Env();
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

            auto valueStoreVersion() const { return m_valueStoreVersion; }

            void addVisibilityRule( const Term& toImport, const Term& destination, bool transitive = true );

            const auto& templateRuleSet() const { return m_templateRuleSet; }
            const auto& invocationRuleSet() const { return m_invocationRuleSet; }
            const auto& unificationRuleSet() const { return m_unificationRuleSet; }

            auto& templateRuleSet() { return m_templateRuleSet; }
            auto& invocationRuleSet() { return m_invocationRuleSet; }
            auto& unificationRuleSet() { return m_unificationRuleSet; }

            static auto NewUniqueId() { return ms_nextUniqueId++; }

            auto& extDropValue() { return m_extDropValue; }
            const auto& extDropValue() const { return m_extDropValue; }

            auto& extDestroyValue() { return m_extDestroyValue; }







|



|







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

            auto valueStoreVersion() const { return m_valueStoreVersion; }

            void addVisibilityRule( const Term& toImport, const Term& destination, bool transitive = true );

            const auto& templateRuleSet() const { return m_templateRuleSet; }
            const auto& invocationRuleSet() const { return m_invocationRuleSet; }
            const auto& typeCheckingRuleSet() const { return m_unificationRuleSet; }

            auto& templateRuleSet() { return m_templateRuleSet; }
            auto& invocationRuleSet() { return m_invocationRuleSet; }
            auto& typeCheckingRuleSet() { return m_unificationRuleSet; }

            static auto NewUniqueId() { return ms_nextUniqueId++; }

            auto& extDropValue() { return m_extDropValue; }
            const auto& extDropValue() const { return m_extDropValue; }

            auto& extDestroyValue() { return m_extDestroyValue; }
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
                return ms_llrFuncs.back();
            }

        private:
            Trie< ptr< ValueProvider > >        m_valueStore;
            ptr< TemplateRuleSet >              m_templateRuleSet;
            ptr< InvocationRuleSet >            m_invocationRuleSet;
            ptr< UnificationRuleSet >           m_unificationRuleSet;

            ptr< OverloadSet >                  m_extDropValue;
            ptr< OverloadSet >                  m_extDestroyValue;
            ptr< OverloadSet >                  m_extInitialize;

            ptr< OverloadSet >                  m_extLowerTypeForRuntime;
            ptr< OverloadSet >                  m_extLowerConstantForRuntime;







|







74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
                return ms_llrFuncs.back();
            }

        private:
            Trie< ptr< ValueProvider > >        m_valueStore;
            ptr< TemplateRuleSet >              m_templateRuleSet;
            ptr< InvocationRuleSet >            m_invocationRuleSet;
            ptr< TypeCheckingRuleSet >           m_unificationRuleSet;

            ptr< OverloadSet >                  m_extDropValue;
            ptr< OverloadSet >                  m_extDestroyValue;
            ptr< OverloadSet >                  m_extInitialize;

            ptr< OverloadSet >                  m_extLowerTypeForRuntime;
            ptr< OverloadSet >                  m_extLowerConstantForRuntime;
Changes to bs/sema/inv-ruleset.h.
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
            virtual bool canBeInvoked( const Context& c, const Value& callee ) const
            {
                return true;
            }

            virtual Value resolveInvocation( const Context& c, uint32_t locationId, const Value& callee, const Term& args ) const = 0;

            virtual Value invoke( const Context& c, uint32_t locationId, const Value& callee, const Term& args, const Term& unifiedCallPat, UnificationContext& uc ) const
            {
                return PoisonValue();
            }

            virtual optional< Term > getSignature( const Value& callee ) const
            {
                return nullopt;
            }

            virtual Value prepareFunc( const Context& c, uint32_t funcValLocation, const Value& callee, const Term& unifiedCallPat, UnificationContext& uc ) const
            {
                return PoisonValue();
            }
    };

    class InvocationRuleSet
    {







|









|







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
            virtual bool canBeInvoked( const Context& c, const Value& callee ) const
            {
                return true;
            }

            virtual Value resolveInvocation( const Context& c, uint32_t locationId, const Value& callee, const Term& args ) const = 0;

            virtual Value invoke( const Context& c, uint32_t locationId, const Value& callee, const Term& args, const Term& unifiedCallPat, TypeCheckingContext& tcc ) const
            {
                return PoisonValue();
            }

            virtual optional< Term > getSignature( const Value& callee ) const
            {
                return nullopt;
            }

            virtual Value prepareFunc( const Context& c, uint32_t funcValLocation, const Value& callee, const Term& unifiedCallPat, TypeCheckingContext& tcc ) const
            {
                return PoisonValue();
            }
    };

    class InvocationRuleSet
    {
Changes to bs/sema/meson.build.
1
2
3
4
5
6

7
8
9
10
11
12
13
14
15
16
17
18
goose_sema = library( 'goose-sema',
    'env.cpp',

    'hole.cpp',
    'uni-context.cpp',
    'uni-ruleset.cpp',

    'uni-basicrules.cpp',
    'uni-holes.cpp',
    'uni-quote.cpp',
    'uni-postproc.cpp',
    'unify.cpp',
    'substitute.cpp',
    'postprocess.cpp',

    'inv-ruleset.cpp',
    'invocation.cpp',

    'tpl-ruleset.cpp',




|
|
>



<
|







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
18
goose_sema = library( 'goose-sema',
    'env.cpp',

    'hole.cpp',
    'tc-context.cpp',
    'tc-ruleset.cpp',
    'tc-postproc.cpp',
    'uni-basicrules.cpp',
    'uni-holes.cpp',
    'uni-quote.cpp',

    'typecheck.cpp',
    'substitute.cpp',
    'postprocess.cpp',

    'inv-ruleset.cpp',
    'invocation.cpp',

    'tpl-ruleset.cpp',
Changes to bs/sema/overloadset.cpp.
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
            return { pInvRule, callee };
        } );
    } );

    return success;
}

OverloadSet::UniGen OverloadSet::unify( const Term& argsPat, const Term& rtPat, const UnificationContext& uc ) const
{
    auto argDecomp = Decompose( argsPat,
        Val< pvec >()
    );

    if( !argDecomp )
        co_return;

    for( auto&& [paramsVec,uniParamsVec,rtTrie,uc] : m_trie->unify( *argDecomp->get(), uc ) )
    {
        auto params = TERM( make_shared< Vector >( paramsVec ) );
        auto uniParams = TERM( make_shared< Vector >( uniParamsVec ) );

        for( auto&& [rt,ovl] : Enumerate( rtTrie ) )
        {
            auto localC = uc;
            localC.addComplexity( GetComplexity( rt ) + GetComplexity( rtPat ) );

            for( auto&& [uniRt,uc] : Unify( rt, rtPat, localC ) )
            {
                auto uniCall = TERM( Vector::Make( uniParams, uniRt ) );
                co_yield { move( uniCall ), ovl, uc };
            }
        }
    }
}







|








|






|


|


|




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
            return { pInvRule, callee };
        } );
    } );

    return success;
}

OverloadSet::TCGen OverloadSet::typeCheck( const Term& argsPat, const Term& rtPat, const TypeCheckingContext& tcc ) const
{
    auto argDecomp = Decompose( argsPat,
        Val< pvec >()
    );

    if( !argDecomp )
        co_return;

    for( auto&& [paramsVec,uniParamsVec,rtTrie,tcc] : m_trie->typeCheck( *argDecomp->get(), tcc ) )
    {
        auto params = TERM( make_shared< Vector >( paramsVec ) );
        auto uniParams = TERM( make_shared< Vector >( uniParamsVec ) );

        for( auto&& [rt,ovl] : Enumerate( rtTrie ) )
        {
            auto localC = tcc;
            localC.addComplexity( GetComplexity( rt ) + GetComplexity( rtPat ) );

            for( auto&& [uniRt,tcc] : Unify( rt, rtPat, localC ) )
            {
                auto uniCall = TERM( Vector::Make( uniParams, uniRt ) );
                co_yield { move( uniCall ), ovl, tcc };
            }
        }
    }
}
Changes to bs/sema/overloadset.h.
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
            {}

            const auto& identity() const { return m_identity; }

            bool add( const Env& e, const Value& callee );
            bool add( const Env& e, const Value& callee, const ptr< InvocationRule >& pInvRule );

            using UniGen = Generator< tuple<
                Term,
                const Overload&,
                UnificationContext
            > >;

            UniGen unify( const Term& argsPat, const Term& rtPat, const UnificationContext& uc ) const;

        private:
            Term m_identity;

            using trie_type = UTrie< Trie< Overload > >;
            ptr< trie_type > m_trie = make_shared< trie_type >();
    };
}

#endif







|


|


|




|





17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
            {}

            const auto& identity() const { return m_identity; }

            bool add( const Env& e, const Value& callee );
            bool add( const Env& e, const Value& callee, const ptr< InvocationRule >& pInvRule );

            using TCGen = Generator< tuple<
                Term,
                const Overload&,
                TypeCheckingContext
            > >;

            TCGen typeCheck( const Term& argsPat, const Term& rtPat, const TypeCheckingContext& tcc ) const;

        private:
            Term m_identity;

            using trie_type = TCTrie< Trie< Overload > >;
            ptr< trie_type > m_trie = make_shared< trie_type >();
    };
}

#endif
Changes to bs/sema/postprocess.cpp.
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
        if( !result )
            return nullopt;

        auto&& [pPP, t] = *result;
        return make_pair( t, static_pointer_cast< PostProcFunc >( pPP ) );
    }

    optional< Term > Postprocess( const Term& src, UnificationContext& uc )
    {
        if( auto optHole = HoleFromIRExpr( src ) )
        {
            const auto& hole = *optHole;

            if( !hole.name.isNumerical() )
            {
                // If the name is not found, output it as is.
                auto index = uc.getLHSHoleIndex( hole.name );
                if( index == UnificationContext::InvalidIndex )
                    return src;

                const auto& val = uc.getValue( index );
                if( !val )
                    return src;

                return Postprocess( *val, uc );
            }
            else
            {
                const auto& optVal = uc.getValue( hole.name.id() );
                if( !optVal )
                    return HOLE( "_"_sid );

                return Postprocess( *optVal, uc );
            }
        }

        if( !holds_alternative< pvec >( src ) )
            return src;

        if( auto optPP = UnwrapPostprocFunc( src ) )
        {
            auto val = Postprocess( optPP->first, uc );
            if( !val )
                return nullopt;

            return ( *optPP->second )( *val, uc );
        }

        const auto& vec = *get< pvec >( src );

        auto outputTerms = make_shared< Vector >();
        outputTerms->reserve( vec.terms().size() );

        for( auto&& t : vec.terms() )
        {
            auto newT = Postprocess( t, uc );
            if( !newT )
                return nullopt;

            outputTerms->append( move( *newT ) );
        }

        return outputTerms;







|








|
|


|



|



|



|








|



|









|







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
        if( !result )
            return nullopt;

        auto&& [pPP, t] = *result;
        return make_pair( t, static_pointer_cast< PostProcFunc >( pPP ) );
    }

    optional< Term > Postprocess( const Term& src, TypeCheckingContext& tcc )
    {
        if( auto optHole = HoleFromIRExpr( src ) )
        {
            const auto& hole = *optHole;

            if( !hole.name.isNumerical() )
            {
                // If the name is not found, output it as is.
                auto index = tcc.getLHSHoleIndex( hole.name );
                if( index == TypeCheckingContext::InvalidIndex )
                    return src;

                const auto& val = tcc.getValue( index );
                if( !val )
                    return src;

                return Postprocess( *val, tcc );
            }
            else
            {
                const auto& optVal = tcc.getValue( hole.name.id() );
                if( !optVal )
                    return HOLE( "_"_sid );

                return Postprocess( *optVal, tcc );
            }
        }

        if( !holds_alternative< pvec >( src ) )
            return src;

        if( auto optPP = UnwrapPostprocFunc( src ) )
        {
            auto val = Postprocess( optPP->first, tcc );
            if( !val )
                return nullopt;

            return ( *optPP->second )( *val, tcc );
        }

        const auto& vec = *get< pvec >( src );

        auto outputTerms = make_shared< Vector >();
        outputTerms->reserve( vec.terms().size() );

        for( auto&& t : vec.terms() )
        {
            auto newT = Postprocess( t, tcc );
            if( !newT )
                return nullopt;

            outputTerms->append( move( *newT ) );
        }

        return outputTerms;
Changes to bs/sema/postprocess.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef GOOSE_SEMA_POSTPROCESS_H
#define GOOSE_SEMA_POSTPROCESS_H

namespace goose::sema
{
    using PostProcFunc = function< optional< Term > ( const Term& t, UnificationContext& uc ) >;

    extern Term WrapWithPostprocFunc( const Term& t, const ptr< PostProcFunc >& pp );
    extern Term WrapWithPostprocFunc( const Term& t, PostProcFunc&& pp );
    extern optional< pair< Term, ptr< PostProcFunc > > > UnwrapPostprocFunc( const Term& ppt );

    extern void SetupPostProcUnificationRules( UnificationRuleSet& ruleSet );

    extern optional< Term > Postprocess( const Term& src, UnificationContext& uc );
}

#endif





|





|

|



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

namespace goose::sema
{
    using PostProcFunc = function< optional< Term > ( const Term& t, TypeCheckingContext& tcc ) >;

    extern Term WrapWithPostprocFunc( const Term& t, const ptr< PostProcFunc >& pp );
    extern Term WrapWithPostprocFunc( const Term& t, PostProcFunc&& pp );
    extern optional< pair< Term, ptr< PostProcFunc > > > UnwrapPostprocFunc( const Term& ppt );

    extern void SetupPostProcUnificationRules( TypeCheckingRuleSet& ruleSet );

    extern optional< Term > Postprocess( const Term& src, TypeCheckingContext& tcc );
}

#endif
Changes to bs/sema/sema.h.
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
}

#include "hole.h"
#include "context.h"
#include "ctmm.h"
#include "env.h"

#include "uni-score.h"
#include "uni-context.h"
#include "unify.h"
#include "uni-ruleset.h"
#include "uni-basicrules.h"
#include "uni-holes.h"
#include "uni-quote.h"
#include "substitute.h"
#include "postprocess.h"

#include "utrie.h"

#include "inv-ruleset.h"
#include "invocation.h"

#include "tpl-ruleset.h"
#include "template.h"

#include "overloadset.h"
#include "codebuilder.h"

#include "utrie.inl"
#include "utrie-uni.inl"

#endif







|
|
|
|






|










|
|


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
}

#include "hole.h"
#include "context.h"
#include "ctmm.h"
#include "env.h"

#include "tc-score.h"
#include "tc-context.h"
#include "typecheck.h"
#include "tc-ruleset.h"
#include "uni-basicrules.h"
#include "uni-holes.h"
#include "uni-quote.h"
#include "substitute.h"
#include "postprocess.h"

#include "tctrie.h"

#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
Changes to bs/sema/substitute.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
#include "sema.h"

namespace goose::sema
{
    Term Substitute( const Term& src, const UnificationContext& context )
    {
        if( auto optHole = HoleFromIRExpr( src ) )
        {
            const auto& hole = *optHole;

            // We only substitute indexed holes. If we encounter a named hole,
            // output it as is.




|







1
2
3
4
5
6
7
8
9
10
11
12
#include "sema.h"

namespace goose::sema
{
    Term Substitute( const Term& src, const TypeCheckingContext& context )
    {
        if( auto optHole = HoleFromIRExpr( src ) )
        {
            const auto& hole = *optHole;

            // We only substitute indexed holes. If we encounter a named hole,
            // output it as is.
Changes to bs/sema/substitute.h.
1
2
3
4
5
6
7
8
9
#ifndef GOOSE_SEMA_SUBSTITUTE_H
#define GOOSE_SEMA_SUBSTITUTE_H

namespace goose::sema
{
    extern Term Substitute( const Term& src, const UnificationContext& context );
}

#endif





|



1
2
3
4
5
6
7
8
9
#ifndef GOOSE_SEMA_SUBSTITUTE_H
#define GOOSE_SEMA_SUBSTITUTE_H

namespace goose::sema
{
    extern Term Substitute( const Term& src, const TypeCheckingContext& context );
}

#endif
Name change from bs/sema/uni-context.cpp to bs/sema/tc-context.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
#include "sema.h"

using namespace goose;
using namespace goose::sema;

UnificationContext::UnificationContext( const Context& c ) :
    m_context( c )
{}

UnificationContext::UnificationContext( Context&& c ) :
    m_context( move( c ) )
{}

uint32_t UnificationContext::getLHSHoleIndex( const StringId& name ) const
{
    if( name == "_"_sid )
        return InvalidIndex;

    HoleName holeName( name, m_currentLHSNamespaceIndex );

    auto it = m_pCow->holeDict.find( holeName );
    if( it == m_pCow->holeDict.end() )
        return InvalidIndex;

    return it->second;
}

uint32_t UnificationContext::getRHSHoleIndex( const StringId& name ) const
{
    if( name == "_"_sid )
        return InvalidIndex;

    HoleName holeName( name, m_currentRHSNamespaceIndex );

    auto it = m_pCow->holeDict.find( holeName );
    if( it == m_pCow->holeDict.end() )
        return InvalidIndex;

    return it->second;
}

uint32_t UnificationContext::createValue()
{
    CoW( m_pCow )->values.push_back( StoredValue() );
    uint32_t index = m_pCow->values.size() - 1;
    return index;
}

void UnificationContext::setValueRequired( uint32_t index )
{
    assert( m_pCow->values.size() > index );

    if( m_pCow->values[index].m_required )
        return;

    CoW( m_pCow )->values[index] = { m_pCow->values[index].m_term, true };

    if( !m_pCow->values[index].m_term )
        ++m_numUnknownValues;
}

void UnificationContext::setLHSHoleIndex( const StringId& name, uint32_t index )
{
    if( name == "_"_sid )
    {
        ++m_numAnonymousHoles;
        return;
    }

    HoleName holeName( name, m_currentLHSNamespaceIndex );
    CoW( m_pCow )->holeDict.emplace( holeName, index );

    if( m_valuesAreRequired )
        setValueRequired( index );
}

void UnificationContext::setRHSHoleIndex( const StringId& name, uint32_t index )
{
    if( name == "_"_sid )
    {
        ++m_numAnonymousHoles;
        return;
    }

    HoleName holeName( name, m_currentRHSNamespaceIndex );
    CoW( m_pCow )->holeDict.emplace( holeName, index );

    if( m_valuesAreRequired )
        setValueRequired( index );
}

void UnificationContext::eraseLHSName( const StringId& name )
{
    HoleName holeName( name, m_currentLHSNamespaceIndex );
    CoW( m_pCow )->holeDict.erase( holeName );
}

void UnificationContext::eraseRHSName( const StringId& name )
{
    HoleName holeName( name, m_currentRHSNamespaceIndex );
    CoW( m_pCow )->holeDict.erase( holeName );
}

bool UnificationContext::isHoleLocked( uint32_t index ) const
{
    return m_pCow->lockedHoles.find( index ) != m_pCow->lockedHoles.end();
}

void UnificationContext::lockHole( uint32_t index )
{
    CoW( m_pCow )->lockedHoles.emplace( index );
}

void UnificationContext::unlockHole( uint32_t index )
{
    CoW( m_pCow )->lockedHoles.erase( index );
}





|



|



|













|













|






|












|














|














|





|





|




|




|



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
#include "sema.h"

using namespace goose;
using namespace goose::sema;

TypeCheckingContext::TypeCheckingContext( const Context& c ) :
    m_context( c )
{}

TypeCheckingContext::TypeCheckingContext( Context&& c ) :
    m_context( move( c ) )
{}

uint32_t TypeCheckingContext::getLHSHoleIndex( const StringId& name ) const
{
    if( name == "_"_sid )
        return InvalidIndex;

    HoleName holeName( name, m_currentLHSNamespaceIndex );

    auto it = m_pCow->holeDict.find( holeName );
    if( it == m_pCow->holeDict.end() )
        return InvalidIndex;

    return it->second;
}

uint32_t TypeCheckingContext::getRHSHoleIndex( const StringId& name ) const
{
    if( name == "_"_sid )
        return InvalidIndex;

    HoleName holeName( name, m_currentRHSNamespaceIndex );

    auto it = m_pCow->holeDict.find( holeName );
    if( it == m_pCow->holeDict.end() )
        return InvalidIndex;

    return it->second;
}

uint32_t TypeCheckingContext::createValue()
{
    CoW( m_pCow )->values.push_back( StoredValue() );
    uint32_t index = m_pCow->values.size() - 1;
    return index;
}

void TypeCheckingContext::setValueRequired( uint32_t index )
{
    assert( m_pCow->values.size() > index );

    if( m_pCow->values[index].m_required )
        return;

    CoW( m_pCow )->values[index] = { m_pCow->values[index].m_term, true };

    if( !m_pCow->values[index].m_term )
        ++m_numUnknownValues;
}

void TypeCheckingContext::setLHSHoleIndex( const StringId& name, uint32_t index )
{
    if( name == "_"_sid )
    {
        ++m_numAnonymousHoles;
        return;
    }

    HoleName holeName( name, m_currentLHSNamespaceIndex );
    CoW( m_pCow )->holeDict.emplace( holeName, index );

    if( m_valuesAreRequired )
        setValueRequired( index );
}

void TypeCheckingContext::setRHSHoleIndex( const StringId& name, uint32_t index )
{
    if( name == "_"_sid )
    {
        ++m_numAnonymousHoles;
        return;
    }

    HoleName holeName( name, m_currentRHSNamespaceIndex );
    CoW( m_pCow )->holeDict.emplace( holeName, index );

    if( m_valuesAreRequired )
        setValueRequired( index );
}

void TypeCheckingContext::eraseLHSName( const StringId& name )
{
    HoleName holeName( name, m_currentLHSNamespaceIndex );
    CoW( m_pCow )->holeDict.erase( holeName );
}

void TypeCheckingContext::eraseRHSName( const StringId& name )
{
    HoleName holeName( name, m_currentRHSNamespaceIndex );
    CoW( m_pCow )->holeDict.erase( holeName );
}

bool TypeCheckingContext::isHoleLocked( uint32_t index ) const
{
    return m_pCow->lockedHoles.find( index ) != m_pCow->lockedHoles.end();
}

void TypeCheckingContext::lockHole( uint32_t index )
{
    CoW( m_pCow )->lockedHoles.emplace( index );
}

void TypeCheckingContext::unlockHole( uint32_t index )
{
    CoW( m_pCow )->lockedHoles.erase( index );
}
Name change from bs/sema/uni-context.h to bs/sema/tc-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
#ifndef GOOSE_SEMA_UNI_CONTEXT_H
#define GOOSE_SEMA_UNI_CONTEXT_H

namespace goose::sema
{
    class UnificationContext
    {
        public:
            static constexpr uint32_t InvalidIndex = numeric_limits< uint32_t >::max();

            UnificationContext( const Context& c );
            UnificationContext( Context&& c );

            const auto& context() const { return m_context; }
            const auto& env() const { return m_context.env(); }
            const auto& rules() const { return env()->unificationRuleSet(); }

            uint32_t getLHSHoleIndex( const StringId& name ) const;
            uint32_t getRHSHoleIndex( const StringId& name ) const;

            uint32_t createValue();

            void setLHSHoleIndex( const StringId& name, uint32_t index );
|
|



|




|
|



|







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

namespace goose::sema
{
    class TypeCheckingContext
    {
        public:
            static constexpr uint32_t InvalidIndex = numeric_limits< uint32_t >::max();

            TypeCheckingContext( const Context& c );
            TypeCheckingContext( Context&& c );

            const auto& context() const { return m_context; }
            const auto& env() const { return m_context.env(); }
            const auto& rules() const { return env()->typeCheckingRuleSet(); }

            uint32_t getLHSHoleIndex( const StringId& name ) const;
            uint32_t getRHSHoleIndex( const StringId& name ) const;

            uint32_t createValue();

            void setLHSHoleIndex( const StringId& name, uint32_t index );
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
                    m_complexity -= GetComplexity( *m_pCow->values[index].m_term );

                CoW( m_pCow )->values[index] = { forward< T >( val ), true };

                m_complexity += GetComplexity( *m_pCow->values[index].m_term );
            }

            UnificationContext& flip()
            {
                swap( m_currentLHSNamespaceIndex, m_currentRHSNamespaceIndex );
                return *this;
            }

            uint32_t numUnknownValues() const { return m_numUnknownValues; }
            uint32_t complexity() const { return m_complexity; }

            void addComplexity( uint32_t c ) { m_complexity +=c; }
            void subComplexity( uint32_t c ) { m_complexity -=c; }
            void setComplexity( uint32_t complexity ) { m_complexity = complexity; }

            void addAnonymousHole() { ++m_numAnonymousHoles; }

            auto score() const { return UnificationScore( m_complexity, m_pCow->holeDict.size() + m_numAnonymousHoles ); }

            // Used to detect and reject recursive hole nesting.
            bool isHoleLocked( uint32_t index ) const;
            void lockHole( uint32_t index );
            void unlockHole( uint32_t index );

        private:







|














|







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
                    m_complexity -= GetComplexity( *m_pCow->values[index].m_term );

                CoW( m_pCow )->values[index] = { forward< T >( val ), true };

                m_complexity += GetComplexity( *m_pCow->values[index].m_term );
            }

            TypeCheckingContext& flip()
            {
                swap( m_currentLHSNamespaceIndex, m_currentRHSNamespaceIndex );
                return *this;
            }

            uint32_t numUnknownValues() const { return m_numUnknownValues; }
            uint32_t complexity() const { return m_complexity; }

            void addComplexity( uint32_t c ) { m_complexity +=c; }
            void subComplexity( uint32_t c ) { m_complexity -=c; }
            void setComplexity( uint32_t complexity ) { m_complexity = complexity; }

            void addAnonymousHole() { ++m_numAnonymousHoles; }

            auto score() const { return TypeCheckingScore( m_complexity, m_pCow->holeDict.size() + m_numAnonymousHoles ); }

            // Used to detect and reject recursive hole nesting.
            bool isHoleLocked( uint32_t index ) const;
            void lockHole( uint32_t index );
            void unlockHole( uint32_t index );

        private:
Name change from bs/sema/uni-postproc.cpp to bs/sema/tc-postproc.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
#include "sema.h"

namespace goose::sema
{
    // When encountering a postprocess wrapper during unification, strip it away
    // and unify the content. The wrapper will be put back by the inner unification
    // rule.
    void SetupPostProcUnificationRules( UnificationRuleSet& ruleSet )
    {
        ruleSet.addSymRule( URINFOS, VEC( TSID( postproc ), ANYTERM( _ ), ANYTERM( _ ) ), ANYTERM( _ ),
            []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
            {
                auto unwrap = UnwrapPostprocFunc( lhs );
                if( !unwrap )
                    co_return;
                auto&& [t,pp] = *unwrap;

                co_yield Unify( t, rhs, uc );

                for( auto&& [s,uc] : Unify( t, rhs, uc ) )
                    co_yield { t, uc };
            }  );

        ruleSet.addAsymRule( URINFOS,
            VEC( TSID( postproc ), ANYTERM( _ ), ANYTERM( _ ) ),
            VEC( TSID( postproc ), ANYTERM( _ ), ANYTERM( _ ) ),
            []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
            {
                auto unwrap = UnwrapPostprocFunc( lhs );
                if( !unwrap )
                    co_return;
                auto&& [t,pp] = *unwrap;

                for( auto&& [s,c] : Unify( t, rhs, uc ) )
                    co_yield { t, uc };
            }  );
    }
}







|

|
|






|

|
|


|

<
|






|
|



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 "sema.h"

namespace goose::sema
{
    // When encountering a postprocess wrapper during unification, strip it away
    // and unify the content. The wrapper will be put back by the inner unification
    // rule.
    void SetupPostProcUnificationRules( TypeCheckingRuleSet& ruleSet )
    {
        ruleSet.addUnificationRule( TCRINFOS, VEC( TSID( postproc ), ANYTERM( _ ), ANYTERM( _ ) ), ANYTERM( _ ),
            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                auto unwrap = UnwrapPostprocFunc( lhs );
                if( !unwrap )
                    co_return;
                auto&& [t,pp] = *unwrap;

                co_yield Unify( t, rhs, tcc );

                for( auto&& [s,tcc] : Unify( t, rhs, tcc ) )
                    co_yield { t, tcc };
            }  );

        ruleSet.addUnificationRule( TCRINFOS,
            VEC( TSID( postproc ), ANYTERM( _ ), ANYTERM( _ ) ),

            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                auto unwrap = UnwrapPostprocFunc( lhs );
                if( !unwrap )
                    co_return;
                auto&& [t,pp] = *unwrap;

                for( auto&& [s,c] : Unify( t, rhs, tcc ) )
                    co_yield { t, tcc };
            }  );
    }
}
Name change from bs/sema/uni-ruleset.cpp to bs/sema/tc-ruleset.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 "sema.h"

using namespace goose;
using namespace goose::sema;

UnificationRuleSet::UnificationRuleSet()
{
    SetupBasicUnificationRules( *this );
    SetupPostProcUnificationRules( *this );
    SetupHoleUnificationRules( *this );
    SetupQuoteUnificationRules( *this );
}

void UnificationRuleSet::addSymRule( UniRuleInfo&& infos, const Term& pat, UniFunc f )
{
    m_uniRules = Merge( m_uniRules, VEC( pat, pat ), [&]( auto&& ){ return UniRule{ f, infos }; } );
}

UniGen FlippedContextAdapter( const Term& lhs, const Term& rhs, UnificationContext uc, UnificationRuleSet::UniFunc f )
{
    for( auto&& [e,uc] : f( rhs, lhs, uc.flip() ) )
        co_yield { move( e ), uc.flip() };
}

void UnificationRuleSet::addSymRule( UniRuleInfo&& infos, const Term& pat1, const Term& pat2, UniFunc f )
{
    m_uniRules = Merge( m_uniRules, VEC( pat1, pat2 ), [&]( auto&& ){ return UniRule{ f, infos }; } );
    m_uniRules = Merge( m_uniRules, VEC( pat2, pat1 ),
        [&]( auto&& ){ return UniRule{ UniFunc( [&,f]( auto&& lhs, auto&& rhs, auto&& c ) -> UniGen
        {
            return FlippedContextAdapter( lhs, rhs, c, f );
        } ), infos }; }
    );
}

void UnificationRuleSet::addAsymRule( UniRuleInfo&& infos, const Term& pat1, const Term& pat2, UniFunc f )
{
    m_uniRules = Merge( m_uniRules, VEC( pat1, pat2 ), [&]( auto&& ){ return UniRule{ f, infos }; } );
}

void UnificationRuleSet::addHalfUnificationRule( UniRuleInfo&& infos, const Term& pat, HalfUniFunc f )
{
    m_halfUniRules = Merge( m_halfUniRules, pat, [&]( auto&& ){ return HalfUniRule{ f, infos }; } );
}





|







|




|

|
|


|



|






|

|


|



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 "sema.h"

using namespace goose;
using namespace goose::sema;

TypeCheckingRuleSet::TypeCheckingRuleSet()
{
    SetupBasicUnificationRules( *this );
    SetupPostProcUnificationRules( *this );
    SetupHoleUnificationRules( *this );
    SetupQuoteUnificationRules( *this );
}

void TypeCheckingRuleSet::addUnificationRule( TCRuleInfo&& infos, const Term& pat, UniFunc f )
{
    m_uniRules = Merge( m_uniRules, VEC( pat, pat ), [&]( auto&& ){ return UniRule{ f, infos }; } );
}

TCGen FlippedContextAdapter( const Term& lhs, const Term& rhs, TypeCheckingContext tcc, TypeCheckingRuleSet::UniFunc f )
{
    for( auto&& [e,tcc] : f( rhs, lhs, tcc.flip() ) )
        co_yield { move( e ), tcc.flip() };
}

void TypeCheckingRuleSet::addUnificationRule( TCRuleInfo&& infos, const Term& pat1, const Term& pat2, UniFunc f )
{
    m_uniRules = Merge( m_uniRules, VEC( pat1, pat2 ), [&]( auto&& ){ return UniRule{ f, infos }; } );
    m_uniRules = Merge( m_uniRules, VEC( pat2, pat1 ),
        [&]( auto&& ){ return UniRule{ UniFunc( [&,f]( auto&& lhs, auto&& rhs, auto&& c ) -> TCGen
        {
            return FlippedContextAdapter( lhs, rhs, c, f );
        } ), infos }; }
    );
}

void TypeCheckingRuleSet::addTypeCheckingRule( TCRuleInfo&& infos, const Term& pat1, const Term& pat2, UniFunc f )
{
    m_typeCheckingRules = Merge( m_typeCheckingRules, VEC( pat1, pat2 ), [&]( auto&& ){ return UniRule{ f, infos }; } );
}

void TypeCheckingRuleSet::addHalfUnificationRule( TCRuleInfo&& infos, const Term& pat, HalfUniFunc f )
{
    m_halfUniRules = Merge( m_halfUniRules, pat, [&]( auto&& ){ return HalfUniRule{ f, infos }; } );
}
Name change from bs/sema/uni-ruleset.h to bs/sema/tc-ruleset.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
#ifndef GOOSE_SEMA_UNI_RULESET_H
#define GOOSE_SEMA_UNI_RULESET_H

//#define UNIRULES_DEBUG

namespace goose::sema
{
    class UnificationContext;

#ifdef UNIRULES_DEBUG
    struct UniRuleInfo
    {
        const char* pFilename = nullptr;
        uint32_t line = 0;
    };

    #define URINFOS UniRuleInfo{ __FILE__, __LINE__ }
#else
    struct UniRuleInfo
    {};

    #define URINFOS UniRuleInfo{}
#endif

    class UnificationRuleSet
    {
        public:
            using UniFunc = function< UniGen ( const Term& lhs, const Term& rhs, const UnificationContext& ) >;
            using HalfUniFunc = function< UniGen ( const Term& lhs, const UnificationContext& ) >;

            struct UniRule
            {
                UniFunc func;
                UniRuleInfo infos;
            };

            struct HalfUniRule
            {
                HalfUniFunc func;
                UniRuleInfo infos;
            };

            UnificationRuleSet();

            void addSymRule( UniRuleInfo&& infos, const Term& pat, UniFunc f );

            void addSymRule( UniRuleInfo&& infos, const Term& pat1, const Term& pat2, UniFunc f );
            void addAsymRule( UniRuleInfo&& infos, const Term& pat1, const Term& pat2, UniFunc f );

            void addHalfUnificationRule( UniRuleInfo&& infos, const Term& pat, HalfUniFunc f );


            const auto& uniRules() const { return m_uniRules; }
            const auto& halfUniRules() const { return m_halfUniRules; }

        private:

            Trie< UniRule > m_uniRules;
            Trie< HalfUniRule > m_halfUniRules;
    };
}

#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
58
59
60
61
62
63
#ifndef GOOSE_SEMA_TC_RULESET_H
#define GOOSE_SEMA_TC_RULESET_H

//#define TCRULES_DEBUG

namespace goose::sema
{
    class TypeCheckingContext;

#ifdef TCRULES_DEBUG
    struct TCRuleInfo
    {
        const char* pFilename = nullptr;
        uint32_t line = 0;
    };

    #define TCRINFOS TCRuleInfo{ __FILE__, __LINE__ }
#else
    struct TCRuleInfo
    {};

    #define TCRINFOS TCRuleInfo{}
#endif

    class TypeCheckingRuleSet
    {
        public:
            using UniFunc = function< TCGen ( const Term& lhs, const Term& rhs, const TypeCheckingContext& ) >;
            using HalfUniFunc = function< TCGen ( const Term& lhs, const TypeCheckingContext& ) >;

            struct UniRule
            {
                UniFunc func;
                TCRuleInfo infos;
            };

            struct HalfUniRule
            {
                HalfUniFunc func;
                TCRuleInfo infos;
            };

            TypeCheckingRuleSet();

            void addTypeCheckingRule( TCRuleInfo&& infos, const Term& pat1, const Term& pat2, UniFunc f );

            void addUnificationRule( TCRuleInfo&& infos, const Term& pat, UniFunc f );
            void addUnificationRule( TCRuleInfo&& infos, const Term& pat1, const Term& pat2, UniFunc f );

            void addHalfUnificationRule( TCRuleInfo&& infos, const Term& pat, HalfUniFunc f );

            const auto& typeCheckingRules() const { return m_typeCheckingRules; }
            const auto& uniRules() const { return m_uniRules; }
            const auto& halfUniRules() const { return m_halfUniRules; }

        private:
            Trie< UniRule > m_typeCheckingRules;
            Trie< UniRule > m_uniRules;
            Trie< HalfUniRule > m_halfUniRules;
    };
}

#endif
Name change from bs/sema/uni-score.h to bs/sema/tc-score.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
#ifndef GOOSE_SEMA_UNI_SCORE_H
#define GOOSE_SEMA_UNI_SCORE_H

namespace goose::sema
{
    class UnificationScore
    {
        public:
            UnificationScore() {}

            UnificationScore( uint32_t complexity, uint32_t uniqueHoles ) :
                m_complexity( complexity ),
                m_uniqueHoles( uniqueHoles )
            {}

            auto& operator+=( const UnificationScore& rhs )
            {
                m_complexity += rhs.m_complexity;
                m_uniqueHoles += rhs.m_uniqueHoles;
                return *this;
            }

            auto operator+( const UnificationScore& rhs ) const
            {
                return UnificationScore( m_complexity + rhs.m_complexity, m_uniqueHoles + rhs.m_uniqueHoles );
            }

            bool operator==( const UnificationScore& rhs ) const
            {
                return m_complexity == rhs.m_complexity
                    && m_uniqueHoles == rhs.m_uniqueHoles;
            }

            bool operator!=( const UnificationScore& rhs ) const
            {
                return !operator==( rhs );
            }

            bool operator<( const UnificationScore& rhs ) const
            {
                if( m_complexity != rhs.m_complexity )
                    return m_complexity < rhs.m_complexity ;

                return m_uniqueHoles > rhs.m_uniqueHoles;
            }

            bool operator>( const UnificationScore& rhs ) const
            {
                if( m_complexity != rhs.m_complexity )
                    return m_complexity > rhs.m_complexity ;

                return m_uniqueHoles < rhs.m_uniqueHoles;
            }

            bool operator<=( const UnificationScore& rhs ) const
            {
                return !operator>( rhs );
            }

            bool operator>=( const UnificationScore& rhs ) const
            {
                return !operator<( rhs );
            }

            friend ostream& operator<<( ostream& out, const UnificationScore& s )
            {
                return out << '(' << s.m_complexity << ',' << s.m_uniqueHoles << ')';
            }

        private:
            uint32_t m_complexity = 0;
            uint32_t m_uniqueHoles = 0;
|
|



|


|

|




|






|

|


|





|




|







|







|




|




|







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
#ifndef GOOSE_SEMA_TC_SCORE_H
#define GOOSE_SEMA_TC_SCORE_H

namespace goose::sema
{
    class TypeCheckingScore
    {
        public:
            TypeCheckingScore() {}

            TypeCheckingScore( uint32_t complexity, uint32_t uniqueHoles ) :
                m_complexity( complexity ),
                m_uniqueHoles( uniqueHoles )
            {}

            auto& operator+=( const TypeCheckingScore& rhs )
            {
                m_complexity += rhs.m_complexity;
                m_uniqueHoles += rhs.m_uniqueHoles;
                return *this;
            }

            auto operator+( const TypeCheckingScore& rhs ) const
            {
                return TypeCheckingScore( m_complexity + rhs.m_complexity, m_uniqueHoles + rhs.m_uniqueHoles );
            }

            bool operator==( const TypeCheckingScore& rhs ) const
            {
                return m_complexity == rhs.m_complexity
                    && m_uniqueHoles == rhs.m_uniqueHoles;
            }

            bool operator!=( const TypeCheckingScore& rhs ) const
            {
                return !operator==( rhs );
            }

            bool operator<( const TypeCheckingScore& rhs ) const
            {
                if( m_complexity != rhs.m_complexity )
                    return m_complexity < rhs.m_complexity ;

                return m_uniqueHoles > rhs.m_uniqueHoles;
            }

            bool operator>( const TypeCheckingScore& rhs ) const
            {
                if( m_complexity != rhs.m_complexity )
                    return m_complexity > rhs.m_complexity ;

                return m_uniqueHoles < rhs.m_uniqueHoles;
            }

            bool operator<=( const TypeCheckingScore& rhs ) const
            {
                return !operator>( rhs );
            }

            bool operator>=( const TypeCheckingScore& rhs ) const
            {
                return !operator<( rhs );
            }

            friend ostream& operator<<( ostream& out, const TypeCheckingScore& s )
            {
                return out << '(' << s.m_complexity << ',' << s.m_uniqueHoles << ')';
            }

        private:
            uint32_t m_complexity = 0;
            uint32_t m_uniqueHoles = 0;
Name change from bs/sema/utrie-uni.inl to bs/sema/tctrie-typecheck.inl.
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
134
135
136
137
138
139
#ifndef GOOSE_SEMA_UTRIE_UNI_INL
#define GOOSE_SEMA_UTRIE_UNI_INL

namespace goose::sema
{
    template< typename U > typename UTrie< U >::template UniGen< U >
    UTrie< U >::UnifyRepetition( VecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec,
        const RepetitionNode& rptNode, const UnificationContext& uc )
    {
        if( vgen.finished() )
            co_yield { lhsVec, solutionVec, rptNode.m_next, uc };
        else
        {
            const auto& vt = vgen();

            for( auto&& rt : Enumerate( rptNode.m_repetition ) )
            {
                auto newC = uc;
                newC.addComplexity( GetComplexity( rt ) + GetComplexity( vt ) );

                auto localRhsVec = lhsVec;
                localRhsVec.setRepetitionTerm( rt );

                for( auto&& [s,uc] : sema::Unify( rt, vt, newC ) )
                    co_yield UnifyRepetition( vgen, localRhsVec, Vector::MakeAppend( solutionVec, move( s ) ), rptNode, uc );
            }
        }
    }

    template< typename U > template< typename T > Generator< tuple< Vector, Vector, const T&, UnificationContext, VecGenerator > >
    UTrie< U >::Unify( VecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec, const branch_t& branch, const UnificationContext& uc )
    {
        if( branch.index() == 0 )
        {
            const auto& vt = vgen();

            for( auto&& [t, payload] : Enumerate( get< 0 >( branch )->m_trie ) )
            {
                auto newC = uc;
                newC.addComplexity( GetComplexity( t ) + GetComplexity( vt ) );

                auto lvec = Vector::MakeAppend( lhsVec, t );

                for( auto&& [s, uc] : sema::Unify( t, vt, newC ) )
                    co_yield Unify< T >( vgen, lvec, Vector::MakeAppend( solutionVec, move( s ) ), payload, uc );
            }
        }
        else
            co_yield { lhsVec, solutionVec, *any_cast< T >( &get< 1 >( branch ) ), uc, vgen };
    }

    template< typename U >
    typename UTrie< U >::template UniGen< U > UTrie< U >::unify( const Vector& rhs, UnificationContext uc ) const
    {
        auto exprLen = rhs.length();

        uc.addComplexity( 2 );

        auto it = m_fixedLengthBranches.find( exprLen.minLength() );
        if( it != m_fixedLengthBranches.end() )
        {
            VecGenerator vgen( rhs );
            Vector lhs;
            Vector sol;

            for( auto&& [lv, s, content, c, g] : Unify< U >( vgen, lhs, sol, it->second, uc ) )
                co_yield { lv, s, content, c };
        }

        using rpt_node = ptr< RepetitionNode >;

        it = m_variableLengthBranches.begin();
        auto end = m_variableLengthBranches.upper_bound( exprLen.minLength() );
        while( it != end )
        {
            VecGenerator vgen( rhs );
            Vector lhs;
            Vector sol;

            for( auto&& [lv, s, pRptNode, c, g] : Unify< rpt_node >( vgen, lhs, sol, it->second, uc ) )
                co_yield UnifyRepetition( g, lv, sol, *pRptNode, c );

            ++it;
        }
    }

    template< typename U > template< typename T > typename UTrie< U >::template UniGen< T >
    UTrie< U >::HalfUnify( const Vector& lhsVec, const Vector& solutionVec, const branch_t& branch, const UnificationContext& uc )
    {
        if( branch.index() == 0 )
        {
            for( auto&& [t, payload] : Enumerate( get< 0 >( branch )->m_trie ) )
            {
                auto newC = uc;
                newC.addComplexity( GetComplexity( t ) );

                auto lvec = Vector::MakeAppend( lhsVec, t );

                for( auto&& [s, uc] : sema::HalfUnify( t, newC ) )
                    co_yield HalfUnify< T >( lvec, Vector::MakeAppend( solutionVec, move( s ) ), payload, uc );
            }
        }
        else
            co_yield { lhsVec, solutionVec, *any_cast< T >( &get< 1 >( branch ) ), uc };
    }

    template< typename U >
    typename UTrie< U >::template UniGen< U > UTrie< U >::halfUnify( UnificationContext uc ) const
    {
        uc.addComplexity( 1 );

        for( auto&& [len, branch] : m_fixedLengthBranches )
        {
            Vector lhs;
            Vector sol;

            for( auto&& [lv, s, content, c] : HalfUnify< U >( lhs, sol, branch, uc ) )
                co_yield { lv, s, content, c };
        }

        using rpt_node = ptr< TrieContainerRepetitionNode< U > >;

        for( auto&& [minLen, branch] : m_variableLengthBranches )
        {
            Vector lhs;
            Vector sol;

            for( auto&& [lv, sol, pRptNode, uc] : HalfUnify< rpt_node >( lhs, sol, branch, uc ) )
            {
                for( auto&& [rt, payload] : Enumerate( pRptNode->m_repetition ) )
                {
                    auto newC = uc;
                    newC.addComplexity( GetComplexity( rt ) );

                    for( auto&& [s, c] : sema::HalfUnify( rt, newC ) )
                    {
                        auto localLhsVec = lv;
                        localLhsVec.setRepetitionTerm( rt );

|
|



|
|
|


|






|





|
|




|
|







|



|
<
|



|



|



|








|













|
|





|
|





|




|
|



|



|

|






|










|



|







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
134
135
136
137
138
#ifndef GOOSE_SEMA_TCTRIE_TYPECHECK_INL
#define GOOSE_SEMA_TCTRIE_TYPECHECK_INL

namespace goose::sema
{
    template< typename U > typename TCTrie< U >::template TCGen< U >
    TCTrie< U >::TypeCheckRepetition( VecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec,
        const RepetitionNode& rptNode, const TypeCheckingContext& tcc )
    {
        if( vgen.finished() )
            co_yield { lhsVec, solutionVec, rptNode.m_next, tcc };
        else
        {
            const auto& vt = vgen();

            for( auto&& rt : Enumerate( rptNode.m_repetition ) )
            {
                auto newC = tcc;
                newC.addComplexity( GetComplexity( rt ) + GetComplexity( vt ) );

                auto localRhsVec = lhsVec;
                localRhsVec.setRepetitionTerm( rt );

                for( auto&& [s,tcc] : sema::TypeCheck( rt, vt, newC ) )
                    co_yield TypeCheckRepetition( vgen, localRhsVec, Vector::MakeAppend( solutionVec, move( s ) ), rptNode, tcc );
            }
        }
    }

    template< typename U > template< typename T > Generator< tuple< Vector, Vector, const T&, TypeCheckingContext, VecGenerator > >
    TCTrie< U >::TypeCheck( VecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec, const branch_t& branch, const TypeCheckingContext& tcc )
    {
        if( branch.index() == 0 )
        {
            const auto& vt = vgen();

            for( auto&& [t, payload] : Enumerate( get< 0 >( branch )->m_trie ) )
            {
                auto newC = tcc;
                newC.addComplexity( GetComplexity( t ) + GetComplexity( vt ) );

                auto lvec = Vector::MakeAppend( lhsVec, t );
                for( auto&& [s, tcc] : sema::TypeCheck( t, vt, newC ) )

                    co_yield TypeCheck< T >( vgen, lvec, Vector::MakeAppend( solutionVec, move( s ) ), payload, tcc );
            }
        }
        else
            co_yield { lhsVec, solutionVec, *any_cast< T >( &get< 1 >( branch ) ), tcc, vgen };
    }

    template< typename U >
    typename TCTrie< U >::template TCGen< U > TCTrie< U >::typeCheck( const Vector& rhs, TypeCheckingContext tcc ) const
    {
        auto exprLen = rhs.length();

        tcc.addComplexity( 2 );

        auto it = m_fixedLengthBranches.find( exprLen.minLength() );
        if( it != m_fixedLengthBranches.end() )
        {
            VecGenerator vgen( rhs );
            Vector lhs;
            Vector sol;

            for( auto&& [lv, s, content, c, g] : TypeCheck< U >( vgen, lhs, sol, it->second, tcc ) )
                co_yield { lv, s, content, c };
        }

        using rpt_node = ptr< RepetitionNode >;

        it = m_variableLengthBranches.begin();
        auto end = m_variableLengthBranches.upper_bound( exprLen.minLength() );
        while( it != end )
        {
            VecGenerator vgen( rhs );
            Vector lhs;
            Vector sol;

            for( auto&& [lv, s, pRptNode, c, g] : TypeCheck< rpt_node >( vgen, lhs, sol, it->second, tcc ) )
                co_yield TypeCheckRepetition( g, lv, sol, *pRptNode, c );

            ++it;
        }
    }

    template< typename U > template< typename T > typename TCTrie< U >::template TCGen< T >
    TCTrie< U >::HalfUnify( const Vector& lhsVec, const Vector& solutionVec, const branch_t& branch, const TypeCheckingContext& tcc )
    {
        if( branch.index() == 0 )
        {
            for( auto&& [t, payload] : Enumerate( get< 0 >( branch )->m_trie ) )
            {
                auto newC = tcc;
                newC.addComplexity( GetComplexity( t ) );

                auto lvec = Vector::MakeAppend( lhsVec, t );

                for( auto&& [s, tcc] : sema::HalfUnify( t, newC ) )
                    co_yield HalfUnify< T >( lvec, Vector::MakeAppend( solutionVec, move( s ) ), payload, tcc );
            }
        }
        else
            co_yield { lhsVec, solutionVec, *any_cast< T >( &get< 1 >( branch ) ), tcc };
    }

    template< typename U >
    typename TCTrie< U >::template TCGen< U > TCTrie< U >::halfUnify( TypeCheckingContext tcc ) const
    {
        tcc.addComplexity( 1 );

        for( auto&& [len, branch] : m_fixedLengthBranches )
        {
            Vector lhs;
            Vector sol;

            for( auto&& [lv, s, content, c] : HalfUnify< U >( lhs, sol, branch, tcc ) )
                co_yield { lv, s, content, c };
        }

        using rpt_node = ptr< TrieContainerRepetitionNode< U > >;

        for( auto&& [minLen, branch] : m_variableLengthBranches )
        {
            Vector lhs;
            Vector sol;

            for( auto&& [lv, sol, pRptNode, tcc] : HalfUnify< rpt_node >( lhs, sol, branch, tcc ) )
            {
                for( auto&& [rt, payload] : Enumerate( pRptNode->m_repetition ) )
                {
                    auto newC = tcc;
                    newC.addComplexity( GetComplexity( rt ) );

                    for( auto&& [s, c] : sema::HalfUnify( rt, newC ) )
                    {
                        auto localLhsVec = lv;
                        localLhsVec.setRepetitionTerm( rt );

Name change from bs/sema/utrie.h to bs/sema/tctrie.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
#ifndef GOOSE_SEMA_UTRIE_H
#define GOOSE_SEMA_UTRIE_H

namespace goose::sema
{
    // Similar to an ir trie, but instead of being used to find the best pattern among a set of patterns,
    // it is used to find the best unification among a set of vector terms.
    // This is used to construct overload sets and to efficiently resolve overloads.
    template< typename U >
    class UTrie
    {
        public:
            template< typename F >
            ptr< UTrie > merge( const Vector& v, F&& func ) const;

            template< typename T >
            using UniGen = Generator< tuple< Vector, Vector, const T&, UnificationContext > >;

            UniGen< U > unify( const Vector& rhs, UnificationContext c ) const;
            UniGen< U > halfUnify( UnificationContext c ) const;

        private:
            struct Node;
            struct RepetitionNode;
            using branch_t = variant< ptr< Node >, any >;

            template< typename T, typename IT, typename F >
            static branch_t Merge( const branch_t& b, const IT& it, const IT& end, F&& next );

            template< typename F >
            static ptr< RepetitionNode > Merge( const ptr< RepetitionNode >& lhs, const Term& rhs, F&& next );

            static UniGen< U > UnifyRepetition( VecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec,
                const RepetitionNode& rptNode, const UnificationContext& c );

            template< typename T > static Generator< tuple< Vector, Vector, const T&, UnificationContext, VecGenerator > >
            Unify( VecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec, const branch_t& branch, const UnificationContext& c );

            template< typename T > static UniGen< T >
            HalfUnify( const Vector& lhsVec, const Vector& solutionVec, const branch_t& branch, const UnificationContext& c );

            struct Node
            {
                Trie< branch_t > m_trie;
            };

            struct RepetitionNode
|
|




|


|



|


|

|
|












|
|

|
|

|
|







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
#ifndef GOOSE_SEMA_TCTRIE_H
#define GOOSE_SEMA_TCTRIE_H

namespace goose::sema
{
    // Similar to an ir trie, but instead of being used to find the best pattern among a set of patterns,
    // it is used to find the best typing among a set of vector terms.
    // This is used to construct overload sets and to efficiently resolve overloads.
    template< typename U >
    class TCTrie
    {
        public:
            template< typename F >
            ptr< TCTrie > merge( const Vector& v, F&& func ) const;

            template< typename T >
            using TCGen = Generator< tuple< Vector, Vector, const T&, TypeCheckingContext > >;

            TCGen< U > typeCheck( const Vector& rhs, TypeCheckingContext c ) const;
            TCGen< U > halfUnify( TypeCheckingContext c ) const;

        private:
            struct Node;
            struct RepetitionNode;
            using branch_t = variant< ptr< Node >, any >;

            template< typename T, typename IT, typename F >
            static branch_t Merge( const branch_t& b, const IT& it, const IT& end, F&& next );

            template< typename F >
            static ptr< RepetitionNode > Merge( const ptr< RepetitionNode >& lhs, const Term& rhs, F&& next );

            static TCGen< U > TypeCheckRepetition( VecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec,
                const RepetitionNode& rptNode, const TypeCheckingContext& c );

            template< typename T > static Generator< tuple< Vector, Vector, const T&, TypeCheckingContext, VecGenerator > >
            TypeCheck( VecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec, const branch_t& branch, const TypeCheckingContext& c );

            template< typename T > static TCGen< T >
            HalfUnify( const Vector& lhsVec, const Vector& solutionVec, const branch_t& branch, const TypeCheckingContext& c );

            struct Node
            {
                Trie< branch_t > m_trie;
            };

            struct RepetitionNode
Name change from bs/sema/utrie.inl to bs/sema/tctrie.inl.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef GOOSE_SEMA_UTRIE_INL
#define GOOSE_SEMA_UTRIE_INL

namespace goose::sema
{
    template< typename U > template< typename T, typename IT, typename F >
    typename UTrie< U >::branch_t UTrie< U >::Merge( const branch_t& b, const IT& it, const IT& end, F&& next )
    {
        if( it == end )
        {
            if( b.index() == 1 )
                return any( next( *any_cast< T >( &get< 1 >( b ) ) ) );

            return any( next( T() ) );
|
|




|







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

namespace goose::sema
{
    template< typename U > template< typename T, typename IT, typename F >
    typename TCTrie< U >::branch_t TCTrie< U >::Merge( const branch_t& b, const IT& it, const IT& end, F&& next )
    {
        if( it == end )
        {
            if( b.index() == 1 )
                return any( next( *any_cast< T >( &get< 1 >( b ) ) ) );

            return any( next( T() ) );
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
        } );

        pNewNode->m_trie = ir::Merge( pNewNode->m_trie, *it, move( f ) );
        return pNewNode;
    }

    template< typename U > template< typename F >
    ptr< typename UTrie< U >::RepetitionNode > UTrie< U >::Merge( const ptr< RepetitionNode >& lhs, const Term& rhs, F&& next )
    {
        auto pNewNode = make_shared< RepetitionNode >();

        pNewNode->m_next = next( lhs ? lhs->m_next : U() );

        if( lhs )
            pNewNode->m_repetition = lhs->m_repetition;

        pNewNode->m_repetition = ir::Merge( pNewNode->m_repetition, rhs );
        return pNewNode;
    }

    template< typename U > template< typename F >
    ptr< UTrie< U > > UTrie< U >::merge( const Vector& v, F&& next ) const
    {
        auto pNewTrie = make_shared< UTrie< U > >( *this );

        auto length = v.length();
        if( length.isVariable() )
        {
            using rpt_node = ptr< RepetitionNode >;

            auto f = [&]( auto&& payload )







|













|

|







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

        pNewNode->m_trie = ir::Merge( pNewNode->m_trie, *it, move( f ) );
        return pNewNode;
    }

    template< typename U > template< typename F >
    ptr< typename TCTrie< U >::RepetitionNode > TCTrie< U >::Merge( const ptr< RepetitionNode >& lhs, const Term& rhs, F&& next )
    {
        auto pNewNode = make_shared< RepetitionNode >();

        pNewNode->m_next = next( lhs ? lhs->m_next : U() );

        if( lhs )
            pNewNode->m_repetition = lhs->m_repetition;

        pNewNode->m_repetition = ir::Merge( pNewNode->m_repetition, rhs );
        return pNewNode;
    }

    template< typename U > template< typename F >
    ptr< TCTrie< U > > TCTrie< U >::merge( const Vector& v, F&& next ) const
    {
        auto pNewTrie = make_shared< TCTrie< U > >( *this );

        auto length = v.length();
        if( length.isVariable() )
        {
            using rpt_node = ptr< RepetitionNode >;

            auto f = [&]( auto&& payload )
Changes to bs/sema/template.cpp.
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
        const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
        if( !pTemplateRuleSet )
            return PoisonValue();

        return pTemplateRuleSet->buildParamDecl( c, *ValueFromIRExpr( tpl ), *ValueFromIRExpr( arg ) );
    }

    void TemplateSetup( const Context& c, UnificationContext uc, const Term& tpl )
    {
        const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
        if( !pTemplateRuleSet )
            return;

        pTemplateRuleSet->setup( c, uc, *ValueFromIRExpr( tpl ) );
    }

    extern optional< Term > BuildTemplateArgPattern( const Context& c, const Term& tpl )
    {
        const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
        if( !pTemplateRuleSet )
            return nullopt;







|





|







47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
        const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
        if( !pTemplateRuleSet )
            return PoisonValue();

        return pTemplateRuleSet->buildParamDecl( c, *ValueFromIRExpr( tpl ), *ValueFromIRExpr( arg ) );
    }

    void TemplateSetup( const Context& c, TypeCheckingContext tcc, const Term& tpl )
    {
        const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
        if( !pTemplateRuleSet )
            return;

        pTemplateRuleSet->setup( c, tcc, *ValueFromIRExpr( tpl ) );
    }

    extern optional< Term > BuildTemplateArgPattern( const Context& c, const Term& tpl )
    {
        const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
        if( !pTemplateRuleSet )
            return nullopt;
Changes to bs/sema/template.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef GOOSE_SEMA_TEMPLATE_H
#define GOOSE_SEMA_TEMPLATE_H

namespace goose::sema
{
    extern ptr< TemplateRule > GetTemplateRuleSet( const Context& c, const Term& tpl );
    extern optional< Term > BuildTemplateSignature( const Context& c, const Term& tpl );
    extern Value BuildTemplateParam( const Context& c, const Term& tpl, const Term& arg );
    extern void TemplateSetup( const Context& c, UnificationContext uc, const Term& tpl );

    extern optional< Term > BuildTemplateArgPattern( const Context& c, const Term& tpl );
}

#endif








|





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

namespace goose::sema
{
    extern ptr< TemplateRule > GetTemplateRuleSet( const Context& c, const Term& tpl );
    extern optional< Term > BuildTemplateSignature( const Context& c, const Term& tpl );
    extern Value BuildTemplateParam( const Context& c, const Term& tpl, const Term& arg );
    extern void TemplateSetup( const Context& c, TypeCheckingContext tcc, const Term& tpl );

    extern optional< Term > BuildTemplateArgPattern( const Context& c, const Term& tpl );
}

#endif
Changes to bs/sema/tests/meson.build.
1
2
3
4
5
6
7
8
9
10
11
tests = [
    'unify-holes',
    'utrie-merge',
    'utrie-unify'
]

foreach t : tests
    exe = executable( 'sema-' + t, t + '.cpp',
        link_with:
        [
            goose_util,


|
|







1
2
3
4
5
6
7
8
9
10
11
tests = [
    'unify-holes',
    'tctrie-merge',
    'tctrie-typecheck'
]

foreach t : tests
    exe = executable( 'sema-' + t, t + '.cpp',
        link_with:
        [
            goose_util,
Name change from bs/sema/tests/utrie-merge.cpp to bs/sema/tests/tctrie-merge.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "sema/sema.h"
#include "builtins/builtins.h"

using namespace std;
using namespace goose;
using namespace goose::ir;
using namespace goose::sema;
using namespace goose::builtins;

SCENARIO( "UTrie merge works", "[utrie-merge]" )
{
    WHEN( "Merging various expressions into an utrie" )
    {
        auto vec1 = Vector::Make( TSTR( "foo" ), TSTR( "bar" ) );

        auto vec2 = Vector::Make( HOLE( "a"_sid ), HOLE( "a"_sid ) );
        auto vec3 = Vector::Make( TSTR( "meh" ), TSTR( "meh" ) );











|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "sema/sema.h"
#include "builtins/builtins.h"

using namespace std;
using namespace goose;
using namespace goose::ir;
using namespace goose::sema;
using namespace goose::builtins;

SCENARIO( "TCTrie merge works", "[utrie-merge]" )
{
    WHEN( "Merging various expressions into an utrie" )
    {
        auto vec1 = Vector::Make( TSTR( "foo" ), TSTR( "bar" ) );

        auto vec2 = Vector::Make( HOLE( "a"_sid ), HOLE( "a"_sid ) );
        auto vec3 = Vector::Make( TSTR( "meh" ), TSTR( "meh" ) );
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

        auto vec10 = Vector::Make( HOLE( "x"_sid ), TERM( vec4 ),      HOLE( "z"_sid ), HOLE( "z"_sid ) );
        auto vec11 = Vector::Make( TERM( vec5 ),      HOLE( "y"_sid ), HOLE( "y"_sid ), TERM( vec5 ) );

        auto vec12 = Vector::Make( VEC( HOLE( "a"_sid ), TSTR( "foo" ) ), HOLE( "a"_sid ) );
        auto vec13 = Vector::Make( HOLE( "b"_sid ),                        HOLE( "b"_sid ) );

        auto trie = UTrie< string >().merge( *vec1, []( auto&& ){ return "vec1"s; } );
        trie = trie->merge( *vec2, []( auto&& ){ return "vec2"s; } );
        trie = trie->merge( *vec3, []( auto&& ){ return "vec3"s; } );
        trie = trie->merge( *vec4, []( auto&& ){ return "vec4"s; } );
        trie = trie->merge( *vec5, []( auto&& ){ return "vec5"s; } );
        trie = trie->merge( *vec6, []( auto&& ){ return "vec6"s; } );
        trie = trie->merge( *vec7, []( auto&& ){ return "vec7"s; } );
        trie = trie->merge( *vec8, []( auto&& ){ return "vec8"s; } );
        trie = trie->merge( *vec9, []( auto&& ){ return "vec9"s; } );
        trie = trie->merge( *vec10, []( auto&& ){ return "vec10"s; } );
        trie = trie->merge( *vec11, []( auto&& ){ return "vec11"s; } );
        trie = trie->merge( *vec12, []( auto&& ){ return "vec12"s; } );
        trie = trie->merge( *vec13, []( auto&& ){ return "vec13"s; } );

        THEN( "Half-unifications yields the expected solutions" )
        {
            Context ctxt( make_shared< Env >(), VEC( TSID( g0 ) ) );
            UnificationContext context( ctxt );

            vector< pair< string, UnificationScore > > results;

            for( auto&& [lv, s,content,uc] : trie->halfUnify( context ) )
                results.emplace_back( content, uc.score() );

            sort( results.begin(), results.end(), []( auto&& a, auto&& b )
            {
                return a.first < b.first;
            } );

            REQUIRE( results.size() == 13 );
            REQUIRE( results[0] == make_pair( "vec1"s, UnificationScore( 1, 0 ) ) );
            REQUIRE( results[1] == make_pair( "vec10"s, UnificationScore( 2, 3 ) ) );
            REQUIRE( results[2] == make_pair( "vec11"s, UnificationScore( 3, 2 ) ) );
            REQUIRE( results[3] == make_pair( "vec12"s, UnificationScore( 2, 1 ) ) );
            REQUIRE( results[4] == make_pair( "vec13"s, UnificationScore( 1, 1 ) ) );
            REQUIRE( results[5] == make_pair( "vec2"s, UnificationScore( 1, 1 ) ) );
            REQUIRE( results[6] == make_pair( "vec3"s, UnificationScore( 1, 0 ) ) );
            REQUIRE( results[7] == make_pair( "vec4"s, UnificationScore( 1, 1 ) ) );
            REQUIRE( results[8] == make_pair( "vec5"s, UnificationScore( 1, 1 ) ) );
            REQUIRE( results[9] == make_pair( "vec6"s, UnificationScore( 1, 2 ) ) );
            REQUIRE( results[10] == make_pair( "vec7"s, UnificationScore( 1, 1 ) ) );
            REQUIRE( results[11] == make_pair( "vec8"s, UnificationScore( 1, 2 ) ) );
            REQUIRE( results[12] == make_pair( "vec9"s, UnificationScore( 3, 1 ) ) );
        }
    }
}







|
















|

|

|
|







|
|
|
|
|
|
|
|
|
|
|
|
|



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

        auto vec10 = Vector::Make( HOLE( "x"_sid ), TERM( vec4 ),      HOLE( "z"_sid ), HOLE( "z"_sid ) );
        auto vec11 = Vector::Make( TERM( vec5 ),      HOLE( "y"_sid ), HOLE( "y"_sid ), TERM( vec5 ) );

        auto vec12 = Vector::Make( VEC( HOLE( "a"_sid ), TSTR( "foo" ) ), HOLE( "a"_sid ) );
        auto vec13 = Vector::Make( HOLE( "b"_sid ),                        HOLE( "b"_sid ) );

        auto trie = TCTrie< string >().merge( *vec1, []( auto&& ){ return "vec1"s; } );
        trie = trie->merge( *vec2, []( auto&& ){ return "vec2"s; } );
        trie = trie->merge( *vec3, []( auto&& ){ return "vec3"s; } );
        trie = trie->merge( *vec4, []( auto&& ){ return "vec4"s; } );
        trie = trie->merge( *vec5, []( auto&& ){ return "vec5"s; } );
        trie = trie->merge( *vec6, []( auto&& ){ return "vec6"s; } );
        trie = trie->merge( *vec7, []( auto&& ){ return "vec7"s; } );
        trie = trie->merge( *vec8, []( auto&& ){ return "vec8"s; } );
        trie = trie->merge( *vec9, []( auto&& ){ return "vec9"s; } );
        trie = trie->merge( *vec10, []( auto&& ){ return "vec10"s; } );
        trie = trie->merge( *vec11, []( auto&& ){ return "vec11"s; } );
        trie = trie->merge( *vec12, []( auto&& ){ return "vec12"s; } );
        trie = trie->merge( *vec13, []( auto&& ){ return "vec13"s; } );

        THEN( "Half-unifications yields the expected solutions" )
        {
            Context ctxt( make_shared< Env >(), VEC( TSID( g0 ) ) );
            TypeCheckingContext context( ctxt );

            vector< pair< string, TypeCheckingScore > > results;

            for( auto&& [lv, s,content,tcc] : trie->halfUnify( context ) )
                results.emplace_back( content, tcc.score() );

            sort( results.begin(), results.end(), []( auto&& a, auto&& b )
            {
                return a.first < b.first;
            } );

            REQUIRE( results.size() == 13 );
            REQUIRE( results[0] == make_pair( "vec1"s, TypeCheckingScore( 1, 0 ) ) );
            REQUIRE( results[1] == make_pair( "vec10"s, TypeCheckingScore( 2, 3 ) ) );
            REQUIRE( results[2] == make_pair( "vec11"s, TypeCheckingScore( 3, 2 ) ) );
            REQUIRE( results[3] == make_pair( "vec12"s, TypeCheckingScore( 2, 1 ) ) );
            REQUIRE( results[4] == make_pair( "vec13"s, TypeCheckingScore( 1, 1 ) ) );
            REQUIRE( results[5] == make_pair( "vec2"s, TypeCheckingScore( 1, 1 ) ) );
            REQUIRE( results[6] == make_pair( "vec3"s, TypeCheckingScore( 1, 0 ) ) );
            REQUIRE( results[7] == make_pair( "vec4"s, TypeCheckingScore( 1, 1 ) ) );
            REQUIRE( results[8] == make_pair( "vec5"s, TypeCheckingScore( 1, 1 ) ) );
            REQUIRE( results[9] == make_pair( "vec6"s, TypeCheckingScore( 1, 2 ) ) );
            REQUIRE( results[10] == make_pair( "vec7"s, TypeCheckingScore( 1, 1 ) ) );
            REQUIRE( results[11] == make_pair( "vec8"s, TypeCheckingScore( 1, 2 ) ) );
            REQUIRE( results[12] == make_pair( "vec9"s, TypeCheckingScore( 3, 1 ) ) );
        }
    }
}
Name change from bs/sema/tests/utrie-unify.cpp to bs/sema/tests/tctrie-typecheck.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
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "sema/sema.h"
#include "builtins/builtins.h"

using namespace std;
using namespace goose;
using namespace goose::ir;
using namespace goose::sema;
using namespace goose::builtins;

namespace
{
    auto GetSortedSolutions( ptr< UTrie< string > >& trie, const Vector& vec )
    {
        Context ctxt( make_shared< Env >(), VEC( TSID( g0 ) ) );
        UnificationContext context( ctxt );

        vector< tuple< Term, string, UnificationScore > > solutions;

        for( auto&& [lv, v,content,c] : trie->unify( vec, context ) )
        {
            if( c.numUnknownValues() )
                continue;

            auto sol = Postprocess( TERM( make_shared< Vector >( v ) ), c );
            solutions.emplace_back( *sol, content, c.score() );
        }

        sort( solutions.begin(), solutions.end(), []( auto&& a, auto&& b )
        {
            return get< 1 >( a ) < get< 1 >( b );
        } );

        return solutions;
    }
}

SCENARIO( "UTrie unification works", "[unify]" )
{
    WHEN( "Unifying a utrie with various expressions" )
    {










        auto vec1 = Vector::Make( TSTR( "foo" ), TSTR( "bar" ) );

        auto vec2 = Vector::Make( HOLE( "a"_sid ), HOLE( "a"_sid ) );
        auto vec3 = Vector::Make( TSTR( "meh" ), TSTR( "meh" ) );

        auto vec4 = Vector::Make( HOLE( "a"_sid ), TSTR( "bar" ) );
        auto vec5 = Vector::Make( TSTR( "foo" ), HOLE( "b"_sid ) );

        auto vec9 = Vector::Make( TERM( vec5 ), TERM( vec1 ) );

        auto vec10 = Vector::Make( HOLE( "x"_sid ), TERM( vec4 ),                   HOLE( "z"_sid ), HOLE( "z"_sid ) );
        auto vec11 = Vector::Make( TERM( vec5 ),                   HOLE( "y"_sid ), HOLE( "y"_sid ), TERM( vec5 ) );

        auto vec12 = Vector::Make( VEC( HOLE( "a"_sid ), TSTR( "foo" ) ), HOLE( "a"_sid ) );
        auto vec13 = Vector::Make( HOLE( "b"_sid ),                        HOLE( "b"_sid ) );

        auto trie = UTrie< string >().merge( *vec2, []( auto&& ){ return "vec2"s; } );
        trie = trie->merge( *vec4, []( auto&& ){ return "vec4"s; } );
        trie = trie->merge( *vec10, []( auto&& ){ return "vec10"s; } );
        trie = trie->merge( *vec12, []( auto&& ){ return "vec12"s; } );

        THEN( "We obtain the expected solutions" )
        {
            auto sols1 = GetSortedSolutions( trie, *vec1 );
            auto sols3 = GetSortedSolutions( trie, *vec3 );
            auto sols5 = GetSortedSolutions( trie, *vec5 );
            auto sols9 = GetSortedSolutions( trie, *vec9 );
            auto sols11 = GetSortedSolutions( trie, *vec11 );
            auto sols13 = GetSortedSolutions( trie, *vec13 );

            REQUIRE( sols1.size() == 1 );
            REQUIRE( sols1[0] == make_tuple( VEC( TSTR( "foo" ), TSTR( "bar" ) ), "vec4"s, UnificationScore( 2, 1 ) ) );

            REQUIRE( sols3.size() == 1 );
            REQUIRE( sols3[0] == make_tuple( VEC( TSTR( "meh" ), TSTR( "meh" ) ), "vec2"s, UnificationScore( 2, 1 ) ) );

            REQUIRE( sols5.size() == 2 );
            REQUIRE( sols5[0] == make_tuple( VEC( TSTR( "foo" ), TSTR( "foo" ) ), "vec2"s, UnificationScore( 2, 2 ) ) );
            REQUIRE( sols5[1] == make_tuple( VEC( TSTR( "foo" ), TSTR( "bar" ) ), "vec4"s, UnificationScore( 2, 2 ) ) );

            REQUIRE( sols9.size() == 1 );
            REQUIRE( sols9[0] == make_tuple(
                VEC(
                    VEC( TSTR( "foo" ), TSTR( "bar" ) ),
                    VEC( TSTR( "foo" ), TSTR( "bar" ) )
                ), "vec2"s, UnificationScore( 4, 2 ) )
            );

            REQUIRE( sols11.size() == 1 );
            REQUIRE( sols11[0] == make_tuple(
                VEC(
                    VEC( TSTR( "foo" ), TSTR( "bar" ) ),
                    VEC( TSTR( "foo" ), TSTR( "bar" ) ),
                    VEC( TSTR( "foo" ), TSTR( "bar" ) ),
                    VEC( TSTR( "foo" ), TSTR( "bar" ) )
                ), "vec10"s, UnificationScore( 5, 5 ) )
            );

            REQUIRE( sols13.size() == 1 );
            REQUIRE( sols13[0] == make_tuple( VEC( TSTR( "bar" ), TSTR( "bar" ) ), "vec4"s, UnificationScore( 2, 2 ) ) );
        }
    }
}













|

|
|

|

|

















|

|

>
>
>
>
>
>
>
>
>
>
















|






|
|
|
|
|
|


|


|


|
|






|









|



|



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
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "sema/sema.h"
#include "builtins/builtins.h"

using namespace std;
using namespace goose;
using namespace goose::ir;
using namespace goose::sema;
using namespace goose::builtins;

namespace
{
    auto GetSortedSolutions( const ptr< Env >& e, ptr< TCTrie< string > >& trie, const Vector& vec )
    {
        Context ctxt( e, VEC( TSID( g0 ) ) );
        TypeCheckingContext context( ctxt );

        vector< tuple< Term, string, TypeCheckingScore > > solutions;

        for( auto&& [lv, v,content,c] : trie->typeCheck( vec, context ) )
        {
            if( c.numUnknownValues() )
                continue;

            auto sol = Postprocess( TERM( make_shared< Vector >( v ) ), c );
            solutions.emplace_back( *sol, content, c.score() );
        }

        sort( solutions.begin(), solutions.end(), []( auto&& a, auto&& b )
        {
            return get< 1 >( a ) < get< 1 >( b );
        } );

        return solutions;
    }
}

SCENARIO( "TCTrie type checking works", "[typecheck]" )
{
    WHEN( "Type checking a tctrie with various expressions" )
    {
        // We don't have all the builtin type check rules here but we do need some kind
        // of default rule to "typecheck" two identical strings for this test, so we just create it here.
        auto e = make_shared< Env >();

        e->typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS, ANYTERM( T ), ANYTERM( T ), []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            assert( lhs == rhs );
            co_yield { lhs, tcc };
        } );

        auto vec1 = Vector::Make( TSTR( "foo" ), TSTR( "bar" ) );

        auto vec2 = Vector::Make( HOLE( "a"_sid ), HOLE( "a"_sid ) );
        auto vec3 = Vector::Make( TSTR( "meh" ), TSTR( "meh" ) );

        auto vec4 = Vector::Make( HOLE( "a"_sid ), TSTR( "bar" ) );
        auto vec5 = Vector::Make( TSTR( "foo" ), HOLE( "b"_sid ) );

        auto vec9 = Vector::Make( TERM( vec5 ), TERM( vec1 ) );

        auto vec10 = Vector::Make( HOLE( "x"_sid ), TERM( vec4 ),                   HOLE( "z"_sid ), HOLE( "z"_sid ) );
        auto vec11 = Vector::Make( TERM( vec5 ),                   HOLE( "y"_sid ), HOLE( "y"_sid ), TERM( vec5 ) );

        auto vec12 = Vector::Make( VEC( HOLE( "a"_sid ), TSTR( "foo" ) ), HOLE( "a"_sid ) );
        auto vec13 = Vector::Make( HOLE( "b"_sid ),                        HOLE( "b"_sid ) );

        auto trie = TCTrie< string >().merge( *vec2, []( auto&& ){ return "vec2"s; } );
        trie = trie->merge( *vec4, []( auto&& ){ return "vec4"s; } );
        trie = trie->merge( *vec10, []( auto&& ){ return "vec10"s; } );
        trie = trie->merge( *vec12, []( auto&& ){ return "vec12"s; } );

        THEN( "We obtain the expected solutions" )
        {
            auto sols1 = GetSortedSolutions( e, trie, *vec1 );
            auto sols3 = GetSortedSolutions( e, trie, *vec3 );
            auto sols5 = GetSortedSolutions( e, trie, *vec5 );
            auto sols9 = GetSortedSolutions( e, trie, *vec9 );
            auto sols11 = GetSortedSolutions( e, trie, *vec11 );
            auto sols13 = GetSortedSolutions( e, trie, *vec13 );

            REQUIRE( sols1.size() == 1 );
            REQUIRE( sols1[0] == make_tuple( VEC( TSTR( "foo" ), TSTR( "bar" ) ), "vec4"s, TypeCheckingScore( 2, 1 ) ) );

            REQUIRE( sols3.size() == 1 );
            REQUIRE( sols3[0] == make_tuple( VEC( TSTR( "meh" ), TSTR( "meh" ) ), "vec2"s, TypeCheckingScore( 2, 1 ) ) );

            REQUIRE( sols5.size() == 2 );
            REQUIRE( sols5[0] == make_tuple( VEC( TSTR( "foo" ), TSTR( "foo" ) ), "vec2"s, TypeCheckingScore( 2, 2 ) ) );
            REQUIRE( sols5[1] == make_tuple( VEC( TSTR( "foo" ), TSTR( "bar" ) ), "vec4"s, TypeCheckingScore( 2, 2 ) ) );

            REQUIRE( sols9.size() == 1 );
            REQUIRE( sols9[0] == make_tuple(
                VEC(
                    VEC( TSTR( "foo" ), TSTR( "bar" ) ),
                    VEC( TSTR( "foo" ), TSTR( "bar" ) )
                ), "vec2"s, TypeCheckingScore( 4, 2 ) )
            );

            REQUIRE( sols11.size() == 1 );
            REQUIRE( sols11[0] == make_tuple(
                VEC(
                    VEC( TSTR( "foo" ), TSTR( "bar" ) ),
                    VEC( TSTR( "foo" ), TSTR( "bar" ) ),
                    VEC( TSTR( "foo" ), TSTR( "bar" ) ),
                    VEC( TSTR( "foo" ), TSTR( "bar" ) )
                ), "vec10"s, TypeCheckingScore( 5, 5 ) )
            );

            REQUIRE( sols13.size() == 1 );
            REQUIRE( sols13[0] == make_tuple( VEC( TSTR( "bar" ), TSTR( "bar" ) ), "vec4"s, TypeCheckingScore( 2, 2 ) ) );
        }
    }
}
Changes to bs/sema/tests/unify-holes.cpp.
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
using namespace goose::sema;
using namespace goose::builtins;

namespace
{
    // Verifies that the unification of lhs and rhs yields only one solution, that it is complete,
    // and that this solution is the expected one.
    void CheckForUniqueSolution( const Term& lhs, const Term& rhs, const Term& expectedSolution, const UnificationScore& expectedScore )
    {
        Context ctxt( make_shared< Env >(), VEC( TSID( g0 ) ) );
        UnificationContext uc( ctxt );
        uc.setComplexity( GetComplexity( lhs ) + GetComplexity( rhs ) );

        auto g = Unify( lhs, rhs, uc );

        auto it = g.begin();
        REQUIRE( it != g.end() );

        auto&& [e,c] = *it;

        REQUIRE( c.numUnknownValues() == 0 );
        REQUIRE( c.score() == expectedScore );

        auto sol = Postprocess( e, c );
        REQUIRE( sol == expectedSolution );

        ++it;
        REQUIRE( it == g.end() );
    }

    void CheckForNoSolution( const Term& lhs, const Term& rhs )
    {
        Context ctxt( make_shared< Env >(), VEC( TSID( g0 ) ) );
        UnificationContext uc( ctxt );
        uc.setComplexity( GetComplexity( lhs ) + GetComplexity( rhs ) );

        auto g = Unify( lhs, rhs, uc );

        auto it = g.begin();
        REQUIRE( it == g.end() );
    }
}

SCENARIO( "Unify works", "[unify]" )







|


|
|

|



















|
|

|







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
using namespace goose::sema;
using namespace goose::builtins;

namespace
{
    // Verifies that the unification of lhs and rhs yields only one solution, that it is complete,
    // and that this solution is the expected one.
    void CheckForUniqueSolution( const Term& lhs, const Term& rhs, const Term& expectedSolution, const TypeCheckingScore& expectedScore )
    {
        Context ctxt( make_shared< Env >(), VEC( TSID( g0 ) ) );
        TypeCheckingContext tcc( ctxt );
        tcc.setComplexity( GetComplexity( lhs ) + GetComplexity( rhs ) );

        auto g = Unify( lhs, rhs, tcc );

        auto it = g.begin();
        REQUIRE( it != g.end() );

        auto&& [e,c] = *it;

        REQUIRE( c.numUnknownValues() == 0 );
        REQUIRE( c.score() == expectedScore );

        auto sol = Postprocess( e, c );
        REQUIRE( sol == expectedSolution );

        ++it;
        REQUIRE( it == g.end() );
    }

    void CheckForNoSolution( const Term& lhs, const Term& rhs )
    {
        Context ctxt( make_shared< Env >(), VEC( TSID( g0 ) ) );
        TypeCheckingContext tcc( ctxt );
        tcc.setComplexity( GetComplexity( lhs ) + GetComplexity( rhs ) );

        auto g = Unify( lhs, rhs, tcc );

        auto it = g.begin();
        REQUIRE( it == g.end() );
    }
}

SCENARIO( "Unify works", "[unify]" )
Changes to bs/sema/tpl-ruleset.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef GOOSE_SEMA_TPL_RULESET_H
#define GOOSE_SEMA_TPL_RULESET_H

namespace goose::sema
{
    class TemplateRule
    {
        public:
            virtual ~TemplateRule() {}

            virtual optional< Term > buildSignature( const Context& c, const Value& tpl ) const = 0;
            virtual Value buildParamDecl( const Context& c, const Value& param, const Value& arg ) const = 0;
            virtual void setup( const Context& c, UnificationContext& uc, const Value& tpl ) const {};
            virtual optional< Term > buildArgPattern( const Context& c, const Value& tpl ) const = 0;
    };

    class TemplateRuleSet
    {
        public:
            TemplateRuleSet() {}












|







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

namespace goose::sema
{
    class TemplateRule
    {
        public:
            virtual ~TemplateRule() {}

            virtual optional< Term > buildSignature( const Context& c, const Value& tpl ) const = 0;
            virtual Value buildParamDecl( const Context& c, const Value& param, const Value& arg ) const = 0;
            virtual void setup( const Context& c, TypeCheckingContext& tcc, const Value& tpl ) const {};
            virtual optional< Term > buildArgPattern( const Context& c, const Value& tpl ) const = 0;
    };

    class TemplateRuleSet
    {
        public:
            TemplateRuleSet() {}
Name change from bs/sema/unify.cpp to bs/sema/typecheck.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
#include "sema.h"

namespace goose::sema
{
    UnificationSolution FindBestUnification( const Term& lhs, const Term& rhs, const Context& context )
    {
        optional< UnificationContext > bestUC;
        optional< Term > bestSol;
        bool ambiguous = false;

        UnificationContext uc( context );
        uc.setComplexity( GetComplexity( lhs ) + GetComplexity( rhs ) );

        for( auto&& [s, uc] : Unify( lhs, rhs, uc ) )
        {
            if( uc.numUnknownValues() )
                continue;

            if( bestUC && uc.score() < bestUC->score() )
                continue;

            auto pps = Postprocess( s, uc );
            if( !pps )
                continue;

            if( bestUC && uc.score() == bestUC->score() )
            {
                ambiguous = true;
                continue;
            }

            bestUC = uc;
            bestSol = move( *pps );
            ambiguous = false;
        }

        if( ambiguous )
            return AmbiguousUnification{};

        if( !bestSol )
            return {};

        return make_pair( move( *bestSol ), move( *bestUC ) );
    }




































    UniGen Unify( const Term& lhs, const Term& rhs, const UnificationContext& context )
    {
        const auto& rules = context.rules()->uniRules();

        MatchSolution bestSol;
        optional< UnificationRuleSet::UniRule > bestRule;
        bool ambiguous = false;

        auto expr = VEC( lhs, rhs );
        for( auto&& [s, rule] : Match( expr, rules ) )
        {
            if( !bestRule || s > bestSol )
            {




|

|



|
|

|

|


|


|



|





|





|




|


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|




|







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
#include "sema.h"

namespace goose::sema
{
    TypeCheckingSolution FindBestTyping( const Term& lhs, const Term& rhs, const Context& context )
    {
        optional< TypeCheckingContext > bestTCC;
        optional< Term > bestSol;
        bool ambiguous = false;

        TypeCheckingContext tcc( context );
        tcc.setComplexity( GetComplexity( lhs ) + GetComplexity( rhs ) );

        for( auto&& [s, tcc] : TypeCheck( lhs, rhs, tcc ) )
        {
            if( tcc.numUnknownValues() )
                continue;

            if( bestTCC && tcc.score() < bestTCC->score() )
                continue;

            auto pps = Postprocess( s, tcc );
            if( !pps )
                continue;

            if( bestTCC && tcc.score() == bestTCC->score() )
            {
                ambiguous = true;
                continue;
            }

            bestTCC = tcc;
            bestSol = move( *pps );
            ambiguous = false;
        }

        if( ambiguous )
            return AmbiguousTypeCheck{};

        if( !bestSol )
            return {};

        return make_pair( move( *bestSol ), move( *bestTCC ) );
    }

    TCGen TypeCheck( const Term& lhs, const Term& rhs, const TypeCheckingContext& context )
    {
        const auto& rules = context.rules()->typeCheckingRules();

        MatchSolution bestSol;
        optional< TypeCheckingRuleSet::UniRule > bestRule;
        bool ambiguous = false;

        auto expr = VEC( lhs, rhs );
        for( auto&& [s, rule] : Match( expr, rules ) )
        {
            if( !bestRule || s > bestSol )
            {
                bestSol = s;
                bestRule = rule;
                ambiguous = false;
                continue;
            }

            if( s < bestSol )
                continue;

            ambiguous = true;
        }

        // Let's not really worry about how to properly report this for now.
        if( ambiguous )
            throw runtime_error( "ambiguous unification rule" );

        if( !bestRule )
            co_return;

        co_yield bestRule->func( lhs, rhs, context );
    }

    TCGen Unify( const Term& lhs, const Term& rhs, const TypeCheckingContext& context )
    {
        const auto& rules = context.rules()->uniRules();

        MatchSolution bestSol;
        optional< TypeCheckingRuleSet::UniRule > bestRule;
        bool ambiguous = false;

        auto expr = VEC( lhs, rhs );
        for( auto&& [s, rule] : Match( expr, rules ) )
        {
            if( !bestRule || s > bestSol )
            {
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93

        if( !bestRule )
            co_return;

        co_yield bestRule->func( lhs, rhs, context );
    }

    UniGen HalfUnify( const Term& lhs, const UnificationContext& context )
    {
        const auto& rules = context.rules()->halfUniRules();

        MatchSolution bestSol;
        optional< UnificationRuleSet::HalfUniRule > bestRule;
        bool ambiguous = false;

        for( auto&& [s, rule] : Match( lhs, rules ) )
        {
            if( !bestRule || s > bestSol )
            {
                bestSol = s;







|




|







109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128

        if( !bestRule )
            co_return;

        co_yield bestRule->func( lhs, rhs, context );
    }

    TCGen HalfUnify( const Term& lhs, const TypeCheckingContext& context )
    {
        const auto& rules = context.rules()->halfUniRules();

        MatchSolution bestSol;
        optional< TypeCheckingRuleSet::HalfUniRule > bestRule;
        bool ambiguous = false;

        for( auto&& [s, rule] : Match( lhs, rules ) )
        {
            if( !bestRule || s > bestSol )
            {
                bestSol = s;
Name change from bs/sema/unify.h to bs/sema/typecheck.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
#ifndef GOOSE_SEMA_UNIFY_H
#define GOOSE_SEMA_UNIFY_H

namespace goose::sema
{
    struct NoUnification {};
    struct AmbiguousUnification {};
    using UniSol = pair< Term, UnificationContext >;

    using UnificationSolution = variant
    <
        NoUnification,
        AmbiguousUnification,
        UniSol
    >;

    UnificationSolution FindBestUnification( const Term& lhs, const Term& rhs, const Context& context );

    using UniGen = Generator< pair< Term, UnificationContext > >;



    UniGen Unify( const Term& lhs, const Term& rhs, const UnificationContext& context );
    UniGen HalfUnify( const Term& lhs, const UnificationContext& context );
}

#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
#ifndef GOOSE_SEMA_TYPECHECK_H
#define GOOSE_SEMA_TYPECHECK_H

namespace goose::sema
{
    struct NoUnification {};
    struct AmbiguousTypeCheck {};
    using TCSol = pair< Term, TypeCheckingContext >;

    using TypeCheckingSolution = variant
    <
        NoUnification,
        AmbiguousTypeCheck,
        TCSol
    >;

    TypeCheckingSolution FindBestTyping( const Term& lhs, const Term& rhs, const Context& context );

    using TCGen = Generator< pair< Term, TypeCheckingContext > >;

    TCGen TypeCheck( const Term& lhs, const Term& rhs, const TypeCheckingContext& context );

    TCGen Unify( const Term& lhs, const Term& rhs, const TypeCheckingContext& context );
    TCGen HalfUnify( const Term& lhs, const TypeCheckingContext& context );
}

#endif
Changes to bs/sema/uni-basicrules.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
134
135
136
137
138
#include "sema.h"

namespace goose::sema
{
    UniGen HalfUnifyVectors( const Vector::container_type< Term >& lTerms, size_t i, const Vector& solutionVec, UnificationContext uc )
    {
        assert( i < lTerms.size() );

        for( auto&& [u,uc] : HalfUnify( lTerms[i], uc ) )
        {
            auto vec = Vector::MakeAppend( solutionVec, move( u ) );

            if( i == ( lTerms.size() - 1 ) )
                co_yield { TERM( make_shared< Vector >( move( vec ) ) ), uc };
            else
                co_yield HalfUnifyVectors( lTerms, i + 1, vec, uc );
        }
    }

    UniGen UnifyVectors( const Vector::container_type< Term >& lTerms, const Vector::container_type< Term >& rTerms, size_t i,
        const Vector& solutionVec, UnificationContext uc )
    {
        assert( lTerms.size() == rTerms.size() );
        assert( i < lTerms.size() );

        for( auto&& [u,uc] : Unify( lTerms[i], rTerms[i], uc ) )









        {








            auto vec = Vector::MakeAppend( solutionVec, move( u ) );

            if( i == ( lTerms.size() - 1 ) )
                co_yield { TERM( make_shared< Vector >( move( vec ) ) ), uc };
            else
                co_yield UnifyVectors( lTerms, rTerms, i + 1, vec, uc );
        }
    }

    void SetupBasicUnificationRules( UnificationRuleSet& ruleSet )
    {
        // Default half-unification rule
        ruleSet.addHalfUnificationRule( URINFOS, ANYTERM( _ ), []( const Term& lhs, const UnificationContext& uc ) -> UniGen
        {
            // NOTE: the score here is always 0 because:
            // Vectors are going to fall into the vector half-unification rule below,
            // and holes are going to have their own rules. So we only are here for terminal expressions.
            co_yield { lhs, uc };
        } );

        // Identity unification rule
        ruleSet.addSymRule( URINFOS, ANYTERM( T ), []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
        {
            assert( lhs == rhs );

            // NOTE: the score here is always 0 because:
            // Vectors of identical size are going to fall into the vector unification rule below,
            // and holes are going to have their own rules. So we only are here for literal matches
            // of terminal expressions.
            co_yield { lhs, uc };
        } );

        // LocationId unification rule
        ruleSet.addSymRule( URINFOS, TERM( LocationId( 0 ) ), []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
        {
            // This rule doesn't have any semantic impact, it's just here to try and preserve locationIds
            // and poisoning accross unification as much as possible. This only impacts the quality of
            // error messages.
            auto lloc = static_cast< uint32_t >( get< LocationId >( lhs ) );
            auto rloc = static_cast< uint32_t >( get< LocationId >( rhs ) );

            // We keep the max location index of the two:
            //  - if either loc is invalid (0), this will take the other one.
            //  - if either loc is poisoned (~0), the result will be poisoned.
            //  - otherwise it will pick the most recently created location of the two,
            //    which is as good an heuristic as any.
            uint32_t result = max( lloc, rloc );

            co_yield { static_cast< LocationId >( result ), uc };
        } );

        // void* unification rule
        ruleSet.addSymRule( URINFOS, TERM( nullptr ), []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
        {
            // Only preserve void* accross unification when they are equal. Otherwise,
            // just clear it: it's mostly an optimisation to try and keep llvm types
            // around as much as possible, but if we can't, codegen will recompute them
            // in the end if necessary.
            auto lpv = get< void* >( lhs );
            auto rpv = get< void* >( rhs );
            auto result = lpv == rpv ? lpv : nullptr;
            co_yield { result, uc };
        } );

        // Half-Unification rule for vectors:
        // They are recursed into and each contained members are half-unified.
        // Note: the repetition term, if any, is ignored. The repetition term is only for vector patterns
        // for now. We'll have to see how to handle variadic functions later.
        ruleSet.addHalfUnificationRule( URINFOS, VECOFLENGTH( _ ), []( const Term& lhs, const UnificationContext& uc ) -> UniGen
        {
            assert( holds_alternative< pvec >( lhs ) );

            const auto& lVector = get< pvec >( lhs );
            const auto& lTerms = lVector->terms();

            if( lTerms.empty() )
            {
                co_yield { lhs, uc };
                co_return;
            }

            Vector sol;
            co_yield HalfUnifyVectors( lTerms, 0, sol, uc );
        } );

        // Unification rule for vectors of identical length:
        // They are recursed into and each contained members are unified.
        // Note: the repetition term, if any, is ignored. The repetition term is only for vector patterns
        // for now. We'll have to see how to handle variadic functions later.
        ruleSet.addSymRule( URINFOS, VECOFLENGTH( VL ), []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen





















        {
            assert( holds_alternative< pvec >( lhs ) );
            assert( holds_alternative< pvec >( rhs ) );

            const auto& lVector = get< pvec >( lhs );
            const auto& rVector = get< pvec >( rhs );

            const auto& lTerms = lVector->terms();
            const auto& rTerms = rVector->terms();

            if( lTerms.empty() )
            {
                co_yield { lhs, uc };
                co_return;
            }

            Vector sol;
            co_yield UnifyVectors( lTerms, rTerms, 0, sol, uc );
        } );
    }
}




|



|




|

|



|
|




|
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>



|

|



|


|




|



|







|



|














|



|








|






|








|




|






|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>












|




|



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
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
#include "sema.h"

namespace goose::sema
{
    TCGen HalfUnifyVectors( const Vector::container_type< Term >& lTerms, size_t i, const Vector& solutionVec, TypeCheckingContext tcc )
    {
        assert( i < lTerms.size() );

        for( auto&& [u,tcc] : HalfUnify( lTerms[i], tcc ) )
        {
            auto vec = Vector::MakeAppend( solutionVec, move( u ) );

            if( i == ( lTerms.size() - 1 ) )
                co_yield { TERM( make_shared< Vector >( move( vec ) ) ), tcc };
            else
                co_yield HalfUnifyVectors( lTerms, i + 1, vec, tcc );
        }
    }

    TCGen UnifyVectors( const Vector::container_type< Term >& lTerms, const Vector::container_type< Term >& rTerms, size_t i,
        const Vector& solutionVec, TypeCheckingContext tcc )
    {
        assert( lTerms.size() == rTerms.size() );
        assert( i < lTerms.size() );

        for( auto&& [u,tcc] : Unify( lTerms[i], rTerms[i], tcc ) )
        {
            auto vec = Vector::MakeAppend( solutionVec, move( u ) );

            if( i == ( lTerms.size() - 1 ) )
                co_yield { TERM( make_shared< Vector >( move( vec ) ) ), tcc };
            else
                co_yield UnifyVectors( lTerms, rTerms, i + 1, vec, tcc );
        }
    }

    TCGen TypeCheckVectors( const Vector::container_type< Term >& lTerms, const Vector::container_type< Term >& rTerms, size_t i,
        const Vector& solutionVec, TypeCheckingContext tcc )
    {
        assert( lTerms.size() == rTerms.size() );
        assert( i < lTerms.size() );

        for( auto&& [u,tcc] : TypeCheck( lTerms[i], rTerms[i], tcc ) )
        {
            auto vec = Vector::MakeAppend( solutionVec, move( u ) );

            if( i == ( lTerms.size() - 1 ) )
                co_yield { TERM( make_shared< Vector >( move( vec ) ) ), tcc };
            else
                co_yield TypeCheckVectors( lTerms, rTerms, i + 1, vec, tcc );
        }
    }

    void SetupBasicUnificationRules( TypeCheckingRuleSet& ruleSet )
    {
        // Default half-unification rule
        ruleSet.addHalfUnificationRule( TCRINFOS, ANYTERM( _ ), []( const Term& lhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            // NOTE: the score here is always 0 because:
            // Vectors are going to fall into the vector half-unification rule below,
            // and holes are going to have their own rules. So we only are here for terminal expressions.
            co_yield { lhs, tcc };
        } );

        // Identity unification rule
        ruleSet.addUnificationRule( TCRINFOS, ANYTERM( T ), []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            assert( lhs == rhs );

            // NOTE: the score here is always 0 because:
            // Vectors of identical size are going to fall into the vector unification rule below,
            // and holes are going to have their own rules. So we only are here for literal matches
            // of terminal expressions.
            co_yield { lhs, tcc };
        } );

        // LocationId unification rule
        ruleSet.addUnificationRule( TCRINFOS, TERM( LocationId( 0 ) ), []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            // This rule doesn't have any semantic impact, it's just here to try and preserve locationIds
            // and poisoning accross unification as much as possible. This only impacts the quality of
            // error messages.
            auto lloc = static_cast< uint32_t >( get< LocationId >( lhs ) );
            auto rloc = static_cast< uint32_t >( get< LocationId >( rhs ) );

            // We keep the max location index of the two:
            //  - if either loc is invalid (0), this will take the other one.
            //  - if either loc is poisoned (~0), the result will be poisoned.
            //  - otherwise it will pick the most recently created location of the two,
            //    which is as good an heuristic as any.
            uint32_t result = max( lloc, rloc );

            co_yield { static_cast< LocationId >( result ), tcc };
        } );

        // void* unification rule
        ruleSet.addUnificationRule( TCRINFOS, TERM( nullptr ), []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            // Only preserve void* accross unification when they are equal. Otherwise,
            // just clear it: it's mostly an optimisation to try and keep llvm types
            // around as much as possible, but if we can't, codegen will recompute them
            // in the end if necessary.
            auto lpv = get< void* >( lhs );
            auto rpv = get< void* >( rhs );
            auto result = lpv == rpv ? lpv : nullptr;
            co_yield { result, tcc };
        } );

        // Half-Unification rule for vectors:
        // They are recursed into and each contained members are half-unified.
        // Note: the repetition term, if any, is ignored. The repetition term is only for vector patterns
        // for now. We'll have to see how to handle variadic functions later.
        ruleSet.addHalfUnificationRule( TCRINFOS, VECOFLENGTH( _ ), []( const Term& lhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            assert( holds_alternative< pvec >( lhs ) );

            const auto& lVector = get< pvec >( lhs );
            const auto& lTerms = lVector->terms();

            if( lTerms.empty() )
            {
                co_yield { lhs, tcc };
                co_return;
            }

            Vector sol;
            co_yield HalfUnifyVectors( lTerms, 0, sol, tcc );
        } );

        // Unification rule for vectors of identical length:
        // They are recursed into and each contained members are unified.
        // Note: the repetition term, if any, is ignored. The repetition term is only for vector patterns
        // for now. We'll have to see how to handle variadic functions later.
        ruleSet.addUnificationRule( TCRINFOS, VECOFLENGTH( VL ), []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            assert( holds_alternative< pvec >( lhs ) );
            assert( holds_alternative< pvec >( rhs ) );

            const auto& lVector = get< pvec >( lhs );
            const auto& rVector = get< pvec >( rhs );

            const auto& lTerms = lVector->terms();
            const auto& rTerms = rVector->terms();

            if( lTerms.empty() )
            {
                co_yield { lhs, tcc };
                co_return;
            }

            Vector sol;
            co_yield UnifyVectors( lTerms, rTerms, 0, sol, tcc );
        } );

        ruleSet.addTypeCheckingRule( TCRINFOS, VECOFLENGTH( VL ), VECOFLENGTH( VL ), []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            assert( holds_alternative< pvec >( lhs ) );
            assert( holds_alternative< pvec >( rhs ) );

            const auto& lVector = get< pvec >( lhs );
            const auto& rVector = get< pvec >( rhs );

            const auto& lTerms = lVector->terms();
            const auto& rTerms = rVector->terms();

            if( lTerms.empty() )
            {
                co_yield { lhs, tcc };
                co_return;
            }

            Vector sol;
            co_yield TypeCheckVectors( lTerms, rTerms, 0, sol, tcc );
        } );
    }
}
Changes to bs/sema/uni-basicrules.h.
1
2
3
4
5
6
7
8
9
#ifndef GOOSE_SEMA_UNI_BASICRULES_H
#define GOOSE_SEMA_UNI_BASICRULES_H

namespace goose::sema
{
    extern void SetupBasicUnificationRules( UnificationRuleSet& ruleSet );
}

#endif





|



1
2
3
4
5
6
7
8
9
#ifndef GOOSE_SEMA_UNI_BASICRULES_H
#define GOOSE_SEMA_UNI_BASICRULES_H

namespace goose::sema
{
    extern void SetupBasicUnificationRules( TypeCheckingRuleSet& ruleSet );
}

#endif
Changes to bs/sema/uni-holes.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
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

















#include "sema.h"

namespace goose::sema
{
    void SetupHoleUnificationRules( UnificationRuleSet& ruleSet )
    {
        // Anonymous hole half-unification: add 1 to the anon holes count,
        // yield the hole as is.
        ruleSet.addHalfUnificationRule( URINFOS,
            HOLE( "_"_sid ),
        []( const Term& lhs, UnificationContext uc ) -> UniGen
        {
            uc.addAnonymousHole();
            co_yield { lhs, uc };
        } );

        // Anonymous hole versus anything: add 1 to the anon holes count,
        // yield the half unification of the rhs
        ruleSet.addSymRule( URINFOS,
            HOLE( "_"_sid ),
            ANYTERM( _ ),
        []( const Term& lhs, const Term& rhs, UnificationContext uc ) -> UniGen
        {
            uc.addAnonymousHole();

            for( auto&& [s,uc] : HalfUnify( rhs, uc.flip() ) )
                co_yield { s, uc.flip() };
        } );

        // Hole half-unification: Convert it to a numbered hole,
        // If the name wasn't already known, add 1 to the score's unique holes count.
        ruleSet.addHalfUnificationRule( URINFOS,
            HolePattern(),
        []( const Term& lhs, UnificationContext uc ) -> UniGen
        {
            auto lh = *HoleFromIRExpr( lhs );

            if( !lh.name.isNumerical() )
            {
                // This is a named hole: look up its name.
                auto holeIndex = uc.getLHSHoleIndex( lh.name );
                if( holeIndex != UnificationContext::InvalidIndex )
                {
                    if( !uc.isHoleLocked( holeIndex ) )
                        co_yield { HOLE( StringId( holeIndex ) ), uc };
                }
                else
                {
                    // This is a new name: create a new value,
                    // and increment the number of unique holes in the current score.
                    auto index = uc.createValue();
                    uc.setLHSHoleIndex( lh.name, index );
                    co_yield { HOLE( StringId( index ) ), uc };
                }
            }
            else
            {
                // This is already an indexed hole: yield it as is.
                if( !uc.isHoleLocked( lh.name.id() ) )
                    co_yield { lhs, uc };
            }
        } );

        // Hole vs anything
        ruleSet.addSymRule( URINFOS,
            HolePattern(),
            ANYTERM( _ ),
        []( const Term& lhs, const Term& rhs, UnificationContext uc ) -> UniGen
        {
            auto h = *HoleFromIRExpr( lhs );
            uint32_t index = 0;

            // Remember the previous complexity count so we know how much complexity
            // is added by this particular sub-term. This is because we need
            // to be able to subtract it when updating the hole's value with a new solution.
            uint32_t oldComplexity = uc.complexity();

            if( h.name.isNumerical() )
                index = h.name.id();
            else
            {
                // This is a named hole: look up its name.
                index = uc.getLHSHoleIndex( h.name );
                if( index == UnificationContext::InvalidIndex )
                {
                    // This is a new name: create a new value.
                    index = uc.createValue();
                    uc.setLHSHoleIndex( h.name, index );
                    auto holeExpr = HOLE( StringId( index ) );

                    for( auto&& [e,uc] : HalfUnify( rhs, uc.flip() ) )
                    {
                        uc.setValue( index, SetComplexity( move( e ), uc.complexity() - oldComplexity ) );
                        co_yield { move( holeExpr ), uc.flip() };
                    }

                    co_return;
                }
            }

            // Reject recursive hole nesting.
            if( uc.isHoleLocked( index ) )
                co_return;
            uc.lockHole( index );

            auto holeExpr = HOLE( StringId( index ) );

            auto& maybeVal = uc.getValue( index );
            if( maybeVal )
            {
                for( auto&& [e,uc] : Unify( *maybeVal, rhs, uc ) )
                {
                    uc.unlockHole( index );
                    uc.setValue( index, SetComplexity( move( e ), uc.complexity() - oldComplexity ) );
                    co_yield { move( holeExpr ), uc };
                }
            }
            else
            {
                for( auto&& [e,uc] : HalfUnify( rhs, uc.flip() ) )
                {
                    uc.unlockHole( index );
                    uc.setValue( index, SetComplexity( move( e ), uc.complexity() - oldComplexity ) );
                    co_yield { move( holeExpr ), uc.flip() };
                }
            }
        } );

        // Hole vs hole
        ruleSet.addSymRule( URINFOS,
            HolePattern(),
        []( const Term& lhs, const Term& rhs, UnificationContext uc ) -> UniGen
        {
            auto lh = *HoleFromIRExpr( lhs );
            auto rh = *HoleFromIRExpr( rhs );

            StringId lname;
            StringId rname;

            uint32_t lindex = 0;
            uint32_t rindex = 0;

            if( !lh.name.isNumerical() )
            {
                // L is a named hole: look up its name.
                lname = lh.name;
                lindex = uc.getLHSHoleIndex( lname );
            }
            else
                lindex = lh.name.id();

            if( !rh.name.isNumerical() )
            {
                // R is a named hole: look up its name.
                rname = rh.name;
                rindex = uc.getRHSHoleIndex( rname );
            }
            else
                rindex = rh.name.id();

            // If neither hole currently have a value, create a new one.
            if( lindex == UnificationContext::InvalidIndex && rindex == UnificationContext::InvalidIndex )
            {
                auto index = uc.createValue();
                uc.setLHSHoleIndex( lname, index );
                uc.setRHSHoleIndex( rname, index );

                co_yield { HOLE( StringId( index ) ), uc };
                co_return;
            }

            // If both holes actually point to the same value, just yield it as the solution.
            if( lindex == rindex )
            {
                co_yield { HOLE( StringId( lindex ) ), uc };
                co_return;
            }

            // If either hole doesn't have a value yet, assign it the other one's value.
            if( lindex == UnificationContext::InvalidIndex )
            {
                uc.setLHSHoleIndex( lname, rindex );
                co_yield { HOLE( StringId( rindex ) ), uc };
                co_return;
            }

            if( rindex == UnificationContext::InvalidIndex )
            {
                uc.setRHSHoleIndex( rname, lindex );
                co_yield { HOLE( StringId( lindex ) ), uc };
                co_return;
            }

            // Reject recursive hole nesting.
            if( uc.isHoleLocked( lindex ) )
                co_return;
            if( uc.isHoleLocked( rindex ) )
                co_return;

            uc.lockHole( lindex );
            uc.lockHole( rindex );

            // If either hole have an empty value, set it to a hole expression with the id of the value
            // stored in the other one. We can't just copy the value over as we would lose the dependency
            // relationship between the two holes.
            const auto& lval = uc.getValue( lindex );
            const auto& rval = uc.getValue( rindex );

            if( !rval )
            {
                for( auto&& [e,uc] : HalfUnify( *lval, uc ) )
                {
                    uc.unlockHole( lindex );
                    uc.unlockHole( rindex );

                    uc.setValue( rindex, HOLE( StringId( lindex ) ) );
                    co_yield { HOLE( StringId( lindex ) ), uc };
                }
                co_return;
            }

            if( !lval )
            {
                for( auto&& [e,uc] : HalfUnify( *rval, uc.flip() ) )
                {
                    uc.unlockHole( lindex );
                    uc.unlockHole( rindex );

                    uc.setValue( lindex, HOLE( StringId( rindex ) ) );
                    co_yield { HOLE( StringId( rindex ) ), uc.flip() };
                }

                co_return;
            }

            // Both L and R have a value: unify them, store the result in lhs,
            // replace rhs with a hole expression pointing to lhs's value.

            // Remember the previous complexity count so we know how much complexity
            // is added by this particular sub-term. This is because we need
            // to be able to subtract it when updating the hole's value with a new solution.
            uint32_t oldComplexity = uc.complexity();

            for( auto&& [e,uc] : Unify( *lval, *rval, uc ) )
            {
                uc.unlockHole( lindex );
                uc.unlockHole( rindex );

                uc.setValue( lindex, SetComplexity( move( e ), uc.complexity() - oldComplexity ) );
                uc.setValue( rindex, HOLE( StringId( lindex ) ) );
                co_yield { HOLE( StringId( lindex ) ), uc };
            }
        } );
    }









}





















|



|

|

|
|




|


|

|

|
|




|

|






|
|

|
|





|
|
|





|
|




|


|







|






|
|


|
|


|

|
|







|

|



|


|

|
|
|




|

|
|
|





|

|














|








|





|

|
|
|

|






|




|

|
|



|

|
|




|

|


|
|




|
|



|

|
|

|
|






|

|
|

|
|











|

|

|
|

|
|
|


|
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
#include "sema.h"

namespace goose::sema
{
    void SetupHoleUnificationRules( TypeCheckingRuleSet& ruleSet )
    {
        // Anonymous hole half-unification: add 1 to the anon holes count,
        // yield the hole as is.
        ruleSet.addHalfUnificationRule( TCRINFOS,
            HOLE( "_"_sid ),
        []( const Term& lhs, TypeCheckingContext tcc ) -> TCGen
        {
            tcc.addAnonymousHole();
            co_yield { lhs, tcc };
        } );

        // Anonymous hole versus anything: add 1 to the anon holes count,
        // yield the half unification of the rhs
        ruleSet.addUnificationRule( TCRINFOS,
            HOLE( "_"_sid ),
            ANYTERM( _ ),
        []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
        {
            tcc.addAnonymousHole();

            for( auto&& [s,tcc] : HalfUnify( rhs, tcc.flip() ) )
                co_yield { s, tcc.flip() };
        } );

        // Hole half-unification: Convert it to a numbered hole,
        // If the name wasn't already known, add 1 to the score's unique holes count.
        ruleSet.addHalfUnificationRule( TCRINFOS,
            HolePattern(),
        []( const Term& lhs, TypeCheckingContext tcc ) -> TCGen
        {
            auto lh = *HoleFromIRExpr( lhs );

            if( !lh.name.isNumerical() )
            {
                // This is a named hole: look up its name.
                auto holeIndex = tcc.getLHSHoleIndex( lh.name );
                if( holeIndex != TypeCheckingContext::InvalidIndex )
                {
                    if( !tcc.isHoleLocked( holeIndex ) )
                        co_yield { HOLE( StringId( holeIndex ) ), tcc };
                }
                else
                {
                    // This is a new name: create a new value,
                    // and increment the number of unique holes in the current score.
                    auto index = tcc.createValue();
                    tcc.setLHSHoleIndex( lh.name, index );
                    co_yield { HOLE( StringId( index ) ), tcc };
                }
            }
            else
            {
                // This is already an indexed hole: yield it as is.
                if( !tcc.isHoleLocked( lh.name.id() ) )
                    co_yield { lhs, tcc };
            }
        } );

        // Hole vs anything
        ruleSet.addUnificationRule( TCRINFOS,
            HolePattern(),
            ANYTERM( _ ),
        []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
        {
            auto h = *HoleFromIRExpr( lhs );
            uint32_t index = 0;

            // Remember the previous complexity count so we know how much complexity
            // is added by this particular sub-term. This is because we need
            // to be able to subtract it when updating the hole's value with a new solution.
            uint32_t oldComplexity = tcc.complexity();

            if( h.name.isNumerical() )
                index = h.name.id();
            else
            {
                // This is a named hole: look up its name.
                index = tcc.getLHSHoleIndex( h.name );
                if( index == TypeCheckingContext::InvalidIndex )
                {
                    // This is a new name: create a new value.
                    index = tcc.createValue();
                    tcc.setLHSHoleIndex( h.name, index );
                    auto holeExpr = HOLE( StringId( index ) );

                    for( auto&& [e,tcc] : HalfUnify( rhs, tcc.flip() ) )
                    {
                        tcc.setValue( index, SetComplexity( move( e ), tcc.complexity() - oldComplexity ) );
                        co_yield { move( holeExpr ), tcc.flip() };
                    }

                    co_return;
                }
            }

            // Reject recursive hole nesting.
            if( tcc.isHoleLocked( index ) )
                co_return;
            tcc.lockHole( index );

            auto holeExpr = HOLE( StringId( index ) );

            auto& maybeVal = tcc.getValue( index );
            if( maybeVal )
            {
                for( auto&& [e,tcc] : Unify( *maybeVal, rhs, tcc ) )
                {
                    tcc.unlockHole( index );
                    tcc.setValue( index, SetComplexity( move( e ), tcc.complexity() - oldComplexity ) );
                    co_yield { move( holeExpr ), tcc };
                }
            }
            else
            {
                for( auto&& [e,tcc] : HalfUnify( rhs, tcc.flip() ) )
                {
                    tcc.unlockHole( index );
                    tcc.setValue( index, SetComplexity( move( e ), tcc.complexity() - oldComplexity ) );
                    co_yield { move( holeExpr ), tcc.flip() };
                }
            }
        } );

        // Hole vs hole
        ruleSet.addUnificationRule( TCRINFOS,
            HolePattern(),
        []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
        {
            auto lh = *HoleFromIRExpr( lhs );
            auto rh = *HoleFromIRExpr( rhs );

            StringId lname;
            StringId rname;

            uint32_t lindex = 0;
            uint32_t rindex = 0;

            if( !lh.name.isNumerical() )
            {
                // L is a named hole: look up its name.
                lname = lh.name;
                lindex = tcc.getLHSHoleIndex( lname );
            }
            else
                lindex = lh.name.id();

            if( !rh.name.isNumerical() )
            {
                // R is a named hole: look up its name.
                rname = rh.name;
                rindex = tcc.getRHSHoleIndex( rname );
            }
            else
                rindex = rh.name.id();

            // If neither hole currently have a value, create a new one.
            if( lindex == TypeCheckingContext::InvalidIndex && rindex == TypeCheckingContext::InvalidIndex )
            {
                auto index = tcc.createValue();
                tcc.setLHSHoleIndex( lname, index );
                tcc.setRHSHoleIndex( rname, index );

                co_yield { HOLE( StringId( index ) ), tcc };
                co_return;
            }

            // If both holes actually point to the same value, just yield it as the solution.
            if( lindex == rindex )
            {
                co_yield { HOLE( StringId( lindex ) ), tcc };
                co_return;
            }

            // If either hole doesn't have a value yet, assign it the other one's value.
            if( lindex == TypeCheckingContext::InvalidIndex )
            {
                tcc.setLHSHoleIndex( lname, rindex );
                co_yield { HOLE( StringId( rindex ) ), tcc };
                co_return;
            }

            if( rindex == TypeCheckingContext::InvalidIndex )
            {
                tcc.setRHSHoleIndex( rname, lindex );
                co_yield { HOLE( StringId( lindex ) ), tcc };
                co_return;
            }

            // Reject recursive hole nesting.
            if( tcc.isHoleLocked( lindex ) )
                co_return;
            if( tcc.isHoleLocked( rindex ) )
                co_return;

            tcc.lockHole( lindex );
            tcc.lockHole( rindex );

            // If either hole have an empty value, set it to a hole expression with the id of the value
            // stored in the other one. We can't just copy the value over as we would lose the dependency
            // relationship between the two holes.
            const auto& lval = tcc.getValue( lindex );
            const auto& rval = tcc.getValue( rindex );

            if( !rval )
            {
                for( auto&& [e,tcc] : HalfUnify( *lval, tcc ) )
                {
                    tcc.unlockHole( lindex );
                    tcc.unlockHole( rindex );

                    tcc.setValue( rindex, HOLE( StringId( lindex ) ) );
                    co_yield { HOLE( StringId( lindex ) ), tcc };
                }
                co_return;
            }

            if( !lval )
            {
                for( auto&& [e,tcc] : HalfUnify( *rval, tcc.flip() ) )
                {
                    tcc.unlockHole( lindex );
                    tcc.unlockHole( rindex );

                    tcc.setValue( lindex, HOLE( StringId( rindex ) ) );
                    co_yield { HOLE( StringId( rindex ) ), tcc.flip() };
                }

                co_return;
            }

            // Both L and R have a value: unify them, store the result in lhs,
            // replace rhs with a hole expression pointing to lhs's value.

            // Remember the previous complexity count so we know how much complexity
            // is added by this particular sub-term. This is because we need
            // to be able to subtract it when updating the hole's value with a new solution.
            uint32_t oldComplexity = tcc.complexity();

            for( auto&& [e,tcc] : Unify( *lval, *rval, tcc ) )
            {
                tcc.unlockHole( lindex );
                tcc.unlockHole( rindex );

                tcc.setValue( lindex, SetComplexity( move( e ), tcc.complexity() - oldComplexity ) );
                tcc.setValue( rindex, HOLE( StringId( lindex ) ) );
                co_yield { HOLE( StringId( lindex ) ), tcc };
            }
        } );

        // Any typecheck involving a hole on one side
        // becomes an unification
        ruleSet.addTypeCheckingRule( TCRINFOS,
            HolePattern(),
            ANYTERM( _ ),
        []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
        {
            co_yield Unify( lhs, rhs, tcc );
        } );

        ruleSet.addTypeCheckingRule( TCRINFOS,
            ANYTERM( _ ),
            HolePattern(),
        []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
        {
            co_yield Unify( lhs, rhs, tcc );
        } );

        ruleSet.addTypeCheckingRule( TCRINFOS,
            HolePattern(),
            HolePattern(),
        []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
        {
            co_yield Unify( lhs, rhs, tcc );
        } );
    }
}
Changes to bs/sema/uni-holes.h.
1
2
3
4
5
6
7
8
9
#ifndef GOOSE_SEMA_UNI_HOLES_H
#define GOOSE_SEMA_UNI_HOLES_H

namespace goose::sema
{
    extern void SetupHoleUnificationRules( UnificationRuleSet& ruleSet );
}

#endif





|



1
2
3
4
5
6
7
8
9
#ifndef GOOSE_SEMA_UNI_HOLES_H
#define GOOSE_SEMA_UNI_HOLES_H

namespace goose::sema
{
    extern void SetupHoleUnificationRules( TypeCheckingRuleSet& ruleSet );
}

#endif
Changes to bs/sema/uni-quote.cpp.
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
        if( !result )
            return nullopt;

        auto&& [quoted] = *result;
        return quoted;
    }

    void SetupQuoteUnificationRules( UnificationRuleSet& ruleSet )
    {
        ruleSet.addHalfUnificationRule( URINFOS, VEC( TSID( quote ), ANYTERM( _ ) ),
            []( const Term& lhs, const UnificationContext& uc ) -> UniGen
            {
                co_yield { lhs, uc };
            } );

        ruleSet.addSymRule( URINFOS, VEC( TSID( quote ), ANYTERM( _ ) ),
            []( const Term& lhs, const Term& rhs, const UnificationContext& uc ) -> UniGen
            {
                if( lhs == rhs )
                    co_yield { lhs, uc };
            } );
    }
}







|

|
|

|


|
|


|



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
        if( !result )
            return nullopt;

        auto&& [quoted] = *result;
        return quoted;
    }

    void SetupQuoteUnificationRules( TypeCheckingRuleSet& ruleSet )
    {
        ruleSet.addHalfUnificationRule( TCRINFOS, VEC( TSID( quote ), ANYTERM( _ ) ),
            []( const Term& lhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                co_yield { lhs, tcc };
            } );

        ruleSet.addUnificationRule( TCRINFOS, VEC( TSID( quote ), ANYTERM( _ ) ),
            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                if( lhs == rhs )
                    co_yield { lhs, tcc };
            } );
    }
}
Changes to bs/sema/uni-quote.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef GOOSE_SEMA_UNI_QUOTE_H
#define GOOSE_SEMA_UNI_QUOTE_H

namespace goose::sema
{
    // Expressions can be quoted so that they aren't recursed into during
    // unification. This allows to carry unification patterns around (like function
    // signatures) without them causing an unification failure because their holes remain
    // unresolved.
    extern Term Quote( const Term& t );
    extern optional< Term > Unquote( const Term& t );

    extern void SetupQuoteUnificationRules( UnificationRuleSet& ruleSet );
}

#endif












|



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

namespace goose::sema
{
    // Expressions can be quoted so that they aren't recursed into during
    // unification. This allows to carry unification patterns around (like function
    // signatures) without them causing an unification failure because their holes remain
    // unresolved.
    extern Term Quote( const Term& t );
    extern optional< Term > Unquote( const Term& t );

    extern void SetupQuoteUnificationRules( TypeCheckingRuleSet& ruleSet );
}

#endif