Goose  Check-in [ba909d1a94]

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

Overview
Comment:Correctly handle references to references, plus some code cleaning.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: ba909d1a948a6dfd7c1101a29e337c83fdfd66bd0bc3cf2c3a4dbabe1dbeaadf
User & Date: achavasse 2021-02-01 12:56:55.675
Context
2021-02-01
19:26
Added a rule based system to pretty print EIR expressions in a less horrific way. check-in: 6675f81702 user: achavasse tags: trunk
12:56
Correctly handle references to references, plus some code cleaning. check-in: ba909d1a94 user: achavasse tags: trunk
2021-01-21
21:05
Removed the vector "typechecking rule", whose existence made no sense. Typechecking rules should operate only on values and pattern of values. Typechecking multiple values against multiple params is now done through a specific function instead. check-in: 7b9f645074 user: achavasse tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to bs/builtins/types/func/bfunc.inl.
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

            case sema::Env::Status::NoMatch:
                pOvlSet = make_shared< OverloadSet >( identity );
                env.storeValue( identity, ANYTERM( _ ), ValueToEIR( ToValue( pOvlSet ) ) );
                break;

            case sema::Env::Status::AmbiguousMatch:
                throw logic_error( "panic: ambiguous match while registering builtin func "s + name.str() );
        }

        return RegisterBuiltinFunc< FT >( env, pOvlSet, forward< F >( func ) );
    }

    template< typename FT, typename F >
    ptr< FuncVerificationInfos > RegisterBuiltinFunc( Env& env, ptr< OverloadSet > pOvlSet, F&& func )
    {
        auto fvi = make_shared< builtins::FuncVerificationInfos >( RootIdentity() );

        if( !pOvlSet->add( env, ToValue< FT >( forward< F >( func ), fvi ), GetFuncInvocationRule() ) )
            throw logic_error( "panic: duplicate overload registered for builtin func." );

        return fvi;
    }
}

namespace goose::eir
{







|











|







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

            case sema::Env::Status::NoMatch:
                pOvlSet = make_shared< OverloadSet >( identity );
                env.storeValue( identity, ANYTERM( _ ), ValueToEIR( ToValue( pOvlSet ) ) );
                break;

            case sema::Env::Status::AmbiguousMatch:
                G_ERROR( "ambiguous match while registering builtin func "s + name.str() );
        }

        return RegisterBuiltinFunc< FT >( env, pOvlSet, forward< F >( func ) );
    }

    template< typename FT, typename F >
    ptr< FuncVerificationInfos > RegisterBuiltinFunc( Env& env, ptr< OverloadSet > pOvlSet, F&& func )
    {
        auto fvi = make_shared< builtins::FuncVerificationInfos >( RootIdentity() );

        if( !pOvlSet->add( env, ToValue< FT >( forward< F >( func ), fvi ), GetFuncInvocationRule() ) )
            G_ERROR( "duplicate overload registered for builtin func." );

        return fvi;
    }
}

namespace goose::eir
{
Changes to bs/builtins/types/func/func.cpp.
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
    Term BuildArgListForCall( const FuncType& ft, const Term& unifiedArgs )
    {
        auto av = make_shared< Vector >();
        av->reserve( VecSize( ft.params() ) );

        ForEachInVectorTerms( ft.params(), unifiedArgs, [&]( auto&& p, auto&& a )
        {
            auto vp = ValuePatternFromIRExpr( p );
            if( vp->val() == HOLE( "_"_sid ) )
                av->append( a );

            return true;
        } );

        return av;







|







130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
    Term BuildArgListForCall( const FuncType& ft, const Term& unifiedArgs )
    {
        auto av = make_shared< Vector >();
        av->reserve( VecSize( ft.params() ) );

        ForEachInVectorTerms( ft.params(), unifiedArgs, [&]( auto&& p, auto&& a )
        {
            auto vp = ValuePatternFromEIR( p );
            if( vp->val() == HOLE( "_"_sid ) )
                av->append( a );

            return true;
        } );

        return av;
Changes to bs/builtins/types/func/lower.cpp.
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
        ForEachInVectorTerm( ft.params(), [&]( auto&& p )
        {
            // Params are not stored explicitely, but as patterns
            // for expediency (where the value is either a hole or an actual
            // value depending on whether this is a specialization param),
            // which is useful in most places except here as we need to extract
            // back the type from the pattern (and skip specialization params).
            auto vp = ValuePatternFromIRExpr( p );
            if( !vp )
            {
                success = false;
                return false;
            }

            if( vp->val() == HOLE( "_"_sid ) )







|







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
        ForEachInVectorTerm( ft.params(), [&]( auto&& p )
        {
            // Params are not stored explicitely, but as patterns
            // for expediency (where the value is either a hole or an actual
            // value depending on whether this is a specialization param),
            // which is useful in most places except here as we need to extract
            // back the type from the pattern (and skip specialization params).
            auto vp = ValuePatternFromEIR( p );
            if( !vp )
            {
                success = false;
                return false;
            }

            if( vp->val() == HOLE( "_"_sid ) )
Changes to bs/builtins/types/func/typecheck.cpp.
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
            ValueToEIR( 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( ValueToEIR( lhsVal ), rhs, tcc );
        } );

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

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

            ValueToEIR( 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 ) )
                {







|



















|
|







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

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

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

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

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

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            auto lhsVal = *ValuePatternFromEIR( lhs );
            auto rhsVal = *ValuePatternFromEIR( 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 ) )
                {
Changes 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
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::eir;
using namespace goose::cir;

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

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

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

            ParamPat( localVarPattern ),

            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                localVarPattern,
                ANYTERM( _ ) ) ),

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

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

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












<
<
<












|

|







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

using namespace goose;
using namespace goose::eir;
using namespace goose::cir;

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




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

            ParamPat( localVarPattern ),

            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                localVarPattern,
                ANYTERM( _ ) ) ),

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

                auto rhsVal = *ValuePatternFromEIR( rhs );
                auto rvarType = *FromValue< LocalVarType >( *ValueFromEIR( rhsVal.type() ) );

                for( auto&& [s, tcc] : Unify( lvarType.type(), rvarType.type(), tcc ) )
                {
                    co_yield { ValueToEIR( Value( ValueToEIR( ToValue( LocalVarType( s ) ) ),
                        rhsVal.val() ) ), tcc };
                }
Changes to bs/builtins/types/overloadset/helpers.cpp.
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

        switch( env.retrieveValue( identity, RootIdentity(), result ) )
        {
            case sema::Env::Status::Success:
                return *FromValue< ptr< OverloadSet > >( *ValueFromEIR( result ) );

            case sema::Env::Status::NoMatch:
                throw logic_error( format( "fatal: overload set {} not found", name ) );

            case sema::Env::Status::AmbiguousMatch:
                throw logic_error( format( "fatal: ambiguous match for overload set {}", name ) );
        }

        return nullptr;
    }

    ptr< OverloadSet > GetOrCreateOverloadSet( Env& env, const StringId& name )
    {







|


|







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

        switch( env.retrieveValue( identity, RootIdentity(), result ) )
        {
            case sema::Env::Status::Success:
                return *FromValue< ptr< OverloadSet > >( *ValueFromEIR( result ) );

            case sema::Env::Status::NoMatch:
                G_ERROR( format( "fatal: overload set {} not found", name ) );

            case sema::Env::Status::AmbiguousMatch:
                G_ERROR( format( "fatal: ambiguous match for overload set {}", name ) );
        }

        return nullptr;
    }

    ptr< OverloadSet > GetOrCreateOverloadSet( Env& env, const StringId& name )
    {
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
            {
                auto pOvlSet = make_shared< OverloadSet >( identity );
                env.storeValue( identity, ANYTERM( _ ), ValueToEIR( ToValue( pOvlSet ) ) );
                return pOvlSet;
            }

            case sema::Env::Status::AmbiguousMatch:
                throw logic_error( format( "fatal: ambiguous match for overload set {}", name ) );
        }

        return nullptr;
    }

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







|







47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
            {
                auto pOvlSet = make_shared< OverloadSet >( identity );
                env.storeValue( identity, ANYTERM( _ ), ValueToEIR( ToValue( pOvlSet ) ) );
                return pOvlSet;
            }

            case sema::Env::Status::AmbiguousMatch:
                G_ERROR( format( "fatal: ambiguous match for overload set {}", name ) );
        }

        return nullptr;
    }

    Value InvokeOverloadSet( const Context& c, const ptr< OverloadSet >& pOvlSet, Value args )
    {
Changes to bs/builtins/types/param.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
#ifndef GOOSE_BUILTINS_TYPES_PARAM_H
#define GOOSE_BUILTINS_TYPES_PARAM_H

namespace goose::builtins
{
    template< typename T, typename V >
    auto ParamPat( T&& type, V&& val )
    {
        // 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 = ValueFromEIR( type ); typeVal && IsTuple( *typeVal ) )
            return ValueToEIR( Value( ValueToEIR( TupleOfTypesToTupleType( *typeVal ) ), HOLE( "_"_sid ) ) );

        return ValueToEIR( Value( forward< T >( type ), forward< V >( val ) ) );
    }

    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 = ValueFromEIR( type ); typeVal && IsTuple( *typeVal ) )
            return ValueToEIR( ValuePattern( HOLE( "_"_sid ), ValueToEIR( TupleOfTypesToTupleType( *typeVal ) ), HOLE( "_"_sid ) ) );

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








|










|







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_BUILTINS_TYPES_PARAM_H
#define GOOSE_BUILTINS_TYPES_PARAM_H

namespace goose::builtins
{
    template< typename T, typename V >
    auto ParamPat( T&& type, V&& val )
    {
        // If the type is a tuple of types, 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 = ValueFromEIR( type ); typeVal && IsTuple( *typeVal ) )
            return ValueToEIR( Value( ValueToEIR( TupleOfTypesToTupleType( *typeVal ) ), HOLE( "_"_sid ) ) );

        return ValueToEIR( Value( forward< T >( type ), forward< V >( val ) ) );
    }

    template< typename T >
    auto ParamPat( T&& type )
    {
        // If the type is a tuple of types, 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 = ValueFromEIR( type ); typeVal && IsTuple( *typeVal ) )
            return ValueToEIR( ValuePattern( HOLE( "_"_sid ), ValueToEIR( TupleOfTypesToTupleType( *typeVal ) ), HOLE( "_"_sid ) ) );

        return ValueToEIR( ValuePattern( TSID( param ), forward< T >( type ), HOLE( "_"_sid ) ) );
    }
}
Changes to bs/builtins/types/reference/reference.cpp.
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
        return pattern;
    }

    // Returns an instruction that computes the address of whatever's contained in the locvar.
    // If the locvar contains a reference, this will return what's referenced by it. (TODO)
    ptr< cir::Instruction > GetAddrFromLocalVar( const LocalVar& lv )
    {
        if( IsReferenceType( lv.type() ) )
        {
            return make_shared< cir::Instruction >(
                cir::Load( make_shared< Instruction >( cir::VarAddr( lv.index() ) ), lv.type() ) );
        }

        return make_shared< cir::Instruction >( cir::VarAddr( lv.index() ) );
    }

    Value BuildLocalVarMutRef( const LocalVar& lv )
    {
        // If the local var already contains a reference, unwrap it (because references of references
        // are problematic as the reference typechecking rule preempts the implicit dereferencing
        // type checking rule)
        if( IsReferenceType( lv.type() ) )
        {
            return BuildComputedValue( lv.type(),
                cir::Load( make_shared< Instruction >( cir::VarAddr( lv.index() ) ), lv.type() ) );
        }

        auto rt = ValueToEIR( ToValue( ReferenceType{ lv.type(), TSID( mut ) } ) );
        return BuildComputedValue( move( rt ), cir::VarAddr( lv.index() ) );
    }
}

namespace goose::eir
{







<
<
<
<
<
<





<
<
<
<
<
<
<
<
<







38
39
40
41
42
43
44






45
46
47
48
49









50
51
52
53
54
55
56
        return pattern;
    }

    // Returns an instruction that computes the address of whatever's contained in the locvar.
    // If the locvar contains a reference, this will return what's referenced by it. (TODO)
    ptr< cir::Instruction > GetAddrFromLocalVar( const LocalVar& lv )
    {






        return make_shared< cir::Instruction >( cir::VarAddr( lv.index() ) );
    }

    Value BuildLocalVarMutRef( const LocalVar& lv )
    {









        auto rt = ValueToEIR( ToValue( ReferenceType{ lv.type(), TSID( mut ) } ) );
        return BuildComputedValue( move( rt ), cir::VarAddr( lv.index() ) );
    }
}

namespace goose::eir
{
Changes 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
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::eir;
using namespace goose::cir;























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

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




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

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

        auto refTypePatternTemporary = ValueToEIR(





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










>
>
>







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;
using namespace goose::eir;
using namespace goose::cir;

namespace
{
    using namespace goose::builtins;

    TCGen TypeCheckingDereference( const Term& lhs, const Term& rhs, TypeCheckingContext tcc )
    {
        auto refval = *ValueFromEIR( rhs );
        G_VAL_ASSERT( refval, !refval.isConstant() );

        auto refType = FromValue< ReferenceType >( *ValueFromEIR( refval.type() ) );
        if( !refType )
            co_return;

        auto content = ValueToEIR( BuildComputedValue( refType->type(),
            cir::Load( refval.cir(), refType->type() ) )
            .setLocationId( refval.locationId() ) );

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

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

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

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

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

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

        auto refTypePatternTemporary = ValueToEIR(
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                refTypePattern,
                ANYTERM( _ ) ) ),

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

                auto rhsVal = *ValuePatternFromIRExpr( rhs );
                auto rRefType = *FromValue< ReferenceType >( *ValueFromEIR( 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 ) )







|

|







55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                refTypePattern,
                ANYTERM( _ ) ) ),

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

                auto rhsVal = *ValuePatternFromEIR( rhs );
                auto rRefType = *FromValue< ReferenceType >( *ValueFromEIR( 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 ) )
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
            ANYTERM( _ ),

            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                refTypePattern,
                ANYTERM( _ ) ) ),

            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen

            {
                auto refval = *ValueFromEIR( rhs );
                G_VAL_ASSERT( refval, !refval.isConstant() );




                auto refType = FromValue< ReferenceType >( *ValueFromEIR( refval.type() ) );
                if( !refType )
                    co_return;

                auto content = ValueToEIR( BuildComputedValue( refType->type(),

                    cir::Load( refval.cir(), refType->type() ) )
                    .setLocationId( refval.locationId() ) );

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

        // LocalVar type checking against a param (implicit referencing):
        // Build a mutable ref value, or just unwrap the locVar if it
        // already contains a ref (this is handled by BuildLocalVarMutRef)
        //
        // The unwrapping is necessary: if we don't do it, we try
        // to typecheck a reference of reference against a reference param,
        // but it doesn't go through the implicit dereferencing rule as the
        // reference versus ref param typechecking rule preempts it (and then
        // fails because the referenced types don't match)
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                ANYTERM( _ ),
                ANYTERM( _ ) ) ),

            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                localVarPattern,
                ANYTERM( _ ) ) ),

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

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

            auto ref = ValueToEIR( ToValue( BuildLocalVarMutRef( *locvar ) )







|
>
|
|
<
>
>
>

<
|
<

|
>
|
|

<
|
|

|
<
<
<
<
<
<
<
<














|







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

            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                refTypePattern,
                ANYTERM( _ ) ) ),

            TypeCheckingDereference
        );

        // Implicit dereferencing of references to references:

        // We need to define this one explicitely, otherwise it gets
        // preempted by the reference type checking rule
        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,


            ParamPat( refTypePattern ),


            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                refRefTypePattern,
                ANYTERM( _ ) ) ),


            TypeCheckingDereference
        );

        // LocalVar type checking against a param (implicit referencing)








        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                ANYTERM( _ ),
                ANYTERM( _ ) ) ),

            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                localVarPattern,
                ANYTERM( _ ) ) ),

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

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

            auto ref = ValueToEIR( ToValue( BuildLocalVarMutRef( *locvar ) )
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
        {
            if( !tcc.context().codeBuilder() )
                co_return;

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

            auto lRefType = *FromValue< ReferenceType >( *ValueFromEIR( ValuePatternFromIRExpr( lhs )->type() ) );
            auto lhsPat = ValueToEIR( ValuePattern( TSID( param ), lRefType.type(), HOLE( "_"_sid ) ) );

            for( auto&& [s,tcc] : TypeCheck( lhsPat, rhs, tcc ) )
            {
                auto valPat = *ValuePatternFromIRExpr( s );
                ReferenceType rt( valPat.type(), TSID( temp ) );

                auto refPat = ValueToEIR( ValuePattern( HOLE( "_"_sid ), ValueToEIR( ToValue( rt ) ), HOLE( "_"_sid ) ) );

                // TypeCheck the param with the ref
                for( auto&& [s,tcc] : TypeCheck( lhs, refPat, tcc ) )
                {







|




|







184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
        {
            if( !tcc.context().codeBuilder() )
                co_return;

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

            auto lRefType = *FromValue< ReferenceType >( *ValueFromEIR( ValuePatternFromEIR( lhs )->type() ) );
            auto lhsPat = ValueToEIR( ValuePattern( TSID( param ), lRefType.type(), HOLE( "_"_sid ) ) );

            for( auto&& [s,tcc] : TypeCheck( lhsPat, rhs, tcc ) )
            {
                auto valPat = *ValuePatternFromEIR( s );
                ReferenceType rt( valPat.type(), TSID( temp ) );

                auto refPat = ValueToEIR( ValuePattern( HOLE( "_"_sid ), ValueToEIR( ToValue( rt ) ), HOLE( "_"_sid ) ) );

                // TypeCheck the param with the ref
                for( auto&& [s,tcc] : TypeCheck( lhs, refPat, tcc ) )
                {
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
                            )
                        );

                        assert( result );
                        auto&& [ref, rhs] = *result;

                        auto rhsVal = *ValueFromEIR( rhs );
                        auto refPat = *ValuePatternFromIRExpr( ref );

                        auto tempIndex = tcc.context().codeBuilder()->cfg()->getNewTemporaryIndex();
                        return ValueToEIR( BuildComputedValue( refPat.type(), TempAddr( tempIndex, rhsVal ) ) );
                    } );

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







|











214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
                            )
                        );

                        assert( result );
                        auto&& [ref, rhs] = *result;

                        auto rhsVal = *ValueFromEIR( rhs );
                        auto refPat = *ValuePatternFromEIR( ref );

                        auto tempIndex = tcc.context().codeBuilder()->cfg()->getNewTemporaryIndex();
                        return ValueToEIR( BuildComputedValue( refPat.type(), TempAddr( tempIndex, rhsVal ) ) );
                    } );

                    co_yield { move( wrapped ), tcc };
                }
            }
        } );
    }
}
Changes to bs/builtins/types/runtime/typecheck.cpp.
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
            // 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 = *ValueFromEIR( rhs );
            if( !rhsVal.isConstant() )
                co_return;

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

            auto s = HalfUnify( lhsVal->type(), tcc );
            if( !s )
                co_return;








|







63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
            // 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 = *ValueFromEIR( rhs );
            if( !rhsVal.isConstant() )
                co_return;

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

            auto s = HalfUnify( lhsVal->type(), tcc );
            if( !s )
                co_return;

159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
                ANYTERM( _ ),
                GetValueType< string >(),
                ANYTERM( _ ) ) ),

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

            co_yield { ValueToEIR(
                BuildComputedValue( lhsVal.type(), cir::LoadConstStr( str ) ) ), c };
        } );

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







|







159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
                ANYTERM( _ ),
                GetValueType< string >(),
                ANYTERM( _ ) ) ),

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

            co_yield { ValueToEIR(
                BuildComputedValue( lhsVal.type(), cir::LoadConstStr( str ) ) ), c };
        } );

        auto ptrTypePattern = Value( TypeType(), MkStdType( TSID( pointer ),
            ANYTERM( _ ) ) );
182
183
184
185
186
187
188
189
190
191
192
193
            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                GetValueType< NullPointer >(),
                ANYTERM( _ ) ) ),

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







|




182
183
184
185
186
187
188
189
190
191
192
193
            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                GetValueType< NullPointer >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& c ) -> TCGen
        {
            auto lVal = *ValuePatternFromEIR( lhs );
            co_yield { ValueToEIR( Value( lVal.type(), 0U ) ), c };
        } );
    }
}
Changes to bs/builtins/types/tuple/typecheck.cpp.
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
        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 { ValueToEIR( newOut ), tcc };
            else
                co_yield TypeCheckConstantTuple( tcc, tupType, tupArg, index + 1, newOut );







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
        auto argType = GetTupleElementType( tupArg, index );
        auto arg = GetTupleElement( tupArg, index );

        auto tupSize = TupleSize( tupArg );

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

            if( index == ( tupSize - 1 ) )
                co_yield { ValueToEIR( newOut ), tcc };
            else
                co_yield TypeCheckConstantTuple( tcc, tupType, tupArg, index + 1, newOut );
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
        auto argRef = ValueToEIR( BuildComputedValue( ValueToEIR( ToValue( rt ) ),
            move( argAddr ) ) );

        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 { ValueToEIR( newOut ), tcc };
            else
                co_yield TypeCheckComputedTuple( tcc, tupType, tupArg, tupArgRef, index + 1, newOut );







|







40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
        auto argRef = ValueToEIR( BuildComputedValue( ValueToEIR( ToValue( rt ) ),
            move( argAddr ) ) );

        auto tupSize = TupleTypeSize( tupType );

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

            if( index == ( tupSize - 1 ) )
                co_yield { ValueToEIR( newOut ), tcc };
            else
                co_yield TypeCheckComputedTuple( tcc, tupType, tupArg, tupArgRef, index + 1, newOut );
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
            ValueToEIR( ValuePattern(
                TSID( constant ),
                ValueToEIR( MkTupleType( ANYTERM( _ ), VECOFLENGTH( L ) ) ),
                VECOFLENGTH( L ) ) ),

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

            if( !ltup || !rtup )
                co_return;

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







|







67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
            ValueToEIR( ValuePattern(
                TSID( constant ),
                ValueToEIR( MkTupleType( ANYTERM( _ ), VECOFLENGTH( L ) ) ),
                VECOFLENGTH( L ) ) ),

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

            if( !ltup || !rtup )
                co_return;

            auto tupType = *ValueFromEIR( ltup->type() );
            assert( TupleTypeSize( tupType ) == TupleSize( *rtup ) );
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
            ValueToEIR( ValuePattern(
                TSID( computed ),
                ValueToEIR( MkTupleType( ANYTERM( _ ), VECOFLENGTH( L ) ) ),
                ANYTERM( _ ) ) ),

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

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

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







|







93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
            ValueToEIR( ValuePattern(
                TSID( computed ),
                ValueToEIR( MkTupleType( ANYTERM( _ ), VECOFLENGTH( L ) ) ),
                ANYTERM( _ ) ) ),

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

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

            auto tupType = *ValueFromEIR( ltup->type() );
            assert( TupleTypeSize( tupType ) == TupleTypeSize( *ValueFromEIR( rtup->type() ) ) );
Changes to bs/diagnostics/diagnosticsmanager.h.
69
70
71
72
73
74
75














76
77
78
79
80
81
82
83
84
85
            unordered_map< CustomDiagIdentifier, string > m_customDiagnostics;

            bool m_emittedFirstError = false;
            bool m_forceColors = false;
            bool m_traceMode = false;
    };
}















#define G_VAL_ASSERT( val, cond ) \
    if( !( cond ) ) \
    { \
        DiagnosticsManager::GetInstance().emitErrorMessage( val.locationId(), \
            ::fmt::format( "Internal error: assertion " #cond " failed at {}:{}", __FILE__, __LINE__ ) ); \
        abort(); \
    }

#endif







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




|
|




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
            unordered_map< CustomDiagIdentifier, string > m_customDiagnostics;

            bool m_emittedFirstError = false;
            bool m_forceColors = false;
            bool m_traceMode = false;
    };
}

#define G_ERROR( message ) \
    { \
        DiagnosticsManager::GetInstance().emitErrorMessage( 0, \
            ::fmt::format( "Internal error: {}:{}: {}", __FILE__, __LINE__, message ) ); \
        abort(); \
    }

#define G_VAL_ERROR( val, message ) \
    { \
        DiagnosticsManager::GetInstance().emitErrorMessage( (val).locationId(), \
            ::fmt::format( "Internal error: {}:{}: {}", __FILE__, __LINE__, message ) ); \
        abort(); \
    }

#define G_VAL_ASSERT( val, cond ) \
    if( !( cond ) ) \
    { \
        DiagnosticsManager::GetInstance().emitErrorMessage( (val).locationId(), \
            ::fmt::format( "Internal error: {}:{}: assertion " #cond " failed", __FILE__, __LINE__ ) ); \
        abort(); \
    }

#endif
Changes to bs/eir/match.h.
1
2
3
4
5






















6
7
8
9
10


11
12
13
14
15
16
17
#ifndef GOOSE_EIR_MATCH_H
#define GOOSE_EIR_MATCH_H

namespace goose::eir
{






















    class MatchSolution
    {
        public:
            size_t complexity() const { return m_complexity; }
            size_t numVars() const;



            template< typename T >
            const T* getVar( const StringId& name ) const
            {
                if( name == "_"_sid )
                    return nullptr;






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





>
>







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
#ifndef GOOSE_EIR_MATCH_H
#define GOOSE_EIR_MATCH_H

namespace goose::eir
{
    struct MatchScore
    {
        size_t m_complexity = 0;
        size_t m_numVars = 0;

        bool operator<( const MatchScore& rhs ) const
        {
            if( m_complexity != rhs.m_complexity )
                return m_complexity < rhs.m_complexity;

            return m_numVars > rhs.m_numVars;
        }

        bool operator>( const MatchScore& rhs ) const
        {
            if( m_complexity != rhs.m_complexity )
                return m_complexity > rhs.m_complexity;

            return m_numVars < rhs.m_numVars;
        }
    };

    class MatchSolution
    {
        public:
            size_t complexity() const { return m_complexity; }
            size_t numVars() const;

            MatchScore score() const { return MatchScore{ m_complexity, m_pVars ? m_pVars->size() : 0 }; }

            template< typename T >
            const T* getVar( const StringId& name ) const
            {
                if( name == "_"_sid )
                    return nullptr;

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
                setupVars();
                m_pVars->emplace( name, forward< T >( val ) );
                return true;
            }

            void addComplexity( uint32_t n ) { m_complexity += n; }

            bool operator<( const MatchSolution& rhs ) const
            {
                if( m_complexity != rhs.m_complexity )
                    return m_complexity < rhs.m_complexity;

                return numVars() > rhs.numVars();
            }

            bool operator>( const MatchSolution& rhs ) const
            {
                if( m_complexity != rhs.m_complexity )
                    return m_complexity > rhs.m_complexity;

                return numVars() < rhs.numVars();
            }

        private:
            void setupVars();

            ptr< unordered_map< StringId, any > > m_pVars;
            uint32_t m_complexity = 0;    // Each matched structural element of the pattern (vector) adds 1,
                                          // each matched literal element of the pattern adds 2
    };







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







73
74
75
76
77
78
79
















80
81
82
83
84
85
86
                setupVars();
                m_pVars->emplace( name, forward< T >( val ) );
                return true;
            }

            void addComplexity( uint32_t n ) { m_complexity += n; }

















        private:
            void setupVars();

            ptr< unordered_map< StringId, any > > m_pVars;
            uint32_t m_complexity = 0;    // Each matched structural element of the pattern (vector) adds 1,
                                          // each matched literal element of the pattern adds 2
    };
Changes to bs/eir/value.cpp.
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
    }

    Term ValueToEIR( const ValuePattern& v )
    {
        return VEC( TSID( value ), v.sort(), v.type(), v.val(), static_cast< LocationId >( v.locationId() ) );
    }

    optional< ValuePattern > ValuePatternFromIRExpr( const Term& t )
    {
        auto result = Decompose( t,
            Vec(
               Lit( "value"_sid ),
               SubTerm(),
               SubTerm(),
               SubTerm(),







|







104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
    }

    Term ValueToEIR( const ValuePattern& v )
    {
        return VEC( TSID( value ), v.sort(), v.type(), v.val(), static_cast< LocationId >( v.locationId() ) );
    }

    optional< ValuePattern > ValuePatternFromEIR( const Term& t )
    {
        auto result = Decompose( t,
            Vec(
               Lit( "value"_sid ),
               SubTerm(),
               SubTerm(),
               SubTerm(),
Changes to bs/eir/value.h.
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

    extern const Term& TypeType();

    extern Term ValueToEIR( const Value& v );
    extern optional< Value > ValueFromEIR( const Term& t );

    extern Term ValueToEIR( const ValuePattern& v );
    extern optional< ValuePattern > ValuePatternFromIRExpr( const Term& t );

    class Value
    {
        public:
            Value() : m_locationId( ~0 ) {}

            template< typename T, typename VL >







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

    extern const Term& TypeType();

    extern Term ValueToEIR( const Value& v );
    extern optional< Value > ValueFromEIR( const Term& t );

    extern Term ValueToEIR( const ValuePattern& v );
    extern optional< ValuePattern > ValuePatternFromEIR( const Term& t );

    class Value
    {
        public:
            Value() : m_locationId( ~0 ) {}

            template< typename T, typename VL >
Changes to bs/sema/env.cpp.
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
        } );

    ++m_valueStoreVersion;
}

Env::Status Env::retrieveValue( const Term& id, const Term& contextId, Term& result )
{
    MatchSolution bestSol;
    ptr< ValueProvider > bestProvider;
    bool ambiguous = false;

    auto pat = VEC( id, contextId );

    for( auto&& [s, provider] : Match( pat, m_valueStore ) )
    {

        if( !bestProvider || s > bestSol )
        {
            bestSol = s;
            bestProvider = provider;
            ambiguous = false;
            continue;
        }

        if( s < bestSol )
            continue;

        ambiguous = true;
    }

    if( !bestProvider )
        return Status::NoMatch;







|







>
|

|





|







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

    ++m_valueStoreVersion;
}

Env::Status Env::retrieveValue( const Term& id, const Term& contextId, Term& result )
{
    MatchScore bestScore;
    ptr< ValueProvider > bestProvider;
    bool ambiguous = false;

    auto pat = VEC( id, contextId );

    for( auto&& [s, provider] : Match( pat, m_valueStore ) )
    {
        auto score = s.score();
        if( !bestProvider || score > bestScore )
        {
            bestScore = score;
            bestProvider = provider;
            ambiguous = false;
            continue;
        }

        if( score < bestScore )
            continue;

        ambiguous = true;
    }

    if( !bestProvider )
        return Status::NoMatch;
Changes to bs/sema/invocation.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
#include "sema.h"
#include "builtins/builtins.h"
#include "execute/execute.h"

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

        MatchSolution bestSol;
        ptr< InvocationRule > pBestRule;
        bool ambiguous = false;

        auto calleeTerm = ValueToEIR( callee );

        for( auto&& [s, rule] : Match( calleeTerm, rules ) )
        {

            if( !pBestRule || s > bestSol )
            {
                bestSol = s;
                pBestRule = 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 invocation rule" );

        return pBestRule;
    }

    bool CanBeInvoked( const Context& c, const Value& callee )
    {
        auto pIR = GetInvocationRule( *c.env(), callee );










|







>
|

|





|





<

|







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

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

        MatchScore bestScore;
        ptr< InvocationRule > pBestRule;
        bool ambiguous = false;

        auto calleeTerm = ValueToEIR( callee );

        for( auto&& [s, rule] : Match( calleeTerm, rules ) )
        {
            auto score = s.score();
            if( !pBestRule || score > bestScore )
            {
                bestScore = score;
                pBestRule = rule;
                ambiguous = false;
                continue;
            }

            if( score < bestScore )
                continue;

            ambiguous = true;
        }


        if( ambiguous )
            G_VAL_ERROR( callee, "ambiguous invocation rule" );

        return pBestRule;
    }

    bool CanBeInvoked( const Context& c, const Value& callee )
    {
        auto pIR = GetInvocationRule( *c.env(), callee );
Changes to bs/sema/tc-context.h.
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
            uint32_t RHSNamespaceIndex() const { return m_currentRHSNamespaceIndex; }

            void setLHSNamespaceIndex( uint32_t index ) { m_currentLHSNamespaceIndex = index; }
            void setRHSNamespaceIndex( uint32_t index ) { m_currentRHSNamespaceIndex = index; }

            uint32_t newNamespaceIndex() { return m_nextNamespaceIndex++; }

            // By default, any encountered hole will be considered as required, that it
            // they will count towards numUnknownValues() if we can't solve them.
            // This function allows to temporarily disable this, so that any hole
            // encountered from that point on will not count towards unresolved holes,
            // unless they also appear in a section where holes are required.
            void setValueResolutionRequired( bool required )
            {
                m_valuesAreRequired = required;







|







30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
            uint32_t RHSNamespaceIndex() const { return m_currentRHSNamespaceIndex; }

            void setLHSNamespaceIndex( uint32_t index ) { m_currentLHSNamespaceIndex = index; }
            void setRHSNamespaceIndex( uint32_t index ) { m_currentRHSNamespaceIndex = index; }

            uint32_t newNamespaceIndex() { return m_nextNamespaceIndex++; }

            // By default, any encountered hole will be considered as required, ie
            // they will count towards numUnknownValues() if we can't solve them.
            // This function allows to temporarily disable this, so that any hole
            // encountered from that point on will not count towards unresolved holes,
            // unless they also appear in a section where holes are required.
            void setValueResolutionRequired( bool required )
            {
                m_valuesAreRequired = required;
Changes 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;

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













>
>
>
>
>
|

|


|





|

|

|






<
<
<
<
<
|

|

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::addTypeCheckingRule( TCRuleInfo&& infos, const Term& pat1, const Term& pat2, BinaryFunc f )
{
    m_typeCheckingRules = Merge( m_typeCheckingRules, VEC( pat1, pat2 ), [&]( auto&& ){ return BinaryRule{ f, infos }; } );
}

void TypeCheckingRuleSet::addUnificationRule( TCRuleInfo&& infos, const Term& pat, BinaryFunc f )
{
    m_uniRules = Merge( m_uniRules, VEC( pat, pat ), [&]( auto&& ){ return BinaryRule{ f, infos }; } );
}

TCGen FlippedContextAdapter( const Term& lhs, const Term& rhs, TypeCheckingContext tcc, TypeCheckingRuleSet::BinaryFunc 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, BinaryFunc f )
{
    m_uniRules = Merge( m_uniRules, VEC( pat1, pat2 ), [&]( auto&& ){ return BinaryRule{ f, infos }; } );
    m_uniRules = Merge( m_uniRules, VEC( pat2, pat1 ),
        [&]( auto&& ){ return BinaryRule{ BinaryFunc( [&,f]( auto&& lhs, auto&& rhs, auto&& c ) -> TCGen
        {
            return FlippedContextAdapter( lhs, rhs, c, f );
        } ), infos }; }
    );
}






void TypeCheckingRuleSet::addHalfUnificationRule( TCRuleInfo&& infos, const Term& pat, UnaryFunc f )
{
    m_halfUniRules = Merge( m_halfUniRules, pat, [&]( auto&& ){ return UnaryRule{ f, infos }; } );
}
Changes to bs/sema/tc-ruleset.h.
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

    #define TCRINFOS TCRuleInfo{}
#endif

    class TypeCheckingRuleSet
    {
        public:
            using UniFunc = function< TCGen ( const Term& lhs, const Term& rhs, const TypeCheckingContext& ) >;
            using HalfUniFunc = function< optional< Term > ( const Term& lhs, 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







|
|

|

|



|

|





|

|
|

|






|
|
|




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

    #define TCRINFOS TCRuleInfo{}
#endif

    class TypeCheckingRuleSet
    {
        public:
            using BinaryFunc = function< TCGen ( const Term& lhs, const Term& rhs, const TypeCheckingContext& ) >;
            using UnaryFunc = function< optional< Term > ( const Term& lhs, TypeCheckingContext& ) >;

            struct BinaryRule
            {
                BinaryFunc func;
                TCRuleInfo infos;
            };

            struct UnaryRule
            {
                UnaryFunc func;
                TCRuleInfo infos;
            };

            TypeCheckingRuleSet();

            void addTypeCheckingRule( TCRuleInfo&& infos, const Term& pat1, const Term& pat2, BinaryFunc f );

            void addUnificationRule( TCRuleInfo&& infos, const Term& pat, BinaryFunc f );
            void addUnificationRule( TCRuleInfo&& infos, const Term& pat1, const Term& pat2, BinaryFunc f );

            void addHalfUnificationRule( TCRuleInfo&& infos, const Term& pat, UnaryFunc f );

            const auto& typeCheckingRules() const { return m_typeCheckingRules; }
            const auto& uniRules() const { return m_uniRules; }
            const auto& halfUniRules() const { return m_halfUniRules; }

        private:
            Trie< BinaryRule > m_typeCheckingRules;
            Trie< BinaryRule > m_uniRules;
            Trie< UnaryRule > m_halfUniRules;
    };
}

#endif
Changes to bs/sema/tctrie-typecheck.inl.
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
        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







|




















|







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
        else
        {
            const auto& vt = vgen();

            for( auto&& rt : Enumerate( rptNode.m_repetition ) )
            {
                auto newC = tcc;
                newC.addComplexity( GetComplexity( rt ) );

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

                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
Changes to bs/sema/template.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
{
    ptr< TemplateRule > GetTemplateRuleSet( const Context& c, const Term& tpl )
    {
        const auto& rules = c.env()->templateRuleSet()->rules();

        MatchSolution bestSol;
        ptr< TemplateRule > pBestRule;
        bool ambiguous = false;

        for( auto&& [s, rule] : Match( tpl, rules ) )
        {

            if( !pBestRule || s > bestSol )
            {
                bestSol = s;
                pBestRule = 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 template rule" );

        return pBestRule;
    }

    optional< Term > BuildTemplateSignature( const Context& c, const Term& tpl )
    {
        const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );








|





>
|

|





|





<

|







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
{
    ptr< TemplateRule > GetTemplateRuleSet( const Context& c, const Term& tpl )
    {
        const auto& rules = c.env()->templateRuleSet()->rules();

        MatchScore bestScore;
        ptr< TemplateRule > pBestRule;
        bool ambiguous = false;

        for( auto&& [s, rule] : Match( tpl, rules ) )
        {
            auto score = s.score();
            if( !pBestRule || score > bestScore )
            {
                bestScore = score;
                pBestRule = rule;
                ambiguous = false;
                continue;
            }

            if( score < bestScore )
                continue;

            ambiguous = true;
        }


        if( ambiguous )
            G_ERROR( "ambiguous template rule" );

        return pBestRule;
    }

    optional< Term > BuildTemplateSignature( const Context& c, const Term& tpl )
    {
        const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
Changes to bs/sema/tests/meson.build.
1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
tests = [
    'unify-holes',
    'tctrie-merge',
    'tctrie-typecheck'
]

foreach t : tests
    exe = executable( 'sema-' + t, t + '.cpp',
        link_with:
        [
            goose_util,
            goose_eir,
            goose_sema

        ],
        include_directories: bsinc,
        dependencies: [catch2_dep, fmt_dep, llvm_dep]
    )
    test( 'sema-' + t, exe )
endforeach












|
>






1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
tests = [
    'unify-holes',
    'tctrie-merge',
    'tctrie-typecheck'
]

foreach t : tests
    exe = executable( 'sema-' + t, t + '.cpp',
        link_with:
        [
            goose_util,
            goose_eir,
            goose_sema,
            goose_diagnostics
        ],
        include_directories: bsinc,
        dependencies: [catch2_dep, fmt_dep, llvm_dep]
    )
    test( 'sema-' + t, exe )
endforeach
Changes to bs/sema/tests/tctrie-merge.cpp.
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
        auto vec8 = Vector::Make( HOLE( "a"_sid ), HOLE( "c"_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( *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; } );







|







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
        auto vec8 = Vector::Make( HOLE( "a"_sid ), HOLE( "c"_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( *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; } );
Changes to bs/sema/tests/tctrie-typecheck.cpp.
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
        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" )







|
|


|







56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
        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" )
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
            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 ) ) );
        }
    }
}







|









|







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
            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( 2, 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( 3, 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.
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

        auto expr6 = VEC( HOLE( "a"_sid ), HOLE( "a"_sid ) );
        auto expr7 = VEC( TSTR( "foo" ),   HOLE( "b"_sid ) );

        auto expr8 = VEC( HOLE( "a"_sid ), HOLE( "a"_sid ) );
        auto expr9 = VEC( expr5, expr1 );

        auto expr10 = VEC( HOLE( "x"_sid ), expr4,                   HOLE( "z"_sid ), HOLE( "z"_sid ) );
        auto expr11 = VEC( expr5,                   HOLE( "y"_sid ), HOLE( "y"_sid ), expr5 );

        auto expr12 = VEC( VEC( HOLE( "a"_sid ), TSTR( "foo" ) ), HOLE( "a"_sid ) );
        auto expr13 = VEC( HOLE( "b"_sid ),                       HOLE( "b"_sid ) );

        THEN( "Unifications yields the expected solutions" )
        {
            CheckForUniqueSolution( expr0, expr1, VEC( TSTR( "foo" ), TSTR( "bar" ) ), { 1, 1 } );







|
|







64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

        auto expr6 = VEC( HOLE( "a"_sid ), HOLE( "a"_sid ) );
        auto expr7 = VEC( TSTR( "foo" ),   HOLE( "b"_sid ) );

        auto expr8 = VEC( HOLE( "a"_sid ), HOLE( "a"_sid ) );
        auto expr9 = VEC( expr5, expr1 );

        auto expr10 = VEC( HOLE( "x"_sid ), expr4,           HOLE( "z"_sid ), HOLE( "z"_sid ) );
        auto expr11 = VEC( expr5,           HOLE( "y"_sid ), HOLE( "y"_sid ), expr5           );

        auto expr12 = VEC( VEC( HOLE( "a"_sid ), TSTR( "foo" ) ), HOLE( "a"_sid ) );
        auto expr13 = VEC( HOLE( "b"_sid ),                       HOLE( "b"_sid ) );

        THEN( "Unifications yields the expected solutions" )
        {
            CheckForUniqueSolution( expr0, expr1, VEC( TSTR( "foo" ), TSTR( "bar" ) ), { 1, 1 } );
Changes 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
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
#include "sema.h"

namespace goose::sema
{
    template< typename F >
    TypeCheckingSolution FindBestTyping( const Term& lhs, const Term& rhs, const Context& context, F&& typeChecker )
    {
        optional< TypeCheckingContext > bestTCC;
        optional< Term > bestSol;
        bool ambiguous = false;

        TypeCheckingContext tcc( context );
        tcc.setComplexity( GetComplexity( lhs ) + GetComplexity( rhs ) );

        for( auto&& [s, tcc] : typeChecker( 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 ) );
    }

    TypeCheckingSolution FindBestTyping( const Term& lhs, const Term& rhs, const Context& context )
    {
        return FindBestTyping( lhs, rhs, context,
            []( auto&& lhs, auto&& rhs, auto&& tcc ) { return TypeCheck( lhs, rhs, tcc ); } );
    }

    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 type checking rule" );

        if( !bestRule )
            co_return;

#ifdef TCRULES_DEBUG
        cout << "calling typecheck rule " << bestRule->infos.pFilename << ':' << bestRule->infos.line << endl;
        cout << "  lhs " << lhs << endl;
        cout << "  rhs " << rhs << endl;
#endif

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

    optional< Term > HalfUnify( const Term& lhs, 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;
                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 half-unification rule" );

        if( !bestRule )
            return nullopt;

        return bestRule->func( lhs, context );
    }









|



|




















|






|


|












|
|





>
|

|





|





<

|





|











|
|





>
|

|





|





<

|











|
|




>
|

|





|





<

|







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

namespace goose::sema
{
    template< typename F >
    TypeCheckingSolution FindBestTyping( const Term& lhs, const Term& rhs, const Context& context, F&& typeChecker )
    {
        optional< TypeCheckingContext > bestTCC;
        optional< Term > bestScore;
        bool ambiguous = false;

        TypeCheckingContext tcc( context );
        tcc.setComplexity( GetComplexity( lhs ) );

        for( auto&& [s, tcc] : typeChecker( 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;
            bestScore = move( *pps );
            ambiguous = false;
        }

        if( ambiguous )
            return AmbiguousTypeCheck{};

        if( !bestScore )
            return {};

        return make_pair( move( *bestScore ), move( *bestTCC ) );
    }

    TypeCheckingSolution FindBestTyping( const Term& lhs, const Term& rhs, const Context& context )
    {
        return FindBestTyping( lhs, rhs, context,
            []( auto&& lhs, auto&& rhs, auto&& tcc ) { return TypeCheck( lhs, rhs, tcc ); } );
    }

    TCGen TypeCheck( const Term& lhs, const Term& rhs, const TypeCheckingContext& context )
    {
        const auto& rules = context.rules()->typeCheckingRules();

        MatchScore bestScore;
        optional< TypeCheckingRuleSet::BinaryRule > bestRule;
        bool ambiguous = false;

        auto expr = VEC( lhs, rhs );
        for( auto&& [s, rule] : Match( expr, rules ) )
        {
            auto score = s.score();
            if( !bestRule || score > bestScore )
            {
                bestScore = score;
                bestRule = rule;
                ambiguous = false;
                continue;
            }

            if( score < bestScore )
                continue;

            ambiguous = true;
        }


        if( ambiguous )
            G_VAL_ERROR( *ValuePatternFromEIR( rhs ), "ambiguous type checking rule" );

        if( !bestRule )
            co_return;

#ifdef TCRULES_DEBUG
        cout << "calling typechecking rule " << bestRule->infos.pFilename << ':' << bestRule->infos.line << endl;
        cout << "  lhs " << lhs << endl;
        cout << "  rhs " << rhs << endl;
#endif

        co_yield bestRule->func( lhs, rhs, context );
    }

    TCGen Unify( const Term& lhs, const Term& rhs, const TypeCheckingContext& context )
    {
        const auto& rules = context.rules()->uniRules();

        MatchScore bestScore;
        optional< TypeCheckingRuleSet::BinaryRule > bestRule;
        bool ambiguous = false;

        auto expr = VEC( lhs, rhs );
        for( auto&& [s, rule] : Match( expr, rules ) )
        {
            auto score = s.score();
            if( !bestRule || score > bestScore )
            {
                bestScore = score;
                bestRule = rule;
                ambiguous = false;
                continue;
            }

            if( score < bestScore )
                continue;

            ambiguous = true;
        }


        if( ambiguous )
            G_ERROR( "ambiguous unification rule" );

        if( !bestRule )
            co_return;

        co_yield bestRule->func( lhs, rhs, context );
    }

    optional< Term > HalfUnify( const Term& lhs, TypeCheckingContext& context )
    {
        const auto& rules = context.rules()->halfUniRules();

        MatchScore bestScore;
        optional< TypeCheckingRuleSet::UnaryRule > bestRule;
        bool ambiguous = false;

        for( auto&& [s, rule] : Match( lhs, rules ) )
        {
            auto score = s.score();
            if( !bestRule || score > bestScore )
            {
                bestScore = score;
                bestRule = rule;
                ambiguous = false;
                continue;
            }

            if( score < bestScore )
                continue;

            ambiguous = true;
        }


        if( ambiguous )
            G_ERROR( "ambiguous half-unification rule" );

        if( !bestRule )
            return nullopt;

        return bestRule->func( lhs, context );
    }

Changes to bs/verify/condition.cpp.
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

    for( auto&& unsatExpr : unsatCore )
    {
        auto it = find_if( m_idAndLocs.begin(), m_idAndLocs.end(),
            [&]( auto&&x ) { return x.first == unsatExpr.id(); } );

        if( it == m_idAndLocs.end() )
            throw runtime_error( "Condition: panic: unknown id in unsat core" );

        errorLocations.push_back( it->second );
    }

    // Sort the unsatisfiable conditions by location ids, because z3 doesn't
    // provide them in a deterministic order.
    sort( errorLocations.begin(), errorLocations.end() );







|







104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

    for( auto&& unsatExpr : unsatCore )
    {
        auto it = find_if( m_idAndLocs.begin(), m_idAndLocs.end(),
            [&]( auto&&x ) { return x.first == unsatExpr.id(); } );

        if( it == m_idAndLocs.end() )
            G_ERROR( "unknown id in unsat core" );

        errorLocations.push_back( it->second );
    }

    // Sort the unsatisfiable conditions by location ids, because z3 doesn't
    // provide them in a deterministic order.
    sort( errorLocations.begin(), errorLocations.end() );