Goose  Check-in [0db147f117]

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

Overview
Comment:Add clang format settings, reformat everything
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | cir-ssa-refactor
Files: files | file ages | folders
SHA3-256: 0db147f1175506e2c569635cad3031e740515d5edc2662ff93ecf656140264af
User & Date: achavasse 2024-09-15 20:24:31.168
Original Comment: Add clanf format settings, reformat everything
Context
2024-09-15
21:15
  • Fix index Values EIR encoding
  • Fix CFG printer
check-in: 184c1add90 user: achavasse tags: cir-ssa-refactor
20:24
Add clang format settings, reformat everything check-in: 0db147f117 user: achavasse tags: cir-ssa-refactor
2024-09-13
21:05
Drop in the most apocalyptic changes: new CIR instruction structure, new sequence builder, new compile time address representation, do almost just the bare minimum to compile again. A few simple things are still working somehow check-in: 57d4d0c79e user: achavasse tags: cir-ssa-refactor
Changes
Unified Diff Ignore Whitespace Patch
Added .clang-format.


























































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
AccessModifierOffset: -2
AlignAfterOpenBracket: false
AlignArrayOfStructures: Right
AlignConsecutiveAssignments:
  Enabled: false
  AcrossEmptyLines: true
  AcrossComments: false
AlignConsecutiveBitFields:
  Enabled: false
  AcrossEmptyLines: true
  AcrossComments: false
AlignConsecutiveDeclarations:
  Enabled: false
  AcrossEmptyLines: true
  AcrossComments: false
AlignConsecutiveMacros:
  Enabled: false
  AcrossEmptyLines: true
  AcrossComments: false
AlignConsecutiveShortCaseStatements:
  Enabled: false
  AcrossEmptyLines: true
  AcrossComments: true
  AlignCaseColons: false
AlignEscapedNewlines: DontAlign
AlignOperands: DontAlign
AlignTrailingComments:
  Kind: Never
  OverEmptyLines: 2
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowBreakBeforeNoexceptSpecifier: OnlyWithParen
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortCompoundRequirementOnASingleLine: true
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: false
AllowShortLambdasOnASingleLine: true
AllowShortLoopsOnASingleLine: false
AlwaysBreakBeforeMultilineStrings: true
BinPackArguments: true
BinPackParameters: true
BitFieldColonSpacing: After
BracedInitializerIndentWidth: 4
BreakAdjacentStringLiterals: true
BreakAfterAttributes: Always
BreakAfterReturnType: Automatic
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Allman
BreakBeforeConceptDeclarations: Always
BreakBeforeTernaryOperators: false
BreakBinaryOperations: Never
BreakConstructorInitializers: AfterColon
BreakFunctionDefinitionParameters: false
BreakInheritanceList: AfterComma
BreakStringLiterals: true
BreakTemplateDeclarations: Yes
CompactNamespaces: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
ColumnLimit: 100
Cpp11BracedListStyle: false
DerivePointerAlignment: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: Always
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
IncludeBlocks: Regroup
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: true
IndentExternBlock: true
IndentGotoLabels: false
IndentPPDirectives: BeforeHash
IndentRequiresClause: true
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: true
InsertTrailingCommas: None
IntegerLiteralSeparator:
  Binary: 4
  Decimal: 3
  DecimalMinDigits: 5
  Hex: 4
  HexMinDigits: 6
KeepEmptyLines:
  AtEndOfFile: false
  AtStartOfBlock: false
  AtStartOfFile: false
LambdaBodyIndentation: Signature
MainIncludeChar: Any
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
PPIndentWidth: 4
PackConstructorInitializers: Never
PointerAlignment: Left
QualifierAlignment: Left
QualifierOrder: [static, inline, const, type, volatile]
ReferenceAlignment: Left
ReflowComments: true
RemoveBracesLLVM: true
RemoveParentheses: ReturnStatement
RemoveSemicolon: true
RequiresClausePosition: WithPreceding
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Always
ShortNamespaceLines: 2
SkipMacroDefinitionBody: false
SortIncludes: Never
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceAroundPointerQualifiers: After
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Always
SpacesInContainerLiterals: false
SpacesInLineCommentPrefix:
  Minimum: 1
  Maximum: -1
SpacesInParens: Custom
SpacesInParensOptions:
  ExceptDoubleParentheses: false
  InConditionalStatements: true
  InEmptyParentheses: false
  Other: true
SpacesInSquareBrackets: false
Standard: c++20
TabWidth: 4
UseTab: Never
Added .vscode/launch.json.


































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "lldb",
            "request": "launch",
            "name": "Launch",
            "program": "${workspaceFolder}/../dbuild/goose",
            "args": [],
            "cwd": "${workspaceFolder}/../dbuild/"
        }

    ]
}
Changes to bs/builtins/builders/cb_interface.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

#include "builtins/builtins.h"
#include "parse/parse.h"

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

namespace goose::builtins
{
    void SetupCodeBuilderInterface( Env& e )
    {
        RegisterBuiltinFunc< Eager< void > ( TypeWrapper< ptr< CodeBuilder > > ) >( e, e.extPoisonBuilder(),

            []( const TypeWrapper< ptr< CodeBuilder > >& cb )
            {
                cb->poison();
            } );

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

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

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

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

        RegisterBuiltinFunc< Eager< void > ( TypeWrapper< ptr< CodeBuilder > > ) >( e, e.extBeginLifetimeScope(),

            []( const TypeWrapper< ptr< CodeBuilder > >& cb )
            {
                cb->beginLifetimeScope();
            } );

        RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< CodeBuilder > > ) > >( e, e.extEndLifetimeScope(),
            []( auto&& c, const Value& b )
            {
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( b );
                cb->endLifetimeScope( c );
            } );

        RegisterBuiltinFunc< Eager< void > ( TypeWrapper< ptr< CodeBuilder > > ) >( e, e.extBeginBreakableScope(),

            []( const TypeWrapper< ptr< CodeBuilder > >& cb )
            {
                cb->beginBreakableScope();
            } );

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

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

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

        RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< CodeBuilder > >, Value, uint32_t ) > >( e, e.extDeclareValue(),

            []( auto&& c, const Value& cbv, const Value& v, const Value& index )
            {
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( cbv );
                cb->declareValue( c, v, *FromValue< uint32_t >( index ) );
            } );

        RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< CodeBuilder > >, Value ) > >( e, e.extDestroyLiveValue(),

            []( auto&& c, const Value& cbv, const Value& val )
            {
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( cbv );
                return ToValue( cb->destroyLiveValue( c, val ) );
            } );

        RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< CodeBuilder > > ) > >( e, e.extDestroyAllLiveValues(),

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

        RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< CodeBuilder > >, uint32_t ) > >( e, e.extDestroyAllLiveValuesFromBreakScope(),

            []( auto&& c, const Value& cbv, const Value& index )
            {
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( cbv );

                return ToValue( cb->destroyAllLiveValuesFromBreakScope( c, *FromValue< uint32_t >( index ) ) );
            } );


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

            } );
    }
}











|
>
|
|
<
<
|
|
|
|
<
<
<
|
|
<
|
<

|
|
<
|
<

|
|
<
|
<

|
>
|
|
<
<
|
|






|
>
|
|
<
<
|
|
|
|
<
<
|
|
|
|
<
<
|
|
|
|
<
<
|
|
>






|
>






|
>






|
>



>
|


>
|



|
>


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

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

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



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




        RegisterBuiltinFunc< Eager< TypeWrapper< ptr< cir::CFG > > >(
            TypeWrapper< ptr< CodeBuilder > > ) >( e, e.extGetCFG(),

            []( const TypeWrapper< ptr< CodeBuilder > >& cb ) { return cb->cfg(); } );


        RegisterBuiltinFunc< Eager< uint32_t >( TypeWrapper< ptr< CodeBuilder > > ) >( e,
            e.extGetBreakableScopeLevels(), []( const TypeWrapper< ptr< CodeBuilder > >& cb )

            { return cb->breakableScopeLevels(); } );


        RegisterBuiltinFunc< Eager< uint32_t >( TypeWrapper< ptr< CodeBuilder > > ) >( e,
            e.extGetContinuableScopeLevels(), []( const TypeWrapper< ptr< CodeBuilder > >& cb )

            { return cb->continuableScopeLevels(); } );


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



        RegisterBuiltinFunc< Intrinsic< void( TypeWrapper< ptr< CodeBuilder > > ) > >( e,
            e.extEndLifetimeScope(),
            []( auto&& c, const Value& b )
            {
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( b );
                cb->endLifetimeScope( c );
            } );

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



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



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



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



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

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

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

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

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

} // namespace goose::builtins
Changes to bs/builtins/builders/codebuilder.h.
1
2
3
4
5
6
7
8
9
10
11
12
13


14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#ifndef GOOSE_BUILTINS_BUILDERS_CODEBUILDER_H
#define GOOSE_BUILTINS_BUILDERS_CODEBUILDER_H

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



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

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

            template< typename V >
            void append( V&& v )
            {
                cfg()->currentBB()->append( forward< V >( v ) );
            }

        private:
            ptr< cir::CFG > m_cfg;
    };
}

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

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

        {
        }

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

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

        template< typename V > void append( V&& v )

        {
            cfg()->currentBB()->append( forward< V >( v ) );
        }

      private:
        ptr< cir::CFG > m_cfg;
    };
} // namespace goose::builtins

#endif
Changes to bs/builtins/builders/default_interface.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

#include "builtins/builtins.h"
#include "parse/parse.h"

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

namespace goose::builtins
{
    // Default builder behavior, for cases where we don't want a builder at all, or for simple builders
    // that need to overload only a few builder operations.
    void SetupDefaultBuilderInterface( Env& e )
    {
        RegisterBuiltinFunc< Eager< void > ( Value ) >( e, e.extPoisonBuilder(),
            []( const Value& b )
            {} );

        RegisterBuiltinFunc< Eager< bool > ( Value ) >( e, e.extBuilderAllowsOverloading(),
            []( const Value& b )
            {
                return false;
            } );

        RegisterBuiltinFunc< Eager< TypeWrapper< ptr< cir::CFG > > > ( Value ) >( e, e.extGetCFG(),
            []( const Value& b )
            {
                return nullptr;
            } );

        RegisterBuiltinFunc< Eager< uint32_t > ( Value ) >( e, e.extGetBreakableScopeLevels(),
            []( const Value& b )
            {
                return 0;
            } );

        RegisterBuiltinFunc< Eager< uint32_t > ( Value ) >( e, e.extGetContinuableScopeLevels(),
            []( const Value& b )
            {
                return 0;
            } );

        RegisterBuiltinFunc< Intrinsic< void ( Value ) > >( e, e.extBeginVisibilityScope(),
            []( auto&& c, const Value& b )
            {
                c.beginVisibilityScope();
            } );

        RegisterBuiltinFunc< Eager< void > ( Value ) >( e, e.extBeginLifetimeScope(),
            []( const Value& b )
            {} );

        RegisterBuiltinFunc< Eager< void > ( Value ) >( e, e.extEndLifetimeScope(),
            []( const Value& b )
            {} );

        RegisterBuiltinFunc< Eager< void > ( Value ) >( e, e.extBeginBreakableScope(),
            []( const Value& b )
            {} );

        RegisterBuiltinFunc< Eager< void > ( Value ) >( e, e.extEndBreakableScope(),
            []( const Value& b )
            {} );

        RegisterBuiltinFunc< Eager< void > ( Value ) >( e, e.extBeginContinuableScope(),
            []( const Value& b )
            {} );

        RegisterBuiltinFunc< Eager< void > ( Value ) >( e, e.extEndContinuableScope(),
            []( const Value& b )
            {} );

        RegisterBuiltinFunc< Intrinsic< void ( Value, Value, uint32_t ) > >( e, e.extDeclareValue(),
            []( auto&& c, const Value& b, const Value& v, const Value& index )
            {} );

        RegisterBuiltinFunc< Intrinsic< void ( Value, Value, uint32_t ) > >( e, e.extOnValueDeclared(),

            []( auto&& c, const Value& b, const Value& v, const Value& index )
            {} );

        RegisterBuiltinFunc< Eager< bool > ( Value ) >( e, e.extDestroyLiveValue(),
            []( const Value& b )
            {
                return true;
            } );

        RegisterBuiltinFunc< Eager< bool > ( Value ) >( e, e.extDestroyAllLiveValues(),
            []( const Value& b )
            {
                return true;
            } );

        RegisterBuiltinFunc< Eager< bool > ( Value, uint32_t ) >( e, e.extDestroyAllLiveValuesFromBreakScope(),

            []( const Value& b, uint32_t index )
            {
                return true;
            } );

        RegisterBuiltinFunc< Eager< bool > ( Value, uint32_t ) >( e, e.extDestroyAllLiveValuesFromContinueScope(),
            []( const Value& b, uint32_t index )
            {
                return true;
            } );

        RegisterBuiltinFunc< Eager< bool > ( Value ) >( e, e.extInVerificationCode(),
            []( const Value& b )
            {
                return false;
            } );

        RegisterBuiltinFunc< Eager< bool > ( Value ) >( e, e.extIsBranchingAllowed(),
            []( const Value& b )
            {
                return true;
            } );
    }
}









|
|


|
|
<

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

|
|
|
<
<
<
|
|
<

|
|
<

|
|
<

|
|
<

|
|
<

|
|
<

|
|
<

|
>
|
<

|
|
|
<
<
<
|
|
<
<
<

|
>
|
|
<
<
|
|
|
|
<
<
<
|
|
|
<
<
<
|
|
<
<
<

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

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

namespace goose::builtins
{
    // Default builder behavior, for cases where we don't want a builder at all, or for simple
    // builders that need to overload only a few builder operations.
    void SetupDefaultBuilderInterface( Env& e )
    {
        RegisterBuiltinFunc< Eager< void >( Value ) >(
            e, e.extPoisonBuilder(), []( const Value& b ) {} );


        RegisterBuiltinFunc< Eager< bool >( Value ) >(
            e, e.extBuilderAllowsOverloading(), []( const Value& b ) { return false; } );




        RegisterBuiltinFunc< Eager< TypeWrapper< ptr< cir::CFG > > >( Value ) >(
            e, e.extGetCFG(), []( const Value& b ) { return nullptr; } );




        RegisterBuiltinFunc< Eager< uint32_t >( Value ) >(
            e, e.extGetBreakableScopeLevels(), []( const Value& b ) { return 0; } );




        RegisterBuiltinFunc< Eager< uint32_t >( Value ) >(
            e, e.extGetContinuableScopeLevels(), []( const Value& b ) { return 0; } );




        RegisterBuiltinFunc< Intrinsic< void( Value ) > >( e, e.extBeginVisibilityScope(),
            []( auto&& c, const Value& b ) { c.beginVisibilityScope(); } );




        RegisterBuiltinFunc< Eager< void >( Value ) >(
            e, e.extBeginLifetimeScope(), []( const Value& b ) {} );


        RegisterBuiltinFunc< Eager< void >( Value ) >(
            e, e.extEndLifetimeScope(), []( const Value& b ) {} );


        RegisterBuiltinFunc< Eager< void >( Value ) >(
            e, e.extBeginBreakableScope(), []( const Value& b ) {} );


        RegisterBuiltinFunc< Eager< void >( Value ) >(
            e, e.extEndBreakableScope(), []( const Value& b ) {} );


        RegisterBuiltinFunc< Eager< void >( Value ) >(
            e, e.extBeginContinuableScope(), []( const Value& b ) {} );


        RegisterBuiltinFunc< Eager< void >( Value ) >(
            e, e.extEndContinuableScope(), []( const Value& b ) {} );


        RegisterBuiltinFunc< Intrinsic< void( Value, Value, uint32_t ) > >( e, e.extDeclareValue(),
            []( auto&& c, const Value& b, const Value& v, const Value& index ) {} );


        RegisterBuiltinFunc< Intrinsic< void( Value, Value, uint32_t ) > >( e,
            e.extOnValueDeclared(),
            []( auto&& c, const Value& b, const Value& v, const Value& index ) {} );


        RegisterBuiltinFunc< Eager< bool >( Value ) >(
            e, e.extDestroyLiveValue(), []( const Value& b ) { return true; } );




        RegisterBuiltinFunc< Eager< bool >( Value ) >(
            e, e.extDestroyAllLiveValues(), []( const Value& b ) { return true; } );




        RegisterBuiltinFunc< Eager< bool >( Value, uint32_t ) >( e,
            e.extDestroyAllLiveValuesFromBreakScope(),
            []( const Value& b, uint32_t index ) { return true; } );



        RegisterBuiltinFunc< Eager< bool >( Value, uint32_t ) >( e,
            e.extDestroyAllLiveValuesFromContinueScope(),
            []( const Value& b, uint32_t index ) { return true; } );




        RegisterBuiltinFunc< Eager< bool >( Value ) >(
            e, e.extInVerificationCode(), []( const Value& b ) { return false; } );




        RegisterBuiltinFunc< Eager< bool >( Value ) >(
            e, e.extIsBranchingAllowed(), []( const Value& b ) { return true; } );



    }

} // namespace goose::builtins
Changes to bs/builtins/builders/ghostcode_interface.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
#include "builtins/builtins.h"
#include "parse/parse.h"

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

namespace goose::builtins
{
    void SetupGhostCodeBuilderInterface( Env& e )
    {
        RegisterBuiltinFunc< Eager< void > ( TypeWrapper< ptr< GhostCode > > ) >( e, e.extPoisonBuilder(),

            []( const TypeWrapper< ptr< GhostCode > >& gcb )
            {
                gcb->poison();
            } );

        RegisterBuiltinFunc< Eager< bool > ( TypeWrapper< ptr< GhostCode > > ) >( e, e.extInVerificationCode(),
            []( const TypeWrapper< ptr< GhostCode > >& gcb )
            {
                return true;
            } );

        RegisterBuiltinFunc< Eager< bool > ( TypeWrapper< ptr< GhostCode > > ) >( e, e.extIsBranchingAllowed(),
            []( const TypeWrapper< ptr< GhostCode > >& gcb )
            {
                return gcb->branchingAllowed();
            } );

        RegisterBuiltinFunc< Eager< TypeWrapper< ptr< cir::CFG > > > ( TypeWrapper< ptr< GhostCode > > ) >( e, e.extGetCFG(),

            []( const TypeWrapper< ptr< GhostCode > >& gcb )
            {
                return gcb->cfg();
            } );

        RegisterBuiltinFunc< Eager< void > ( TypeWrapper< ptr< GhostCode > > ) >( e, e.extBeginLifetimeScope(),
            []( const TypeWrapper< ptr< GhostCode > >& gcb )
            {
                gcb->beginLifetimeScope();
            } );

        RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< GhostCode > > ) > >( e, e.extEndLifetimeScope(),
            []( auto&& c, const Value& b )
            {
                auto gcb = *FromValue< TypeWrapper< ptr< GhostCode > > >( b );
                gcb->endLifetimeScope( c );
            } );


        RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< GhostCode > >, Value, uint32_t ) > >( e, e.extDeclareValue(),

            []( auto&& c, const Value& cbv, const Value& v, const Value& index )
            {
                auto gcb = *FromValue< TypeWrapper< ptr< GhostCode > > >( cbv );
                gcb->declareValue( c, v, *FromValue< uint32_t >( index ) );
            } );

        RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< GhostCode > >, Value ) > >( e, e.extDestroyLiveValue(),

            []( auto&& c, const Value& cbv, const Value& val )
            {
                auto gcb = *FromValue< TypeWrapper< ptr< GhostCode > > >( cbv );
                return ToValue( gcb->destroyLiveValue( c, val ) );
            } );

        RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< GhostCode > > ) > >( e, e.extDestroyAllLiveValues(),

            []( auto&& c, const Value& cbv )
            {
                auto gcb = *FromValue< TypeWrapper< ptr< GhostCode > > >( cbv );
                return ToValue( gcb->destroyAllLiveValues( c ) );
            } );
    }
}










|
>
|
|
<
<
|
|
|
|
<
<
|
|
|
|
<
<
<
|
>
|
|
<
<
|
|
|
|
<
<
|
|






>
|
>






|
>






|
>






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

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

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



        RegisterBuiltinFunc< Eager< bool >( TypeWrapper< ptr< GhostCode > > ) >( e,
            e.extInVerificationCode(),
            []( const TypeWrapper< ptr< GhostCode > >& gcb ) { return true; } );



        RegisterBuiltinFunc< Eager< bool >( TypeWrapper< ptr< GhostCode > > ) >( e,
            e.extIsBranchingAllowed(),
            []( const TypeWrapper< ptr< GhostCode > >& gcb ) { return gcb->branchingAllowed(); } );




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



        RegisterBuiltinFunc< Eager< void >( TypeWrapper< ptr< GhostCode > > ) >( e,
            e.extBeginLifetimeScope(),
            []( const TypeWrapper< ptr< GhostCode > >& gcb ) { gcb->beginLifetimeScope(); } );



        RegisterBuiltinFunc< Intrinsic< void( TypeWrapper< ptr< GhostCode > > ) > >( e,
            e.extEndLifetimeScope(),
            []( auto&& c, const Value& b )
            {
                auto gcb = *FromValue< TypeWrapper< ptr< GhostCode > > >( b );
                gcb->endLifetimeScope( c );
            } );

        RegisterBuiltinFunc<
            Intrinsic< void( TypeWrapper< ptr< GhostCode > >, Value, uint32_t ) > >( e,
            e.extDeclareValue(),
            []( auto&& c, const Value& cbv, const Value& v, const Value& index )
            {
                auto gcb = *FromValue< TypeWrapper< ptr< GhostCode > > >( cbv );
                gcb->declareValue( c, v, *FromValue< uint32_t >( index ) );
            } );

        RegisterBuiltinFunc< Intrinsic< bool( TypeWrapper< ptr< GhostCode > >, Value ) > >( e,
            e.extDestroyLiveValue(),
            []( auto&& c, const Value& cbv, const Value& val )
            {
                auto gcb = *FromValue< TypeWrapper< ptr< GhostCode > > >( cbv );
                return ToValue( gcb->destroyLiveValue( c, val ) );
            } );

        RegisterBuiltinFunc< Intrinsic< bool( TypeWrapper< ptr< GhostCode > > ) > >( e,
            e.extDestroyAllLiveValues(),
            []( auto&& c, const Value& cbv )
            {
                auto gcb = *FromValue< TypeWrapper< ptr< GhostCode > > >( cbv );
                return ToValue( gcb->destroyAllLiveValues( c ) );
            } );
    }
} // namespace goose::builtins
Changes to bs/builtins/builders/lifecycle_manager.h.
1
2
3
4
5
6
7
8
9
10

11
12
13

14
15
16

17
18
19

20

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91


92
93
94
95
96
97
98
99
100
101
102
103
104
105

106
107
#ifndef GOOSE_BUILTINS_BUILDERS_LIFECYCLE_MANAGER_H
#define GOOSE_BUILTINS_BUILDERS_LIFECYCLE_MANAGER_H

namespace goose::builtins
{
    template< class C >
    class LifeCycleManager
    {
        public:
            void beginLifetimeScope() { ++m_lifetimeScopeLevels; }

            void endLifetimeScope( Context& c );

            void beginBreakableScope() { ++m_breakableScopeLevels; }

            void endBreakableScope() { --m_breakableScopeLevels; }

            void beginContinuableScope() { ++m_continuableScopeLevels; }

            void endContinuableScope() { --m_continuableScopeLevels; }

            uint32_t lifetimeScopeLevels() const { return m_lifetimeScopeLevels; }

            uint32_t breakableScopeLevels() const { return m_breakableScopeLevels; }

            uint32_t continuableScopeLevels() const { return m_continuableScopeLevels; }

            template< typename T >
            void declareValue( const Context& c, T&& v, uint32_t index )
            {
                OnValueDeclared( c, v, index );

                m_liveValuesList.emplace_front(
                    forward< T >( v ), index,
                    m_lifetimeScopeLevels,
                    m_breakableScopeLevels,
                    m_continuableScopeLevels
                );
            }

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

            // Destruction
            // Destroys an individual value by invoking _DestroyValue, appends any resulting cir
            // to the current basic block if necessary.
            bool destroyLiveValue( const Context& c, const Value& v, CURRENT_LOC );

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

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

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

        private:
            C& crtp() { return static_cast< C& >( *this ); }

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

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

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

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

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

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



                Value m_value;
                uint32_t m_index = 0;

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

            list< LiveValue > m_liveValuesList;
    };
}


#endif





|
<

|
|
>
|

|
>
|

|
>
|

|
>
|
>
|

<
|
|
|

|
<
<
|
<
<
|

|
|

|
|
|
|

|
|
|

|
|
|
|

|
|
|
|

|
|

|
|
|
|
|
|

|
|

|
|

|
|

|
|
|
|
|

|
|
|
|
|
|
|
<
|
>
>
|
|

|
|
|
|
|
|
|

|

<
>


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

namespace goose::builtins
{
    template< class C > class LifeCycleManager

    {
      public:
        void beginLifetimeScope() { ++m_lifetimeScopeLevels; }

        void endLifetimeScope( Context& c );

        void beginBreakableScope() { ++m_breakableScopeLevels; }

        void endBreakableScope() { --m_breakableScopeLevels; }

        void beginContinuableScope() { ++m_continuableScopeLevels; }

        void endContinuableScope() { --m_continuableScopeLevels; }

        uint32_t lifetimeScopeLevels() const { return m_lifetimeScopeLevels; }

        uint32_t breakableScopeLevels() const { return m_breakableScopeLevels; }

        uint32_t continuableScopeLevels() const { return m_continuableScopeLevels; }


        template< typename T > void declareValue( const Context& c, T&& v, uint32_t index )
        {
            OnValueDeclared( c, v, index );

            m_liveValuesList.emplace_front( forward< T >( v ), index, m_lifetimeScopeLevels,


                m_breakableScopeLevels, m_continuableScopeLevels );


        }

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

        // Destruction
        // Destroys an individual value by invoking _DestroyValue, appends any resulting cir
        // to the current basic block if necessary.
        bool destroyLiveValue( const Context& c, const Value& v, CURRENT_LOC );

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

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

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

      private:
        C& crtp() { return static_cast< C& >( *this ); }

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

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

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

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

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

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

            {
            }

            Value m_value;
            uint32_t m_index = 0;

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

        list< LiveValue > m_liveValuesList;
    };

} // namespace goose::builtins

#endif
Changes to bs/builtins/builders/lifecycle_manager.inl.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef GOOSE_BUILTINS_BUILDERS_LIFECYCLE_MANAGER_INL
#define GOOSE_BUILTINS_BUILDERS_LIFECYCLE_MANAGER_INL

namespace goose::builtins
{
    template< class C >
    void LifeCycleManager< C >::endLifetimeScope( Context& c )
    {
        destroyCurrentLifetimeScopeValues( c );
        --m_lifetimeScopeLevels;
    }

    template< class C >
    void LifeCycleManager< C >::destroyCurrentLifetimeScopeValues( const Context& c )





<
|







1
2
3
4
5

6
7
8
9
10
11
12
13
#ifndef GOOSE_BUILTINS_BUILDERS_LIFECYCLE_MANAGER_INL
#define GOOSE_BUILTINS_BUILDERS_LIFECYCLE_MANAGER_INL

namespace goose::builtins
{

    template< class C > void LifeCycleManager< C >::endLifetimeScope( Context& c )
    {
        destroyCurrentLifetimeScopeValues( c );
        --m_lifetimeScopeLevels;
    }

    template< class C >
    void LifeCycleManager< C >::destroyCurrentLifetimeScopeValues( const Context& c )
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
                return;
            }

            it = m_liveValuesList.erase( it );
        }
    }

    template< class C >
    bool LifeCycleManager< C >::destroyAllLiveValues( const Context& c )
    {
        for( auto it = m_liveValuesList.begin(); it != m_liveValuesList.end(); ++it )
        {
            if( !destroyLiveValue( c, it->m_value ) )
            {
                m_liveValuesList.clear();
                crtp().poison();
                return false;
            }
        }

        return true;
    }

    template< class C >
    bool LifeCycleManager< C >::destroyAllLiveValuesFromBreakScope( const Context& c, uint32_t breakScopeLevel )

    {
        for( auto it = m_liveValuesList.begin(); it != m_liveValuesList.end(); ++it )
        {
            if( it->m_breakableScopeLevel < breakScopeLevel )
                return true;

            if( !destroyLiveValue( c, it->m_value ) )
            {
                m_liveValuesList.clear();
                crtp().poison();
                return false;
            }
        }

        return true;
    }

    template< class C >
    bool LifeCycleManager< C >::destroyAllLiveValuesFromContinueScope( const Context& c, uint32_t continueScopeLevel )

    {
        for( auto it = m_liveValuesList.begin(); it != m_liveValuesList.end(); ++it )
        {
            if( it->m_continuableScopeLevel < continueScopeLevel )
                return true;

            if( !destroyLiveValue( c, it->m_value ) )
            {
                m_liveValuesList.clear();
                crtp().poison();
                return false;
            }
        }

        return true;
    }

    template< class C >
    bool LifeCycleManager< C >::destroyLiveValue( const Context& c, const Value& v, source_location sloc )

    {
        if( v.isPoison() )
        {
            crtp().poison();
            return false;
        }

        DiagnosticsContext dc( v.locationId(), "When invoking _DestroyValue." );
        auto result = InvokeOverloadSet( c, c.env()->extDestroyValue(),
            MakeClosedTuple( v ), Location::Create( sloc ) );

        if( result.isPoison() )
        {
            crtp().poison();
            return false;
        }

        if( !result.isConstant() )
            crtp().append( move( result ) );

        return true;
    }

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

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

        --it->m_lifetimeScopeLevel;

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

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

        m_liveValuesList.emplace( last, move( lvToExtend ) );
    }
}

#endif







<
|















|
>


















|
>


















|
>








|
|













<
|



|
<
<
<








<
<
|
<









|


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

            it = m_liveValuesList.erase( it );
        }
    }


    template< class C > bool LifeCycleManager< C >::destroyAllLiveValues( const Context& c )
    {
        for( auto it = m_liveValuesList.begin(); it != m_liveValuesList.end(); ++it )
        {
            if( !destroyLiveValue( c, it->m_value ) )
            {
                m_liveValuesList.clear();
                crtp().poison();
                return false;
            }
        }

        return true;
    }

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

            if( !destroyLiveValue( c, it->m_value ) )
            {
                m_liveValuesList.clear();
                crtp().poison();
                return false;
            }
        }

        return true;
    }

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

            if( !destroyLiveValue( c, it->m_value ) )
            {
                m_liveValuesList.clear();
                crtp().poison();
                return false;
            }
        }

        return true;
    }

    template< class C >
    bool LifeCycleManager< C >::destroyLiveValue(
        const Context& c, const Value& v, source_location sloc )
    {
        if( v.isPoison() )
        {
            crtp().poison();
            return false;
        }

        DiagnosticsContext dc( v.locationId(), "When invoking _DestroyValue." );
        auto result = InvokeOverloadSet(
            c, c.env()->extDestroyValue(), MakeClosedTuple( v ), Location::Create( sloc ) );

        if( result.isPoison() )
        {
            crtp().poison();
            return false;
        }

        if( !result.isConstant() )
            crtp().append( move( result ) );

        return true;
    }


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




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

        --it->m_lifetimeScopeLevel;

        // Look for the last value that belongs to the parent lifetime scope, if any.
        auto last = find_if( it, m_liveValuesList.end(),


            [&]( auto&& lv ) { return lv.m_lifetimeScopeLevel < it->m_lifetimeScopeLevel; } );


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

        m_liveValuesList.emplace( last, move( lvToExtend ) );
    }
} // namespace goose::builtins

#endif
Changes to bs/builtins/builders/props_interface.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
#include "builtins/builtins.h"
#include "parse/parse.h"

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

namespace goose::builtins
{
    void SetupPropositionsBuilderInterface( Env& e )
    {
        RegisterBuiltinFunc< Eager< void > ( TypeWrapper< ptr< Propositions > > ) >( e, e.extPoisonBuilder(),

            []( const TypeWrapper< ptr< Propositions > >& pb )
            {
                pb->poison();
            } );

        RegisterBuiltinFunc< Eager< bool > ( TypeWrapper< ptr< Propositions > > ) >( e, e.extInVerificationCode(),
            []( const TypeWrapper< ptr< Propositions > >& pb )
            {
                return true;
            } );

        RegisterBuiltinFunc< Eager< void > ( TypeWrapper< ptr< Propositions > > ) >( e, e.extBeginLifetimeScope(),
            []( const TypeWrapper< ptr< Propositions > >& pb )
            {
                pb->beginLifetimeScope();
            } );

        RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< Propositions > > ) > >( e, e.extEndLifetimeScope(),
            []( auto&& c, const Value& b )
            {
                auto pb = *FromValue< TypeWrapper< ptr< Propositions > > >( b );
                pb->endLifetimeScope( c );
            } );


        RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< Propositions > >, Value, uint32_t ) > >( e, e.extDeclareValue(),

            []( auto&& c, const Value& cbv, const Value& v, const Value& index )
            {
                auto pb = *FromValue< TypeWrapper< ptr< Propositions > > >( cbv );
                pb->declareValue( c, v, *FromValue< uint32_t >( index ) );
            } );

        RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< Propositions > >, Value ) > >( e, e.extDestroyLiveValue(),

            []( auto&& c, const Value& cbv, const Value& val )
            {
                auto pb = *FromValue< TypeWrapper< ptr< Propositions > > >( cbv );
                return ToValue( pb->destroyLiveValue( c, val ) );
            } );

        RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< Propositions > > ) > >( e, e.extDestroyAllLiveValues(),

            []( auto&& c, const Value& cbv )
            {
                auto pb = *FromValue< TypeWrapper< ptr< Propositions > > >( cbv );
                return ToValue( pb->destroyAllLiveValues( c ) );
            } );
    }
}










|
>
|
|
<
<
|
|
|
|
<
<
|
|
|
|
<
<
|
|






>
|
>






|
>






|
>






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

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

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



        RegisterBuiltinFunc< Eager< bool >( TypeWrapper< ptr< Propositions > > ) >( e,
            e.extInVerificationCode(),
            []( const TypeWrapper< ptr< Propositions > >& pb ) { return true; } );



        RegisterBuiltinFunc< Eager< void >( TypeWrapper< ptr< Propositions > > ) >( e,
            e.extBeginLifetimeScope(),
            []( const TypeWrapper< ptr< Propositions > >& pb ) { pb->beginLifetimeScope(); } );



        RegisterBuiltinFunc< Intrinsic< void( TypeWrapper< ptr< Propositions > > ) > >( e,
            e.extEndLifetimeScope(),
            []( auto&& c, const Value& b )
            {
                auto pb = *FromValue< TypeWrapper< ptr< Propositions > > >( b );
                pb->endLifetimeScope( c );
            } );

        RegisterBuiltinFunc<
            Intrinsic< void( TypeWrapper< ptr< Propositions > >, Value, uint32_t ) > >( e,
            e.extDeclareValue(),
            []( auto&& c, const Value& cbv, const Value& v, const Value& index )
            {
                auto pb = *FromValue< TypeWrapper< ptr< Propositions > > >( cbv );
                pb->declareValue( c, v, *FromValue< uint32_t >( index ) );
            } );

        RegisterBuiltinFunc< Intrinsic< bool( TypeWrapper< ptr< Propositions > >, Value ) > >( e,
            e.extDestroyLiveValue(),
            []( auto&& c, const Value& cbv, const Value& val )
            {
                auto pb = *FromValue< TypeWrapper< ptr< Propositions > > >( cbv );
                return ToValue( pb->destroyLiveValue( c, val ) );
            } );

        RegisterBuiltinFunc< Intrinsic< bool( TypeWrapper< ptr< Propositions > > ) > >( e,
            e.extDestroyAllLiveValues(),
            []( auto&& c, const Value& cbv )
            {
                auto pb = *FromValue< TypeWrapper< ptr< Propositions > > >( cbv );
                return ToValue( pb->destroyAllLiveValues( c ) );
            } );
    }
} // namespace goose::builtins
Changes to bs/builtins/builders/struct_interface.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
#include "builtins/builtins.h"
#include "parse/parse.h"

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

namespace goose::builtins
{
    void SetupStructBuilderInterface( Env& e )
    {
        RegisterBuiltinFunc< Eager< TypeWrapper< ptr< cir::CFG > > > ( TypeWrapper< ptr< StructBuilder > > ) >( e, e.extGetCFG(),
            []( const TypeWrapper< ptr< StructBuilder > >& sb )
            {
                return sb->defaultCtorCFG();
            } );

        // Default implementation for dropvalue inside a struct builder:
        // Append side effects of computed void values to the current CFG (needed by member initialisers),
        // otherwise error out. The things that we handle differently inside of structs (such as decls)
        // will have struct specific implementations of dropValue().
        RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< StructBuilder > >, Value ) > >( e, e.extDropValue(),

            []( const Context& c, const Value& b, const Value& v )
            {
                if( v.isConstant() || v.type() != GetValueType< void >() || !DoesInstrSeqHaveSideEffects( *v.cir() ) )

                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( v.locationId(), "this is not allowed here.", 0 );

                    PoisonBuilder( c );
                    return;
                }

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

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

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

        // DropValue for Decl inside a struct: declare a member value.

        RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< StructBuilder > >, AnyDeclType ) > >( e, e.extDropValue(),

            []( const Context& c, const Value& b, const Value& v )
            {
                auto sb = *FromValue< TypeWrapper< ptr< StructBuilder > > >( b );
                auto decl = *FromValue< Decl >( v );

                sb->declareMemberVar( c, decl.name(), move( decl.type() ), v.locationId() );
            } );

        // DropValue for DeclWithInit inside a struct: declare a member value with initialization.
        // TODO: TNamedDecls

        RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< StructBuilder > >, AnyDeclWithInitType ) > >( e, e.extDropValue(),

            []( const Context& c, const Value& b, const Value& v )
            {
                auto sb = *FromValue< TypeWrapper< ptr< StructBuilder > > >( b );
                auto decl = *FromValue< DeclWithInit >( v );


                sb->declareMemberVar( c, decl.name(), move( decl.type() ), v.locationId(), move( decl.init() ) );
            } );

        // Finalize for structs: call the builder's finalize method, which will use all the collected members to actually create the struct


        RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< StructBuilder > >, TypePatternParam< StructType, StructType::Pattern > ) > >( e, e.extFinalize(),
            []( const Context& c, const Value& b, const Value& st )
            {
                auto sb = *FromValue< TypeWrapper< ptr< StructBuilder > > >( b );
                return ToValue( sb->finalize( c, st ) );
            } );
    }
}










|
|
<
|
<


|
|
|
|
>


|
>

|
>
















>
|
>










>
|
>





>
|


|
>
>
|






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

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

namespace goose::builtins
{
    void SetupStructBuilderInterface( Env& e )
    {
        RegisterBuiltinFunc< Eager< TypeWrapper< ptr< cir::CFG > > >(
            TypeWrapper< ptr< StructBuilder > > ) >( e, e.extGetCFG(),

            []( const TypeWrapper< ptr< StructBuilder > >& sb ) { return sb->defaultCtorCFG(); } );


        // Default implementation for dropvalue inside a struct builder:
        // Append side effects of computed void values to the current CFG (needed by member
        // initialisers), otherwise error out. The things that we handle differently inside of
        // structs (such as decls) will have struct specific implementations of dropValue().
        RegisterBuiltinFunc< Intrinsic< void( TypeWrapper< ptr< StructBuilder > >, Value ) > >( e,
            e.extDropValue(),
            []( const Context& c, const Value& b, const Value& v )
            {
                if( v.isConstant() || v.type() != GetValueType< void >()
                    || !DoesInstrSeqHaveSideEffects( *v.cir() ) )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                        v.locationId(), "this is not allowed here.", 0 );
                    PoisonBuilder( c );
                    return;
                }

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

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

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

        // DropValue for Decl inside a struct: declare a member value.
        RegisterBuiltinFunc<
            Intrinsic< void( TypeWrapper< ptr< StructBuilder > >, AnyDeclType ) > >( e,
            e.extDropValue(),
            []( const Context& c, const Value& b, const Value& v )
            {
                auto sb = *FromValue< TypeWrapper< ptr< StructBuilder > > >( b );
                auto decl = *FromValue< Decl >( v );

                sb->declareMemberVar( c, decl.name(), move( decl.type() ), v.locationId() );
            } );

        // DropValue for DeclWithInit inside a struct: declare a member value with initialization.
        // TODO: TNamedDecls
        RegisterBuiltinFunc<
            Intrinsic< void( TypeWrapper< ptr< StructBuilder > >, AnyDeclWithInitType ) > >( e,
            e.extDropValue(),
            []( const Context& c, const Value& b, const Value& v )
            {
                auto sb = *FromValue< TypeWrapper< ptr< StructBuilder > > >( b );
                auto decl = *FromValue< DeclWithInit >( v );

                sb->declareMemberVar(
                    c, decl.name(), move( decl.type() ), v.locationId(), move( decl.init() ) );
            } );

        // Finalize for structs: call the builder's finalize method, which will use all the
        // collected members to actually create the struct
        RegisterBuiltinFunc< Intrinsic< bool( TypeWrapper< ptr< StructBuilder > >,
            TypePatternParam< StructType, StructType::Pattern > ) > >( e, e.extFinalize(),
            []( const Context& c, const Value& b, const Value& st )
            {
                auto sb = *FromValue< TypeWrapper< ptr< StructBuilder > > >( b );
                return ToValue( sb->finalize( c, st ) );
            } );
    }
} // namespace goose::builtins
Changes to bs/builtins/builtins.cpp.
40
41
42
43
44
45
46
47

48

49
50
51
52
53
54
55
        e.extEndContinuableScope() = CreateOverloadSet( e, "_EndContinuableScope"_sid );

        e.extDeclareValue() = CreateOverloadSet( e, "_DeclareValue"_sid );
        e.extOnValueDeclared() = CreateOverloadSet( e, "_OnValueDeclared"_sid );

        e.extDestroyLiveValue() = CreateOverloadSet( e, "_DestroyLiveValue"_sid );
        e.extDestroyAllLiveValues() = CreateOverloadSet( e, "_DestroyAllLiveValues"_sid );
        e.extDestroyAllLiveValuesFromBreakScope() = CreateOverloadSet( e, "_DestroyAllLiveValuesFromBreakScope"_sid );

        e.extDestroyAllLiveValuesFromContinueScope() = CreateOverloadSet( e, "_DestroyAllLiveValuesFromContinueScope"_sid );


        e.extInVerificationCode() = CreateOverloadSet( e, "_InVerificationCode"_sid );
        e.extIsBranchingAllowed() = CreateOverloadSet( e, "_IsBranchingAllowed"_sid );

        e.extCTForEach() = CreateOverloadSet( e, "_CTForEach"_sid );

        SetupBuiltinTypes( e );







|
>
|
>







40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
        e.extEndContinuableScope() = CreateOverloadSet( e, "_EndContinuableScope"_sid );

        e.extDeclareValue() = CreateOverloadSet( e, "_DeclareValue"_sid );
        e.extOnValueDeclared() = CreateOverloadSet( e, "_OnValueDeclared"_sid );

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

        e.extInVerificationCode() = CreateOverloadSet( e, "_InVerificationCode"_sid );
        e.extIsBranchingAllowed() = CreateOverloadSet( e, "_IsBranchingAllowed"_sid );

        e.extCTForEach() = CreateOverloadSet( e, "_CTForEach"_sid );

        SetupBuiltinTypes( e );
65
66
67
68
69
70
71
72
    }

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







|
67
68
69
70
71
72
73
74
    }

    const Term& RootG0Identity()
    {
        static auto identity = VEC( TSID( g0 ) );
        return identity;
    }
} // namespace goose::builtins
Changes to bs/builtins/builtins.h.
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
namespace goose::builtins
{
    using namespace eir;
    using namespace sema;
    using namespace diagnostics;

    extern const Term& RootG0Identity();
}

#include "codegen/codegen.h"

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

#include "builders/lifecycle_manager.inl"

namespace goose::builtins
{
    extern void SetupDefaultBuilderInterface( Env& e );
    extern void SetupCodeBuilderInterface( Env& e );
    extern void SetupPropositionsBuilderInterface( Env& e );
    extern void SetupGhostCodeBuilderInterface( Env& e );
    extern void SetupStructBuilderInterface( Env& e );
    extern void SetupBuiltins( Env& e );
}

#endif







|




















|


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
namespace goose::builtins
{
    using namespace eir;
    using namespace sema;
    using namespace diagnostics;

    extern const Term& RootG0Identity();
} // namespace goose::builtins

#include "codegen/codegen.h"

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

#include "builders/lifecycle_manager.inl"

namespace goose::builtins
{
    extern void SetupDefaultBuilderInterface( Env& e );
    extern void SetupCodeBuilderInterface( Env& e );
    extern void SetupPropositionsBuilderInterface( Env& e );
    extern void SetupGhostCodeBuilderInterface( Env& e );
    extern void SetupStructBuilderInterface( Env& e );
    extern void SetupBuiltins( Env& e );
} // namespace goose::builtins

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

22
23
24
25
26
27
28
29

30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
45

46
47
48
49
50
51
52
53

54
55
56
57
58
59
60
61

62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
77

78
79
80
81
82
83
84

85
86
87
88
89
90
91
92
93

94
95
96
97
98
99
100
101

102
103
104
105
106
107
108
109

110
111
112
113
#ifndef GOOSE_BUILTINS_EXPRHELPERS_H
#define GOOSE_BUILTINS_EXPRHELPERS_H

#include "parse/parse.h"

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

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

    }

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

    }

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

    }

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

    }

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

    }

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

    }

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

    }

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

    }

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

            forward< L >( lhs ), forward< R >( rhs ), cir::SGT( locId ) ).setLocationId( locId );
    }

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

    }

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

    }

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

    }
}

#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

64
65
66
67
68
69
70
71

72
73
74
75
76
77
78
79

80
81
82
83
84
85
86
87

88
89
90
91
92
93
94
95

96
97
98
99
100
101
102
103

104
105
106
107
108
109
110
111
112
#ifndef GOOSE_BUILTINS_EXPRHELPERS_H
#define GOOSE_BUILTINS_EXPRHELPERS_H

#include "parse/parse.h"

namespace goose::builtins::exprhelpers
{
    template< typename V > Value Not( V&& val )

    {
        auto locId = val.locationId();
        return BuildComputedValue( GetValueType< bool >(), forward< V >( val ), cir::Not( locId ) )
            .setLocationId( locId );
    }

    template< typename L, typename R > Value Or( L&& lhs, R&& rhs )

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

    template< typename L, typename R > Value And( L&& lhs, R&& rhs )

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

    template< typename L, typename R > Value Eq( L&& lhs, R&& rhs )

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

    template< typename L, typename R > Value Neq( L&& lhs, R&& rhs )

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

    template< typename L, typename R > Value UGT( L&& lhs, R&& rhs )

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

    template< typename L, typename R > Value UGE( L&& lhs, R&& rhs )

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

    template< typename L, typename R > Value ULT( L&& lhs, R&& rhs )

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

    template< typename L, typename R > Value ULE( L&& lhs, R&& rhs )

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

    template< typename L, typename R > Value SGT( L&& lhs, R&& rhs )

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

    template< typename L, typename R > Value SGE( L&& lhs, R&& rhs )

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

    template< typename L, typename R > Value SLT( L&& lhs, R&& rhs )

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

    template< typename L, typename R > Value SLE( L&& lhs, R&& rhs )

    {
        auto locId = max( lhs.locationId(), rhs.locationId() );
        return BuildComputedValue(
            GetValueType< bool >(), forward< L >( lhs ), forward< R >( rhs ), cir::SLE( locId ) )
            .setLocationId( locId );
    }
} // namespace goose::builtins::exprhelpers

#endif
Changes to bs/builtins/helpers.cpp.
1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
16
#include "builtins/builtins.h"

using namespace goose::parse;

namespace goose::builtins
{
    void RegisterRule( sema::Env& env, StringId name, parse::Rule&& rule )
    {

        parse::RegisterRule( env, AppendToVectorTerm( RootG0Identity(), TERM( name ) ), move( rule ) );
    }

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








>
|







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

using namespace goose::parse;

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

    ptr< cir::BasicBlock > ParseSubStatement( Parser& p, uint32_t precedence )
    {
        auto next = p.resolver()->lookAheadUnresolved();
        if( !next )
            return nullptr;
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299


























300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
        else if( !np.parseExpression( precedence ) )
            return nullptr;

        np.flushValue();
        return bb;
    }

    variant< Value, ValUnifyError > ConvertValueToType( const Context& c, const Value& val, const Term& type )

    {
        auto valTerm = ValueToEIR( 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,_] = get< TCSol >( us );
        return *EIRToValue( s );
    }

    void PoisonBuilder( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _PoisonBuilder." );
        InvokeOverloadSet( c, c.env()->extPoisonBuilder(),
            MakeClosedTuple( c.builder() ), loc );
    }

    bool BuilderAllowsOverloading( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _BuilderAllowsOverloading." );
        auto result = InvokeOverloadSet( c, c.env()->extBuilderAllowsOverloading(),
            MakeClosedTuple( c.builder() ), loc );

        if( result.isPoison() )
            return false;

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

        return *success;
    }

    ptr< cir::CFG > GetCFG( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _GetCFG." );
        auto result = InvokeOverloadSet( c, c.env()->extGetCFG(),
            MakeClosedTuple( c.builder() ), loc );

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

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

        return *cfg;
    }

    uint32_t GetBreakableScopeLevels( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _GetBreakableScopeLevels." );
        auto result = InvokeOverloadSet( c, c.env()->extGetBreakableScopeLevels(),
            MakeClosedTuple( c.builder() ), loc );

        if( result.isPoison() )
            return 0;

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

        return *levels;
    }

    uint32_t GetContinuableScopeLevels( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _GetContinuableScopeLevels." );
        auto result = InvokeOverloadSet( c, c.env()->extGetContinuableScopeLevels(),
            MakeClosedTuple( c.builder() ), loc );

        if( result.isPoison() )
            return 0;

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

        return *levels;
    }

    void BeginVisibilityScope( Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _BeginVisibilityScope." );
        InvokeOverloadSet( c, c.env()->extBeginVisibilityScope(),
            MakeClosedTuple( c.builder() ), loc );
    }

    void BeginLifetimeScope( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _BeginLifetimeScope." );
        InvokeOverloadSet( c, c.env()->extBeginLifetimeScope(),
            MakeClosedTuple( c.builder() ), loc );
    }

    void EndLifetimeScope( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _EndLifetimeScope." );
        InvokeOverloadSet( c, c.env()->extEndLifetimeScope(),
            MakeClosedTuple( c.builder() ), loc );
    }

    void BeginBreakableScope( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _BeginBreakableScope." );
        InvokeOverloadSet( c, c.env()->extBeginBreakableScope(),
            MakeClosedTuple( c.builder() ), loc );
    }

    void EndBreakableScope( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _EndBreakableScope." );
        InvokeOverloadSet( c, c.env()->extEndBreakableScope(),
            MakeClosedTuple( c.builder() ), loc );
    }

    void BeginContinuableScope( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _BeginContinuableScope." );
        InvokeOverloadSet( c, c.env()->extBeginContinuableScope(),
            MakeClosedTuple( c.builder() ), loc );
    }

    void EndContinuableScope( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _EndContinuableScope." );
        InvokeOverloadSet( c, c.env()->extEndContinuableScope(),
            MakeClosedTuple( c.builder() ), loc );
    }

    void DeclareValue( const Context& c, const Value& val, uint32_t index, source_location sloc )
    {
        DiagnosticsContext dc( val.locationId(), "When invoking _DeclareValue." );
        InvokeOverloadSet( c, c.env()->extDeclareValue(),
            MakeClosedTuple( c.builder(), val, ToValue( index ) ), Location::Create( sloc ) );
    }

    void OnValueDeclared( const Context& c, const Value& val, uint32_t index, source_location sloc )
    {
        DiagnosticsContext dc( val.locationId(), "When invoking _OnValueDeclared." );
        InvokeOverloadSet( c, c.env()->extOnValueDeclared(),
            MakeClosedTuple( c.builder(), val, ToValue( index ) ), Location::Create( sloc ) );
    }

    bool DestroyLiveValue( const Context& c, const Value& val, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _DestroyLiveValue." );
        auto result = InvokeOverloadSet( c, c.env()->extDestroyLiveValue(),
            MakeClosedTuple( c.builder(), val ), loc );

        if( result.isPoison() )
            return false;

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

        return *success;
    }

    bool DestroyAllLiveValues( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _DestroyAllLiveValues." );
        auto result = InvokeOverloadSet( c, c.env()->extDestroyAllLiveValues(),
            MakeClosedTuple( c.builder() ), loc );

        if( result.isPoison() )
            return false;

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

        return *success;
    }

    bool DestroyAllLiveValuesFromBreakScope( const Context& c, uint32_t breakScopeLevel, source_location sloc )

    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _DestroyAllLiveValuesFromBreakScope." );
        auto result = InvokeOverloadSet( c, c.env()->extDestroyAllLiveValuesFromBreakScope(),
            MakeClosedTuple( c.builder(), ToValue( breakScopeLevel ) ), loc );

        if( result.isPoison() )
            return false;

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

        return *success;
    }

    bool DestroyAllLiveValuesFromContinueScope( const Context& c, uint32_t continueScopeLevel, source_location sloc )

    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _DestroyAllLiveValuesFromContinueScope." );
        auto result = InvokeOverloadSet( c, c.env()->extDestroyAllLiveValuesFromContinueScope(),
            MakeClosedTuple( c.builder(), ToValue( continueScopeLevel ) ), loc );

        if( result.isPoison() )
            return false;

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

        return *success;
    }

    bool InVerificationCode( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _InVerificationCode." );
        auto result = InvokeOverloadSet( c, c.env()->extInVerificationCode(),
            MakeClosedTuple( c.builder() ), loc );

        if( result.isPoison() )
            return false;

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

        return *success;
    }

    bool IsBranchingAllowed( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _IsBranchingAllowed." );
        auto result = InvokeOverloadSet( c, c.env()->extIsBranchingAllowed(),
            MakeClosedTuple( c.builder() ), loc );



























        if( result.isPoison() )
            return false;

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

        return *success;
    }

    void CTForEach( const Context& c, const Value& decl, const Value& container, const ptr< vector< TermLoc > >& body, source_location sloc )
    {
        DiagnosticsContext dc( decl.locationId(), "When invoking _CTForEach." );
        InvokeOverloadSet( c, c.env()->extCTForEach(),
            MakeClosedTuple( decl, container, Wrap( body ) ), Location::Create( sloc ) );
    }

    bool IsTrivialInitialization( const Context& c, const Value& lhsType, const Value& rhsType, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _IsTrivialInitialization." );
        auto result = InvokeOverloadSet( c, c.env()->extIsTrivialInitialization(),
            MakeClosedTuple( lhsType, rhsType ), loc );

        if( result.isPoison() )
            return false;

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

        return *success;
    }

    bool IsTrivialAssignment( const Context& c, const Value& lhsType, const Value& rhsType, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _IsTrivialAssignment." );
        auto result = InvokeOverloadSet( c, c.env()->extIsTrivialAssignment(),
            MakeClosedTuple( lhsType, rhsType ), loc );

        if( result.isPoison() )
            return false;

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

        return *success;
    }
}







|
>












|







|
<






|
|















|
|















|
|















|
|















|
|






|
|






|
<






|
|






|
|






|
|






|
|




















|
|















|
|











|
>
















|
>




















|
|















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











<
<
<
<
<
<
|
|


|
|
|










|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338






339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356

















        else if( !np.parseExpression( precedence ) )
            return nullptr;

        np.flushValue();
        return bb;
    }

    variant< Value, ValUnifyError > ConvertValueToType(
        const Context& c, const Value& val, const Term& type )
    {
        auto valTerm = ValueToEIR( 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, _] = get< TCSol >( us );
        return *EIRToValue( s );
    }

    void PoisonBuilder( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _PoisonBuilder." );
        InvokeOverloadSet( c, c.env()->extPoisonBuilder(), MakeClosedTuple( c.builder() ), loc );

    }

    bool BuilderAllowsOverloading( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _BuilderAllowsOverloading." );
        auto result = InvokeOverloadSet(
            c, c.env()->extBuilderAllowsOverloading(), MakeClosedTuple( c.builder() ), loc );

        if( result.isPoison() )
            return false;

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

        return *success;
    }

    ptr< cir::CFG > GetCFG( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _GetCFG." );
        auto result =
            InvokeOverloadSet( c, c.env()->extGetCFG(), MakeClosedTuple( c.builder() ), loc );

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

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

        return *cfg;
    }

    uint32_t GetBreakableScopeLevels( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _GetBreakableScopeLevels." );
        auto result = InvokeOverloadSet(
            c, c.env()->extGetBreakableScopeLevels(), MakeClosedTuple( c.builder() ), loc );

        if( result.isPoison() )
            return 0;

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

        return *levels;
    }

    uint32_t GetContinuableScopeLevels( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _GetContinuableScopeLevels." );
        auto result = InvokeOverloadSet(
            c, c.env()->extGetContinuableScopeLevels(), MakeClosedTuple( c.builder() ), loc );

        if( result.isPoison() )
            return 0;

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

        return *levels;
    }

    void BeginVisibilityScope( Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _BeginVisibilityScope." );
        InvokeOverloadSet(
            c, c.env()->extBeginVisibilityScope(), MakeClosedTuple( c.builder() ), loc );
    }

    void BeginLifetimeScope( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _BeginLifetimeScope." );
        InvokeOverloadSet(
            c, c.env()->extBeginLifetimeScope(), MakeClosedTuple( c.builder() ), loc );
    }

    void EndLifetimeScope( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _EndLifetimeScope." );
        InvokeOverloadSet( c, c.env()->extEndLifetimeScope(), MakeClosedTuple( c.builder() ), loc );

    }

    void BeginBreakableScope( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _BeginBreakableScope." );
        InvokeOverloadSet(
            c, c.env()->extBeginBreakableScope(), MakeClosedTuple( c.builder() ), loc );
    }

    void EndBreakableScope( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _EndBreakableScope." );
        InvokeOverloadSet(
            c, c.env()->extEndBreakableScope(), MakeClosedTuple( c.builder() ), loc );
    }

    void BeginContinuableScope( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _BeginContinuableScope." );
        InvokeOverloadSet(
            c, c.env()->extBeginContinuableScope(), MakeClosedTuple( c.builder() ), loc );
    }

    void EndContinuableScope( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _EndContinuableScope." );
        InvokeOverloadSet(
            c, c.env()->extEndContinuableScope(), MakeClosedTuple( c.builder() ), loc );
    }

    void DeclareValue( const Context& c, const Value& val, uint32_t index, source_location sloc )
    {
        DiagnosticsContext dc( val.locationId(), "When invoking _DeclareValue." );
        InvokeOverloadSet( c, c.env()->extDeclareValue(),
            MakeClosedTuple( c.builder(), val, ToValue( index ) ), Location::Create( sloc ) );
    }

    void OnValueDeclared( const Context& c, const Value& val, uint32_t index, source_location sloc )
    {
        DiagnosticsContext dc( val.locationId(), "When invoking _OnValueDeclared." );
        InvokeOverloadSet( c, c.env()->extOnValueDeclared(),
            MakeClosedTuple( c.builder(), val, ToValue( index ) ), Location::Create( sloc ) );
    }

    bool DestroyLiveValue( const Context& c, const Value& val, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _DestroyLiveValue." );
        auto result = InvokeOverloadSet(
            c, c.env()->extDestroyLiveValue(), MakeClosedTuple( c.builder(), val ), loc );

        if( result.isPoison() )
            return false;

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

        return *success;
    }

    bool DestroyAllLiveValues( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _DestroyAllLiveValues." );
        auto result = InvokeOverloadSet(
            c, c.env()->extDestroyAllLiveValues(), MakeClosedTuple( c.builder() ), loc );

        if( result.isPoison() )
            return false;

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

        return *success;
    }

    bool DestroyAllLiveValuesFromBreakScope(
        const Context& c, uint32_t breakScopeLevel, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _DestroyAllLiveValuesFromBreakScope." );
        auto result = InvokeOverloadSet( c, c.env()->extDestroyAllLiveValuesFromBreakScope(),
            MakeClosedTuple( c.builder(), ToValue( breakScopeLevel ) ), loc );

        if( result.isPoison() )
            return false;

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

        return *success;
    }

    bool DestroyAllLiveValuesFromContinueScope(
        const Context& c, uint32_t continueScopeLevel, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _DestroyAllLiveValuesFromContinueScope." );
        auto result = InvokeOverloadSet( c, c.env()->extDestroyAllLiveValuesFromContinueScope(),
            MakeClosedTuple( c.builder(), ToValue( continueScopeLevel ) ), loc );

        if( result.isPoison() )
            return false;

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

        return *success;
    }

    bool InVerificationCode( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _InVerificationCode." );
        auto result = InvokeOverloadSet(
            c, c.env()->extInVerificationCode(), MakeClosedTuple( c.builder() ), loc );

        if( result.isPoison() )
            return false;

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

        return *success;
    }

    bool IsBranchingAllowed( const Context& c, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _IsBranchingAllowed." );
        auto result = InvokeOverloadSet(
            c, c.env()->extIsBranchingAllowed(), MakeClosedTuple( c.builder() ), loc );

        if( result.isPoison() )
            return false;

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

        return *success;
    }

    void CTForEach( const Context& c, const Value& decl, const Value& container,
        const ptr< vector< TermLoc > >& body, source_location sloc )
    {
        DiagnosticsContext dc( decl.locationId(), "When invoking _CTForEach." );
        InvokeOverloadSet( c, c.env()->extCTForEach(),
            MakeClosedTuple( decl, container, Wrap( body ) ), Location::Create( sloc ) );
    }

    bool IsTrivialInitialization(
        const Context& c, const Value& lhsType, const Value& rhsType, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _IsTrivialInitialization." );
        auto result = InvokeOverloadSet(
            c, c.env()->extIsTrivialInitialization(), MakeClosedTuple( lhsType, rhsType ), loc );

        if( result.isPoison() )
            return false;

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

        return *success;
    }







    bool IsTrivialAssignment(
        const Context& c, const Value& lhsType, const Value& rhsType, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        DiagnosticsContext dc( loc, "When invoking _IsTrivialAssignment." );
        auto result = InvokeOverloadSet(
            c, c.env()->extIsTrivialAssignment(), MakeClosedTuple( lhsType, rhsType ), loc );

        if( result.isPoison() )
            return false;

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

        return *success;
    }
} // namespace goose::builtins

















Changes to bs/builtins/helpers.h.
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

namespace goose::builtins
{
    class Propositions;

    extern const Term& EmptyPredicates();

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

    }

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

    // Utility function used to parse flow control statements, such as if and loops.
    // This parses a sub statement, which can be enclosed in a brace block or not.
    // It will get its own scope, with visibility rules setup to see the current
    // scope.
    // It returns a pointer to the final basic block generated by the statement.
    ptr< cir::BasicBlock > ParseSubStatement( parse::Parser& p, uint32_t precedence );

    enum class ValUnifyError
    {
        NoSolution,
        Ambiguous
    };

    // Typecheck the provided value with a value placeholder of the specified type,
    // and return the result or an error code.
    variant< Value, ValUnifyError > ConvertValueToType( const Context& c, const Value& val, const Term& type );


    // Helpers to create a standard type construction, as a vector starting with an identity, followed by an optional pointer to predicates
    // and an optional pointer to a llvm type.
    template< typename I, typename... T >
    auto MkStdType( I&& identity, T&&... extra )
    {
        return VEC( forward< I >( identity ), EmptyPredicates(), TERM( (void*)nullptr ), forward< T >( extra )... );

    }

    template< typename I >
    auto MkStdType( I&& identity )
    {
        return VEC( forward< I >( identity ), EmptyPredicates(), TERM( (void*)nullptr ) );
    }

    // Same as above, but directly specify the llvm type pointer.
    template< typename I, typename... T >
    auto MkStdRTType( I&& identity, const void* cgType, T&&... extra )
    {
        return VEC( forward< I >( identity ), EmptyPredicates(), const_cast< void* >( cgType ), forward< T >( extra )... );

    }

    template< typename I >
    auto MkStdRTType( I&& identity, const void* cgType )
    {
        return VEC( forward< I >( identity ), EmptyPredicates(), const_cast< void* >( cgType ) );
    }

    extern void PoisonBuilder( const Context& c, CURRENT_LOC );

    extern bool BuilderAllowsOverloading( const Context& c, CURRENT_LOC );







<
|

|
>



















|
>

|
|
|
<

|
>


|
<








|
>


<
|







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

namespace goose::builtins
{
    class Propositions;

    extern const Term& EmptyPredicates();


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

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

    // Utility function used to parse flow control statements, such as if and loops.
    // This parses a sub statement, which can be enclosed in a brace block or not.
    // It will get its own scope, with visibility rules setup to see the current
    // scope.
    // It returns a pointer to the final basic block generated by the statement.
    ptr< cir::BasicBlock > ParseSubStatement( parse::Parser& p, uint32_t precedence );

    enum class ValUnifyError
    {
        NoSolution,
        Ambiguous
    };

    // Typecheck the provided value with a value placeholder of the specified type,
    // and return the result or an error code.
    variant< Value, ValUnifyError > ConvertValueToType(
        const Context& c, const Value& val, const Term& type );

    // Helpers to create a standard type construction, as a vector starting with an identity,
    // followed by an optional pointer to predicates and an optional pointer to a llvm type.
    template< typename I, typename... T > auto MkStdType( I&& identity, T&&... extra )

    {
        return VEC( forward< I >( identity ), EmptyPredicates(), TERM( (void*)nullptr ),
            forward< T >( extra )... );
    }

    template< typename I > auto MkStdType( I&& identity )

    {
        return VEC( forward< I >( identity ), EmptyPredicates(), TERM( (void*)nullptr ) );
    }

    // Same as above, but directly specify the llvm type pointer.
    template< typename I, typename... T >
    auto MkStdRTType( I&& identity, const void* cgType, T&&... extra )
    {
        return VEC( forward< I >( identity ), EmptyPredicates(), const_cast< void* >( cgType ),
            forward< T >( extra )... );
    }


    template< typename I > auto MkStdRTType( I&& identity, const void* cgType )
    {
        return VEC( forward< I >( identity ), EmptyPredicates(), const_cast< void* >( cgType ) );
    }

    extern void PoisonBuilder( const Context& c, CURRENT_LOC );

    extern bool BuilderAllowsOverloading( const Context& c, CURRENT_LOC );
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
    extern void EndContinuableScope( const Context& c, CURRENT_LOC );

    extern void DeclareValue( const Context& c, const Value& val, uint32_t index, CURRENT_LOC );
    extern void OnValueDeclared( const Context& c, const Value& val, uint32_t index, CURRENT_LOC );

    extern bool DestroyLiveValue( const Context& c, const Value& val, CURRENT_LOC );
    extern bool DestroyAllLiveValues( const Context& c, CURRENT_LOC );
    extern bool DestroyAllLiveValuesFromBreakScope( const Context& c, uint32_t breakScopeLevel, CURRENT_LOC );

    extern bool DestroyAllLiveValuesFromContinueScope( const Context& c, uint32_t continueScopeLevel, CURRENT_LOC );


    extern bool InVerificationCode( const Context& c, CURRENT_LOC );

    extern bool IsBranchingAllowed( const Context& c, CURRENT_LOC );

    extern void CTForEach( const Context& c, const Value& decl, const Value& container, const ptr< vector< TermLoc > >& body, CURRENT_LOC );



    extern bool IsTrivialInitialization( const Context& c, const Value& lhsType, const Value& rhsType, CURRENT_LOC );

    extern bool IsTrivialAssignment( const Context& c, const Value& lhsType, const Value& rhsType, CURRENT_LOC );

    struct TypeTypePattern
    {
        static const Term& GetPattern()
        {
            static auto pat = TypeType();
            return pat;
        }
    };

    struct CTTypePattern
    {
        static const Term& GetPattern()
        {

            static auto pat = ValueToEIR( Value( TypeType(), VEC( TSID( ct_type ), REPEAT( HOLE( "_"_sid ) ) ) ) );
            return pat;
        }
    };

    struct RTTypePattern
    {
        static const Term& GetPattern()
        {

            static auto pat = ValueToEIR( Value( TypeType(), VEC( TSID( rt_type ), REPEAT( HOLE( "_"_sid ) ) ) ) );
            return pat;
        }
    };
}

#endif







|
>
|
>





|
>

>
|
>
|














>
|








>
|



|


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
    extern void EndContinuableScope( const Context& c, CURRENT_LOC );

    extern void DeclareValue( const Context& c, const Value& val, uint32_t index, CURRENT_LOC );
    extern void OnValueDeclared( const Context& c, const Value& val, uint32_t index, CURRENT_LOC );

    extern bool DestroyLiveValue( const Context& c, const Value& val, CURRENT_LOC );
    extern bool DestroyAllLiveValues( const Context& c, CURRENT_LOC );
    extern bool DestroyAllLiveValuesFromBreakScope(
        const Context& c, uint32_t breakScopeLevel, CURRENT_LOC );
    extern bool DestroyAllLiveValuesFromContinueScope(
        const Context& c, uint32_t continueScopeLevel, CURRENT_LOC );

    extern bool InVerificationCode( const Context& c, CURRENT_LOC );

    extern bool IsBranchingAllowed( const Context& c, CURRENT_LOC );

    extern void CTForEach( const Context& c, const Value& decl, const Value& container,
        const ptr< vector< TermLoc > >& body, CURRENT_LOC );

    extern bool IsTrivialInitialization(
        const Context& c, const Value& lhsType, const Value& rhsType, CURRENT_LOC );
    extern bool IsTrivialAssignment(
        const Context& c, const Value& lhsType, const Value& rhsType, CURRENT_LOC );

    struct TypeTypePattern
    {
        static const Term& GetPattern()
        {
            static auto pat = TypeType();
            return pat;
        }
    };

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

    struct RTTypePattern
    {
        static const Term& GetPattern()
        {
            static auto pat = ValueToEIR(
                Value( TypeType(), VEC( TSID( rt_type ), REPEAT( HOLE( "_"_sid ) ) ) ) );
            return pat;
        }
    };
} // namespace goose::builtins

#endif
Changes to bs/builtins/operators/apostrophe.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
53
54
55
56
57
58
59
60
61
62
63
64
65
66

namespace goose::builtins
{
    void SetupApostropheOp( Env& e )
    {
        Rule r;

        MakeLeftAssInfixOp( e, r, "'"_sid, precedence::ApostropheOp, []( auto&& p, auto&& lhs, auto&& rhs )

        {
            bool isTVarAS = lhs.type() != GetValueType< AccessSpecifier >();
            bool isTVarLT = rhs.type() != GetValueType< Lifetime >();

            if( isTVarAS && !IsTVar( lhs ) )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage( lhs.locationId(),
                    "expected an access specifier or a template variable before the ' operator." );

                return PoisonValue();
            }

            if( isTVarLT && !IsTVar( rhs ) )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage( rhs.locationId(),
                    "expected an lifetime or a template variable after the ' operator." );
                return PoisonValue();
            }

            // If the lifetime is a TVar, wrap it into a TDecl constrained to the lifetime type.
            if( isTVarLT )
            {
                auto tv = *FromValue< TVar >( rhs );
                rhs = ToValue( TDecl( GetValueType< Lifetime >(), tv.name() ) )
                    .setLocationId( rhs.locationId() );
            }

            // Likewise for the access specifier.
            if( isTVarAS )
            {
                auto tv = *FromValue< TVar >( lhs );
                lhs = ToValue( TDecl( GetValueType< AccessSpecifier >(), tv.name() ) )
                    .setLocationId( lhs.locationId() );

                return ToValue( AccessSpecifier{ ValueToEIR( lhs ), ValueToEIR( rhs ) } );
            }

            auto as = *FromValue< AccessSpecifier >( lhs );

            if( as.lifetime() != Lifetime::Unspecified() )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( lhs.locationId(),
                    "a lifetime has already been attached to this access specifier." );
                return PoisonValue();
            }

            as.lifetime() = ValueToEIR( rhs );
            return ToValue( as );
        } );
    }
}







|
>
|
|
|

|
|
|
|
>
|
|

|
|
|
|
|
|

|
|
|
|
|
|
|

|
|
|
|
|
|

|
|

|

|
|
|
|
|
|

|
|
|

|
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

namespace goose::builtins
{
    void SetupApostropheOp( Env& e )
    {
        Rule r;

        MakeLeftAssInfixOp( e, r, "'"_sid, precedence::ApostropheOp,
            []( auto&& p, auto&& lhs, auto&& rhs )
            {
                bool isTVarAS = lhs.type() != GetValueType< AccessSpecifier >();
                bool isTVarLT = rhs.type() != GetValueType< Lifetime >();

                if( isTVarAS && !IsTVar( lhs ) )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( lhs.locationId(),
                        "expected an access specifier or a template variable before the ' "
                        "operator." );
                    return PoisonValue();
                }

                if( isTVarLT && !IsTVar( rhs ) )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( rhs.locationId(),
                        "expected an lifetime or a template variable after the ' operator." );
                    return PoisonValue();
                }

                // If the lifetime is a TVar, wrap it into a TDecl constrained to the lifetime type.
                if( isTVarLT )
                {
                    auto tv = *FromValue< TVar >( rhs );
                    rhs = ToValue( TDecl( GetValueType< Lifetime >(), tv.name() ) )
                              .setLocationId( rhs.locationId() );
                }

                // Likewise for the access specifier.
                if( isTVarAS )
                {
                    auto tv = *FromValue< TVar >( lhs );
                    lhs = ToValue( TDecl( GetValueType< AccessSpecifier >(), tv.name() ) )
                              .setLocationId( lhs.locationId() );

                    return ToValue( AccessSpecifier{ ValueToEIR( lhs ), ValueToEIR( rhs ) } );
                }

                auto as = *FromValue< AccessSpecifier >( lhs );

                if( as.lifetime() != Lifetime::Unspecified() )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( lhs.locationId(),
                        "a lifetime has already been attached to this access specifier." );
                    return PoisonValue();
                }

                as.lifetime() = ValueToEIR( rhs );
                return ToValue( as );
            } );
    }
} // namespace goose::builtins
Changes to bs/builtins/operators/arith.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
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

using namespace goose::parse;

namespace goose::builtins
{
    void SetupArithOps( Env& e )
    {
        BuildParseRule( e, "+"_sid,
            PrefixOp( "operator_unary_plus"_sid, precedence::UnaryOps,
                BuildGenericTupleOperator(),
                ForType< BigInt >( []< typename O >( auto&& c, O&& operand ) -> Value
                {
                    return forward< O >( operand );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []< typename O >( auto&& c, O&& operand ) -> Value
                {
                    return forward< O >( operand );
                } )
            ),
            LeftAssInfixOp( "operator_add"_sid, precedence::AddSubOp,
                BuildGenericTupleOperator(),
                ForType< BigInt, Add >(),
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, Add( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, "-"_sid,
            PrefixOp( "operator_unary_minus"_sid, precedence::UnaryOps,
                BuildGenericTupleOperator(),

                ForType< BigInt >( []( auto&& c, auto&& operand ) -> Value
                {
                    return BuildComputedValue( GetValueType< BigInt >(),
                        ToValue( BigInt() ), operand, Sub( c.locationId() ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                []( auto&& c, auto&& operand ) -> Value
                {
                    auto opTypeVal = *EIRToValue( operand.type() );
                    auto opType = *FromValue< IntegerType >( opTypeVal );
                    return BuildComputedValue( operand.type(),
                        Value( operand.type(), APSInt( opType.m_numBits, false ) ),
                        operand, Sub( c.locationId() ) );
                } )
            ),
            LeftAssInfixOp( "operator_sub"_sid, precedence::AddSubOp,
                BuildGenericTupleOperator(),
                ForType< BigInt, Sub >(),
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, Sub( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, "*"_sid,
            LeftAssInfixOp( "operator_multiply"_sid, precedence::MulDivOp,
                BuildGenericTupleOperator(),
                ForType< BigInt, Mul >(),
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, Mul( c.locationId() ) );
                } )
            )
        );

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

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

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

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

                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

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

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

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

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

                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, UDiv( c.locationId() ) );
                } )
            )
        );

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

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

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

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

                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

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

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

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

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

                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, URem( c.locationId() ) );
                } )
            )
        );
    }
}








|
<

<
|
<

|
<
|
<
<
|
<


|
<
|
<
|
|
<
<

|
<
>
|
|
|
|
|

|
|
|
|
|
|
|
|
<
|
<


|
<
|
<
|
<
<



|
<

|
<
|
<
|
<
<



|
<

|
|
|

|
|

|
|
|
|

|
|

<
|
<

|
<
|

|
|
|

|
|

|
|
|
|

|
|

<
|
<

|
<
|
<
<



|
<

|
|
|

|
|

|
|
|
|

|
|

<
|
<

|
<
|

|
|
|

|
|

|
|
|
|

|
|

<
|
<

|
<
|
<
<

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

namespace goose::builtins
{
    void SetupArithOps( Env& e )
    {
        BuildParseRule( e, "+"_sid,
            PrefixOp( "operator_unary_plus"_sid, precedence::UnaryOps, BuildGenericTupleOperator(),

                ForType< BigInt >( []< typename O >( auto&& c, O&& operand ) -> Value

                    { return forward< O >( operand ); } ),

                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                    []< typename O >( auto&& c, O&& operand ) -> Value

                    { return forward< O >( operand ); } ) ),


            LeftAssInfixOp( "operator_add"_sid, precedence::AddSubOp, BuildGenericTupleOperator(),

                ForType< BigInt, Add >(),
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value {

                        return BuildComputedValue( lhs.type(), lhs, rhs, Add( c.locationId() ) );

                    } ) ) );



        BuildParseRule( e, "-"_sid,
            PrefixOp( "operator_unary_minus"_sid, precedence::UnaryOps, BuildGenericTupleOperator(),

                ForType< BigInt >(
                    []( auto&& c, auto&& operand ) -> Value
                    {
                        return BuildComputedValue( GetValueType< BigInt >(), ToValue( BigInt() ),
                            operand, Sub( c.locationId() ) );
                    } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                    []( auto&& c, auto&& operand ) -> Value
                    {
                        auto opTypeVal = *EIRToValue( operand.type() );
                        auto opType = *FromValue< IntegerType >( opTypeVal );
                        return BuildComputedValue( operand.type(),
                            Value( operand.type(), APSInt( opType.m_numBits, false ) ), operand,
                            Sub( c.locationId() ) );
                    } ) ),

            LeftAssInfixOp( "operator_sub"_sid, precedence::AddSubOp, BuildGenericTupleOperator(),

                ForType< BigInt, Sub >(),
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value {

                        return BuildComputedValue( lhs.type(), lhs, rhs, Sub( c.locationId() ) );

                    } ) ) );



        BuildParseRule( e, "*"_sid,
            LeftAssInfixOp( "operator_multiply"_sid, precedence::MulDivOp,
                BuildGenericTupleOperator(), ForType< BigInt, Mul >(),

                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value {

                        return BuildComputedValue( lhs.type(), lhs, rhs, Mul( c.locationId() ) );

                    } ) ) );



        BuildParseRule( e, "/"_sid,
            LeftAssInfixOp( "operator_divide"_sid, precedence::MulDivOp,
                BuildGenericTupleOperator(), ForType< BigInt, SDiv >(),

                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                    {
                        using namespace goose::builtins::exprhelpers;

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

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

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


                        cfg->currentBB()->append( move( cond ), cir::Assert( rhs.locationId() ) );


                        return BuildComputedValue( lhs.type(), lhs, rhs, SDiv( c.locationId() ) );

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

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

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

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


                        cfg->currentBB()->append( move( cond ), cir::Assert( rhs.locationId() ) );


                        return BuildComputedValue( lhs.type(), lhs, rhs, UDiv( c.locationId() ) );

                    } ) ) );



        BuildParseRule( e, "%"_sid,
            LeftAssInfixOp( "operator_modulo"_sid, precedence::MulDivOp,
                BuildGenericTupleOperator(), ForType< BigInt, SRem >(),

                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                    {
                        using namespace goose::builtins::exprhelpers;

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

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

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


                        cfg->currentBB()->append( move( cond ), cir::Assert( rhs.locationId() ) );


                        return BuildComputedValue( lhs.type(), lhs, rhs, SRem( c.locationId() ) );

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

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

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

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


                        cfg->currentBB()->append( move( cond ), cir::Assert( rhs.locationId() ) );


                        return BuildComputedValue( lhs.type(), lhs, rhs, URem( c.locationId() ) );

                    } ) ) );


    }

} // namespace goose::builtins
Changes to bs/builtins/operators/assignment.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

#include "builtins/builtins.h"
#include "precedence.h"
#include "helpers.h"
#include "tuple.h"

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

namespace goose::builtins
{

    extern Value NonRefTupleAssignment( const ptr< OverloadSet >& assOp, Context& c, const Value& lhs, const Value& rhs );

    extern Value MutRefTupleAssignment( const ptr< OverloadSet >& assOp, Context& c, const Value& lhs, const Value& rhs );

    void SetupAssignmentOps( Env& e )
    {
        auto assOp = GetOrCreateOverloadSet( e, "operator_assign"_sid );

        // Generic function to perform the assignation of a builtin type
        // to a mutable reference of that same type.
        auto BuiltinTypeAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
        {
            G_VAL_ASSERT( lhs, !lhs.isConstant() );

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

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

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

                return PoisonValue();
            }
            else if( auto bb = cfg->currentBB() )
                bb->append( rhs, lhs, Store( refType.type(), rhs.locationId(), lhs.locationId() ) );

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

        // Generic function to assign a value to a decl: create a DeclWithInit
        auto DeclAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
        {
            auto decl = *FromValue< Decl >( lhs );
            return ToValue( DeclWithInit( decl.type(), decl.name(), rhs, lhs.locationId() ) );
        };

        // Generic function to assign a value to a template named decl (local variable declaration with local type inference)

        auto TNamedDeclAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
        {
            auto tndecl = *FromValue< TNamedDecl >( lhs );

            return ToValue( TNamedDeclWithInit( tndecl.type(), tndecl.name(), rhs, lhs.locationId() ) );
        };

        BuildParseRule( e, "="_sid,
            RightAssInfixOp( "operator_assign"_sid, precedence::AssignmentOp,
                // Assignment with a decl of type $T to the left and a value of type $T to the right:
                // Local variable declaration and initialization.
                ForTypes< CustomPattern< Decl, Decl::Pattern >, CustomPattern< Value, ForwarderPattern > >( DeclAssignment ),


                // Assignment with a tnameddecl to the left and a value to the right:
                // Local variable declaration and initialization with type inference.
                ForTypes< CustomPattern< TNamedDecl, TNamedDecl::Pattern >, CustomPattern< Value, ForwarderPattern > >( TNamedDeclAssignment ),


                // Compilation time tuple from compilation time tuple
                ForTypes< CustomConstantPattern< Value, TuplePattern >, CustomConstantPattern< Value, TuplePattern > >(

                    [assOp]( auto&& c, auto&& lhs, auto&& rhs ) { return NonRefTupleAssignment( assOp, c, lhs, rhs ); } ),


                // Compilation time tuple from tuple reference
                ForTypes< CustomConstantPattern< Value, TuplePattern >, CustomPattern< Value, ReferenceType::PatternAnyOf< TuplePattern > > >(

                    [assOp]( auto&& c, auto&& lhs, auto&& rhs ) { return NonRefTupleAssignment( assOp, c, lhs, rhs ); } ),


                // Mutable tuple ref from compilation time tuple
                ForTypes< CustomPattern< Value, ReferenceType::PatternMutableOf< TuplePattern > >, CustomConstantPattern< Value, TuplePattern > >(

                    [assOp]( auto&& c, auto&& lhs, auto&& rhs ) { return MutRefTupleAssignment( assOp, c, lhs, rhs ); } ),


                // Mutable tuple ref from tuple reference
                ForTypes< CustomPattern< Value, ReferenceType::PatternMutableOf< TuplePattern > >, CustomPattern< Value, ReferenceType::PatternAnyOf< TuplePattern > > >(

                    [assOp]( auto&& c, auto&& lhs, auto&& rhs ) { return MutRefTupleAssignment( assOp, c, lhs, rhs ); } ),


                // Mutable ref assignment for compile time builtin types
                ForTypes< CustomPattern< Value, ReferenceType::PatternMutableOf< CTTypePattern > >,
                    CustomPattern< Value, ValuePatternT > >( BuiltinTypeAssignment ),

                // Mutable ref assignment for run time builtin types
                ForTypes< CustomPattern< Value, ReferenceType::PatternMutableOf< RTTypePattern > >,
                    CustomPattern< Value, ValuePatternT > >( BuiltinTypeAssignment ),

                // Explicit overload for assigning an rt_int to an rt_int reference:
                // We need this to be able to assign a ct_int to a rt_int: the generic version
                // above can't perform the necessary implicit conversion.

                ForTypes< CustomPattern< Value, ReferenceType::PatternMutableOf< IntegerType::Pattern > >,
                    CustomPattern< Value, IntegerType::Pattern > >(
                    BuiltinTypeAssignment )
            )
        );
    }
}













>
|
>
|



















|
>
















|
>



>
|




|
|
|
>



|
>


|
>
|
>


|
>
|
>


|
>
|
>


|
>
|
>












>
|
|
<
<
<

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

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

namespace goose::builtins
{
    extern Value NonRefTupleAssignment(
        const ptr< OverloadSet >& assOp, Context& c, const Value& lhs, const Value& rhs );
    extern Value MutRefTupleAssignment(
        const ptr< OverloadSet >& assOp, Context& c, const Value& lhs, const Value& rhs );

    void SetupAssignmentOps( Env& e )
    {
        auto assOp = GetOrCreateOverloadSet( e, "operator_assign"_sid );

        // Generic function to perform the assignation of a builtin type
        // to a mutable reference of that same type.
        auto BuiltinTypeAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
        {
            G_VAL_ASSERT( lhs, !lhs.isConstant() );

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

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

            auto cfg = GetCFG( c );
            if( !cfg )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                    0, "assignments are not allowed here." );
                return PoisonValue();
            }
            else if( auto bb = cfg->currentBB() )
                bb->append( rhs, lhs, Store( refType.type(), rhs.locationId(), lhs.locationId() ) );

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

        // Generic function to assign a value to a decl: create a DeclWithInit
        auto DeclAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
        {
            auto decl = *FromValue< Decl >( lhs );
            return ToValue( DeclWithInit( decl.type(), decl.name(), rhs, lhs.locationId() ) );
        };

        // Generic function to assign a value to a template named decl (local variable declaration
        // with local type inference)
        auto TNamedDeclAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
        {
            auto tndecl = *FromValue< TNamedDecl >( lhs );
            return ToValue(
                TNamedDeclWithInit( tndecl.type(), tndecl.name(), rhs, lhs.locationId() ) );
        };

        BuildParseRule( e, "="_sid,
            RightAssInfixOp( "operator_assign"_sid, precedence::AssignmentOp,
                // Assignment with a decl of type $T to the left and a value of type $T to the
                // right: Local variable declaration and initialization.
                ForTypes< CustomPattern< Decl, Decl::Pattern >,
                    CustomPattern< Value, ForwarderPattern > >( DeclAssignment ),

                // Assignment with a tnameddecl to the left and a value to the right:
                // Local variable declaration and initialization with type inference.
                ForTypes< CustomPattern< TNamedDecl, TNamedDecl::Pattern >,
                    CustomPattern< Value, ForwarderPattern > >( TNamedDeclAssignment ),

                // Compilation time tuple from compilation time tuple
                ForTypes< CustomConstantPattern< Value, TuplePattern >,
                    CustomConstantPattern< Value, TuplePattern > >(
                    [assOp]( auto&& c, auto&& lhs, auto&& rhs )
                    { return NonRefTupleAssignment( assOp, c, lhs, rhs ); } ),

                // Compilation time tuple from tuple reference
                ForTypes< CustomConstantPattern< Value, TuplePattern >,
                    CustomPattern< Value, ReferenceType::PatternAnyOf< TuplePattern > > >(
                    [assOp]( auto&& c, auto&& lhs, auto&& rhs )
                    { return NonRefTupleAssignment( assOp, c, lhs, rhs ); } ),

                // Mutable tuple ref from compilation time tuple
                ForTypes< CustomPattern< Value, ReferenceType::PatternMutableOf< TuplePattern > >,
                    CustomConstantPattern< Value, TuplePattern > >(
                    [assOp]( auto&& c, auto&& lhs, auto&& rhs )
                    { return MutRefTupleAssignment( assOp, c, lhs, rhs ); } ),

                // Mutable tuple ref from tuple reference
                ForTypes< CustomPattern< Value, ReferenceType::PatternMutableOf< TuplePattern > >,
                    CustomPattern< Value, ReferenceType::PatternAnyOf< TuplePattern > > >(
                    [assOp]( auto&& c, auto&& lhs, auto&& rhs )
                    { return MutRefTupleAssignment( assOp, c, lhs, rhs ); } ),

                // Mutable ref assignment for compile time builtin types
                ForTypes< CustomPattern< Value, ReferenceType::PatternMutableOf< CTTypePattern > >,
                    CustomPattern< Value, ValuePatternT > >( BuiltinTypeAssignment ),

                // Mutable ref assignment for run time builtin types
                ForTypes< CustomPattern< Value, ReferenceType::PatternMutableOf< RTTypePattern > >,
                    CustomPattern< Value, ValuePatternT > >( BuiltinTypeAssignment ),

                // Explicit overload for assigning an rt_int to an rt_int reference:
                // We need this to be able to assign a ct_int to a rt_int: the generic version
                // above can't perform the necessary implicit conversion.
                ForTypes<
                    CustomPattern< Value, ReferenceType::PatternMutableOf< IntegerType::Pattern > >,
                    CustomPattern< Value, IntegerType::Pattern > >( BuiltinTypeAssignment ) ) );



    }

} // namespace goose::builtins
Changes to bs/builtins/operators/comma.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

#include "builtins/builtins.h"
#include "precedence.h"
#include "helpers.h"

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

namespace goose::builtins
{
    void SetupCommaOp( Env& e )
    {
        using RefOfAnyType =
            CustomPattern< Value, ReferenceType::PatternAny >;

        BuildParseRule( e, ","_sid,
            LeftAssInfixOp( "operator_comma"_sid, precedence::CommaOp,

                // Overload: open tuple, open tuple
                ForTypes< CustomPattern< Value, TupleOpenPattern >, CustomConstantPattern< Value, TupleOpenPattern > >(

                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return ConcatenateTuples( lhs, rhs );
                } ),

                // Overload: open tuple, anything
                ForTypes< CustomConstantPattern< Value, TupleOpenPattern >, Value >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return AppendToTuple( lhs, rhs );
                } ),

                // Overload: anything, open tuple
                ForTypes< Value, CustomConstantPattern< Value, TupleOpenPattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return PrependToTuple( lhs, rhs );
                } ),

                // Overloads for computed tuples
                ForTypes< CustomPattern< Value, TupleOpenPattern >, CustomPattern< Value, TupleOpenPattern > >(

                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    G_ERROR( "concatenation of computed tuples is not yet implemented." );
                    return PoisonValue();
                } ),

                // Overload: open tuple, anything
                ForTypes< CustomPattern< Value, TupleOpenPattern >, Value >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    G_ERROR( "appending to a computed tuple is not yet implemented." );
                    return PoisonValue();
                } ),

                // Overload: anything, open tuple
                ForTypes< Value, CustomPattern< Value, TupleOpenPattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    G_ERROR( "prepending to a computed tuple is not yet implemented." );
                    return PoisonValue();
                } ),

                // Overload: anything, anything
                ForType< Value >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return AppendToTuple( AppendToTuple( EmptyOpenTuple(), lhs ), rhs );
                } ),

                // We want to be able to put references in tuples without them getting
                // automatically dereferenced, so we have specific overloads that treat
                // them explicitely.
                ForType< RefOfAnyType >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return AppendToTuple( AppendToTuple( EmptyOpenTuple(), lhs ), rhs );
                } ),

                ForTypes< Value, RefOfAnyType >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return AppendToTuple( AppendToTuple( EmptyOpenTuple(), lhs ), rhs );
                } ),

                ForTypes< RefOfAnyType, Value >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return AppendToTuple( AppendToTuple( EmptyOpenTuple(), lhs ), rhs );
                } ),

                ForTypes< CustomConstantPattern< Value, TupleOpenPattern >, RefOfAnyType >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return AppendToTuple( lhs, rhs );
                } ),

                ForTypes< RefOfAnyType, CustomConstantPattern< Value, TupleOpenPattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return PrependToTuple( lhs, rhs );
                } )
            )
        );
    }
}













<
|





|
>
|
<
|
<



|
<
|
<



|
<
|
<


|
>
|
|
|
|
|



|
|
|
|
|



|
|
|
|
|


<
|
<
|
<




<
|
<
|
<

|
<
<
|
<

|
<
<
|
<


|
<
|
<


|
<
|
<
<
<

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

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

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

        using RefOfAnyType = CustomPattern< Value, ReferenceType::PatternAny >;

        BuildParseRule( e, ","_sid,
            LeftAssInfixOp( "operator_comma"_sid, precedence::CommaOp,

                // Overload: open tuple, open tuple
                ForTypes< CustomPattern< Value, TupleOpenPattern >,
                    CustomConstantPattern< Value, TupleOpenPattern > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value

                    { return ConcatenateTuples( lhs, rhs ); } ),


                // Overload: open tuple, anything
                ForTypes< CustomConstantPattern< Value, TupleOpenPattern >, Value >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value

                    { return AppendToTuple( lhs, rhs ); } ),


                // Overload: anything, open tuple
                ForTypes< Value, CustomConstantPattern< Value, TupleOpenPattern > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value

                    { return PrependToTuple( lhs, rhs ); } ),


                // Overloads for computed tuples
                ForTypes< CustomPattern< Value, TupleOpenPattern >,
                    CustomPattern< Value, TupleOpenPattern > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                    {
                        G_ERROR( "concatenation of computed tuples is not yet implemented." );
                        return PoisonValue();
                    } ),

                // Overload: open tuple, anything
                ForTypes< CustomPattern< Value, TupleOpenPattern >, Value >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                    {
                        G_ERROR( "appending to a computed tuple is not yet implemented." );
                        return PoisonValue();
                    } ),

                // Overload: anything, open tuple
                ForTypes< Value, CustomPattern< Value, TupleOpenPattern > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                    {
                        G_ERROR( "prepending to a computed tuple is not yet implemented." );
                        return PoisonValue();
                    } ),

                // Overload: anything, anything

                ForType< Value >( []( auto&& c, auto&& lhs, auto&& rhs ) -> Value

                    { return AppendToTuple( AppendToTuple( EmptyOpenTuple(), lhs ), rhs ); } ),


                // We want to be able to put references in tuples without them getting
                // automatically dereferenced, so we have specific overloads that treat
                // them explicitely.

                ForType< RefOfAnyType >( []( auto&& c, auto&& lhs, auto&& rhs ) -> Value

                    { return AppendToTuple( AppendToTuple( EmptyOpenTuple(), lhs ), rhs ); } ),


                ForTypes< Value, RefOfAnyType >( []( auto&& c, auto&& lhs, auto&& rhs ) -> Value


                    { return AppendToTuple( AppendToTuple( EmptyOpenTuple(), lhs ), rhs ); } ),


                ForTypes< RefOfAnyType, Value >( []( auto&& c, auto&& lhs, auto&& rhs ) -> Value


                    { return AppendToTuple( AppendToTuple( EmptyOpenTuple(), lhs ), rhs ); } ),


                ForTypes< CustomConstantPattern< Value, TupleOpenPattern >, RefOfAnyType >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value

                    { return AppendToTuple( lhs, rhs ); } ),


                ForTypes< RefOfAnyType, CustomConstantPattern< Value, TupleOpenPattern > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value

                    { return PrependToTuple( lhs, rhs ); } ) ) );



    }

} // namespace goose::builtins
Changes to bs/builtins/operators/comparison.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

#include "builtins/builtins.h"
#include "precedence.h"
#include "helpers.h"
#include "tuple.h"

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

// TODO: tuple comparisons. Bit of a pita to implement and cba right now. Maybe something to implement in the lib.


namespace goose::builtins
{
    void SetupComparisonOps( Env& e )
    {
        BuildParseRule( e, "=="_sid,
            LeftAssInfixOp( "operator_equals"_sid, precedence::EqualityOp,
                ForType< bool, Eq, bool >(),
                ForType< BigInt, Eq, bool >(),
                ForType< char32_t, Eq, bool >(),
                ForType< string, Eq, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, Eq( c.locationId() ) );
                } ),
                ForType< CustomPattern< Value, TypeTypePattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return ToValue( lhs == rhs );
                } )
            )
        );

        BuildParseRule( e, "!="_sid,
            LeftAssInfixOp( "operator_not_equals"_sid, precedence::EqualityOp,
                ForType< bool, Neq, bool >(),
                ForType< BigInt, Neq, bool >(),
                ForType< char32_t, Neq, bool >(),
                ForType< string, Neq, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, Neq( c.locationId() ) );
                } ),
                ForType< CustomPattern< Value, TypeTypePattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return ToValue( lhs != rhs );
                } )
            )
        );

        BuildParseRule( e, ">"_sid,
            LeftAssInfixOp( "operator_greater"_sid, precedence::GreaterLesserOp,
                ForType< BigInt, SGT, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, SGT( c.locationId() ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, UGT( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, ">="_sid,
            LeftAssInfixOp( "operator_greater_or_equals"_sid, precedence::GreaterLesserOp,
                ForType< BigInt, SGE, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, SGE( c.locationId() ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, UGE( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, "<"_sid,
            LeftAssInfixOp( "operator_lesser"_sid, precedence::GreaterLesserOp,
                ForType< BigInt, SLT, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, SLT( c.locationId() ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, ULT( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, "<="_sid,
            LeftAssInfixOp( "operator_lesser_or_equals"_sid, precedence::GreaterLesserOp,
                ForType< BigInt, SLE, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, SLE( c.locationId() ) );
                } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        lhs, rhs, ULE( c.locationId() ) );
                } )
            )
        );
    }
}











|
>







|
<
|
<

|
<
|
|
|

|
<
|
<
<
<



|
<
|
<

|
<
|
|
|

|
<
|
<
<
<





|
<
|
|
|

|
<
|
|
|
<
<





|
<
|
|
|

|
<
|
|
|
<
<





|
<
|
|
|

|
<
|
|
|
<
<





|
<
|
|
|

|
<
|
|
|
<
<

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

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

// TODO: tuple comparisons. Bit of a pita to implement and cba right now. Maybe something to
// implement in the lib.

namespace goose::builtins
{
    void SetupComparisonOps( Env& e )
    {
        BuildParseRule( e, "=="_sid,
            LeftAssInfixOp( "operator_equals"_sid, precedence::EqualityOp,
                ForType< bool, Eq, bool >(), ForType< BigInt, Eq, bool >(),

                ForType< char32_t, Eq, bool >(), ForType< string, Eq, bool >(),

                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value {

                        return BuildComputedValue(
                            GetValueType< bool >(), lhs, rhs, Eq( c.locationId() ) );
                    } ),
                ForType< CustomPattern< Value, TypeTypePattern > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value

                    { return ToValue( lhs == rhs ); } ) ) );




        BuildParseRule( e, "!="_sid,
            LeftAssInfixOp( "operator_not_equals"_sid, precedence::EqualityOp,
                ForType< bool, Neq, bool >(), ForType< BigInt, Neq, bool >(),

                ForType< char32_t, Neq, bool >(), ForType< string, Neq, bool >(),

                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value {

                        return BuildComputedValue(
                            GetValueType< bool >(), lhs, rhs, Neq( c.locationId() ) );
                    } ),
                ForType< CustomPattern< Value, TypeTypePattern > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value

                    { return ToValue( lhs != rhs ); } ) ) );




        BuildParseRule( e, ">"_sid,
            LeftAssInfixOp( "operator_greater"_sid, precedence::GreaterLesserOp,
                ForType< BigInt, SGT, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value {

                        return BuildComputedValue(
                            GetValueType< bool >(), lhs, rhs, SGT( c.locationId() ) );
                    } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value {

                        return BuildComputedValue(
                            GetValueType< bool >(), lhs, rhs, UGT( c.locationId() ) );
                    } ) ) );



        BuildParseRule( e, ">="_sid,
            LeftAssInfixOp( "operator_greater_or_equals"_sid, precedence::GreaterLesserOp,
                ForType< BigInt, SGE, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value {

                        return BuildComputedValue(
                            GetValueType< bool >(), lhs, rhs, SGE( c.locationId() ) );
                    } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value {

                        return BuildComputedValue(
                            GetValueType< bool >(), lhs, rhs, UGE( c.locationId() ) );
                    } ) ) );



        BuildParseRule( e, "<"_sid,
            LeftAssInfixOp( "operator_lesser"_sid, precedence::GreaterLesserOp,
                ForType< BigInt, SLT, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value {

                        return BuildComputedValue(
                            GetValueType< bool >(), lhs, rhs, SLT( c.locationId() ) );
                    } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value {

                        return BuildComputedValue(
                            GetValueType< bool >(), lhs, rhs, ULT( c.locationId() ) );
                    } ) ) );



        BuildParseRule( e, "<="_sid,
            LeftAssInfixOp( "operator_lesser_or_equals"_sid, precedence::GreaterLesserOp,
                ForType< BigInt, SLE, bool >(),
                ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value {

                        return BuildComputedValue(
                            GetValueType< bool >(), lhs, rhs, SLE( c.locationId() ) );
                    } ),
                ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value {

                        return BuildComputedValue(
                            GetValueType< bool >(), lhs, rhs, ULE( c.locationId() ) );
                    } ) ) );


    }

} // namespace goose::builtins
Changes to bs/builtins/operators/compoundass.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
#include "builtins/builtins.h"
#include "precedence.h"
#include "helpers.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::cir;
using namespace goose::parse;

namespace goose::builtins
{
    // Helper to build a compound assignment operator
    template< typename L, typename R >
    auto MakeCompoundAssOpFunc( Env& e, StringId opName )
    {
        auto assOp = GetOverloadSet( e, "operator_assign"_sid );
        auto opOvlSet = GetOverloadSet( e, opName );

        return ForTypes< L, R >(
        [=]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
        {
            // Resolve the wanted operator call.
            auto newVal = PoisonValue();

            {
                DiagnosticsContext dc( 0, format( "When invoking {}.", opName.str() ) );

                newVal = InvokeOverloadSet( c,
                    opOvlSet, MakeClosedTuple( lhs, rhs ) );
            }

            if( newVal.isPoison() )
                return newVal;

            DiagnosticsContext dc( 0, "When invoking operator_assign." );

            // Resolve the assignment operator call.
            return InvokeOverloadSet( c,
                assOp, MakeClosedTuple( lhs, newVal ) );
        } );
    };


    void SetupCompoundAssOps( Env& e )
    {
        auto CompoundAssOp = [&]( StringId opName )
        {
            auto opOvlSet = GetOverloadSet( e, opName );
            return MakeCompoundAssOpFunc<
                CustomPattern< Value, ReferenceType::PatternAnyMutable >, Value >( e, opName );
        };

        // The reason we don't use BuildGenericTupleOperator() for the tuple version
        // is to that we go through the tuple assignment operator, which will take care
        // of building the result in temporaries before performong the assignment,
        // avoiding tuple values clobbering each other before we are done with the
        // original values.
        auto TupleCompoundAssOp = [&]( StringId opName )
        {
            auto opOvlSet = GetOverloadSet( e, opName );
            return MakeCompoundAssOpFunc<
                CustomPattern< Value, TuplePattern >, CustomPattern< Value, TuplePattern > >( e, opName );
        };

        BuildParseRule( e, "+="_sid,
            RightAssInfixOp( "operator_compound_add"_sid, precedence::AssignmentOp,
                TupleCompoundAssOp( "operator_add"_sid ),
                CompoundAssOp( "operator_add"_sid ) ) );

        BuildParseRule( e, "-="_sid,
            RightAssInfixOp( "operator_compound_sub"_sid, precedence::AssignmentOp,
                TupleCompoundAssOp( "operator_sub"_sid ),
                CompoundAssOp( "operator_sub"_sid ) ) );

        BuildParseRule( e, "*="_sid,
            RightAssInfixOp( "operator_compound_multiply"_sid, precedence::AssignmentOp,
                TupleCompoundAssOp( "operator_multiply"_sid ),
                CompoundAssOp( "operator_multiply"_sid ) ) );

        BuildParseRule( e, "/="_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
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
#include "builtins/builtins.h"
#include "precedence.h"
#include "helpers.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::cir;
using namespace goose::parse;

namespace goose::builtins
{
    // Helper to build a compound assignment operator
    template< typename L, typename R > auto MakeCompoundAssOpFunc( Env& e, StringId opName )

    {
        auto assOp = GetOverloadSet( e, "operator_assign"_sid );
        auto opOvlSet = GetOverloadSet( e, opName );

        return ForTypes< L, R >(
            [=]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
            {
                // Resolve the wanted operator call.
                auto newVal = PoisonValue();

                {
                    DiagnosticsContext dc( 0, format( "When invoking {}.", opName.str() ) );

                    newVal = InvokeOverloadSet( c, opOvlSet, MakeClosedTuple( lhs, rhs ) );

                }

                if( newVal.isPoison() )
                    return newVal;

                DiagnosticsContext dc( 0, "When invoking operator_assign." );

                // Resolve the assignment operator call.
                return InvokeOverloadSet( c, assOp, MakeClosedTuple( lhs, newVal ) );

            } );

    }

    void SetupCompoundAssOps( Env& e )
    {
        auto CompoundAssOp = [&]( StringId opName )
        {
            auto opOvlSet = GetOverloadSet( e, opName );
            return MakeCompoundAssOpFunc< CustomPattern< Value, ReferenceType::PatternAnyMutable >,
                Value >( e, opName );
        };

        // The reason we don't use BuildGenericTupleOperator() for the tuple version is to that we
        // go through the tuple assignment operator, which will take care of building the result in
        // temporaries before performong the assignment, avoiding tuple values clobbering each other
        // before we are done with the original values.

        auto TupleCompoundAssOp = [&]( StringId opName )
        {
            auto opOvlSet = GetOverloadSet( e, opName );
            return MakeCompoundAssOpFunc< CustomPattern< Value, TuplePattern >,
                CustomPattern< Value, TuplePattern > >( e, opName );
        };

        BuildParseRule( e, "+="_sid,
            RightAssInfixOp( "operator_compound_add"_sid, precedence::AssignmentOp,
                TupleCompoundAssOp( "operator_add"_sid ), CompoundAssOp( "operator_add"_sid ) ) );


        BuildParseRule( e, "-="_sid,
            RightAssInfixOp( "operator_compound_sub"_sid, precedence::AssignmentOp,
                TupleCompoundAssOp( "operator_sub"_sid ), CompoundAssOp( "operator_sub"_sid ) ) );


        BuildParseRule( e, "*="_sid,
            RightAssInfixOp( "operator_compound_multiply"_sid, precedence::AssignmentOp,
                TupleCompoundAssOp( "operator_multiply"_sid ),
                CompoundAssOp( "operator_multiply"_sid ) ) );

        BuildParseRule( e, "/="_sid,
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113

        BuildParseRule( e, ">>="_sid,
            RightAssInfixOp( "operator_compound_shift_right"_sid, precedence::AssignmentOp,
                TupleCompoundAssOp( "operator_shift_right"_sid ),
                CompoundAssOp( "operator_shift_right"_sid ) ) );

        BuildParseRule( e, "&="_sid,
            RightAssInfixOp( "operator_compound_and"_sid, precedence::AssignmentOp,
                TupleCompoundAssOp( "operator_and"_sid ),
                CompoundAssOp( "operator_and"_sid ) ) );

        BuildParseRule( e, "|="_sid,
            RightAssInfixOp( "operator_compound_or"_sid, precedence::AssignmentOp,
                TupleCompoundAssOp( "operator_or"_sid ),
                CompoundAssOp( "operator_or"_sid ) ) );

        BuildParseRule( e, "^="_sid,
            RightAssInfixOp( "operator_compound_xor"_sid, precedence::AssignmentOp,
                TupleCompoundAssOp( "operator_xor"_sid ),
                CompoundAssOp( "operator_xor"_sid ) ) );
    }
}








|
<



|
<



|
<

<
>
87
88
89
90
91
92
93
94

95
96
97
98

99
100
101
102

103

104
        BuildParseRule( e, ">>="_sid,
            RightAssInfixOp( "operator_compound_shift_right"_sid, precedence::AssignmentOp,
                TupleCompoundAssOp( "operator_shift_right"_sid ),
                CompoundAssOp( "operator_shift_right"_sid ) ) );

        BuildParseRule( e, "&="_sid,
            RightAssInfixOp( "operator_compound_and"_sid, precedence::AssignmentOp,
                TupleCompoundAssOp( "operator_and"_sid ), CompoundAssOp( "operator_and"_sid ) ) );


        BuildParseRule( e, "|="_sid,
            RightAssInfixOp( "operator_compound_or"_sid, precedence::AssignmentOp,
                TupleCompoundAssOp( "operator_or"_sid ), CompoundAssOp( "operator_or"_sid ) ) );


        BuildParseRule( e, "^="_sid,
            RightAssInfixOp( "operator_compound_xor"_sid, precedence::AssignmentOp,
                TupleCompoundAssOp( "operator_xor"_sid ), CompoundAssOp( "operator_xor"_sid ) ) );

    }

} // namespace goose::builtins
Changes to bs/builtins/operators/comptime.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

namespace goose::builtins
{
    void SetupComptimeOp( Env& e )
    {
        // TODO error out if function is not regular or already set as comptime
        BuildParseRule( e, "comptime"_sid,
            PrefixOp( "operator_comptime"_sid, precedence::FuncQualifier,
                ForType< TypePatternParam< FuncType, FuncPattern > >( []< typename O >( auto&& c, O&& operand ) -> Value

                {
                    auto ft = *FromValue< FuncType >( operand );
                    ft.setKind( FuncType::Kind::Comptime );
                    return ToValue( ft );
                } ),
                ForType< TypePatternParam< TFuncType, TFuncPattern > >( []< typename O >( auto&& c, O&& operand ) -> Value

                {
                    auto tft = *FromValue< TFuncType >( operand );
                    tft.setKind( FuncType::Kind::Comptime );
                    return ToValue( tft );
                } ),

                ForType< FuncDecl >( []< typename O >( auto&& c, O&& operand ) -> Value
                {
                    auto fd = *FromValue< FuncDecl >( operand );
                    fd.func().type().setKind( FuncType::Kind::Comptime );
                    return ToValue( fd );
                } ),

                ForType< TFuncDecl >( []< typename O >( auto&& c, O&& operand ) -> Value
                {
                    auto tfd = *FromValue< TFuncDecl >( operand );
                    tfd.tFunc().type().setKind( FuncType::Kind::Comptime );
                    return ToValue( tfd );
                } )
            )
        );
    }
}








|
>
|
|
|
|
|
|
>
|
|
|
|
|
>
|
|
|
|
|
|
>
|
|
|
|
|
|
<
<

<
>
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
namespace goose::builtins
{
    void SetupComptimeOp( Env& e )
    {
        // TODO error out if function is not regular or already set as comptime
        BuildParseRule( e, "comptime"_sid,
            PrefixOp( "operator_comptime"_sid, precedence::FuncQualifier,
                ForType< TypePatternParam< FuncType, FuncPattern > >(
                    []< typename O >( auto&& c, O&& operand ) -> Value
                    {
                        auto ft = *FromValue< FuncType >( operand );
                        ft.setKind( FuncType::Kind::Comptime );
                        return ToValue( ft );
                    } ),
                ForType< TypePatternParam< TFuncType, TFuncPattern > >(
                    []< typename O >( auto&& c, O&& operand ) -> Value
                    {
                        auto tft = *FromValue< TFuncType >( operand );
                        tft.setKind( FuncType::Kind::Comptime );
                        return ToValue( tft );
                    } ),
                ForType< FuncDecl >(
                    []< typename O >( auto&& c, O&& operand ) -> Value
                    {
                        auto fd = *FromValue< FuncDecl >( operand );
                        fd.func().type().setKind( FuncType::Kind::Comptime );
                        return ToValue( fd );
                    } ),
                ForType< TFuncDecl >(
                    []< typename O >( auto&& c, O&& operand ) -> Value
                    {
                        auto tfd = *FromValue< TFuncDecl >( operand );
                        tfd.tFunc().type().setKind( FuncType::Kind::Comptime );
                        return ToValue( tfd );
                    } ) ) );


    }

} // namespace goose::builtins
Changes to bs/builtins/operators/contract.cpp.
28
29
30
31
32
33
34

35
36
37
38
39
40
41

42
43
44
45
46
47
48
49
        auto type = p.peekLastValue();
        if( !type )
            return false;

        auto delim = p.resolver()->lookAheadUnresolved();
        if( !delim )
        {

            dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), format( "'[' expected after '{}'.", name ), 0 );
            return false;
        }

        auto decomp = Decompose( delim->first, Val< Delimiter >() );
        if( !decomp || *decomp != Delimiter::OpenBracket )
        {

            dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), format( "'[' expected after '{}'.", name ), 0 );
            return false;
        }

        UnparsedPropositions* unparsedProps = nullptr;

        if( auto ft = FromValue< FuncType >( *type ) )
            unparsedProps = func( *ft );







>
|






>
|







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
        auto type = p.peekLastValue();
        if( !type )
            return false;

        auto delim = p.resolver()->lookAheadUnresolved();
        if( !delim )
        {
            dm.emitSyntaxErrorMessage(
                p.resolver()->currentLocation(), format( "'[' expected after '{}'.", name ), 0 );
            return false;
        }

        auto decomp = Decompose( delim->first, Val< Delimiter >() );
        if( !decomp || *decomp != Delimiter::OpenBracket )
        {
            dm.emitSyntaxErrorMessage(
                p.resolver()->currentLocation(), format( "'[' expected after '{}'.", name ), 0 );
            return false;
        }

        UnparsedPropositions* unparsedProps = nullptr;

        if( auto ft = FromValue< FuncType >( *type ) )
            unparsedProps = func( *ft );
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
        toks.resize( toks.size() - 1 );

        return true;
    }

    bool ParseRequiresOp( Parser& p, LocationId locationId, uint32_t prec )
    {
        return ParseVerifOp( p, locationId, prec, "requires", [&]< typename FT >( FT& ft )

        {
            if constexpr( is_same_v< FT, FuncType > )
            {
                return &ft.verifInfos()->preConds()->unparsed();
            }
            else
            {
                return &ft.preConds()->unparsed();
            }
        } );
    }

    bool ParseEnsuresOp( Parser& p, LocationId locationId, uint32_t prec )
    {
        return ParseVerifOp( p, locationId, prec, "ensures", [&]< typename FT >( FT& ft )

        {
            if constexpr( is_same_v< FT, FuncType > )
            {
                return &ft.verifInfos()->postConds()->unparsed();
            }
            else
            {
                return &ft.postConds()->unparsed();
            }
        } );
    }
}

namespace goose::builtins
{
    void SetupContractOps( Env& e )
    {
        RegisterRule( e, "requires"_sid, Rule( ContractOpPrecedence, ParseRequiresOp ) );
        RegisterRule( e, "ensures"_sid, Rule( ContractOpPrecedence, ParseEnsuresOp ) );
    }
}







|
>
|
|
<
|
<
|
<
|
<
|




|
>
|
|
<
|
<
|
<
|
<
|

|








|
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
        toks.resize( toks.size() - 1 );

        return true;
    }

    bool ParseRequiresOp( Parser& p, LocationId locationId, uint32_t prec )
    {
        return ParseVerifOp( p, locationId, prec, "requires",
            [&]< typename FT >( FT& ft )
            {
                if constexpr( is_same_v< FT, FuncType > )

                    return &ft.verifInfos()->preConds()->unparsed();

                else

                    return &ft.preConds()->unparsed();

            } );
    }

    bool ParseEnsuresOp( Parser& p, LocationId locationId, uint32_t prec )
    {
        return ParseVerifOp( p, locationId, prec, "ensures",
            [&]< typename FT >( FT& ft )
            {
                if constexpr( is_same_v< FT, FuncType > )

                    return &ft.verifInfos()->postConds()->unparsed();

                else

                    return &ft.postConds()->unparsed();

            } );
    }
} // namespace

namespace goose::builtins
{
    void SetupContractOps( Env& e )
    {
        RegisterRule( e, "requires"_sid, Rule( ContractOpPrecedence, ParseRequiresOp ) );
        RegisterRule( e, "ensures"_sid, Rule( ContractOpPrecedence, ParseEnsuresOp ) );
    }
} // namespace goose::builtins
Changes to bs/builtins/operators/dollar.cpp.
12
13
14
15
16
17
18

19
20
21
22
23
24
25
26
//
// Also, it works both as a prefix operator to construct a TVar,
// and as an infix operator (with a decl or TEXpr as the left value) to
// construct a Decl of the form "<type> $FOO" or a TDecl of the form
// "<TExpr> $FOO" (for instance $T $FOO)
namespace
{

    Value BuildOrRetrieveTVar( const Context& c, LocationId locationId, StringId name, bool forwarding )
    {
        if( name == "_"_sid )
            return forwarding ? ToValue( TTVar( "_"_sid ) ) : ToValue( TVar( "_"_sid ) );

        // Template name bindings are stored with a "$$" prefix so they don't
        // collide with regular names.
        auto captureIdentity = AppendToVectorTerm( c.identity(),







>
|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//
// Also, it works both as a prefix operator to construct a TVar,
// and as an infix operator (with a decl or TEXpr as the left value) to
// construct a Decl of the form "<type> $FOO" or a TDecl of the form
// "<TExpr> $FOO" (for instance $T $FOO)
namespace
{
    Value BuildOrRetrieveTVar(
        const Context& c, LocationId locationId, StringId name, bool forwarding )
    {
        if( name == "_"_sid )
            return forwarding ? ToValue( TTVar( "_"_sid ) ) : ToValue( TVar( "_"_sid ) );

        // Template name bindings are stored with a "$$" prefix so they don't
        // collide with regular names.
        auto captureIdentity = AppendToVectorTerm( c.identity(),
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
    }

    bool ParsePrefixDollarOperator( Parser& p, LocationId locationId, uint32_t prec )
    {
        auto nameTerm = p.resolver()->consumeUnresolved();
        if( !nameTerm )
        {
            DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                "expected an identifier after '$'.", 0 );
            return false;
        }

        const auto* name = get_if< StringId >( &nameTerm->first );
        if( !name )
        {
            DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                "expected an identifier after '$'.", 0 );
            return false;
        }

        const auto& c = p.context();
        auto val = BuildOrRetrieveTVar( c, nameTerm->second, *name, false );

        auto loc = Location::CreateSpanningLocation( locationId, nameTerm->second );
        val.setLocationId( loc );
        p.pushValue( move( val ) );
        return true;
    }

    bool ParsePrefixDollarDollarOperator( Parser& p, LocationId locationId, uint32_t prec )
    {
        auto nameTerm = p.resolver()->consumeUnresolved();
        if( !nameTerm )
        {
            DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                "expected an identifier after '$$'.", 0 );
            return false;
        }

        const auto* name = get_if< StringId >( &nameTerm->first );
        if( !name )
        {
            DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                "expected an identifier after '$$'.", 0 );
            return false;
        }

        const auto& c = p.context();
        auto val = BuildOrRetrieveTVar( c, nameTerm->second, *name, true );

        p.pushValue( move( val ) );







|
|






|
|

















|
|






|
|







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
    }

    bool ParsePrefixDollarOperator( Parser& p, LocationId locationId, uint32_t prec )
    {
        auto nameTerm = p.resolver()->consumeUnresolved();
        if( !nameTerm )
        {
            DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                locationId, "expected an identifier after '$'.", 0 );
            return false;
        }

        const auto* name = get_if< StringId >( &nameTerm->first );
        if( !name )
        {
            DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                locationId, "expected an identifier after '$'.", 0 );
            return false;
        }

        const auto& c = p.context();
        auto val = BuildOrRetrieveTVar( c, nameTerm->second, *name, false );

        auto loc = Location::CreateSpanningLocation( locationId, nameTerm->second );
        val.setLocationId( loc );
        p.pushValue( move( val ) );
        return true;
    }

    bool ParsePrefixDollarDollarOperator( Parser& p, LocationId locationId, uint32_t prec )
    {
        auto nameTerm = p.resolver()->consumeUnresolved();
        if( !nameTerm )
        {
            DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                locationId, "expected an identifier after '$$'.", 0 );
            return false;
        }

        const auto* name = get_if< StringId >( &nameTerm->first );
        if( !name )
        {
            DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                locationId, "expected an identifier after '$$'.", 0 );
            return false;
        }

        const auto& c = p.context();
        auto val = BuildOrRetrieveTVar( c, nameTerm->second, *name, true );

        p.pushValue( move( val ) );
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

    bool ParseInfixDollarOperator( Parser& p, LocationId locationId, uint32_t prec )
    {
        const auto& leftVal = p.peekLastValue();
        if( !leftVal )
            return false;

        if( !builtins::IsType( p.context(), *leftVal ) && !IsTExpr( *leftVal )  )
            return false;

        auto nameTerm = p.resolver()->consumeUnresolved();
        const auto* name = get_if< StringId >( &nameTerm->first );
        const auto& c = p.context();

        auto val = BuildOrRetrieveTVar( c, nameTerm->second, *name, false );

        // If the TVar was already bound, we may end up with something in val
        // that isn't a TVar, which is an error.
        if( !IsTVar( val ) )
        {
            DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( nameTerm->second,
                "expected an unbound identifier after '$'." );
            return false;
        }

        auto loc = Location::CreateSpanningLocation( leftVal->locationId(), nameTerm->second );

        auto typeOrTExpr = ValueToEIR( *p.popValue() );
        auto tdecl = TDecl( move( typeOrTExpr ), *name );

        p.pushValue( ToValue( move( tdecl ) ).setLocationId( loc ) );
        return true;
    }
}

namespace goose::builtins
{
    void SetupDollarOp( Env& e )
    {
        Rule r( ParsePrefixDollarOperator, InfixDollarPrecedence, ParseInfixDollarOperator );
        RegisterRule( e, "$"_sid, move( r ) );

        Rule r2( ParsePrefixDollarDollarOperator );
        RegisterRule( e, "$$"_sid, move( r2 ) );
    }
}







|












|
|











|











|
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

    bool ParseInfixDollarOperator( Parser& p, LocationId locationId, uint32_t prec )
    {
        const auto& leftVal = p.peekLastValue();
        if( !leftVal )
            return false;

        if( !builtins::IsType( p.context(), *leftVal ) && !IsTExpr( *leftVal ) )
            return false;

        auto nameTerm = p.resolver()->consumeUnresolved();
        const auto* name = get_if< StringId >( &nameTerm->first );
        const auto& c = p.context();

        auto val = BuildOrRetrieveTVar( c, nameTerm->second, *name, false );

        // If the TVar was already bound, we may end up with something in val
        // that isn't a TVar, which is an error.
        if( !IsTVar( val ) )
        {
            DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                nameTerm->second, "expected an unbound identifier after '$'." );
            return false;
        }

        auto loc = Location::CreateSpanningLocation( leftVal->locationId(), nameTerm->second );

        auto typeOrTExpr = ValueToEIR( *p.popValue() );
        auto tdecl = TDecl( move( typeOrTExpr ), *name );

        p.pushValue( ToValue( move( tdecl ) ).setLocationId( loc ) );
        return true;
    }
} // namespace

namespace goose::builtins
{
    void SetupDollarOp( Env& e )
    {
        Rule r( ParsePrefixDollarOperator, InfixDollarPrecedence, ParseInfixDollarOperator );
        RegisterRule( e, "$"_sid, move( r ) );

        Rule r2( ParsePrefixDollarDollarOperator );
        RegisterRule( e, "$$"_sid, move( r2 ) );
    }
} // namespace goose::builtins
Changes to bs/builtins/operators/dot.cpp.
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


        BuildParseRule( e, "."_sid,
            LeftAssInfixOp( "operator_dot"_sid, precedence::DotOp,
                // dot operator for a tuple reference:
                // returns a reference to the specified member.
                ForTypes< CustomPattern< Value, ReferenceType::PatternAnyOf< TuplePattern > >,
                    CustomPattern< IntegerType, IntegerType::PatternUnsigned32 > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    G_VAL_ASSERT( lhs, !lhs.isConstant() );

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

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

                    uint32_t index = *FromValue< uint32_t >( rhs );
                    if( index >= TupleTypeSize( tupType ) )
                    {
                        DiagnosticsManager::GetInstance().emitErrorMessage( rhs.locationId(),
                            "the index is out of range." );
                        return PoisonValue();
                    }

                    auto rt = ValueToEIR( ToValue( ReferenceType( GetTupleTypeElement( tupType, index ), refType.accessSpec() ) ) );

                    return BuildComputedValue( rt, lhs, Select( index, c.locationId() ) );
                } ),

                // Overload for constant tuples: directly return the wanted element
                ForTypes< CustomConstantPattern< Value, TuplePattern >,
                    CustomPattern< IntegerType, IntegerType::PatternUnsigned32 > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    G_VAL_ASSERT( lhs, lhs.isConstant() );

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

                    uint32_t index = *FromValue< uint32_t >( rhs );
                    if( index >= TupleSize( lhs ) )
                    {
                        DiagnosticsManager::GetInstance().emitErrorMessage( rhs.locationId(),
                            "the index is out of range." );
                        return PoisonValue();
                    }

                    return *EIRToValue( GetConstantTupleElement( lhs, index ) );
                } ),

                // For structs: look up the member name, get its index, and call the dot operator on
                // the underlying tuple

                ForTypes< CustomPattern< Value, ReferenceType::PatternAnyOf< StructType::Pattern > >,
                    StringId >(
                [dotOp]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    // Extract the struct type form the ref type
                    auto refType = *FromValue< ReferenceType >( *EIRToValue( lhs.type() ) );
                    auto st = *EIRToValue( refType.type() );
                    auto structType = *FromValue< StructType >( st );

                    if( !structType.parse( c.env() ) )
                        return PoisonValue();

                    auto memberName = *FromValue< StringId >( rhs );


                    auto memberIdentity = AppendToVectorTerm( structType.identity(), TERM( memberName ) );
                    Term result;

                    switch( c.env()->retrieveValue( memberIdentity, c.identity(), result ) )
                    {
                        case sema::Env::Status::Success:
                            break;

                        case sema::Env::Status::AmbiguousMatch:
                            DiagnosticsManager::GetInstance().emitErrorMessage( st.locationId(),
                                "unexpected ambiguous match when resolving the member name." );
                            return PoisonValue();

                        default:
                            DiagnosticsManager::GetInstance().emitErrorMessage( st.locationId(),
                                "unknown member name." );
                            return PoisonValue();
                    }

                    auto tupType = c.env()->retrieveValue( TSID( underlying_type ), AppendToVectorTerm( structType.identity(), TERM( 0U ) ) );

                    G_VAL_ASSERT( lhs, tupType );

                    refType.type() = move( *tupType );

                    Value newRef( ValueToEIR( ToValue( refType ) ), lhs.cir(), lhs.locationId() );
                    return InvokeOverloadSet( c, dotOp,

                        MakeClosedTuple( newRef, EIRToValue( result )->setLocationId( rhs.locationId() ) ) );
                } )
            )
        );
    }
}








|
|
|

|
|
|
|
|
|

|
|

|
|
|
|
|
|
|

|
>
|
|




|
|
|

|
|
|
|
|
|

|
|
|
|
|
|
|

|
|



>
|

|
|
|
|
|
|

|
|

|

>
|
|

|
|
|
|

|
|
|
|

|
|
|
|
|

|
>
|

|
>
|
|
>
|
|
<
<

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

        BuildParseRule( e, "."_sid,
            LeftAssInfixOp( "operator_dot"_sid, precedence::DotOp,
                // dot operator for a tuple reference:
                // returns a reference to the specified member.
                ForTypes< CustomPattern< Value, ReferenceType::PatternAnyOf< TuplePattern > >,
                    CustomPattern< IntegerType, IntegerType::PatternUnsigned32 > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                    {
                        G_VAL_ASSERT( lhs, !lhs.isConstant() );

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

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

                        uint32_t index = *FromValue< uint32_t >( rhs );
                        if( index >= TupleTypeSize( tupType ) )
                        {
                            DiagnosticsManager::GetInstance().emitErrorMessage(
                                rhs.locationId(), "the index is out of range." );
                            return PoisonValue();
                        }

                        auto rt = ValueToEIR( ToValue( ReferenceType(
                            GetTupleTypeElement( tupType, index ), refType.accessSpec() ) ) );
                        return BuildComputedValue( rt, lhs, Select( index, c.locationId() ) );
                    } ),

                // Overload for constant tuples: directly return the wanted element
                ForTypes< CustomConstantPattern< Value, TuplePattern >,
                    CustomPattern< IntegerType, IntegerType::PatternUnsigned32 > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                    {
                        G_VAL_ASSERT( lhs, lhs.isConstant() );

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

                        uint32_t index = *FromValue< uint32_t >( rhs );
                        if( index >= TupleSize( lhs ) )
                        {
                            DiagnosticsManager::GetInstance().emitErrorMessage(
                                rhs.locationId(), "the index is out of range." );
                            return PoisonValue();
                        }

                        return *EIRToValue( GetConstantTupleElement( lhs, index ) );
                    } ),

                // For structs: look up the member name, get its index, and call the dot operator on
                // the underlying tuple
                ForTypes<
                    CustomPattern< Value, ReferenceType::PatternAnyOf< StructType::Pattern > >,
                    StringId >(
                    [dotOp]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                    {
                        // Extract the struct type form the ref type
                        auto refType = *FromValue< ReferenceType >( *EIRToValue( lhs.type() ) );
                        auto st = *EIRToValue( refType.type() );
                        auto structType = *FromValue< StructType >( st );

                        if( !structType.parse( c.env() ) )
                            return PoisonValue();

                        auto memberName = *FromValue< StringId >( rhs );

                        auto memberIdentity =
                            AppendToVectorTerm( structType.identity(), TERM( memberName ) );
                        Term result;

                        switch( c.env()->retrieveValue( memberIdentity, c.identity(), result ) )
                        {
                            case sema::Env::Status::Success:
                                break;

                            case sema::Env::Status::AmbiguousMatch:
                                DiagnosticsManager::GetInstance().emitErrorMessage( st.locationId(),
                                    "unexpected ambiguous match when resolving the member name." );
                                return PoisonValue();

                            default:
                                DiagnosticsManager::GetInstance().emitErrorMessage(
                                    st.locationId(), "unknown member name." );
                                return PoisonValue();
                        }

                        auto tupType = c.env()->retrieveValue( TSID( underlying_type ),
                            AppendToVectorTerm( structType.identity(), TERM( 0U ) ) );
                        G_VAL_ASSERT( lhs, tupType );

                        refType.type() = move( *tupType );
                        Value newRef(
                            ValueToEIR( ToValue( refType ) ), lhs.cir(), lhs.locationId() );
                        return InvokeOverloadSet( c, dotOp,
                            MakeClosedTuple(
                                newRef, EIRToValue( result )->setLocationId( rhs.locationId() ) ) );
                    } ) ) );


    }

} // namespace goose::builtins
Changes to bs/builtins/operators/ellipsis.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


namespace goose::builtins
{
    void SetupEllipsisOp( Env& e )
    {
        BuildParseRule( e, "..."_sid,
            PostfixOp( "operator_ellipsis"_sid, precedence::EllipsisOp,
                // default ellipsis operator: wrap whatever was given as parameter into a pack TExpr.
                // Complain if the operand isn't a constant.
                //
                // Unpack overloads will be more specific so they will take precedence where applicable.

                ForType< Value >(
                []( auto&& c, auto&& pattern ) -> Value
                {
                    if( !pattern.isConstant() )
                    {
                        DiagnosticsManager::GetInstance().emitErrorMessage( pattern.locationId(),

                            "pack expressions must be types, constants or template expressions." );

                        return PoisonValue();
                    }
                    return ToValue( TPack( ValueToEIR( pattern ) ) );
                } )
            )
        );
    }
}








|
|

|
>

|
|
|
|
|
>
|
>
|
|
|
|
<
<

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

namespace goose::builtins
{
    void SetupEllipsisOp( Env& e )
    {
        BuildParseRule( e, "..."_sid,
            PostfixOp( "operator_ellipsis"_sid, precedence::EllipsisOp,
                // default ellipsis operator: wrap whatever was given as parameter into a pack
                // TExpr. Complain if the operand isn't a constant.
                //
                // Unpack overloads will be more specific so they will take precedence where
                // applicable.
                ForType< Value >(
                    []( auto&& c, auto&& pattern ) -> Value
                    {
                        if( !pattern.isConstant() )
                        {
                            DiagnosticsManager::GetInstance().emitErrorMessage(
                                pattern.locationId(),
                                "pack expressions must be types, constants or template "
                                "expressions." );
                            return PoisonValue();
                        }
                        return ToValue( TPack( ValueToEIR( pattern ) ) );
                    } ) ) );


    }

} // namespace goose::builtins
Changes to bs/builtins/operators/ghost.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

namespace goose::builtins
{
    void SetupGhostOp( Env& e )
    {
        // TODO error out if function isn't a regular function or if its already ghost
        BuildParseRule( e, "ghost"_sid,
            PrefixOp( "operator_ghost"_sid, precedence::FuncQualifier,
                ForType< TypePatternParam< FuncType, FuncPattern > >( []< typename O >( auto&& c, O&& operand ) -> Value

                {
                    auto ft = *FromValue< FuncType >( operand );
                    ft.setKind( FuncType::Kind::Ghost );
                    return ToValue( ft );
                } ),
                ForType< TypePatternParam< TFuncType, TFuncPattern > >( []< typename O >( auto&& c, O&& operand ) -> Value

                {
                    auto tft = *FromValue< TFuncType >( operand );
                    tft.setKind( FuncType::Kind::Ghost );
                    return ToValue( tft );
                } ),

                ForType< FuncDecl >( []< typename O >( auto&& c, O&& operand ) -> Value
                {
                    auto fd = *FromValue< FuncDecl >( operand );
                    fd.func().type().setKind( FuncType::Kind::Ghost );
                    return ToValue( fd );
                } ),

                ForType< TFuncDecl >( []< typename O >( auto&& c, O&& operand ) -> Value
                {
                    auto tfd = *FromValue< TFuncDecl >( operand );
                    tfd.tFunc().type().setKind( FuncType::Kind::Ghost );
                    return ToValue( tfd );
                } )
            )
        );
    }
}








|
>
|
|
|
|
|
|
>
|
|
|
|
|
>
|
|
|
|
|
|
>
|
|
|
|
|
|
<
<

<
>
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
namespace goose::builtins
{
    void SetupGhostOp( Env& e )
    {
        // TODO error out if function isn't a regular function or if its already ghost
        BuildParseRule( e, "ghost"_sid,
            PrefixOp( "operator_ghost"_sid, precedence::FuncQualifier,
                ForType< TypePatternParam< FuncType, FuncPattern > >(
                    []< typename O >( auto&& c, O&& operand ) -> Value
                    {
                        auto ft = *FromValue< FuncType >( operand );
                        ft.setKind( FuncType::Kind::Ghost );
                        return ToValue( ft );
                    } ),
                ForType< TypePatternParam< TFuncType, TFuncPattern > >(
                    []< typename O >( auto&& c, O&& operand ) -> Value
                    {
                        auto tft = *FromValue< TFuncType >( operand );
                        tft.setKind( FuncType::Kind::Ghost );
                        return ToValue( tft );
                    } ),
                ForType< FuncDecl >(
                    []< typename O >( auto&& c, O&& operand ) -> Value
                    {
                        auto fd = *FromValue< FuncDecl >( operand );
                        fd.func().type().setKind( FuncType::Kind::Ghost );
                        return ToValue( fd );
                    } ),
                ForType< TFuncDecl >(
                    []< typename O >( auto&& c, O&& operand ) -> Value
                    {
                        auto tfd = *FromValue< TFuncDecl >( operand );
                        tfd.tFunc().type().setKind( FuncType::Kind::Ghost );
                        return ToValue( tfd );
                    } ) ) );


    }

} // namespace goose::builtins
Changes to bs/builtins/operators/helpers.h.
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
    template< typename... R >
    void BuildParseRule( sema::Env& env, StringId name, R&&... ruleBuilders )
    {
        parse::BuildParseRule( env, name, AppendToVectorTerm( RootG0Identity(), TERM( name ) ),
            forward< R >( ruleBuilders )... );
    }

    struct UnaryOpTag {};



    struct BinaryOpTag {};


    template< typename... R >
    auto PrefixOp( StringId funcName, uint32_t precedence, R&&... rules )
    {
        return [&,precedence,funcName]( auto&& e, auto&& r, auto&& name )
        {
            MakePrefixOp( e, r, name, precedence,
                BuildOpFunc< UnaryOpTag >( e, funcName, forward< R >( rules )... ) );
        };
    }

    template< typename... R >
    auto PostfixOp( StringId funcName, uint32_t precedence, R&&... rules )
    {
        return [&,precedence,funcName]( auto&& e, auto&& r, auto&& name )
        {
            MakePostfixOp( e, r, name, precedence,
                BuildOpFunc< UnaryOpTag >( e, funcName, forward< R >( rules )... ) );
        };
    }

    template< typename... R >
    auto LeftAssInfixOp( StringId funcName, uint32_t precedence, R&&... rules )
    {
        return [&,precedence,funcName]( auto&& e, auto&& r, auto&& name )
        {
            MakeLeftAssInfixOp( e, r, name, precedence,
                BuildOpFunc< BinaryOpTag >( e, funcName, forward< R >( rules )... ) );
        };
    }

    template< typename... R >
    auto RightAssInfixOp( StringId funcName, uint32_t precedence, R&&... rules )
    {
        return [&,precedence,funcName]( auto&& e, auto&& r, auto&& name )
        {
            MakeRightAssInfixOp( e, r, name, precedence,
                BuildOpFunc< BinaryOpTag >( e, funcName, forward< R >( rules )... ) );
        };
    }

    template< typename tag, typename... R >
    auto BuildOpFunc( Env& e, StringId funcName, R&&... ruleBuilders )
    {
        auto pOvlSet = GetOrCreateOverloadSet( e, funcName );
        ( ( ruleBuilders( e, pOvlSet, tag() ) ), ... );

        return [pOvlSet, funcName]< typename... O >( Parser& p, O&&... operands )
        {
            DiagnosticsContext dc( 0, format( "When invoking {}.", funcName.str() ) );
            return InvokeOverloadSet( p.context(), pOvlSet, MakeClosedTuple( forward< O >( operands )... ),
                p.context().locationId() );
        };
    }

    template< typename T, typename I, typename RT = T >
    auto ForType( CURRENT_LOC )
    {
        return [sloc]< typename tag >( auto&& e, auto&& pOvlSet, tag t )
        {
            using intrinsicType = Intrinsic< Value ( T, T ) >;
            auto intrinsicFunc = []( const Context& c, const Value& lhs, const Value& rhs )
            {
                return BuildComputedValue( GetValueType< RT >(), lhs, rhs, I( ( c.locationId() ) ) );
            };

            auto loc = Location::Create( sloc );
            pOvlSet->add( e, ToValue< intrinsicType >( move( intrinsicFunc ) ).setLocationId( loc ),
                GetBuiltinIntrinsicFuncInvocationRule() );
        };
    }

    template< typename T, typename F >
    auto ForType( F&& func, CURRENT_LOC )
    {
        return [&,sloc]< typename tag >( auto&& e, auto&& pOvlSet, tag t )
        {
            auto loc = Location::Create( sloc );
            if constexpr( is_same_v< tag, UnaryOpTag > )
            {
                using intrinsicType = Intrinsic< Value ( T ) >;

                pOvlSet->add( e, ToValue< intrinsicType >( forward< F >( func ) ).setLocationId( loc ),
                    GetBuiltinIntrinsicFuncInvocationRule() );
            }
            else
            {
                using intrinsicType = Intrinsic< Value ( T, T ) >;

                pOvlSet->add( e, ToValue< intrinsicType >( forward< F >( func ) ).setLocationId( loc ),
                    GetBuiltinIntrinsicFuncInvocationRule() );
            }
        };
    }

    template< typename T1, typename T2, typename F >
    auto ForTypes( F&& func, CURRENT_LOC )
    {
        return [=]< typename tag >( auto&& e, auto&& pOvlSet, tag t )
        {
            using intrinsicType = Intrinsic< Value ( T1, T2 ) >;
            auto loc = Location::Create( sloc );
            pOvlSet->add( e, ToValue< intrinsicType >( func ).setLocationId( loc ),
                GetBuiltinIntrinsicFuncInvocationRule() );
        };
    }
}

#endif







|
>
>
>
|
|
>
|
|

|






<
|

|









|









|















|
|



|
<



|

<
|
<







|
<

|




|
>
|




|
>
|





|
<



|





|


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
    template< typename... R >
    void BuildParseRule( sema::Env& env, StringId name, R&&... ruleBuilders )
    {
        parse::BuildParseRule( env, name, AppendToVectorTerm( RootG0Identity(), TERM( name ) ),
            forward< R >( ruleBuilders )... );
    }

    struct UnaryOpTag
    {
    };

    struct BinaryOpTag
    {
    };

    template< typename... R > auto PrefixOp( StringId funcName, uint32_t precedence, R&&... rules )
    {
        return [&, precedence, funcName]( auto&& e, auto&& r, auto&& name )
        {
            MakePrefixOp( e, r, name, precedence,
                BuildOpFunc< UnaryOpTag >( e, funcName, forward< R >( rules )... ) );
        };
    }


    template< typename... R > auto PostfixOp( StringId funcName, uint32_t precedence, R&&... rules )
    {
        return [&, precedence, funcName]( auto&& e, auto&& r, auto&& name )
        {
            MakePostfixOp( e, r, name, precedence,
                BuildOpFunc< UnaryOpTag >( e, funcName, forward< R >( rules )... ) );
        };
    }

    template< typename... R >
    auto LeftAssInfixOp( StringId funcName, uint32_t precedence, R&&... rules )
    {
        return [&, precedence, funcName]( auto&& e, auto&& r, auto&& name )
        {
            MakeLeftAssInfixOp( e, r, name, precedence,
                BuildOpFunc< BinaryOpTag >( e, funcName, forward< R >( rules )... ) );
        };
    }

    template< typename... R >
    auto RightAssInfixOp( StringId funcName, uint32_t precedence, R&&... rules )
    {
        return [&, precedence, funcName]( auto&& e, auto&& r, auto&& name )
        {
            MakeRightAssInfixOp( e, r, name, precedence,
                BuildOpFunc< BinaryOpTag >( e, funcName, forward< R >( rules )... ) );
        };
    }

    template< typename tag, typename... R >
    auto BuildOpFunc( Env& e, StringId funcName, R&&... ruleBuilders )
    {
        auto pOvlSet = GetOrCreateOverloadSet( e, funcName );
        ( ( ruleBuilders( e, pOvlSet, tag() ) ), ... );

        return [pOvlSet, funcName]< typename... O >( Parser& p, O&&... operands )
        {
            DiagnosticsContext dc( 0, format( "When invoking {}.", funcName.str() ) );
            return InvokeOverloadSet( p.context(), pOvlSet,
                MakeClosedTuple( forward< O >( operands )... ), p.context().locationId() );
        };
    }

    template< typename T, typename I, typename RT = T > auto ForType( CURRENT_LOC )

    {
        return [sloc]< typename tag >( auto&& e, auto&& pOvlSet, tag t )
        {
            using intrinsicType = Intrinsic< Value( T, T ) >;
            auto intrinsicFunc = []( const Context& c, const Value& lhs, const Value& rhs )

            { return BuildComputedValue( GetValueType< RT >(), lhs, rhs, I( c.locationId() ) ); };


            auto loc = Location::Create( sloc );
            pOvlSet->add( e, ToValue< intrinsicType >( move( intrinsicFunc ) ).setLocationId( loc ),
                GetBuiltinIntrinsicFuncInvocationRule() );
        };
    }

    template< typename T, typename F > auto ForType( F&& func, CURRENT_LOC )

    {
        return [&, sloc]< typename tag >( auto&& e, auto&& pOvlSet, tag t )
        {
            auto loc = Location::Create( sloc );
            if constexpr( is_same_v< tag, UnaryOpTag > )
            {
                using intrinsicType = Intrinsic< Value( T ) >;
                pOvlSet->add( e,
                    ToValue< intrinsicType >( forward< F >( func ) ).setLocationId( loc ),
                    GetBuiltinIntrinsicFuncInvocationRule() );
            }
            else
            {
                using intrinsicType = Intrinsic< Value( T, T ) >;
                pOvlSet->add( e,
                    ToValue< intrinsicType >( forward< F >( func ) ).setLocationId( loc ),
                    GetBuiltinIntrinsicFuncInvocationRule() );
            }
        };
    }

    template< typename T1, typename T2, typename F > auto ForTypes( F&& func, CURRENT_LOC )

    {
        return [=]< typename tag >( auto&& e, auto&& pOvlSet, tag t )
        {
            using intrinsicType = Intrinsic< Value( T1, T2 ) >;
            auto loc = Location::Create( sloc );
            pOvlSet->add( e, ToValue< intrinsicType >( func ).setLocationId( loc ),
                GetBuiltinIntrinsicFuncInvocationRule() );
        };
    }
} // namespace goose::builtins

#endif
Changes to bs/builtins/operators/inline.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

namespace goose::builtins
{
    void SetupInlineOp( Env& e )
    {
        // TODO error out if function is not regular or already set as inline
        BuildParseRule( e, "inline"_sid,
            PrefixOp( "operator_inline"_sid, precedence::FuncQualifier,
                ForType< TypePatternParam< FuncType, FuncPattern > >( []< typename O >( auto&& c, O&& operand ) -> Value

                {
                    auto ft = *FromValue< FuncType >( operand );
                    ft.setKind( FuncType::Kind::Inline );
                    return ToValue( ft );
                } ),
                ForType< TypePatternParam< TFuncType, TFuncPattern > >( []< typename O >( auto&& c, O&& operand ) -> Value

                {
                    auto tft = *FromValue< TFuncType >( operand );
                    tft.setKind( FuncType::Kind::Inline );
                    return ToValue( tft );
                } ),

                ForType< FuncDecl >( []< typename O >( auto&& c, O&& operand ) -> Value
                {
                    auto fd = *FromValue< FuncDecl >( operand );
                    fd.func().type().setKind( FuncType::Kind::Inline );
                    return ToValue( fd );
                } ),

                ForType< TFuncDecl >( []< typename O >( auto&& c, O&& operand ) -> Value
                {
                    auto tfd = *FromValue< TFuncDecl >( operand );
                    tfd.tFunc().type().setKind( FuncType::Kind::Inline );
                    return ToValue( tfd );
                } )
            )
        );
    }
}








|
>
|
|
|
|
|
|
>
|
|
|
|
|
>
|
|
|
|
|
|
>
|
|
|
|
|
|
<
<

<
>
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
namespace goose::builtins
{
    void SetupInlineOp( Env& e )
    {
        // TODO error out if function is not regular or already set as inline
        BuildParseRule( e, "inline"_sid,
            PrefixOp( "operator_inline"_sid, precedence::FuncQualifier,
                ForType< TypePatternParam< FuncType, FuncPattern > >(
                    []< typename O >( auto&& c, O&& operand ) -> Value
                    {
                        auto ft = *FromValue< FuncType >( operand );
                        ft.setKind( FuncType::Kind::Inline );
                        return ToValue( ft );
                    } ),
                ForType< TypePatternParam< TFuncType, TFuncPattern > >(
                    []< typename O >( auto&& c, O&& operand ) -> Value
                    {
                        auto tft = *FromValue< TFuncType >( operand );
                        tft.setKind( FuncType::Kind::Inline );
                        return ToValue( tft );
                    } ),
                ForType< FuncDecl >(
                    []< typename O >( auto&& c, O&& operand ) -> Value
                    {
                        auto fd = *FromValue< FuncDecl >( operand );
                        fd.func().type().setKind( FuncType::Kind::Inline );
                        return ToValue( fd );
                    } ),
                ForType< TFuncDecl >(
                    []< typename O >( auto&& c, O&& operand ) -> Value
                    {
                        auto tfd = *FromValue< TFuncDecl >( operand );
                        tfd.tFunc().type().setKind( FuncType::Kind::Inline );
                        return ToValue( tfd );
                    } ) ) );


    }

} // namespace goose::builtins
Changes to bs/builtins/operators/intrinsic.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

namespace goose::builtins
{
    void SetupIntrinsicOp( Env& e )
    {
        // TODO error out if function is not regular or already set as intrinsic
        BuildParseRule( e, "intrinsic"_sid,
            PrefixOp( "operator_intrinsic"_sid, precedence::FuncQualifier,
                ForType< TypePatternParam< FuncType, FuncPattern > >( []< typename O >( auto&& c, O&& operand ) -> Value

                {
                    auto ft = *FromValue< FuncType >( operand );
                    ft.setKind( FuncType::Kind::Intrinsic );
                    return ToValue( ft );
                } ),
                ForType< TypePatternParam< TFuncType, TFuncPattern > >( []< typename O >( auto&& c, O&& operand ) -> Value

                {
                    auto tft = *FromValue< TFuncType >( operand );
                    tft.setKind( FuncType::Kind::Intrinsic );
                    return ToValue( tft );
                } ),

                ForType< FuncDecl >( []< typename O >( auto&& c, O&& operand ) -> Value
                {
                    auto fd = *FromValue< FuncDecl >( operand );
                    fd.func().type().setKind( FuncType::Kind::Intrinsic );
                    return ToValue( fd );
                } ),

                ForType< TFuncDecl >( []< typename O >( auto&& c, O&& operand ) -> Value
                {
                    auto tfd = *FromValue< TFuncDecl >( operand );
                    tfd.tFunc().type().setKind( FuncType::Kind::Intrinsic );
                    return ToValue( tfd );
                } )
            )
        );
    }
}








|
>
|
|
|
|
|
|
>
|
|
|
|
|
>
|
|
|
|
|
|
>
|
|
|
|
|
|
<
<

<
>
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
namespace goose::builtins
{
    void SetupIntrinsicOp( Env& e )
    {
        // TODO error out if function is not regular or already set as intrinsic
        BuildParseRule( e, "intrinsic"_sid,
            PrefixOp( "operator_intrinsic"_sid, precedence::FuncQualifier,
                ForType< TypePatternParam< FuncType, FuncPattern > >(
                    []< typename O >( auto&& c, O&& operand ) -> Value
                    {
                        auto ft = *FromValue< FuncType >( operand );
                        ft.setKind( FuncType::Kind::Intrinsic );
                        return ToValue( ft );
                    } ),
                ForType< TypePatternParam< TFuncType, TFuncPattern > >(
                    []< typename O >( auto&& c, O&& operand ) -> Value
                    {
                        auto tft = *FromValue< TFuncType >( operand );
                        tft.setKind( FuncType::Kind::Intrinsic );
                        return ToValue( tft );
                    } ),
                ForType< FuncDecl >(
                    []< typename O >( auto&& c, O&& operand ) -> Value
                    {
                        auto fd = *FromValue< FuncDecl >( operand );
                        fd.func().type().setKind( FuncType::Kind::Intrinsic );
                        return ToValue( fd );
                    } ),
                ForType< TFuncDecl >(
                    []< typename O >( auto&& c, O&& operand ) -> Value
                    {
                        auto tfd = *FromValue< TFuncDecl >( operand );
                        tfd.tFunc().type().setKind( FuncType::Kind::Intrinsic );
                        return ToValue( tfd );
                    } ) ) );


    }

} // namespace goose::builtins
Changes to bs/builtins/operators/logic.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
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
{
    void SetupLogicOps( Env& e )
    {
        auto orOp = GetOrCreateOverloadSet( e, "operator_or"_sid );
        auto andOp = GetOrCreateOverloadSet( e, "operator_and"_sid );

        BuildParseRule( e, "!"_sid,
            PrefixOp( "operator_logical_not"_sid, precedence::UnaryOps,
                BuildGenericTupleOperator(),


                ForType< bool >( []( auto&& c, auto&& operand ) -> Value
                {
                    return BuildComputedValue( GetValueType< bool >(),
                        operand, cir::Not( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, "~"_sid,
            PrefixOp( "operator_bitwise_not"_sid, precedence::UnaryOps,
                BuildGenericTupleOperator(),

                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >( []( auto&& c, auto&& operand ) -> Value

                {
                    auto opTypeVal = *EIRToValue( operand.type() );
                    auto opType = *FromValue< IntegerType >( opTypeVal );
                    return BuildComputedValue( operand.type(),
                        operand, Value( operand.type(), APSInt::getMaxValue( opType.m_numBits, true ) ),
                        Xor( c.locationId() )
                    );
                } )
            )
        );

        BuildParseRule( e, "^"_sid,
            LeftAssInfixOp( "operator_xor"_sid, precedence::OrOp,
                BuildGenericTupleOperator(),

                // Logical xor
                ForType< bool, Xor >(),

                // ct_int xor
                ForType< BigInt >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    if( !lhs.isConstant() || !rhs.isConstant() )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( c.locationId(), "bitwise operations between ct_int values are only allowed on constants." );



                        return PoisonValue();
                    }

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, Xor( c.locationId() ) );
                } ),

                // runtime integer xor, defined to work for any two integers of same
                // bit size and signedness.
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, Xor( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, "|"_sid,
            LeftAssInfixOp( "operator_or"_sid, precedence::OrOp,
                BuildGenericTupleOperator(),

                // ct_int or
                ForType< BigInt >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    if( !lhs.isConstant() || !rhs.isConstant() )
                    {
                        auto loc = Location::CreateSpanningLocation( lhs.locationId(), rhs.locationId() );

                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( loc, "bitwise operations between ct_int values are only allowed on constants." );


                        return PoisonValue();
                    }

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, Or( c.locationId() ) );
                } ),

                // runtime integer or, defined to work for any two integers of same
                // bit size and signedness.
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, Or( c.locationId() ) );
                } ),

                // bool or

                ForType< bool >( [orOp]< typename L, typename R >( auto&& c, L&& lhs, R&& rhs ) -> Value
                {
                    // Handle the case where lhs is constant, so that
                    // the result gets properly eagerly evaluated (in case
                    // we are using the expression as a compile time constant)
                    if( lhs.isConstant() )
                    {
                        if( *FromValue< bool >( lhs ) )
                            return forward< L >( lhs );

                        return forward< R >( rhs );
                    }

                    // This operator have different behaviors depending on the context:
                    // in normal code, we want to generate shortcut evaluation. But in propositions
                    // and ghost code, we want to simply generate a Or instruction with both operands
                    // always evaluated. So we delegate the work to another overload of operator_or that
                    // takes the builder as its first param and is overloaded according to it.

                    return InvokeOverloadSet( c,

                        orOp, MakeClosedTuple( c.builder(), forward< L >( lhs ), forward< R >( rhs ) ) );
                } )
            )
        );

        RegisterBuiltinFunc< Intrinsic< bool ( Value, bool, bool ) > >( e, orOp,
            []( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
            {
                return BuildComputedValue( GetValueType< bool >(), lhs, rhs, Or( c.locationId() ) );
            } );

        // TODO_SSA: revise this
        /*RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< CodeBuilder > >, bool, bool ) > >( e, orOp,

            []( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
            {
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( b );
                const auto& cfg = cb->cfg();

                // Build the control flow for shortcut evaluation.
                const auto& predBB = cfg->currentBB();







|
<

>
|
<
|
|
|
|
<
<

|
<

|
>
|
|
|
|
|
|
<
|
|
<
<

|
<






|
|
|
|
|
>
>
>
|
|

|
<
|




|
<
|
<
|
|
<
<

|
<



|
|
|
|
|
>
|
>
>
|
|

|
<
|




|
<
|
<
<


>
|
|
|
|
|
|
|
|
|

|
|

|
|
|
|
|
>
|
>
|
|
|
<
<
|
|
<




|
>







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
{
    void SetupLogicOps( Env& e )
    {
        auto orOp = GetOrCreateOverloadSet( e, "operator_or"_sid );
        auto andOp = GetOrCreateOverloadSet( e, "operator_and"_sid );

        BuildParseRule( e, "!"_sid,
            PrefixOp( "operator_logical_not"_sid, precedence::UnaryOps, BuildGenericTupleOperator(),


                ForType< bool >(
                    []( auto&& c, auto&& operand ) -> Value {

                        return BuildComputedValue(
                            GetValueType< bool >(), operand, cir::Not( c.locationId() ) );
                    } ) ) );



        BuildParseRule( e, "~"_sid,
            PrefixOp( "operator_bitwise_not"_sid, precedence::UnaryOps, BuildGenericTupleOperator(),


                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                    []( auto&& c, auto&& operand ) -> Value
                    {
                        auto opTypeVal = *EIRToValue( operand.type() );
                        auto opType = *FromValue< IntegerType >( opTypeVal );
                        return BuildComputedValue( operand.type(), operand,
                            Value( operand.type(), APSInt::getMaxValue( opType.m_numBits, true ) ),
                            Xor( c.locationId() ) );

                    } ) ) );



        BuildParseRule( e, "^"_sid,
            LeftAssInfixOp( "operator_xor"_sid, precedence::OrOp, BuildGenericTupleOperator(),


                // Logical xor
                ForType< bool, Xor >(),

                // ct_int xor
                ForType< BigInt >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                    {
                        if( !lhs.isConstant() || !rhs.isConstant() )
                        {
                            DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                                c.locationId(),
                                "bitwise operations between ct_int values are only allowed on "
                                "constants." );
                            return PoisonValue();
                        }

                        return BuildComputedValue( lhs.type(), lhs, rhs, Xor( c.locationId() ) );

                    } ),

                // runtime integer xor, defined to work for any two integers of same
                // bit size and signedness.
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value {

                        return BuildComputedValue( lhs.type(), lhs, rhs, Xor( c.locationId() ) );

                    } ) ) );



        BuildParseRule( e, "|"_sid,
            LeftAssInfixOp( "operator_or"_sid, precedence::OrOp, BuildGenericTupleOperator(),


                // ct_int or
                ForType< BigInt >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                    {
                        if( !lhs.isConstant() || !rhs.isConstant() )
                        {
                            auto loc = Location::CreateSpanningLocation(
                                lhs.locationId(), rhs.locationId() );
                            DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( loc,
                                "bitwise operations between ct_int values are only allowed on "
                                "constants." );
                            return PoisonValue();
                        }

                        return BuildComputedValue( lhs.type(), lhs, rhs, Or( c.locationId() ) );

                    } ),

                // runtime integer or, defined to work for any two integers of same
                // bit size and signedness.
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value

                    { return BuildComputedValue( lhs.type(), lhs, rhs, Or( c.locationId() ) ); } ),



                // bool or
                ForType< bool >(
                    [orOp]< typename L, typename R >( auto&& c, L&& lhs, R&& rhs ) -> Value
                    {
                        // Handle the case where lhs is constant, so that the result gets properly
                        // eagerly evaluated (in case we are using the expression as a compile time
                        // constant)
                        if( lhs.isConstant() )
                        {
                            if( *FromValue< bool >( lhs ) )
                                return forward< L >( lhs );

                            return forward< R >( rhs );
                        }

                        // This operator have different behaviors depending on the context: in
                        // normal code, we want to generate shortcut evaluation. But in propositions
                        // and ghost code, we want to simply generate a Or instruction with both
                        // operands always evaluated. So we delegate the work to another overload of
                        // operator_or that takes the builder as its first param and is overloaded
                        // according to it.
                        return InvokeOverloadSet( c, orOp,
                            MakeClosedTuple(
                                c.builder(), forward< L >( lhs ), forward< R >( rhs ) ) );
                    } ) ) );



        RegisterBuiltinFunc< Intrinsic< bool( Value, bool, bool ) > >( e, orOp,
            []( auto&& c, auto&& b, auto&& lhs, auto&& rhs ) {

                return BuildComputedValue( GetValueType< bool >(), lhs, rhs, Or( c.locationId() ) );
            } );

        // TODO_SSA: revise this
        /*RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< CodeBuilder > >, bool, bool ) >
           >( e, orOp,
            []( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
            {
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( b );
                const auto& cfg = cb->cfg();

                // Build the control flow for shortcut evaluation.
                const auto& predBB = cfg->currentBB();
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
282
283
284
285
286
287
288
289
290
291

292


293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319

320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342



343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370

371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400

401
402
403
404
405
406
407
408
409
410
411
412
413


                // Build the result val which pulls the temporary created above.
                return BuildComputedValue( GetValueType< bool >(),
                    GetTemporary( GetValueType< bool >(), resultIndex, c.locationId() ) );
            } );*/

        BuildParseRule( e, "&"_sid,
            LeftAssInfixOp( "operator_and"_sid, precedence::AndOp,
                BuildGenericTupleOperator(),

                // ct_int and
                ForType< BigInt >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    if( !lhs.isConstant() || !rhs.isConstant() )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( c.locationId(), "bitwise operations between ct_int values are only allowed on constants." );



                        return PoisonValue();
                    }

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, And( c.locationId() ) );
                } ),

                // runtime integer and, defined to work for any two integers of same
                // bit size and signedness.
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, And( c.locationId() ) );
                } ),

                // bool and

                ForType< bool >( [andOp]< typename L, typename R >( auto&& c, L&& lhs, R&& rhs ) -> Value
                {
                    // Handle the case where lhs is constant, so that
                    // the result gets properly eagerly evaluated (in case
                    // we are using the expression as a compile time constant)
                    if( lhs.isConstant() )
                    {
                        if( *FromValue< bool >( lhs ) )
                            return forward< R >( rhs );

                        return forward< L >( lhs );
                    }

                    // This operator have different behaviors depending on the context:
                    // in normal code, we want to generate shortcut evaluation. But in propositions
                    // and ghost code, we want to simply generate a And instruction with both operands
                    // always evaluated. So we delegate the work to another overload of operator_and that
                    // takes the builder as its first param and is overloaded according to it.

                    return InvokeOverloadSet( c,

                        andOp, MakeClosedTuple( c.builder(), forward< L >( lhs ), forward< R >( rhs ) ) );
                } )
            )
        );

        RegisterBuiltinFunc< Intrinsic< bool ( Value, bool, bool ) > >( e, andOp,
            []( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
            {

                return BuildComputedValue( GetValueType< bool >(), lhs, rhs, And( c.locationId() ) );
            } );

        // TODO_SSA revise this
/*        RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< CodeBuilder > >, bool, bool ) > >( e, andOp,

            []( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
            {
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( b );
                const auto& cfg = cb->cfg();

                // Build the control flow for shortcut evaluation.
                const auto& predBB = cfg->currentBB();

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

                // If the lhs is false, skip to the end directly.
                // Otherwise, jump to the BB that computes rhs.
                predBB->append( lhs );
                predBB->setTerminator( CondBranch( pRhsBB, pSuccBB ) );

                auto rhsIndex = util::GenerateNewUID();
                pRhsBB->append( rhs, CreateTemporary( rhsIndex, false, c.locationId() ) );
                pRhsBB->setTerminator( Branch( pSuccBB ) );

                auto resultIndex = util::GenerateNewUID();

                // Build the Phi instruction that will collect the final result.
                auto phi = Phi( GetValueType< bool >(), 2,
                    resultIndex, c.locationId() );

                // If coming directly from the lhs BB, we know the result is false.
                phi.setIncoming( predBB, ToValue( false ) );

                // Otherwise, the result is whatever was computed by the rhs block.
                phi.setIncoming( pRhsBB, BuildComputedValue( GetValueType< bool >(),
                    GetTemporary( GetValueType< bool >(), rhsIndex, c.locationId() ) ) );

                pSuccBB->append( move( phi ) );
                cfg->setCurrentBB( pSuccBB );

                // Build the result val which pulls the temporary created above.
                return BuildComputedValue( GetValueType< bool >(),
                    GetTemporary( GetValueType< bool >(), resultIndex, c.locationId() ) );
            } );*/

        BuildParseRule( e, "<<"_sid,
            LeftAssInfixOp( "operator_shift_left"_sid, precedence::BitShiftOp,
                BuildGenericTupleOperator(),

                // ct_int left shift
                ForTypes< BigInt, uint32_t >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    if( !lhs.isConstant() || !rhs.isConstant() )
                    {
                        auto loc = Location::CreateSpanningLocation( lhs.locationId(), rhs.locationId() );

                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( loc, "bitwise operations between ct_int values are only allowed on constants." );


                        return PoisonValue();
                    }

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, Shl( c.locationId() ) );
                } ),

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

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

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

                    auto cond = ULT( rhs, bitSizeValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(

                        cond.locationId(), "assert"_sid, "the shift amount may be equal or greater than the bitsize." );

                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, Shl( c.locationId() ) );
                } )
            )
        );

        BuildParseRule( e, ">>"_sid,
            LeftAssInfixOp( "operator_shift_right"_sid, precedence::BitShiftOp,
                BuildGenericTupleOperator(),

                // ct_int right shift
                ForTypes< BigInt, uint32_t >(
                []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                {
                    if( !lhs.isConstant() || !rhs.isConstant() )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( c.locationId(), "bitwise operations between ct_int values are only allowed on constants." );



                        return PoisonValue();
                    }

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, AShr( c.locationId() ) );
                } ),

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

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

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

                    auto cond = ULT( rhs, bitSizeValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(

                        cond.locationId(), "assert"_sid, "the shift amount may be equal or greater than the bitsize." );

                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, AShr( c.locationId() ) );
                } ),

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

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

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

                    auto cond = ULT( rhs, bitSizeValue );

                    DiagnosticsManager::GetInstance().defineCustomDiagnostic(

                        cond.locationId(), "assert"_sid, "the shift amount may be equal or greater than the bitsize." );

                    cfg->currentBB()->append(
                        move( cond ), cir::Assert( rhs.locationId() )
                    );

                    return BuildComputedValue( lhs.type(),
                        lhs, rhs, LShr( c.locationId() ) );
                } )
            )
        );
    }
}








|
<



|
|
|
|
|
>
>
>
|
|

|
<
|




|
<
|
<
<


>
|
|
|
|
|
|
|
|
|

|
|

|
|
|
|
|
>
|
>
|
|
|
<
<
|
|
<
>
|



|
>
|
|
|
|

|
|

|
|

|
|
|
|

|
|
|

|

|
|
|

|
|

|
|
|

|
|

|
|
|
|







|
|
|
|
|
>
|
>
>
|
|

|
<
|




|
|
|

|
|

|
|
|
|
|
|

|

|
>
|

<
|
<

|
<
|
<
<







|
|
|
|
|
>
>
>
|
|

|
<
|





|
|
|

|
|

|
|
|
|
|
|

|

|
>
|

<
|
<

|
<
|

|
|

|
|
|

|
|

|
|
|
|
|
|

|

|
>
|

<
|
<

|
<
|
<
<

<
>
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
282
283
284
285
286
287
288
289

290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314

315

316
317

318


319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337

338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363

364

365
366

367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391

392

393
394

395


396

397

                // Build the result val which pulls the temporary created above.
                return BuildComputedValue( GetValueType< bool >(),
                    GetTemporary( GetValueType< bool >(), resultIndex, c.locationId() ) );
            } );*/

        BuildParseRule( e, "&"_sid,
            LeftAssInfixOp( "operator_and"_sid, precedence::AndOp, BuildGenericTupleOperator(),


                // ct_int and
                ForType< BigInt >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                    {
                        if( !lhs.isConstant() || !rhs.isConstant() )
                        {
                            DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                                c.locationId(),
                                "bitwise operations between ct_int values are only allowed on "
                                "constants." );
                            return PoisonValue();
                        }

                        return BuildComputedValue( lhs.type(), lhs, rhs, And( c.locationId() ) );

                    } ),

                // runtime integer and, defined to work for any two integers of same
                // bit size and signedness.
                ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value

                    { return BuildComputedValue( lhs.type(), lhs, rhs, And( c.locationId() ) ); } ),



                // bool and
                ForType< bool >(
                    [andOp]< typename L, typename R >( auto&& c, L&& lhs, R&& rhs ) -> Value
                    {
                        // Handle the case where lhs is constant, so that
                        // the result gets properly eagerly evaluated (in case
                        // we are using the expression as a compile time constant)
                        if( lhs.isConstant() )
                        {
                            if( *FromValue< bool >( lhs ) )
                                return forward< R >( rhs );

                            return forward< L >( lhs );
                        }

                        // This operator have different behaviors depending on the context: in
                        // normal code, we want to generate shortcut evaluation. But in propositions
                        // and ghost code, we want to simply generate a And instruction with both
                        // operands always evaluated. So we delegate the work to another overload of
                        // operator_and that takes the builder as its first param and is overloaded
                        // according to it.
                        return InvokeOverloadSet( c, andOp,
                            MakeClosedTuple(
                                c.builder(), forward< L >( lhs ), forward< R >( rhs ) ) );
                    } ) ) );



        RegisterBuiltinFunc< Intrinsic< bool( Value, bool, bool ) > >( e, andOp,
            []( auto&& c, auto&& b, auto&& lhs, auto&& rhs ) {

                return BuildComputedValue(
                    GetValueType< bool >(), lhs, rhs, And( c.locationId() ) );
            } );

        // TODO_SSA revise this
        /*        RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< CodeBuilder > >, bool,
           bool ) > >( e, andOp,
                    []( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
                    {
                        auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( b );
                        const auto& cfg = cb->cfg();

                        // Build the control flow for shortcut evaluation.
                        const auto& predBB = cfg->currentBB();

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

                        // If the lhs is false, skip to the end directly.
                        // Otherwise, jump to the BB that computes rhs.
                        predBB->append( lhs );
                        predBB->setTerminator( CondBranch( pRhsBB, pSuccBB ) );

                        auto rhsIndex = util::GenerateNewUID();
                        pRhsBB->append( rhs, CreateTemporary( rhsIndex, false, c.locationId() ) );
                        pRhsBB->setTerminator( Branch( pSuccBB ) );

                        auto resultIndex = util::GenerateNewUID();

                        // Build the Phi instruction that will collect the final result.
                        auto phi = Phi( GetValueType< bool >(), 2,
                            resultIndex, c.locationId() );

                        // If coming directly from the lhs BB, we know the result is false.
                        phi.setIncoming( predBB, ToValue( false ) );

                        // Otherwise, the result is whatever was computed by the rhs block.
                        phi.setIncoming( pRhsBB, BuildComputedValue( GetValueType< bool >(),
                            GetTemporary( GetValueType< bool >(), rhsIndex, c.locationId() ) ) );

                        pSuccBB->append( move( phi ) );
                        cfg->setCurrentBB( pSuccBB );

                        // Build the result val which pulls the temporary created above.
                        return BuildComputedValue( GetValueType< bool >(),
                            GetTemporary( GetValueType< bool >(), resultIndex, c.locationId() ) );
                    } );*/

        BuildParseRule( e, "<<"_sid,
            LeftAssInfixOp( "operator_shift_left"_sid, precedence::BitShiftOp,
                BuildGenericTupleOperator(),

                // ct_int left shift
                ForTypes< BigInt, uint32_t >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                    {
                        if( !lhs.isConstant() || !rhs.isConstant() )
                        {
                            auto loc = Location::CreateSpanningLocation(
                                lhs.locationId(), rhs.locationId() );
                            DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( loc,
                                "bitwise operations between ct_int values are only allowed on "
                                "constants." );
                            return PoisonValue();
                        }

                        return BuildComputedValue( lhs.type(), lhs, rhs, Shl( c.locationId() ) );

                    } ),

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

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

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

                        auto cond = ULT( rhs, bitSizeValue );

                        DiagnosticsManager::GetInstance().defineCustomDiagnostic( cond.locationId(),
                            "assert"_sid,
                            "the shift amount may be equal or greater than the bitsize." );


                        cfg->currentBB()->append( move( cond ), cir::Assert( rhs.locationId() ) );


                        return BuildComputedValue( lhs.type(), lhs, rhs, Shl( c.locationId() ) );

                    } ) ) );



        BuildParseRule( e, ">>"_sid,
            LeftAssInfixOp( "operator_shift_right"_sid, precedence::BitShiftOp,
                BuildGenericTupleOperator(),

                // ct_int right shift
                ForTypes< BigInt, uint32_t >(
                    []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
                    {
                        if( !lhs.isConstant() || !rhs.isConstant() )
                        {
                            DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                                c.locationId(),
                                "bitwise operations between ct_int values are only allowed on "
                                "constants." );
                            return PoisonValue();
                        }

                        return BuildComputedValue( lhs.type(), lhs, rhs, AShr( c.locationId() ) );

                    } ),

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

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

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

                        auto cond = ULT( rhs, bitSizeValue );

                        DiagnosticsManager::GetInstance().defineCustomDiagnostic( cond.locationId(),
                            "assert"_sid,
                            "the shift amount may be equal or greater than the bitsize." );


                        cfg->currentBB()->append( move( cond ), cir::Assert( rhs.locationId() ) );


                        return BuildComputedValue( lhs.type(), lhs, rhs, AShr( c.locationId() ) );

                    } ),

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

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

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

                        auto cond = ULT( rhs, bitSizeValue );

                        DiagnosticsManager::GetInstance().defineCustomDiagnostic( cond.locationId(),
                            "assert"_sid,
                            "the shift amount may be equal or greater than the bitsize." );


                        cfg->currentBB()->append( move( cond ), cir::Assert( rhs.locationId() ) );


                        return BuildComputedValue( lhs.type(), lhs, rhs, LShr( c.locationId() ) );

                    } ) ) );


    }

} // namespace goose::builtins
Changes to bs/builtins/operators/operators.h.
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
        SetupDollarOp( e );
        SetupApostropheOp( e );
        SetupLogicOps( e );
        SetupArithOps( e );
        SetupComparisonOps( e );
        SetupAssignmentOps( e );

        // Must be last since it needs to retrieve
        // the overload sets of some previously defined
        // operators
        SetupCompoundAssOps( e );

        SetupDotOp( e );

        SetupIntrinsicOp( e );
        SetupInlineOp( e );
        SetupComptimeOp( e );
        SetupGhostOp( e );
        SetupContractOps( e );
        SetupEllipsisOp( e );
    }
}

#endif







|
<












|


27
28
29
30
31
32
33
34

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
        SetupDollarOp( e );
        SetupApostropheOp( e );
        SetupLogicOps( e );
        SetupArithOps( e );
        SetupComparisonOps( e );
        SetupAssignmentOps( e );

        // Must be last since it needs to retrieve the overload sets of some previously defined

        // operators
        SetupCompoundAssOps( e );

        SetupDotOp( e );

        SetupIntrinsicOp( e );
        SetupInlineOp( e );
        SetupComptimeOp( e );
        SetupGhostOp( e );
        SetupContractOps( e );
        SetupEllipsisOp( e );
    }
} // namespace goose::builtins

#endif
Changes to bs/builtins/operators/semicolon.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
#include "builtins/builtins.h"
#include "precedence.h"

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

namespace
{
    bool parseSemicolon( Parser& p, LocationId locationId, uint32_t prec )
    {
        return p.parseExpression( prec );
    };
}


namespace goose::builtins
{
    void SetupSemicolonOp( Env& e )
    {
        // Semicolon is not a real operator: it just consumes itself and do nothing as a prefix operator,
        // and it pushes the result of evaluating its rhs as an infix operator (if there is any).

        Rule r(
            parseSemicolon,
            []( const Parser& p ) { return precedence::StmtSeparator; },
            parseSemicolon );

        RegisterRule( e, ";"_sid, move( r ) );
    }
}












<
|
>





|
|
>

<
|




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

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

namespace
{
    bool parseSemicolon( Parser& p, LocationId locationId, uint32_t prec )
    {
        return p.parseExpression( prec );

    }
} // namespace

namespace goose::builtins
{
    void SetupSemicolonOp( Env& e )
    {
        // Semicolon is not a real operator: it just consumes itself and do nothing as a prefix
        // operator, and it pushes the result of evaluating its rhs as an infix operator (if there
        // is any).
        Rule r(

            parseSemicolon, []( const Parser& p ) { return precedence::StmtSeparator; },
            parseSemicolon );

        RegisterRule( e, ";"_sid, move( r ) );
    }
} // namespace goose::builtins
Changes to bs/builtins/operators/tupass.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
#include "builtins/builtins.h"
#include "precedence.h"
#include "helpers.h"
#include "tuple.h"

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

namespace goose::builtins
{
    Generator< Value > GenerateRHSValuesFromTupleRef( const ptr< OverloadSet >& assOp, const Context& c, const Value& tup )

    {
        auto cfg = GetCFG( c );
        if( !cfg )
            co_yield Value( PoisonValue() );

        auto tupRefType = *FromValue< ReferenceType >( *EIRToValue( tup.type() ) );

        // Store the source tuple as a temporary, otherwise we're going to recompute rhs
        // for each member assigned (which is bad)
        // TODO_SSA rewrite this (since CreateTemporary makes no sense anymore)
        co_return;
    /*    auto index = util::GenerateNewUID();
        cfg->currentBB()->append( tup, CreateTemporary( index, false, tup.locationId() ) );
        auto srcTup = BuildComputedValue( tupRefType.type(), GetTemporary( tupRefType.type(), index, tup.locationId() ) );


        auto anonVarDecl = ToValue( TNamedDecl( ValueToEIR( ToValue( TVar( "_"_sid ) ) ), ""_sid ) );
        co_yield GenerateValuesFromComputedTuple( srcTup );*/
    };


    // Assignment of a compile time tuple or tuple reference to another compile time tuple, directly. Most tuple assignment will go

    // through one of the other overload that takes a mutref to a tuple on the lhs, but sometimes we have compilation time tuples of
    // objects that can directly be assigned such as decls or refs.
    //
    // So this overload is for all these cases, which encompass initializing multiple comma separated decls from a tuple, as
    // well as variable swapping/shuffling. To make sure that the later works properly, we store all the rhs values into

    // temporary variables first, before assigning them to the lhs values.

    Value NonRefTupleAssignment( const ptr< OverloadSet >& assOp, Context& c, const Value& lhs, const Value& rhs )
    {
        auto tupSize = TupleSize( lhs );

        // Load the values from the rhs tuple into temporary vars.
        vector< Value > tempVars;
        tempVars.reserve( tupSize );

        Generator< Value > gen;
        if( rhs.isConstant() )
            gen = GenerateValuesFromConstantTuple( rhs );
        else
            gen = GenerateRHSValuesFromTupleRef( assOp, c, rhs );

        for( auto&& srcVal : gen )
        {
            // We go at very high level to construct the temporary variables by resolving an invocation
            // of the assignment of the rhs value to a TNamedDecl with an empty name.
            // The reason we need to work at such high level is so that the initializer value gets
            // resolved exactly like if it was a regular assignment. In particular, if the value in question
            // is a locvar, we want it to go through the type checking process to be replaced with its content.
            // This is the simplest and most robust way to achieve this, which should honor every relevant
            // extension point.
            auto tmpVar = DeclareLocalVarWithTypeInference( c,
                ValueToEIR( ToValue( TVar( "_"_sid ) ) ), ""_sid, srcVal, srcVal.locationId() );

            if( tmpVar.isPoison() )
                return PoisonValue();

            tmpVar.setLocationId( srcVal.locationId() );
            tempVars.emplace_back( move( tmpVar ) );
        }

        if( tupSize != tempVars.size() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( 0, "incompatible tuple sizes." );
            return PoisonValue();
        }

        auto result = EmptyOpenTuple();

        size_t i = 0;
        bool success = true;
        ForEachInTuple( lhs, [&]( auto&& destVal )

        {
            auto r = InvokeOverloadSet( c,
                assOp, MakeClosedTuple( destVal, tempVars[i++] ) );

            if( r.isPoison() )
            {
                success = false;
                return false;
            }

            result = AppendToTuple( result, move( r ) );
            return true;
        } );

        if( !success )
            return PoisonValue();

        return result;
    };



    Value MutRefTupleAssignment( const ptr< OverloadSet >& assOp, Context& c, const Value& lhs, const Value& rhs )
    {
        auto lhsRefType = *FromValue< ReferenceType >( *EIRToValue( lhs.type() ) );
        auto lhsTupType = *EIRToValue( lhsRefType.type() );

        auto rhsTupTypeEIR = rhs.type();
        if( !rhs.isConstant() )
        {
            auto rhsRefType = *FromValue< ReferenceType >( *EIRToValue( lhs.type() ) );
            rhsTupTypeEIR = rhsRefType.type();
        }

        auto rhsTupType = *EIRToValue( rhsTupTypeEIR );

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

                return PoisonValue();
            }
            else if( auto bb = cfg->currentBB() )
            {
                bb->append( rhs );
                if( !rhs.isConstant() )
                    bb->append( Load( rhsTupTypeEIR, rhs.locationId() ) );












|
>











|
|
|
>

|
|
<
|
>
|
>
|
|

|
|
>
|
>
|















|
|
|
|
|
|
|
|
|


















|
>
|
|
<

|
|
|
|
|

|
|
|





<
|
>
>
|


















|
>







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

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

namespace goose::builtins
{
    Generator< Value > GenerateRHSValuesFromTupleRef(
        const ptr< OverloadSet >& assOp, const Context& c, const Value& tup )
    {
        auto cfg = GetCFG( c );
        if( !cfg )
            co_yield Value( PoisonValue() );

        auto tupRefType = *FromValue< ReferenceType >( *EIRToValue( tup.type() ) );

        // Store the source tuple as a temporary, otherwise we're going to recompute rhs
        // for each member assigned (which is bad)
        // TODO_SSA rewrite this (since CreateTemporary makes no sense anymore)
        co_return;
        /*    auto index = util::GenerateNewUID();
            cfg->currentBB()->append( tup, CreateTemporary( index, false, tup.locationId() ) );
            auto srcTup = BuildComputedValue( tupRefType.type(), GetTemporary( tupRefType.type(),
           index, tup.locationId() ) );

            auto anonVarDecl = ToValue( TNamedDecl( ValueToEIR( ToValue( TVar( "_"_sid ) ) ), ""_sid
           ) ); co_yield GenerateValuesFromComputedTuple( srcTup );*/

    }

    // Assignment of a compile time tuple or tuple reference to another compile time tuple,
    // directly. Most tuple assignment will go through one of the other overload that takes a mutref
    // to a tuple on the lhs, but sometimes we have compilation time tuples of objects that can
    // directly be assigned such as decls or refs.
    //
    // So this overload is for all these cases, which encompass initializing multiple comma
    // separated decls from a tuple, as well as variable swapping/shuffling. To make sure that the
    // later works properly, we store all the rhs values into temporary variables first, before
    // assigning them to the lhs values.
    Value NonRefTupleAssignment(
        const ptr< OverloadSet >& assOp, Context& c, const Value& lhs, const Value& rhs )
    {
        auto tupSize = TupleSize( lhs );

        // Load the values from the rhs tuple into temporary vars.
        vector< Value > tempVars;
        tempVars.reserve( tupSize );

        Generator< Value > gen;
        if( rhs.isConstant() )
            gen = GenerateValuesFromConstantTuple( rhs );
        else
            gen = GenerateRHSValuesFromTupleRef( assOp, c, rhs );

        for( auto&& srcVal : gen )
        {
            // We go at very high level to construct the temporary variables by resolving an
            // invocation of the assignment of the rhs value to a TNamedDecl with an empty name. The
            // reason we need to work at such high level is so that the initializer value gets
            // resolved exactly like if it was a regular assignment. In particular, if the value in
            // question is a locvar, we want it to go through the type checking process to be
            // replaced with its content. This is the simplest and most robust way to achieve this,
            // which should honor every relevant extension point.
            auto tmpVar = DeclareLocalVarWithTypeInference(
                c, ValueToEIR( ToValue( TVar( "_"_sid ) ) ), ""_sid, srcVal, srcVal.locationId() );

            if( tmpVar.isPoison() )
                return PoisonValue();

            tmpVar.setLocationId( srcVal.locationId() );
            tempVars.emplace_back( move( tmpVar ) );
        }

        if( tupSize != tempVars.size() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( 0, "incompatible tuple sizes." );
            return PoisonValue();
        }

        auto result = EmptyOpenTuple();

        size_t i = 0;
        bool success = true;
        ForEachInTuple( lhs,
            [&]( auto&& destVal )
            {
                auto r = InvokeOverloadSet( c, assOp, MakeClosedTuple( destVal, tempVars[i++] ) );


                if( r.isPoison() )
                {
                    success = false;
                    return false;
                }

                result = AppendToTuple( result, move( r ) );
                return true;
            } );

        if( !success )
            return PoisonValue();

        return result;

    }

    Value MutRefTupleAssignment(
        const ptr< OverloadSet >& assOp, Context& c, const Value& lhs, const Value& rhs )
    {
        auto lhsRefType = *FromValue< ReferenceType >( *EIRToValue( lhs.type() ) );
        auto lhsTupType = *EIRToValue( lhsRefType.type() );

        auto rhsTupTypeEIR = rhs.type();
        if( !rhs.isConstant() )
        {
            auto rhsRefType = *FromValue< ReferenceType >( *EIRToValue( lhs.type() ) );
            rhsTupTypeEIR = rhsRefType.type();
        }

        auto rhsTupType = *EIRToValue( rhsTupTypeEIR );

        if( IsTrivialTupleAssignment( c, lhsTupType, rhsTupType ) )
        {
            auto cfg = GetCFG( c );
            if( !cfg )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                    0, "assignments are not allowed here." );
                return PoisonValue();
            }
            else if( auto bb = cfg->currentBB() )
            {
                bb->append( rhs );
                if( !rhs.isConstant() )
                    bb->append( Load( rhsTupTypeEIR, rhs.locationId() ) );
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

        if( rhs.isConstant() )
            gen = GenerateValuesFromConstantTuple( rhs );
        else
            gen = GenerateRHSValuesFromTupleRef( assOp, c, rhs );

        uint32_t index = 0;
        bool success = true;
        ForEachInTupleType( lhsTupType, [&]( auto&& t )

        {
            if( gen.finished() )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage( 0, "incompatible tuple sizes." );

                success = false;
                return false;
            }

            auto srcVal = gen.consume();
            auto rt = ValueToEIR( ToValue( ReferenceType( t, MutAccessSpecifier() ) ) );

            auto destVal = BuildComputedValue( rt, lhs, cir::Select( index++, lhs.locationId() ) );

            auto r = InvokeOverloadSet( c,
                assOp, MakeClosedTuple( destVal, srcVal ) );

            success = !r.isPoison();
            return success;
        } );

        if( !success )
            return PoisonValue();

        if( !gen.finished() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( 0, "incompatible tuple sizes." );
            return PoisonValue();
        }

        return lhs;
    };
}








|
>
|
|
|
|
>
|
|
|

|
|
>
|

|
<

|
|
|











<
|
>
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
        if( rhs.isConstant() )
            gen = GenerateValuesFromConstantTuple( rhs );
        else
            gen = GenerateRHSValuesFromTupleRef( assOp, c, rhs );

        uint32_t index = 0;
        bool success = true;
        ForEachInTupleType( lhsTupType,
            [&]( auto&& t )
            {
                if( gen.finished() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        0, "incompatible tuple sizes." );
                    success = false;
                    return false;
                }

                auto srcVal = gen.consume();
                auto rt = ValueToEIR( ToValue( ReferenceType( t, MutAccessSpecifier() ) ) );
                auto destVal =
                    BuildComputedValue( rt, lhs, cir::Select( index++, lhs.locationId() ) );

                auto r = InvokeOverloadSet( c, assOp, MakeClosedTuple( destVal, srcVal ) );


                success = !r.isPoison();
                return success;
            } );

        if( !success )
            return PoisonValue();

        if( !gen.finished() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( 0, "incompatible tuple sizes." );
            return PoisonValue();
        }

        return lhs;

    }
} // namespace goose::builtins
Changes to bs/builtins/operators/tuple.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
#ifndef GOOSE_BUILTINS_OPERATORS_TUPLE_H
#define GOOSE_BUILTINS_OPERATORS_TUPLE_H

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

    static inline auto BuildGenericTupleOperator()
    {
        return []< typename tag >( auto&& e, auto&& pOvlSet, tag t )
        {
            if constexpr( is_same_v< tag, UnaryOpTag > )
            {
                using intrinsicType = Intrinsic< Value ( CustomPattern< Value, TuplePattern > ) >;
                auto intrinsicFunc = [pOvlSet]( const Context& c, const Value& operand )
                {
                    auto result = EmptyOpenTuple();

                    ForEachInTuple( operand, [&]( auto&& operand )

                    {
                        auto r = InvokeOverloadSet( c, pOvlSet,
                            MakeClosedTuple( operand ) );
                        result = AppendToTuple( result, r );
                        return true;
                    } );

                    return result;
                };

                pOvlSet->add( e, ToValue< intrinsicType >( move( intrinsicFunc ) ), GetBuiltinIntrinsicFuncInvocationRule() );

            }
            else
            {

                using intrinsicType = Intrinsic< Value ( CustomPattern< Value, TuplePattern >, CustomPattern< Value, TuplePattern > ) >;
                auto intrinsicFunc = [pOvlSet]( const Context& c, const Value& lhs, const Value& rhs )

                {
                    if( TupleSize( lhs ) != TupleSize( rhs ) )
                    {
                        DiagnosticsManager::GetInstance().emitErrorMessage( 0, "Incompatible tuple sizes." );

                        return PoisonValue();
                    }

                    auto result = EmptyOpenTuple();

                    ForEachInTuples( lhs, rhs, [&]( auto&& lhs, auto&& rhs )

                    {
                        auto r = InvokeOverloadSet( c, pOvlSet,
                            MakeClosedTuple( lhs, rhs ) );

                        // Super inefficient, but that's far from the biggest such problem.
                        // This is just the bootstrap compiler anyway. Should hopefully be able to
                        // easily make more optimized stuff when rewriting the self hosted compiler,
                        // some day far away in the future.
                        result = AppendToTuple( result, r );
                        return true;
                    } );

                    return result;
                };

                pOvlSet->add( e, ToValue< intrinsicType >( move( intrinsicFunc ) ), GetBuiltinIntrinsicFuncInvocationRule() );

            }
        };
    }
}

#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
64
65
66
67
68
69
70
71
72
#ifndef GOOSE_BUILTINS_OPERATORS_TUPLE_H
#define GOOSE_BUILTINS_OPERATORS_TUPLE_H

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

    static inline auto BuildGenericTupleOperator()
    {
        return []< typename tag >( auto&& e, auto&& pOvlSet, tag t )
        {
            if constexpr( is_same_v< tag, UnaryOpTag > )
            {
                using intrinsicType = Intrinsic< Value( CustomPattern< Value, TuplePattern > ) >;
                auto intrinsicFunc = [pOvlSet]( const Context& c, const Value& operand )
                {
                    auto result = EmptyOpenTuple();

                    ForEachInTuple( operand,
                        [&]( auto&& operand )
                        {
                            auto r = InvokeOverloadSet( c, pOvlSet, MakeClosedTuple( operand ) );

                            result = AppendToTuple( result, r );
                            return true;
                        } );

                    return result;
                };

                pOvlSet->add( e, ToValue< intrinsicType >( move( intrinsicFunc ) ),
                    GetBuiltinIntrinsicFuncInvocationRule() );
            }
            else
            {
                using intrinsicType = Intrinsic< Value(
                    CustomPattern< Value, TuplePattern >, CustomPattern< Value, TuplePattern > ) >;
                auto intrinsicFunc = [pOvlSet](
                                         const Context& c, const Value& lhs, const Value& rhs )
                {
                    if( TupleSize( lhs ) != TupleSize( rhs ) )
                    {
                        DiagnosticsManager::GetInstance().emitErrorMessage(
                            0, "Incompatible tuple sizes." );
                        return PoisonValue();
                    }

                    auto result = EmptyOpenTuple();

                    ForEachInTuples( lhs, rhs,
                        [&]( auto&& lhs, auto&& rhs )
                        {
                            auto r = InvokeOverloadSet( c, pOvlSet, MakeClosedTuple( lhs, rhs ) );


                            // Super inefficient, but that's far from the biggest such problem.
                            // This is just the bootstrap compiler anyway. Should hopefully be able
                            // to easily make more optimized stuff when rewriting the self hosted
                            // compiler, some day far away in the future.
                            result = AppendToTuple( result, r );
                            return true;
                        } );

                    return result;
                };

                pOvlSet->add( e, ToValue< intrinsicType >( move( intrinsicFunc ) ),
                    GetBuiltinIntrinsicFuncInvocationRule() );
            }
        };
    }
} // namespace goose::builtins

#endif
Changes to bs/builtins/statements/break.cpp.
14
15
16
17
18
19
20

21
22
23
24
25
26
27
28
        {
            auto& dm = DiagnosticsManager::GetInstance();

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

            if( p.isInParenExpr() || !level )
            {

                dm.emitSyntaxErrorMessage( locationId, "the break statement is not allowed here.", 0 );
                return false;
            }

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

            if( !cfg->currentBB() || cfg->currentBB()->terminator() )
            {







>
|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
        {
            auto& dm = DiagnosticsManager::GetInstance();

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

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

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

            if( !cfg->currentBB() || cfg->currentBB()->terminator() )
            {
37
38
39
40
41
42
43
44

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

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







|
38
39
40
41
42
43
44
45

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

        RegisterRule( e, "break"_sid, Rule( handleBreak ) );
    }
} // namespace goose::builtins
Changes to bs/builtins/statements/continue.cpp.
14
15
16
17
18
19
20

21
22
23
24
25
26
27
28
        {
            auto& dm = DiagnosticsManager::GetInstance();

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

            if( p.isInParenExpr() || !level )
            {

                dm.emitSyntaxErrorMessage( locationId, "the continue statement is not allowed here.", 0 );
                return false;
            }

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

            if( !cfg->currentBB() || cfg->currentBB()->terminator() )
            {







>
|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
        {
            auto& dm = DiagnosticsManager::GetInstance();

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

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

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

            if( !cfg->currentBB() || cfg->currentBB()->terminator() )
            {
37
38
39
40
41
42
43
44

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

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







|
38
39
40
41
42
43
44
45

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

        RegisterRule( e, "continue"_sid, Rule( handleContinue ) );
    }
} // namespace goose::builtins
Changes to bs/builtins/statements/ct-for.cpp.
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

        auto handleCTFor = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();

            auto np = p.makeNestedParser();
            if( !np.parseExpression( precedence::ForStmt ) )
            {

                dm.emitSyntaxErrorMessage( locationId, "expected a declaration following the #for statement.", 0 );
                return false;
            }

            auto decl = np.popValue();
            if( !decl )
            {

                dm.emitSyntaxErrorMessage( locationId, "expected a declaration following the #for statement.", 0 );
                return false;
            }

            bool isDecl = IsDecl( *decl );
            bool isTNamedDecl = IsTNamedDecl( *decl );
            bool isTDecl = IsTDecl( *decl );

            // TODO also allow a TVar or TTVar, turn them into a TDecl, like for tfunc params?

            if( !isDecl && !isTNamedDecl && !isTDecl )
            {
                dm.emitSyntaxErrorMessage( decl->locationId(), "expected a declaration.", 0 );
                return false;
            }

            auto next = p.resolver()->consumeUnresolved();
            if( !next )
            {

                dm.emitSyntaxErrorMessage( decl->locationId(), "expected 'in' after declaration.", 0 );
                return false;
            }

            const auto* nextSid = get_if< StringId >( &next->first );
            if( !nextSid )
            {

                dm.emitSyntaxErrorMessage( decl->locationId(), "expected 'in' after declaration.", 0 );
                return false;
            }

            if( *nextSid != "in"_sid )
            {
                dm.emitSyntaxErrorMessage( next->second, "expected 'in'.", 0 );
                return false;
            }

            np = p.makeNestedParser();
            if( !np.parseExpression( precedence::ForStmt ) )
            {

                dm.emitSyntaxErrorMessage( next->second, "expected an expression following 'in'.", 0 );
                return false;
            }

            auto container = np.popValue();
            if( !container )
            {

                dm.emitSyntaxErrorMessage( next->second, "expected an expression following 'in'.", 0 );
                return false;
            }

            auto body = make_shared< vector< TermLoc > >();
            auto g = p.resolver()->consumeUnit();
            move( g.begin(), g.end(), back_inserter( *body ) );

            Value wrappedDecl;

            if( isDecl )
            {
                wrappedDecl = ToValue( Wrap( make_shared< Decl >( *FromValue< Decl >( *decl ) ) ) )
                    .setLocationId( decl->locationId() );
            }
            else if( isTNamedDecl )
            {

                wrappedDecl = ToValue( Wrap( make_shared< TNamedDecl >( *FromValue< TNamedDecl >( *decl ) ) ) )
                    .setLocationId( decl->locationId() );
            }
            else if( isTDecl )
            {

                wrappedDecl = ToValue( Wrap( make_shared< TDecl >( *FromValue< TDecl >( *decl ) ) ) )
                    .setLocationId( decl->locationId() );
            }

            CTForEach( p.context(), wrappedDecl, *container, body );
            return true;
        };

        RegisterRule( e, "#for"_sid, Rule( handleCTFor ) );

        RegisterRule( e, "in"_sid, Rule(
            []( auto&& )->optional< uint32_t >{ return nullopt; },
            []( auto&&, auto&&, auto&& ) { return false; }
        ) );
    }
}








>
|






>
|


















>
|






>
|












>
|






>
|












|



>
|
|



>
|
|








|
|
|
<

<
>
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
        auto handleCTFor = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();

            auto np = p.makeNestedParser();
            if( !np.parseExpression( precedence::ForStmt ) )
            {
                dm.emitSyntaxErrorMessage(
                    locationId, "expected a declaration following the #for statement.", 0 );
                return false;
            }

            auto decl = np.popValue();
            if( !decl )
            {
                dm.emitSyntaxErrorMessage(
                    locationId, "expected a declaration following the #for statement.", 0 );
                return false;
            }

            bool isDecl = IsDecl( *decl );
            bool isTNamedDecl = IsTNamedDecl( *decl );
            bool isTDecl = IsTDecl( *decl );

            // TODO also allow a TVar or TTVar, turn them into a TDecl, like for tfunc params?

            if( !isDecl && !isTNamedDecl && !isTDecl )
            {
                dm.emitSyntaxErrorMessage( decl->locationId(), "expected a declaration.", 0 );
                return false;
            }

            auto next = p.resolver()->consumeUnresolved();
            if( !next )
            {
                dm.emitSyntaxErrorMessage(
                    decl->locationId(), "expected 'in' after declaration.", 0 );
                return false;
            }

            const auto* nextSid = get_if< StringId >( &next->first );
            if( !nextSid )
            {
                dm.emitSyntaxErrorMessage(
                    decl->locationId(), "expected 'in' after declaration.", 0 );
                return false;
            }

            if( *nextSid != "in"_sid )
            {
                dm.emitSyntaxErrorMessage( next->second, "expected 'in'.", 0 );
                return false;
            }

            np = p.makeNestedParser();
            if( !np.parseExpression( precedence::ForStmt ) )
            {
                dm.emitSyntaxErrorMessage(
                    next->second, "expected an expression following 'in'.", 0 );
                return false;
            }

            auto container = np.popValue();
            if( !container )
            {
                dm.emitSyntaxErrorMessage(
                    next->second, "expected an expression following 'in'.", 0 );
                return false;
            }

            auto body = make_shared< vector< TermLoc > >();
            auto g = p.resolver()->consumeUnit();
            move( g.begin(), g.end(), back_inserter( *body ) );

            Value wrappedDecl;

            if( isDecl )
            {
                wrappedDecl = ToValue( Wrap( make_shared< Decl >( *FromValue< Decl >( *decl ) ) ) )
                                  .setLocationId( decl->locationId() );
            }
            else if( isTNamedDecl )
            {
                wrappedDecl = ToValue(
                    Wrap( make_shared< TNamedDecl >( *FromValue< TNamedDecl >( *decl ) ) ) )
                                  .setLocationId( decl->locationId() );
            }
            else if( isTDecl )
            {
                wrappedDecl =
                    ToValue( Wrap( make_shared< TDecl >( *FromValue< TDecl >( *decl ) ) ) )
                        .setLocationId( decl->locationId() );
            }

            CTForEach( p.context(), wrappedDecl, *container, body );
            return true;
        };

        RegisterRule( e, "#for"_sid, Rule( handleCTFor ) );

        RegisterRule( e, "in"_sid,
            Rule( []( auto&& ) -> optional< uint32_t > { return nullopt; },
                []( auto&&, auto&&, auto&& ) { return false; } ) );

    }

} // namespace goose::builtins
Changes to bs/builtins/statements/ct-if.cpp.
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
        auto handleCTIf = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();

            auto np = p.makeNestedParser();
            if( !np.parseExpression( precedence::IfStmt ) )
            {

                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the #if statement.", 0 );
                return false;
            }

            auto condVal = np.popValue();
            if( !condVal )
            {

                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the #if statement.", 0 );
                return false;
            }

            const auto& context = p.context();
            auto converted = ConvertValueToType( context, *condVal, GetValueType< bool >() );
            if( holds_alternative< ValUnifyError >( converted ) )
            {
                switch( get< ValUnifyError >( converted ) )
                {
                    case ValUnifyError::NoSolution:

                        dm.emitErrorMessage( condVal->locationId(), "this doesn't evaluate to a bool constant." );
                        break;

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

                return false;
            }

            auto condBool = get< Value >( converted );

            if( !condBool.isConstant() )
            {

                dm.emitErrorMessage( condVal->locationId(), "this doesn't evaluate to a bool constant." );
                return false;
            }

            auto cond = FromValue< bool >( condBool );

            // If the cond is invalid, both blocks will be excluded, but parsing will continue,
            // to give the best chance of a graceful recovery of the parsing and hopefully
            // be able to detect more legit errors before we bail out.
            // We do mark the current diagnostics context as bust, however, since what we're going to
            // miss in the #if block that should have been parsed is likey to cause cascading errors
            // in the current scope.
            if( !cond )

                dm.emitSyntaxErrorMessage( condVal->locationId(), "this doesn't evaluate to a bool constant." );

            auto next = p.resolver()->lookAheadUnresolved();
            if( !next )
            {

                dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), "brace block expected.", 0 );
                return false;
            }

            auto decomp = Decompose( next->first, Val< Delimiter >() );
            if( !decomp || *decomp != Delimiter::OpenBrace )
            {

                dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), "brace block expected.", 0 );
                return false;
            }

            // Deal with the then block.
            if( cond && *cond )
            {
                // Condition is true: parse the 'then' block.
                if( !p.parseBraceBlock() )
                    return false;
            }
            else
            {
                // Condition is false: skip the 'then' block.
                auto gen = p.resolver()->consumeUnit();
                for_each( gen.begin(), gen.end(), []( auto&& ){} );
            }

            next = p.resolver()->lookAheadUnresolved();
            if( next )
            {
                const auto* nextSid = get_if< StringId >( &next->first );
                if( nextSid && *nextSid == "#else"_sid )
                {
                    p.resolver()->consumeUnresolved();

                    auto next = p.resolver()->lookAheadUnresolved();
                    auto decomp = Decompose( next->first, Val< Delimiter >() );
                    if( !decomp || *decomp != Delimiter::OpenBrace )
                    {

                        dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), "brace block expected.", 0 );
                        return false;
                    }

                    // Deal with the else block.
                    if( cond && !*cond )
                    {
                        // Condition is false: parse the 'else' block.
                        if( !p.parseBraceBlock() )
                            return false;
                    }
                    else
                    {
                        // Condition is true: skip the 'else' block.
                        auto gen = p.resolver()->consumeUnit();
                        for_each( gen.begin(), gen.end(), []( auto&& ){} );
                    }
                }
            }

            return true;
        };

        RegisterRule( e, "#if"_sid, Rule( handleCTIf ) );
    }
}







>
|






>
|










>
|














>
|





|
|
|
|
|
<

>
|




>
|






>
|














|














>
|














|









|
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
        auto handleCTIf = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();

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

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

            const auto& context = p.context();
            auto converted = ConvertValueToType( context, *condVal, GetValueType< bool >() );
            if( holds_alternative< ValUnifyError >( converted ) )
            {
                switch( get< ValUnifyError >( converted ) )
                {
                    case ValUnifyError::NoSolution:
                        dm.emitErrorMessage(
                            condVal->locationId(), "this doesn't evaluate to a bool constant." );
                        break;

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

                return false;
            }

            auto condBool = get< Value >( converted );

            if( !condBool.isConstant() )
            {
                dm.emitErrorMessage(
                    condVal->locationId(), "this doesn't evaluate to a bool constant." );
                return false;
            }

            auto cond = FromValue< bool >( condBool );

            // If the cond is invalid, both blocks will be excluded, but parsing will continue, to
            // give the best chance of a graceful recovery of the parsing and hopefully be able to
            // detect more legit errors before we bail out. We do mark the current diagnostics
            // context as busted, however, since what we're going to miss in the #if block that
            // should have been parsed is likey to cause cascading errors in the current scope.

            if( !cond )
                dm.emitSyntaxErrorMessage(
                    condVal->locationId(), "this doesn't evaluate to a bool constant." );

            auto next = p.resolver()->lookAheadUnresolved();
            if( !next )
            {
                dm.emitSyntaxErrorMessage(
                    p.resolver()->currentLocation(), "brace block expected.", 0 );
                return false;
            }

            auto decomp = Decompose( next->first, Val< Delimiter >() );
            if( !decomp || *decomp != Delimiter::OpenBrace )
            {
                dm.emitSyntaxErrorMessage(
                    p.resolver()->currentLocation(), "brace block expected.", 0 );
                return false;
            }

            // Deal with the then block.
            if( cond && *cond )
            {
                // Condition is true: parse the 'then' block.
                if( !p.parseBraceBlock() )
                    return false;
            }
            else
            {
                // Condition is false: skip the 'then' block.
                auto gen = p.resolver()->consumeUnit();
                for_each( gen.begin(), gen.end(), []( auto&& ) {} );
            }

            next = p.resolver()->lookAheadUnresolved();
            if( next )
            {
                const auto* nextSid = get_if< StringId >( &next->first );
                if( nextSid && *nextSid == "#else"_sid )
                {
                    p.resolver()->consumeUnresolved();

                    auto next = p.resolver()->lookAheadUnresolved();
                    auto decomp = Decompose( next->first, Val< Delimiter >() );
                    if( !decomp || *decomp != Delimiter::OpenBrace )
                    {
                        dm.emitSyntaxErrorMessage(
                            p.resolver()->currentLocation(), "brace block expected.", 0 );
                        return false;
                    }

                    // Deal with the else block.
                    if( cond && !*cond )
                    {
                        // Condition is false: parse the 'else' block.
                        if( !p.parseBraceBlock() )
                            return false;
                    }
                    else
                    {
                        // Condition is true: skip the 'else' block.
                        auto gen = p.resolver()->consumeUnit();
                        for_each( gen.begin(), gen.end(), []( auto&& ) {} );
                    }
                }
            }

            return true;
        };

        RegisterRule( e, "#if"_sid, Rule( handleCTIf ) );
    }
} // namespace goose::builtins
Changes to bs/builtins/statements/forall.cpp.
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
        auto handleForAll = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();
            auto& c = p.context();

            if( !InVerificationCode( c ) )
            {

                dm.emitSyntaxErrorMessage( locationId, "the forall statement is not allowed here.", 0 );
                return false;
            }

            auto np = p.makeNestedParser();
            if( !np.parseExpression( precedence::IfStmt ) )
            {

                dm.emitSyntaxErrorMessage( locationId, "expected a decl or a tuple of decls following the forall statement.", 0 );
                return false;
            }

            auto decls = np.popValue();
            if( !decls )
            {

                dm.emitSyntaxErrorMessage( locationId, "expected a decl or a tuple of decls following the forall statement.", 0 );
                return false;
            }

            if( !IsTuple( *decls ) )
                decls = MakeOpenTuple( move( *decls ) );

            auto is = make_shared< InstrSeq >();


            auto identity = AppendToVectorTerm( c.identity(), TERM( StringId( Env::NewUniqueId() ) ) );
            c.env()->addVisibilityRule( c.identity(), identity );

            bool failed = false;
            uint32_t numArgs = 0;
            ForEachInTuple( *decls, [&]( auto&& d )

            {
                auto decl = FromValue< Decl >( d );
                if( !decl )
                {
                    dm.emitErrorMessage( d.locationId(), "expected a decl." );
                    failed = true;
                    return false;
                }


                auto ph = BuildComputedValue( decl->type(), Placeholder( decl->type(), decl->name(), d.locationId() ) );
                c.env()->storeValue( AppendToVectorTerm( identity, decl->name() ), ANYTERM( _ ),
                    ValueToEIR( ph ) );

                AppendToInstrSeq( *is,
                    PushType( decl->type(), d.locationId() ),
                    PushStringId( decl->name(), d.locationId() ) );

                ++numArgs;
                return true;
            } );

            if( failed )
                return false;

            AppendToInstrSeq( *is, ForAllSetup( numArgs, locationId ) );

            auto delim = np.resolver()->lookAheadUnresolved();
            if( !delim )
            {

                dm.emitSyntaxErrorMessage( np.resolver()->currentLocation(), format( "'[' expected." ), 0 );
                return false;
            }

            auto decomp = Decompose( delim->first, Val< Delimiter >() );
            if( !decomp || *decomp != Delimiter::OpenBracket )
            {

                dm.emitSyntaxErrorMessage( np.resolver()->currentLocation(), format( "'[' expected." ), 0 );
                return false;
            }

            // Parse the propositions block.
            auto [props,ploc] = p.tokenizeBracketBlock();
            props->setIdentity( identity );

            if( !props->parse( c ) )
                return true;

            // Merge the propositions into a conjunction
            bool first = true;
            for( auto&& p : props->props() )
            {
                AppendToInstrSeq( *is, move( p ) );

                if( !first )
                    AppendToInstrSeq( *is, And( locationId ) );

                first = false;
            }

            AppendToInstrSeq( *is, ForAll( locationId ) );

            p.pushValue( BuildComputedValue( GetValueType< bool >(), move( is ) )
                .setLocationId( locationId ) );
            return true;
        };

        RegisterRule( e, "forall"_sid, Rule( handleForAll ) );
    }
}







>
|






>
|






>
|








>
|




|
>
|
|
|
|
|
|
|
|

>
|
|
|

<
|
|

|
|
|









>
|






>
|




|




















|





|
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
        auto handleForAll = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();
            auto& c = p.context();

            if( !InVerificationCode( c ) )
            {
                dm.emitSyntaxErrorMessage(
                    locationId, "the forall statement is not allowed here.", 0 );
                return false;
            }

            auto np = p.makeNestedParser();
            if( !np.parseExpression( precedence::IfStmt ) )
            {
                dm.emitSyntaxErrorMessage( locationId,
                    "expected a decl or a tuple of decls following the forall statement.", 0 );
                return false;
            }

            auto decls = np.popValue();
            if( !decls )
            {
                dm.emitSyntaxErrorMessage( locationId,
                    "expected a decl or a tuple of decls following the forall statement.", 0 );
                return false;
            }

            if( !IsTuple( *decls ) )
                decls = MakeOpenTuple( move( *decls ) );

            auto is = make_shared< InstrSeq >();

            auto identity =
                AppendToVectorTerm( c.identity(), TERM( StringId( Env::NewUniqueId() ) ) );
            c.env()->addVisibilityRule( c.identity(), identity );

            bool failed = false;
            uint32_t numArgs = 0;
            ForEachInTuple( *decls,
                [&]( auto&& d )
                {
                    auto decl = FromValue< Decl >( d );
                    if( !decl )
                    {
                        dm.emitErrorMessage( d.locationId(), "expected a decl." );
                        failed = true;
                        return false;
                    }

                    auto ph = BuildComputedValue(
                        decl->type(), Placeholder( decl->type(), decl->name(), d.locationId() ) );
                    c.env()->storeValue( AppendToVectorTerm( identity, decl->name() ), ANYTERM( _ ),
                        ValueToEIR( ph ) );


                    AppendToInstrSeq( *is, PushType( decl->type(), d.locationId() ),
                        PushStringId( decl->name(), d.locationId() ) );

                    ++numArgs;
                    return true;
                } );

            if( failed )
                return false;

            AppendToInstrSeq( *is, ForAllSetup( numArgs, locationId ) );

            auto delim = np.resolver()->lookAheadUnresolved();
            if( !delim )
            {
                dm.emitSyntaxErrorMessage(
                    np.resolver()->currentLocation(), format( "'[' expected." ), 0 );
                return false;
            }

            auto decomp = Decompose( delim->first, Val< Delimiter >() );
            if( !decomp || *decomp != Delimiter::OpenBracket )
            {
                dm.emitSyntaxErrorMessage(
                    np.resolver()->currentLocation(), format( "'[' expected." ), 0 );
                return false;
            }

            // Parse the propositions block.
            auto [props, ploc] = p.tokenizeBracketBlock();
            props->setIdentity( identity );

            if( !props->parse( c ) )
                return true;

            // Merge the propositions into a conjunction
            bool first = true;
            for( auto&& p : props->props() )
            {
                AppendToInstrSeq( *is, move( p ) );

                if( !first )
                    AppendToInstrSeq( *is, And( locationId ) );

                first = false;
            }

            AppendToInstrSeq( *is, ForAll( locationId ) );

            p.pushValue( BuildComputedValue( GetValueType< bool >(), move( is ) )
                             .setLocationId( locationId ) );
            return true;
        };

        RegisterRule( e, "forall"_sid, Rule( handleForAll ) );
    }
} // namespace goose::builtins
Changes to bs/builtins/statements/if.cpp.
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
            // Create a scope for the entire if, so that any var declared
            // inside of the condition is alive and visible throughout the if.
            Scope s( p.context() );

            auto np = p.makeNestedParser();
            if( !np.parseExpression( precedence::IfStmt ) )
            {

                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the if statement.", 0 );
                return false;
            }

            auto condVal = np.popValue();
            if( !condVal )
            {

                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the if statement.", 0 );
                return false;
            }

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

                        break;

                    case ValUnifyError::Ambiguous:

                        dm.emitSyntaxErrorMessage( condVal->locationId(), "ambiguous if condition bool conversion." );
                        break;
                }

                return false;
            }

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

            // The condition may have emitted additional basic blocks, so get the current block again.

            pPrecBB = cfg->currentBB();

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

                return false;
            }

            // Retrieve the successor block of the then branch, if any
            auto pThenSuccBB = cfg->currentBB();

            ptr< cir::BasicBlock > pElseBB, pElseSuccBB;

            auto next = p.resolver()->lookAheadUnresolved();
            if( next )
            {
                const auto* nextSid = get_if< StringId >( &next->first );
                if( nextSid && *nextSid == "else"_sid )
                {
                    p.resolver()->consumeUnresolved();
                    pElseBB = ParseSubStatement( p, precedence::IfStmt );
                    if( !pElseBB )
                    {
                        dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), "expected a statement after 'else'.", 0 );

                        return false;
                    }

                    // Retrieve the successor block of the else branch, if any
                    pElseSuccBB = cfg->currentBB();
                }
            }

            if( !pPrecBB )
                return true;

            ptr< cir::BasicBlock > pSuccBB;

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

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

            if( pThenSuccBB && !pThenSuccBB->terminator() )
                pThenSuccBB->setTerminator( cir::Branch( pSuccBB ) );

            if( pElseSuccBB && !pElseSuccBB->terminator() )
                pElseSuccBB->setTerminator( cir::Branch( pSuccBB ) );

            // Intentionally set the current BB to null if no
            // successor block was created: it means all our code paths
            // are already terminated and there is no point in emitting any more
            // instructions.
            cfg->setCurrentBB( pSuccBB );
            return true;
        };

        RegisterRule( e, "if"_sid, Rule( handleIf ) );
    }
}







>
|






>
|












|
>



>
|









|
>





|
>


















|
>




















|
<
<

















|
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
            // Create a scope for the entire if, so that any var declared
            // inside of the condition is alive and visible throughout the if.
            Scope s( p.context() );

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

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

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

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

                return false;
            }

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

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

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

            // Retrieve the successor block of the then branch, if any
            auto pThenSuccBB = cfg->currentBB();

            ptr< cir::BasicBlock > pElseBB, pElseSuccBB;

            auto next = p.resolver()->lookAheadUnresolved();
            if( next )
            {
                const auto* nextSid = get_if< StringId >( &next->first );
                if( nextSid && *nextSid == "else"_sid )
                {
                    p.resolver()->consumeUnresolved();
                    pElseBB = ParseSubStatement( p, precedence::IfStmt );
                    if( !pElseBB )
                    {
                        dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(),
                            "expected a statement after 'else'.", 0 );
                        return false;
                    }

                    // Retrieve the successor block of the else branch, if any
                    pElseSuccBB = cfg->currentBB();
                }
            }

            if( !pPrecBB )
                return true;

            ptr< cir::BasicBlock > pSuccBB;

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

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



            if( pThenSuccBB && !pThenSuccBB->terminator() )
                pThenSuccBB->setTerminator( cir::Branch( pSuccBB ) );

            if( pElseSuccBB && !pElseSuccBB->terminator() )
                pElseSuccBB->setTerminator( cir::Branch( pSuccBB ) );

            // Intentionally set the current BB to null if no
            // successor block was created: it means all our code paths
            // are already terminated and there is no point in emitting any more
            // instructions.
            cfg->setCurrentBB( pSuccBB );
            return true;
        };

        RegisterRule( e, "if"_sid, Rule( handleIf ) );
    }
} // namespace goose::builtins
Changes to bs/builtins/statements/return.cpp.
13
14
15
16
17
18
19

20
21
22
23
24
25
26
27
        auto handleReturn = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();
            auto cfg = GetCFG( p.context() );

            if( p.isInParenExpr() || !cfg || !IsBranchingAllowed( p.context() ) )
            {

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

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







>
|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
        auto handleReturn = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();
            auto cfg = GetCFG( p.context() );

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

            if( !cfg->currentBB() || cfg->currentBB()->terminator() )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                    locationId, "unreachable code.", 0 );
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
                cfg->emitTerminator( locationId, cir::RetVoid( locationId ) );
                return true;
            }

            auto np = p.makeNestedParser();
            if( !np.parseExpression( precedence::ReturnStmt + 1 ) )
            {

                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the return statement.", 0 );
                cfg->currentBB()->append( PoisonValue() );
                cfg->emitTerminator( locationId, cir::Ret( locationId ) );
                return false;
            }

            auto retVal = np.popValue();
            if( !retVal )
            {

                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the return statement.", 0 );
                cfg->currentBB()->append( PoisonValue() );
                cfg->emitTerminator( locationId, cir::Ret( locationId ) );
                return false;
            }

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

                    case ValUnifyError::Ambiguous:

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

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

            cfg->currentBB()->append( get< Value >( converted ) );
            cfg->emitTerminator( locationId, cir::Ret( locationId ) );
            return true;
        };

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







>
|








>
|















>
|


















|
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
                cfg->emitTerminator( locationId, cir::RetVoid( locationId ) );
                return true;
            }

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

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

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

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

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

            cfg->currentBB()->append( get< Value >( converted ) );
            cfg->emitTerminator( locationId, cir::Ret( locationId ) );
            return true;
        };

        RegisterRule( e, "return"_sid, Rule( handleReturn ) );
    }
} // namespace goose::builtins
Changes to bs/builtins/statements/statements.h.
21
22
23
24
25
26
27
28
29
30
        SetupCTIfStmt( e );
        SetupWhileStmt( e );
        SetupCTForStmt( e );
        SetupBreakStmt( e );
        SetupContinueStmt( e );
        SetupForAllStmt( e );
    }
}

#endif







|


21
22
23
24
25
26
27
28
29
30
        SetupCTIfStmt( e );
        SetupWhileStmt( e );
        SetupCTForStmt( e );
        SetupBreakStmt( e );
        SetupContinueStmt( e );
        SetupForAllStmt( e );
    }
} // namespace goose::builtins

#endif
Changes to bs/builtins/statements/using.cpp.
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
        auto handleUsing = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();

            auto nameTerm = p.resolver()->consume();
            if( !nameTerm )
            {

                dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), "expected an identifier.", 0 );
                return false;
            }

            const auto* name = get_if< StringId >( &nameTerm->first );
            if( !name )
            {
                dm.emitSyntaxErrorMessage( nameTerm->second, "expected an identifier.", 0 );
                return false;
            }

            sema::Context::CurrentContextGuard ccg( p.context() );

            auto& context = p.context();

            // we don't have a builder to parse using expresions. It disables
            // a number of things, in particular, the ability to overload functions.
            // This avoids unfortunate parsing ambiguiousness in some cases
            // (cf test g0/statements/e-using-2.g0)
            context.setBuilder( ToValue< void >() );

            // Create an identity to contain the visibility of any symbol defined inside of the using statement.
            // Since the current identity is that of the local variables of the current function and those
            // shouldn't be visible inside of the using statement, we simply replace the last element of the identity

            // by the name of the using definition, so that its parent becomes the current function's identity.
            //
            // TODO: we'll probably need a more sophisticated scheme to be able to deal with lambda captures
            // where localvars from the parent(s) functions should be visible, but still not visible inside of
            // using statements. But one mess of a feature at a time.

            auto localIdentity = TakeVectorTerm( context.identity(), VecSize( context.identity() ) - 1 );
            get< pvec >( localIdentity )->terms().back() = nameTerm->first;

            // TODO this probably makes locvars visible from inside the using expression, which is wrong
            // see struct/parse.cpp for a better approach

            context.env()->addVisibilityRule( context.identity(), localIdentity );

            context.setIdentity( localIdentity );

            auto eqTerm = p.resolver()->consumeUnresolved();
            if( !eqTerm )
            {
                dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), "expected '='.", 0 );

                context.env()->storeValue( localIdentity, ANYTERM( _ ), ValueToEIR( PoisonValue() ) );
                return true;
            }

            const auto* eq = get_if< StringId >( &eqTerm->first );
            if( !eq || *eq != "="_sid )
            {
                dm.emitSyntaxErrorMessage( eqTerm->second, "expected '='.", 0 );

                context.env()->storeValue( localIdentity, ANYTERM( _ ), ValueToEIR( PoisonValue() ) );
                return true;
            }

            auto np = p.makeNestedParser();
            if( !np.parseExpression( precedence::UsingStmt ) )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                    "expected an expression.", 0 );

                context.env()->storeValue( localIdentity, ANYTERM( _ ), ValueToEIR( PoisonValue() ) );
                return true;
            }

            if( !np.peekLastValue() )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                    "expected an expression.", 0 );

                context.env()->storeValue( localIdentity, ANYTERM( _ ), ValueToEIR( PoisonValue() ) );
                return true;
            }

            auto val = np.popValue();
            if( !val->isConstant() )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( val->locationId(),
                    "this expression is not a comptime constant.", 0 );

                context.env()->storeValue( localIdentity, ANYTERM( _ ), ValueToEIR( PoisonValue() ) );
                return true;
            }

            context.env()->storeValue( localIdentity, ANYTERM( _ ), ValueToEIR( *val ) );
            return true;
        };

        RegisterRule( e, "using"_sid, Rule( handleUsing ) );
    }
}







>
|




















|
|
|
>
|

|
|
|
>
|


|
|









>
|







>
|






|
|
>
|





|
|
>
|






|
|
>
|









|
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
        auto handleUsing = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();

            auto nameTerm = p.resolver()->consume();
            if( !nameTerm )
            {
                dm.emitSyntaxErrorMessage(
                    p.resolver()->currentLocation(), "expected an identifier.", 0 );
                return false;
            }

            const auto* name = get_if< StringId >( &nameTerm->first );
            if( !name )
            {
                dm.emitSyntaxErrorMessage( nameTerm->second, "expected an identifier.", 0 );
                return false;
            }

            sema::Context::CurrentContextGuard ccg( p.context() );

            auto& context = p.context();

            // we don't have a builder to parse using expresions. It disables
            // a number of things, in particular, the ability to overload functions.
            // This avoids unfortunate parsing ambiguiousness in some cases
            // (cf test g0/statements/e-using-2.g0)
            context.setBuilder( ToValue< void >() );

            // Create an identity to contain the visibility of any symbol defined inside of the
            // using statement. Since the current identity is that of the local variables of the
            // current function and those shouldn't be visible inside of the using statement, we
            // simply replace the last element of the identity by the name of the using definition,
            // so that its parent becomes the current function's identity.
            //
            // TODO: we'll probably need a more sophisticated scheme to be able to deal with lambda
            // captures where localvars from the parent(s) functions should be visible, but still
            // not visible inside of using statements. But one mess of a feature at a time.
            auto localIdentity =
                TakeVectorTerm( context.identity(), VecSize( context.identity() ) - 1 );
            get< pvec >( localIdentity )->terms().back() = nameTerm->first;

            // TODO this probably makes locvars visible from inside the using expression, which is
            // wrong see struct/parse.cpp for a better approach

            context.env()->addVisibilityRule( context.identity(), localIdentity );

            context.setIdentity( localIdentity );

            auto eqTerm = p.resolver()->consumeUnresolved();
            if( !eqTerm )
            {
                dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), "expected '='.", 0 );
                context.env()->storeValue(
                    localIdentity, ANYTERM( _ ), ValueToEIR( PoisonValue() ) );
                return true;
            }

            const auto* eq = get_if< StringId >( &eqTerm->first );
            if( !eq || *eq != "="_sid )
            {
                dm.emitSyntaxErrorMessage( eqTerm->second, "expected '='.", 0 );
                context.env()->storeValue(
                    localIdentity, ANYTERM( _ ), ValueToEIR( PoisonValue() ) );
                return true;
            }

            auto np = p.makeNestedParser();
            if( !np.parseExpression( precedence::UsingStmt ) )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                    locationId, "expected an expression.", 0 );
                context.env()->storeValue(
                    localIdentity, ANYTERM( _ ), ValueToEIR( PoisonValue() ) );
                return true;
            }

            if( !np.peekLastValue() )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                    locationId, "expected an expression.", 0 );
                context.env()->storeValue(
                    localIdentity, ANYTERM( _ ), ValueToEIR( PoisonValue() ) );
                return true;
            }

            auto val = np.popValue();
            if( !val->isConstant() )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                    val->locationId(), "this expression is not a comptime constant.", 0 );
                context.env()->storeValue(
                    localIdentity, ANYTERM( _ ), ValueToEIR( PoisonValue() ) );
                return true;
            }

            context.env()->storeValue( localIdentity, ANYTERM( _ ), ValueToEIR( *val ) );
            return true;
        };

        RegisterRule( e, "using"_sid, Rule( handleUsing ) );
    }
} // namespace goose::builtins
Changes to bs/builtins/statements/while.cpp.
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
        auto handleWhile = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();
            auto cfg = GetCFG( p.context() );

            if( p.isInParenExpr() || !cfg || !IsBranchingAllowed( p.context() ) )
            {

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

            auto pPrecBB = cfg->currentBB();

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

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

            auto np = p.makeNestedParser();
            if( !np.parseExpression( precedence::IfStmt ) )
            {

                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the while statement.", 0 );
                return false;
            }

            auto condVal = np.popValue();
            if( !condVal )
            {

                dm.emitSyntaxErrorMessage( locationId, "expected an expression following the while statement.", 0 );
                return false;
            }

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

                        break;

                    case ValUnifyError::Ambiguous:

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

                return false;
            }

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

            // The condition may have emitted additional basic blocks, so get the current block again.

            pPrecBB = cfg->currentBB();

            auto pHeaderBB = cfg->createBB();

            // Set the loop header's location to a span including the while keyword and the condition


            pHeaderBB->setLocationId( Location::CreateSpanningLocation( locationId, condVal->locationId() ) );

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

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

                return false;
            }

            if( !pPrecBB )
                return true;

            // Retrieve the final block of the loop body, if any







>
|



















>
|






>
|












|
>



>
|









|
>




|
>
>
|







|
>







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
        auto handleWhile = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();
            auto cfg = GetCFG( p.context() );

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

            auto pPrecBB = cfg->currentBB();

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

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

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

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

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

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

                return false;
            }

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

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

            auto pHeaderBB = cfg->createBB();

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

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

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

            if( !pPrecBB )
                return true;

            // Retrieve the final block of the loop body, if any
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

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

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

            {
                const auto& t = bb->terminator();
                if( !t )
                    return;

                if( const auto* pBreak = get_if< cir::Break >( &t->content() ) )
                {
                    if( pBreak->level() == breakLevel )
                        bb->setTerminator( cir::Branch( pSuccBB ) );
                    return;
                }

                if( const auto* pCont = get_if< cir::Continue >( &t->content() ) )
                {
                    if( pCont->level() == continueLevel )
                        bb->setTerminator( cir::Branch( pHeaderBB ) );
                    return;
                }
            } );

            // Emit the conditional branch that will either run an iteration of the loop or exit to the succ bb.

            pHeaderBB->append( get< Value >( converted ) );
            pHeaderBB->setTerminator( cir::CondBranch(
                pBodyBB, pSuccBB ) );

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

        RegisterRule( e, "while"_sid, Rule( handleWhile ) );
    }
}







|
>
|
|
|
|

|
|
|
|
|
|

|
|
|
|
|
|
|

|
>

|
<







|
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 breakLevel = GetBreakableScopeLevels( p.context() );
            auto continueLevel = GetContinuableScopeLevels( p.context() );

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

                    if( const auto* pBreak = get_if< cir::Break >( &t->content() ) )
                    {
                        if( pBreak->level() == breakLevel )
                            bb->setTerminator( cir::Branch( pSuccBB ) );
                        return;
                    }

                    if( const auto* pCont = get_if< cir::Continue >( &t->content() ) )
                    {
                        if( pCont->level() == continueLevel )
                            bb->setTerminator( cir::Branch( pHeaderBB ) );
                        return;
                    }
                } );

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


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

        RegisterRule( e, "while"_sid, Rule( handleWhile ) );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/basic.cpp.
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
        DefineConstant( e, "type"_sid, TypeType() );
        DefineConstant( e, "void"_sid, GetValueType< void >() );
        DefineConstant( e, "bool"_sid, GetValueType< bool >() );
        DefineConstant( e, "ct_int"_sid, GetValueType< BigInt >() );
        DefineConstant( e, "ct_char"_sid, GetValueType< char32_t >() );
        DefineConstant( e, "ct_string"_sid, GetValueType< string >() );

        RegisterBuiltinFunc< Eager< Value > ( Value ) >( e, "ct_sequence"_sid,
            []( const Value& containedType )
            {
                return ToValue( SequenceType( ValueToEIR( containedType ) ) );
            } );

        // bool literals
        DefineConstant( e, "true"_sid, ValueToEIR( ToValue( true ) ) );
        DefineConstant( e, "false"_sid, ValueToEIR( ToValue( false ) ) );

        // _ and var are both aliases for an anonymous tvar ($_)
        DefineConstant( e, "_"_sid, ValueToEIR( ToValue( TVar( "_"_sid ) ) ) );







|

<
|
<







11
12
13
14
15
16
17
18
19

20

21
22
23
24
25
26
27
        DefineConstant( e, "type"_sid, TypeType() );
        DefineConstant( e, "void"_sid, GetValueType< void >() );
        DefineConstant( e, "bool"_sid, GetValueType< bool >() );
        DefineConstant( e, "ct_int"_sid, GetValueType< BigInt >() );
        DefineConstant( e, "ct_char"_sid, GetValueType< char32_t >() );
        DefineConstant( e, "ct_string"_sid, GetValueType< string >() );

        RegisterBuiltinFunc< Eager< Value >( Value ) >( e, "ct_sequence"_sid,
            []( const Value& containedType )

            { return ToValue( SequenceType( ValueToEIR( containedType ) ) ); } );


        // bool literals
        DefineConstant( e, "true"_sid, ValueToEIR( ToValue( true ) ) );
        DefineConstant( e, "false"_sid, ValueToEIR( ToValue( false ) ) );

        // _ and var are both aliases for an anonymous tvar ($_)
        DefineConstant( e, "_"_sid, ValueToEIR( ToValue( TVar( "_"_sid ) ) ) );
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

    bool IsSequenceType( const Value& v )
    {
        if( !v.isConstant() )
            return false;

        auto result = Decompose( v.val(),
            Vec(
                Lit( "ct_type"_sid ),
                SubTerm(),
                Val< void* >(),
                Lit( "sequence"_sid ),
                SubTerm()
            )
        );

        return !!result;
    }

    Term GetSequenceSubType( const Value& v )
    {
        auto result = Decompose( v.val(),
            Vec(
                Lit( "ct_type"_sid ),
                SubTerm(),
                Val< void* >(),
                Lit( "sequence"_sid ),
                SubTerm()
            )
        );

        auto&& [_,__,type] = *result;
        return type;
    }
}


namespace goose::eir
{
    // void
    const Term& Bridge< void >::Type()
    {
        static auto type = ValueToEIR( Value( TypeType(), TSID( void ) ) );
        return type;
    }

    const Value& Bridge< void >::ToValue()
    {
        static auto val = Value( Type(), TSID( void ) );
        return val;
    }

    // identifier (StringId)
    const Term& Bridge< StringId >::Type()
    {

        static auto type = ValueToEIR( Value( TypeType(), MkStdType( TSID( ct_type ), TSID( identifier ) ) ) );
        return type;
    }

    Value Bridge< StringId >::ToValue( StringId x )
    {
        return Value( Type(), TERM( x ) );
    }







<
|
|
<
<
<
<
<







<
|
|
<
<
<
|
<
<
|


<
>



















>
|







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

    bool IsSequenceType( const Value& v )
    {
        if( !v.isConstant() )
            return false;

        auto result = Decompose( v.val(),

            Vec( Lit( "ct_type"_sid ), SubTerm(), Val< void* >(), Lit( "sequence"_sid ),
                SubTerm() ) );






        return !!result;
    }

    Term GetSequenceSubType( const Value& v )
    {
        auto result = Decompose( v.val(),

            Vec( Lit( "ct_type"_sid ), SubTerm(), Val< void* >(), Lit( "sequence"_sid ),
                SubTerm() ) );






        auto&& [_, __, type] = *result;
        return type;
    }

} // namespace goose::builtins

namespace goose::eir
{
    // void
    const Term& Bridge< void >::Type()
    {
        static auto type = ValueToEIR( Value( TypeType(), TSID( void ) ) );
        return type;
    }

    const Value& Bridge< void >::ToValue()
    {
        static auto val = Value( Type(), TSID( void ) );
        return val;
    }

    // identifier (StringId)
    const Term& Bridge< StringId >::Type()
    {
        static auto type =
            ValueToEIR( Value( TypeType(), MkStdType( TSID( ct_type ), TSID( identifier ) ) ) );
        return type;
    }

    Value Bridge< StringId >::ToValue( StringId x )
    {
        return Value( Type(), TERM( x ) );
    }
118
119
120
121
122
123
124

125
126
127
128
129
130
131
132

        return *pint;
    }

    // bool
    const Term& Bridge< bool >::Type()
    {

        static auto type = ValueToEIR( Value( TypeType(), MkStdType( TSID( rt_type ), TSID( bool ) ) ) );
        return type;
    }

    Value Bridge< bool >::ToValue( bool x )
    {
        return Value( Type(), TERM( x ? 1U : 0U ) );
    }







>
|







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

        return *pint;
    }

    // bool
    const Term& Bridge< bool >::Type()
    {
        static auto type =
            ValueToEIR( Value( TypeType(), MkStdType( TSID( rt_type ), TSID( bool ) ) ) );
        return type;
    }

    Value Bridge< bool >::ToValue( bool x )
    {
        return Value( Type(), TERM( x ? 1U : 0U ) );
    }
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

        return *pint;
    }

    // Integers
    const Term& Bridge< BigInt >::Type()
    {

        static auto type = ValueToEIR( Value( TypeType(), MkStdType( TSID( ct_type ), TSID( ct_int ) ) ) );
        return type;
    }

    const BigInt* Bridge< BigInt >::FromValue( const Value& v )
    {
        if( v.type() != Type() )
            return nullptr;

        return get_if< BigInt >( &v.val() );
    }

    // char
    const Term& Bridge< char32_t >::Type()
    {

        static auto type = ValueToEIR( Value( TypeType(), MkStdType( TSID( ct_type ), TSID( ct_char ) ) ) );
        return type;
    }

    Value Bridge< char32_t >::ToValue( char32_t x )
    {
        return Value( Type(), TERM( x ) );
    }

    optional< char32_t > Bridge< char32_t >::FromValue( const Value& v )
    {
        if( v.type() != Type() )
            return nullopt;

        return get< uint64_t >( v.val() );
    }

    // strings
    const Term& Bridge< string >::Type()
    {

        static auto type = ValueToEIR( Value( TypeType(), MkStdType( TSID( ct_type ), TSID( ct_string ) ) ) );
        return type;
    }

    Value Bridge< string >::ToValue( const string& x )
    {
        return Value( Type(), TERM( x ) );
    }

    const string* Bridge< string >::FromValue( const Value& v )
    {
        if( !v.isConstant() || v.type() != Type() )
            return nullptr;

        return get_if< string >( &v.val() );
    }

    // sequences
    Value Bridge< SequenceType >::ToValue( const SequenceType& st )
    {
        return Value( Type(), MkStdType( TSID( ct_type ), TSID( sequence ),
            st.m_containedType ) );
    }

    optional< SequenceType > Bridge< SequenceType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),
            Vec(
                Lit( "ct_type"_sid ),
                SubTerm(),
                Val< void* >(),
                Lit( "sequence"_sid ),
                SubTerm()
            )
        );

        if( !result )
            return nullopt;

        auto&& [predicates, cgType, containedType] = *result;
        return SequenceType( containedType );
    }

    // Generic values
    const Term& Bridge< Value >::Type()
    {
        // This is a trick to get a generic signature for this type when creating a builtin function.
        // This way we can define a builtin function that take any value of any type as is, without conversion.

        static auto type = HOLE( "_"_sid, "tvar"_sid );
        return type;
    }
}







>
|














>
|



















>
|



















|
<





<
|
|
<
<
<
<
<











|
|
>



|
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

        return *pint;
    }

    // Integers
    const Term& Bridge< BigInt >::Type()
    {
        static auto type =
            ValueToEIR( Value( TypeType(), MkStdType( TSID( ct_type ), TSID( ct_int ) ) ) );
        return type;
    }

    const BigInt* Bridge< BigInt >::FromValue( const Value& v )
    {
        if( v.type() != Type() )
            return nullptr;

        return get_if< BigInt >( &v.val() );
    }

    // char
    const Term& Bridge< char32_t >::Type()
    {
        static auto type =
            ValueToEIR( Value( TypeType(), MkStdType( TSID( ct_type ), TSID( ct_char ) ) ) );
        return type;
    }

    Value Bridge< char32_t >::ToValue( char32_t x )
    {
        return Value( Type(), TERM( x ) );
    }

    optional< char32_t > Bridge< char32_t >::FromValue( const Value& v )
    {
        if( v.type() != Type() )
            return nullopt;

        return get< uint64_t >( v.val() );
    }

    // strings
    const Term& Bridge< string >::Type()
    {
        static auto type =
            ValueToEIR( Value( TypeType(), MkStdType( TSID( ct_type ), TSID( ct_string ) ) ) );
        return type;
    }

    Value Bridge< string >::ToValue( const string& x )
    {
        return Value( Type(), TERM( x ) );
    }

    const string* Bridge< string >::FromValue( const Value& v )
    {
        if( !v.isConstant() || v.type() != Type() )
            return nullptr;

        return get_if< string >( &v.val() );
    }

    // sequences
    Value Bridge< SequenceType >::ToValue( const SequenceType& st )
    {
        return Value( Type(), MkStdType( TSID( ct_type ), TSID( sequence ), st.m_containedType ) );

    }

    optional< SequenceType > Bridge< SequenceType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),

            Vec( Lit( "ct_type"_sid ), SubTerm(), Val< void* >(), Lit( "sequence"_sid ),
                SubTerm() ) );






        if( !result )
            return nullopt;

        auto&& [predicates, cgType, containedType] = *result;
        return SequenceType( containedType );
    }

    // Generic values
    const Term& Bridge< Value >::Type()
    {
        // This is a trick to get a generic signature for this type when creating a builtin
        // function. This way we can define a builtin function that take any value of any type as
        // is, without conversion.
        static auto type = HOLE( "_"_sid, "tvar"_sid );
        return type;
    }
} // namespace goose::eir
Changes to bs/builtins/types/basic.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14


15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104

105
106
107
108
109
110
111
112
113
114
115
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
#ifndef GOOSE_BUILTINS_TYPES_BASIC_H
#define GOOSE_BUILTINS_TYPES_BASIC_H

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

    struct SequenceType
    {
        template< typename T >
        SequenceType( T&& containedType ) :
            m_containedType( forward< T >( containedType ) )
        {}



        Term m_containedType;
    };

    struct ValuePatternAny
    {
        static const Term& GetPattern();
    };

    struct ValuePatternT
    {
        static const Term& GetPattern();
    };

    template< typename T >
    struct PatternValueTypeOf
    {
        static const Term& GetPattern()
        {
            static auto pat = GetValueType< T >();
            return pat;
        }
    };

    template< typename T >
    using ValueTypeOf = CustomPattern< Value, PatternValueTypeOf< T > >;

    extern bool IsSequenceType( const Value& v );
    extern Term GetSequenceSubType( const Value& v );
}

namespace goose::eir
{
    template<>
    struct Bridge< void >
    {
        static const Term& Type();
        static const Value& ToValue();
    };

    template<>
    struct Bridge< StringId >
    {
        static const Term& Type();
        static Value ToValue( StringId x );
        static optional< StringId > FromValue( const Value& v );
    };

    template<>
    struct Bridge< bool >
    {
        static const Term& Type();
        static Value ToValue( bool x );
        static optional< bool > FromValue( const Value& v );
    };

    template<>
    struct Bridge< BigInt >
    {
        static const Term& Type();

        template< typename T >
        static Value ToValue( T&& x )
        {
            return Value( Type(), TERM( BigInt( forward< T >( x ) ) ) );
        }

        static const BigInt* FromValue( const Value& v );
    };

    template<>
    struct Bridge< char32_t >
    {
        static const Term& Type();
        static Value ToValue( char32_t x );
        static optional< char32_t > FromValue( const Value& v );
    };

    template<>
    struct Bridge< string >
    {
        static const Term& Type();
        static Value ToValue( const string& x );
        static const string* FromValue( const Value& v );
    };

    template<>
    struct Bridge< builtins::SequenceType >
    {
        static const Term& Type() { return TypeType(); }

        static Value ToValue( const builtins::SequenceType& st );
        static optional< builtins::SequenceType > FromValue( const Value& v );
    };

    template< typename T >
    struct Bridge< Sequence< T > >
    {
        static const Term& Type()
        {
            static auto type = ValueToEIR( goose::eir::ToValue(
                builtins::SequenceType{ GetValueType< T >() } ) );
            return type;
        }

        template< typename S >
        static Value ToValue( S&& s )
        {
            return Value( Type(), make_shared< Sequence< T > >( forward< S >( s ) ) );
        }

        static optional< Sequence< T > > FromValue( const Value& v )
        {
            auto result = Decompose( v.val(),
                Val< ptr< void > >()
            );

            if( !result )
                return nullopt;

            return *static_pointer_cast< Sequence< T > >( result->get() );
        }
    };

    template<>
    struct Bridge< Value >
    {
        static const Term& Type();

        static const Value& ToValue( const Value& v ) { return v; }

        static const Value* FromValue( const Value& v ) { return &v; }
    };

}

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

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

    struct SequenceType
    {
        template< typename T >
        SequenceType( T&& containedType ) :
            m_containedType( forward< T >( containedType ) )

        {
        }

        Term m_containedType;
    };

    struct ValuePatternAny
    {
        static const Term& GetPattern();
    };

    struct ValuePatternT
    {
        static const Term& GetPattern();
    };

    template< typename T > struct PatternValueTypeOf

    {
        static const Term& GetPattern()
        {
            static auto pat = GetValueType< T >();
            return pat;
        }
    };


    template< typename T > using ValueTypeOf = CustomPattern< Value, PatternValueTypeOf< T > >;

    extern bool IsSequenceType( const Value& v );
    extern Term GetSequenceSubType( const Value& v );
} // namespace goose::builtins

namespace goose::eir
{

    template<> struct Bridge< void >
    {
        static const Term& Type();
        static const Value& ToValue();
    };


    template<> struct Bridge< StringId >
    {
        static const Term& Type();
        static Value ToValue( StringId x );
        static optional< StringId > FromValue( const Value& v );
    };


    template<> struct Bridge< bool >
    {
        static const Term& Type();
        static Value ToValue( bool x );
        static optional< bool > FromValue( const Value& v );
    };


    template<> struct Bridge< BigInt >
    {
        static const Term& Type();

        template< typename T > static Value ToValue( T&& x )

        {
            return Value( Type(), TERM( BigInt( forward< T >( x ) ) ) );
        }

        static const BigInt* FromValue( const Value& v );
    };


    template<> struct Bridge< char32_t >
    {
        static const Term& Type();
        static Value ToValue( char32_t x );
        static optional< char32_t > FromValue( const Value& v );
    };


    template<> struct Bridge< string >
    {
        static const Term& Type();
        static Value ToValue( const string& x );
        static const string* FromValue( const Value& v );
    };


    template<> struct Bridge< builtins::SequenceType >
    {
        static const Term& Type() { return TypeType(); }

        static Value ToValue( const builtins::SequenceType& st );
        static optional< builtins::SequenceType > FromValue( const Value& v );
    };

    template< typename T > struct Bridge< Sequence< T > >

    {
        static const Term& Type()
        {
            static auto type =
                ValueToEIR( goose::eir::ToValue( builtins::SequenceType{ GetValueType< T >() } ) );
            return type;
        }

        template< typename S > static Value ToValue( S&& s )

        {
            return Value( Type(), make_shared< Sequence< T > >( forward< S >( s ) ) );
        }

        static optional< Sequence< T > > FromValue( const Value& v )
        {
            auto result = Decompose( v.val(), Val< ptr< void > >() );



            if( !result )
                return nullopt;

            return *static_pointer_cast< Sequence< T > >( result->get() );
        }
    };


    template<> struct Bridge< Value >
    {
        static const Term& Type();

        static const Value& ToValue( const Value& v ) { return v; }

        static const Value* FromValue( const Value& v ) { return &v; }
    };
} // namespace goose::eir


#endif
Changes to bs/builtins/types/constrainedfunc/constrainedfunc.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::builtins;

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

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

        static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( ct_type ), TSID( constrainedfunc ) ) ) );
        return type;
    }

    Value Bridge< builtins::ConstrainedFunc >::ToValue( const builtins::ConstrainedFunc& cf )
    {
        return Value( Type(), VEC(
            Quote( cf.constraintPat() ),
            TERM( static_pointer_cast< void >( cf.invRule() ) ),
            ValueToEIR( cf.func() ) ) );
    }

    optional< builtins::ConstrainedFunc > Bridge< builtins::ConstrainedFunc >::FromValue( const Value& v )

    {
        if( !IsConstrainedFunc( v ) )
            return nullopt;

        auto result = Decompose( v.val(),
            Vec(
                SubTerm(),              // constraint pattern
                Val< ptr< void > >(),   // pInvRule
                SubTerm()               // func
            )
        );
        if( !result )
            return nullopt;

        auto&& [constraintPat, pInvRule, func] = *result;
        return builtins::ConstrainedFunc(
            *Unquote( constraintPat ),
            static_pointer_cast< InvocationRule >( pInvRule ),
            *EIRToValue( func ) );
    }
}














>
|





|
<
|
|


|
>





<
|
|
|
|
<




|
<
|
<

<
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

22
23
24
25
26
27
28
29
30
31
32

33
34
35
36

37
38
39
40
41

42

43

44
#include "builtins/builtins.h"

using namespace goose::builtins;

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

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

    Value Bridge< builtins::ConstrainedFunc >::ToValue( const builtins::ConstrainedFunc& cf )
    {
        return Value( Type(),

            VEC( Quote( cf.constraintPat() ), TERM( static_pointer_cast< void >( cf.invRule() ) ),
                ValueToEIR( cf.func() ) ) );
    }

    optional< builtins::ConstrainedFunc > Bridge< builtins::ConstrainedFunc >::FromValue(
        const Value& v )
    {
        if( !IsConstrainedFunc( v ) )
            return nullopt;

        auto result = Decompose( v.val(),

            Vec( SubTerm(), // constraint pattern
                Val< ptr< void > >(), // pInvRule
                SubTerm() // func
                ) );

        if( !result )
            return nullopt;

        auto&& [constraintPat, pInvRule, func] = *result;
        return builtins::ConstrainedFunc( *Unquote( constraintPat ),

            static_pointer_cast< InvocationRule >( pInvRule ), *EIRToValue( func ) );

    }

} // namespace goose::eir
Changes to bs/builtins/types/constrainedfunc/constrainedfunc.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
#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 ) ),
                m_pInvRule( forward< IR >( pInvRule ) ),
                m_func( forward< F >( func ) )
            {}



            const auto& constraintPat() const { return m_constraintPat; }

            const auto& invRule() const { return m_pInvRule; }

            const auto& func() const { return m_func; }

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

    extern bool IsConstrainedFunc( const Value& tcc );
}


namespace goose::eir
{
    template<>
    struct Bridge< builtins::ConstrainedFunc >
    {
        static const Term& Type();
        static Value ToValue( const builtins::ConstrainedFunc& cf );
        static optional< builtins::ConstrainedFunc > FromValue( const Value& v );
    };
}

#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
#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 ) ),
            m_pInvRule( forward< IR >( pInvRule ) ),
            m_func( forward< F >( func ) )

        {
        }

        const auto& constraintPat() const { return m_constraintPat; }

        const auto& invRule() const { return m_pInvRule; }

        const auto& func() const { return m_func; }

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

    extern bool IsConstrainedFunc( const Value& tcc );

} // namespace goose::builtins

namespace goose::eir
{

    template<> struct Bridge< builtins::ConstrainedFunc >
    {
        static const Term& Type();
        static Value ToValue( const builtins::ConstrainedFunc& cf );
        static optional< builtins::ConstrainedFunc > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/constrainedfunc/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

#include "builtins/builtins.h"

using namespace goose::sema;

namespace goose::builtins
{
    class ConstrainedFuncInvocationRule : public InvocationRule
    {
        public:

            Value resolveInvocation( Context& c, LocationId loc, const Value& callee, const Term& args ) const final
            {
                auto callPat = PrependToVectorTerm( args, HOLE( "_"_sid, "fwd"_sid ) );

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

    void SetupConstrainedFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( Value(
                GetValueType< ConstrainedFunc >(),
                ANYTERM( _ ) ) ),
            make_shared< ConstrainedFuncInvocationRule >() );
    }
}









|
>
|
|
|

|
|
|
|
|

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





<
|
<


<
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

23
24
25

26
27
28
29
30
31
32
33
34
35

36

37
38

39
#include "builtins/builtins.h"

using namespace goose::sema;

namespace goose::builtins
{
    class ConstrainedFuncInvocationRule : public InvocationRule
    {
      public:
        Value resolveInvocation(
            Context& c, LocationId loc, const Value& callee, const Term& args ) const final
        {
            auto callPat = PrependToVectorTerm( args, HOLE( "_"_sid, "fwd"_sid ) );

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

    void SetupConstrainedFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(

            ValueToEIR( Value( GetValueType< ConstrainedFunc >(), ANYTERM( _ ) ) ),

            make_shared< ConstrainedFuncInvocationRule >() );
    }

} // namespace goose::builtins
Changes to bs/builtins/types/constrainedfunc/typecheck.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
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

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

            ParamPat( FuncTypePattern() ),

            ValueToEIR( 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( *EIRToValue( type ) );

            auto rhsVal = *EIRToValue( 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 = ValueToEIR( cfunc->func() );
                    co_yield TypeCheck( lhs, func, tcc );
                    co_return;
                }
            }
        } );

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

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

            ValueToEIR( 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 [tfType,_] = DecomposeTFTSig( type );
            auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *EIRToValue( tfType ) );
            assert( callPat );

            auto rhsVal = *EIRToValue( 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 = ValueToEIR( 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(

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

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

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








<
|
<

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

|
|

|
|
|

|
|
|
|
|
|
|
|
|
|
|





<
|
<


<
|
<

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

|
|
|
|

|
|
|

|
|
|
|
|
|
|
|
|
|
|

|
<

|
<
|
|
|



<
|
<

|
<
<
<

|
<
|
<

<
>
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
    {
        // func type param / constrainedfunc arg
        e.typeCheckingRuleSet()->addTypeCheckingRule(

            ParamPat( FuncTypePattern() ),

            ValueToEIR( 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( *EIRToValue( type ) );

                auto rhsVal = *EIRToValue( 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 = ValueToEIR( cfunc->func() );
                        co_yield TypeCheck( lhs, func, tcc );
                        co_return;
                    }
                }
            } );

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

            ValueToEIR( ValuePattern(

                ANYTERM( _ ), TFuncTypeSigPattern( ANYTERM( _ ), ANYTERM( _ ) ), ANYTERM( _ ) ) ),


            ValueToEIR( 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 [tfType, _] = DecomposeTFTSig( type );
                auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *EIRToValue( tfType ) );
                assert( callPat );

                auto rhsVal = *EIRToValue( 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 = ValueToEIR( 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(

            ValueToEIR( ValuePattern(

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


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




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

            { co_yield { lhs, tcc }; } );

    }

} // namespace goose::builtins
Changes to bs/builtins/types/convert.cpp.
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

{
    void SetupConvert( Env& e )
    {
        using FwdValue = CustomPattern< Value, ForwarderPattern >;

        // Default implementation of ConvertFuncParam():
        // return the param as is.
        RegisterBuiltinFunc< Intrinsic< Value ( FwdValue ) > >( e, e.extConvertFuncParam(),
            []( const Context& c, const Value& p )
            {
                return p;
            } );

        // Default implementation of ConvertFuncArgs():
        // return the args as is.
        // Use generic arguments so that the prelude can override it with a more specific
        // version that explicitely takes Vecs, while still letting us have this default implementation
        // for pre-prelude stuff to be able to work.
        RegisterBuiltinFunc< Intrinsic< Value ( Value, Value ) > >( e, e.extConvertFuncArgs(),
            []( const Context& c, const Value& a, const Value& p )
            {
                return a;
            } );
    }
}








|
|
<
<
<




|
|
|
|
<
<
<

<
>
8
9
10
11
12
13
14
15
16



17
18
19
20
21
22
23
24



25

26
{
    void SetupConvert( Env& e )
    {
        using FwdValue = CustomPattern< Value, ForwarderPattern >;

        // Default implementation of ConvertFuncParam():
        // return the param as is.
        RegisterBuiltinFunc< Intrinsic< Value( FwdValue ) > >(
            e, e.extConvertFuncParam(), []( const Context& c, const Value& p ) { return p; } );




        // Default implementation of ConvertFuncArgs():
        // return the args as is.
        // Use generic arguments so that the prelude can override it with a more specific
        // version that explicitely takes Vecs, while still letting us have this default
        // implementation for pre-prelude stuff to be able to work.
        RegisterBuiltinFunc< Intrinsic< Value( Value, Value ) > >( e, e.extConvertFuncArgs(),
            []( const Context& c, const Value& a, const Value& p ) { return a; } );



    }

} // namespace goose::builtins
Changes to bs/builtins/types/decl.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
#include "builtins/builtins.h"

using namespace goose::builtins;

namespace goose::builtins
{
    bool IsDecl( const Value& d )
    {
        auto typeVal = EIRToValue( d.type() );
        if( !typeVal )
            return false;

        auto result = Decompose( typeVal->val(),
            Vec(
                Lit( "decl"_sid ),
                SubTerm()
            )
        );

        return !!result;
    }

    const Term& Decl::Pattern::GetPattern()
    {
        static auto pattern = ValueToEIR(
            Value( TypeType(), VEC( TSID( decl ), HOLE( "_"_sid ) ) ) );

        return pattern;
    }

    const Term& DeclWithInit::Pattern::GetPattern()
    {
        static auto pattern = ValueToEIR(
            Value( TypeType(), VEC( TSID( decl_with_init ), HOLE( "_"_sid ) ) ) );

        return pattern;
    }
}

namespace goose::eir
{
    Term Bridge< Decl >::Type( const Term& declType )
    {
        return ValueToEIR( Value( TypeType(), VEC( TSID( decl ), declType ) ) );
    }

    Value Bridge< Decl >::ToValue( const Decl& d )
    {
        return Value( Type( d.type() ), TERM( d.name() ) );
    }

    optional< Decl > Bridge< Decl >::FromValue( const Value& v )
    {
        auto typeVal = EIRToValue( v.type() );
        auto result = Decompose( typeVal->val(),
            Vec(
                Lit( "decl"_sid ),
                SubTerm()
            )
        );

        if( !result )
            return nullopt;

        auto&& [type] = *result;

        const auto& name = get< StringId >( v.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
44
45
46
47
48
49
50





51
52
53
54
55
56
57
#include "builtins/builtins.h"

using namespace goose::builtins;

namespace goose::builtins
{
    bool IsDecl( const Value& d )
    {
        auto typeVal = EIRToValue( d.type() );
        if( !typeVal )
            return false;

        auto result = Decompose( typeVal->val(), Vec( Lit( "decl"_sid ), SubTerm() ) );






        return !!result;
    }

    const Term& Decl::Pattern::GetPattern()
    {
        static auto pattern =
            ValueToEIR( Value( TypeType(), VEC( TSID( decl ), HOLE( "_"_sid ) ) ) );

        return pattern;
    }

    const Term& DeclWithInit::Pattern::GetPattern()
    {
        static auto pattern =
            ValueToEIR( Value( TypeType(), VEC( TSID( decl_with_init ), HOLE( "_"_sid ) ) ) );

        return pattern;
    }
} // namespace goose::builtins

namespace goose::eir
{
    Term Bridge< Decl >::Type( const Term& declType )
    {
        return ValueToEIR( Value( TypeType(), VEC( TSID( decl ), declType ) ) );
    }

    Value Bridge< Decl >::ToValue( const Decl& d )
    {
        return Value( Type( d.type() ), TERM( d.name() ) );
    }

    optional< Decl > Bridge< Decl >::FromValue( const Value& v )
    {
        auto typeVal = EIRToValue( v.type() );
        auto result = Decompose( typeVal->val(), Vec( Lit( "decl"_sid ), SubTerm() ) );






        if( !result )
            return nullopt;

        auto&& [type] = *result;

        const auto& name = get< StringId >( v.val() );
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
    {
        return Value( Type( d.type() ), VEC( d.name(), ValueToEIR( d.init() ), d.declLoc() ) );
    }

    optional< DeclWithInit > Bridge< DeclWithInit >::FromValue( const Value& v )
    {
        auto typeVal = EIRToValue( v.type() );
        auto result = Decompose( typeVal->val(),
            Vec(
                Lit( "decl_with_init"_sid ),
                SubTerm()
            )
        );

        if( !result )
            return nullopt;

        auto result2 = Decompose( v.val(),
            Vec(
                Val< StringId >(),
                SubTerm(),
                Val< LocationId >()
            )
        );

        if( !result2 )
            return nullopt;

        auto&& [type] = *result;
        auto&& [name,init,declLoc] = *result2;

        return DeclWithInit( move( type ), name, *EIRToValue( init ), declLoc );
    }
}







|
<
<
<
<
<




|
<
<
<
|
<
<





|



|
67
68
69
70
71
72
73
74





75
76
77
78
79



80


81
82
83
84
85
86
87
88
89
90
    {
        return Value( Type( d.type() ), VEC( d.name(), ValueToEIR( d.init() ), d.declLoc() ) );
    }

    optional< DeclWithInit > Bridge< DeclWithInit >::FromValue( const Value& v )
    {
        auto typeVal = EIRToValue( v.type() );
        auto result = Decompose( typeVal->val(), Vec( Lit( "decl_with_init"_sid ), SubTerm() ) );






        if( !result )
            return nullopt;

        auto result2 =



            Decompose( v.val(), Vec( Val< StringId >(), SubTerm(), Val< LocationId >() ) );



        if( !result2 )
            return nullopt;

        auto&& [type] = *result;
        auto&& [name, init, declLoc] = *result2;

        return DeclWithInit( move( type ), name, *EIRToValue( init ), declLoc );
    }
} // namespace goose::eir
Changes to bs/builtins/types/decl.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14


15

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38


39

40

41

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#ifndef GOOSE_BUILTINS_TYPES_DECL_H
#define GOOSE_BUILTINS_TYPES_DECL_H

namespace goose::builtins
{
    class Decl
    {
        public:
            template< typename T >
            Decl( T&& type, StringId name ) :
                m_type( forward< T >( type ) ),
                m_name( name )
            {}



            const auto& type() const { return m_type; }

            const auto& name() const { return m_name; }

            struct Pattern
            {
                static const Term& GetPattern();
            };

        private:
            Term m_type;
            StringId m_name;
    };

    class DeclWithInit
    {
        public:
            template< typename T, typename V >
            DeclWithInit( T&& type, StringId name, V&& init, LocationId declLoc ) :
                m_init( forward< V >( init ) ),
                m_type( forward< T >( type ) ),
                m_name( name ),
                m_declLoc( declLoc )
            {}



            const auto& type() const { return m_type; }

            const auto& name() const { return m_name; }

            const auto& init() const { return m_init; }

            auto declLoc() const { return m_declLoc; }

            struct Pattern
            {
                static const Term& GetPattern();
            };

        private:
            Value m_init;
            Term m_type;
            StringId m_name;
            LocationId m_declLoc;
    };

    extern bool IsDecl( const Value& d );
}


namespace goose::eir
{
    template<>
    struct Bridge< builtins::Decl >
    {
        static Term Type( const Term& declType );
        static Value ToValue( const builtins::Decl& d );
        static optional< builtins::Decl > FromValue( const Value& v );
    };

    template<>
    struct Bridge< builtins::DeclWithInit >
    {
        static Term Type( const Term& declType );
        static Value ToValue( const builtins::DeclWithInit& d );
        static optional< builtins::DeclWithInit > FromValue( const Value& v );
    };
}

#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
64
65
66

67
68
69
70
71
72
73

74
75
76
77
78
79
80
81
82
#ifndef GOOSE_BUILTINS_TYPES_DECL_H
#define GOOSE_BUILTINS_TYPES_DECL_H

namespace goose::builtins
{
    class Decl
    {
      public:
        template< typename T >
        Decl( T&& type, StringId name ) :
            m_type( forward< T >( type ) ),
            m_name( name )

        {
        }

        const auto& type() const { return m_type; }

        const auto& name() const { return m_name; }

        struct Pattern
        {
            static const Term& GetPattern();
        };

      private:
        Term m_type;
        StringId m_name;
    };

    class DeclWithInit
    {
      public:
        template< typename T, typename V >
        DeclWithInit( T&& type, StringId name, V&& init, LocationId declLoc ) :
            m_init( forward< V >( init ) ),
            m_type( forward< T >( type ) ),
            m_name( name ),
            m_declLoc( declLoc )

        {
        }

        const auto& type() const { return m_type; }

        const auto& name() const { return m_name; }

        const auto& init() const { return m_init; }

        auto declLoc() const { return m_declLoc; }

        struct Pattern
        {
            static const Term& GetPattern();
        };

      private:
        Value m_init;
        Term m_type;
        StringId m_name;
        LocationId m_declLoc;
    };

    extern bool IsDecl( const Value& d );

} // namespace goose::builtins

namespace goose::eir
{

    template<> struct Bridge< builtins::Decl >
    {
        static Term Type( const Term& declType );
        static Value ToValue( const builtins::Decl& d );
        static optional< builtins::Decl > FromValue( const Value& v );
    };


    template<> struct Bridge< builtins::DeclWithInit >
    {
        static Term Type( const Term& declType );
        static Value ToValue( const builtins::DeclWithInit& d );
        static optional< builtins::DeclWithInit > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/destroy.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::parse;
using namespace goose::cir;

namespace goose::builtins
{
    void SetupDestroyValue( Env& e )
    {
        // Default implementation of DestroyValue().
        // Does nothing.
        RegisterBuiltinFunc< Intrinsic< void ( Value ) > >( e, e.extDestroyValue(),
            []( auto&& c, const Value& v ) {} );
    }
}












|
|

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

namespace goose::builtins
{
    void SetupDestroyValue( Env& e )
    {
        // Default implementation of DestroyValue().
        // Does nothing.
        RegisterBuiltinFunc< Intrinsic< void( Value ) > >(
            e, e.extDestroyValue(), []( auto&& c, const Value& v ) {} );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/drop.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

#include "builtins/builtins.h"
#include "parse/parse.h"

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

namespace goose::builtins
{
    void SetupDropValue( Env& e )
    {
        // Default implementation of DropValue().
        // The cir of computed values with side effects is appended to the current BB of the current parser,
        // all other values are discarded and destroyed.
        RegisterBuiltinFunc< Intrinsic< void ( Value, Value ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& b, const Value& v )
            {
                if( v.isConstant() || !DoesInstrSeqHaveSideEffects( *v.cir() ) )
                {
                    DestroyLiveValue( c, v );
                    return;
                }

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

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

        // DropValue for identifier: emit an "undefined identifier" error.
        RegisterBuiltinFunc< Intrinsic< void ( Value, StringId ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& b, const Value& v )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( v.locationId(), "undefined identifier.", 0 );

                return PoisonValue();
            } );

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

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

                    return PoisonValue();
                }

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

        using AnyDeclWithInitType = CustomPattern< DeclWithInit, DeclWithInit::Pattern >;

        // DropValue for DeclWithInit: declare a local variable.
        RegisterBuiltinFunc< Intrinsic< void ( Value, AnyDeclWithInitType ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& b, const Value& v )
            {
                if( !GetCFG( c ) )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "variable declarations are not allowed here." );

                    return PoisonValue();
                }

                auto decl = *FromValue< DeclWithInit >( v );
                return DeclareLocalVar( c, decl.type(), decl.name(), decl.init(), decl.declLoc() );
            } );


        using AnyTNamedDeclWithInitType = CustomPattern< TNamedDeclWithInit, TNamedDeclWithInit::Pattern >;

        // DropValue for TNamedDeclWithInit: declare a local variable with local type inference.
        RegisterBuiltinFunc< Intrinsic< void ( Value, AnyTNamedDeclWithInitType ) > >( e, e.extDropValue(),

            []( const Context& c, const Value& b, const Value& v )
            {
                if( !GetCFG( c ) )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "variable declarations are not allowed here." );

                    return PoisonValue();
                }

                auto decl = *FromValue< TNamedDeclWithInit >( v );
                return DeclareLocalVarWithTypeInference( c, decl.type(), decl.name(), decl.init(), decl.declLoc() );

            } );
    }
}












|
|
|

















|


|
>









|




|
>










|




|
>







>
|


|
>




|
>




|
>


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

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

namespace goose::builtins
{
    void SetupDropValue( Env& e )
    {
        // Default implementation of DropValue().
        // The cir of computed values with side effects is appended to the current BB of the current
        // parser, all other values are discarded and destroyed.
        RegisterBuiltinFunc< Intrinsic< void( Value, Value ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& b, const Value& v )
            {
                if( v.isConstant() || !DoesInstrSeqHaveSideEffects( *v.cir() ) )
                {
                    DestroyLiveValue( c, v );
                    return;
                }

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

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

        // DropValue for identifier: emit an "undefined identifier" error.
        RegisterBuiltinFunc< Intrinsic< void( Value, StringId ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& b, const Value& v )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                    v.locationId(), "undefined identifier.", 0 );
                return PoisonValue();
            } );

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

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

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

        using AnyDeclWithInitType = CustomPattern< DeclWithInit, DeclWithInit::Pattern >;

        // DropValue for DeclWithInit: declare a local variable.
        RegisterBuiltinFunc< Intrinsic< void( Value, AnyDeclWithInitType ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& b, const Value& v )
            {
                if( !GetCFG( c ) )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                        0, "variable declarations are not allowed here." );
                    return PoisonValue();
                }

                auto decl = *FromValue< DeclWithInit >( v );
                return DeclareLocalVar( c, decl.type(), decl.name(), decl.init(), decl.declLoc() );
            } );

        using AnyTNamedDeclWithInitType =
            CustomPattern< TNamedDeclWithInit, TNamedDeclWithInit::Pattern >;

        // DropValue for TNamedDeclWithInit: declare a local variable with local type inference.
        RegisterBuiltinFunc< Intrinsic< void( Value, AnyTNamedDeclWithInitType ) > >( e,
            e.extDropValue(),
            []( const Context& c, const Value& b, const Value& v )
            {
                if( !GetCFG( c ) )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                        0, "variable declarations are not allowed here." );
                    return PoisonValue();
                }

                auto decl = *FromValue< TNamedDeclWithInit >( v );
                return DeclareLocalVarWithTypeInference(
                    c, decl.type(), decl.name(), decl.init(), decl.declLoc() );
            } );
    }

} // namespace goose::builtins
Changes to bs/builtins/types/func/bfunc.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
#include "builtins/builtins.h"

namespace goose::builtins
{
    bool IsBuiltinFunc( const Value& func )
    {
        auto funcType = EIRToValue( func.type() );
        assert( funcType );

        auto decomp = Decompose( funcType->val(),
            Vec(
                Lit( "func"_sid ),
                Lit( "builtin"_sid ),
                SubTerm(),  // return type
                SubTerm(),  // param types
                SubTerm(),  // verif infos
                SubTerm()   // Lowering infos
            )
        );

        if( decomp )
            return true;

        decomp = Decompose( funcType->val(),
            Vec(
                Lit( "func"_sid ),
                Lit( "builtin_eager"_sid ),
                SubTerm(),  // return type
                SubTerm(),  // param types
                SubTerm(),  // verif infos
                SubTerm()   // Lowering infos
            )
        );

        return !!decomp;
    }

    bool IsNonEagerBuiltinFunc( const Value& func )
    {
        auto funcType = EIRToValue( func.type() );
        assert( funcType );

        auto decomp = Decompose( funcType->val(),
            Vec(
                Lit( "func"_sid ),
                Lit( "builtin"_sid ),
                SubTerm(),  // return type
                SubTerm(),  // param types
                SubTerm(),  // verif infos
                SubTerm()   // Lowering infos
            )
        );

        return !!decomp;
    }

    bool IsEagerBuiltinFunc( const Value& func )
    {
        auto funcType = EIRToValue( func.type() );
        assert( funcType );

        auto decomp = Decompose( funcType->val(),
            Vec(
                Lit( "func"_sid ),
                Lit( "builtin_eager"_sid ),
                SubTerm(),  // return type
                SubTerm(),  // param types
                SubTerm(),  // verif infos
                SubTerm()   // Lowering infos
            )
        );

        return !!decomp;
    }

    const Term& BFuncTypePattern()
    {
        static auto BfuncTypePat = ValueToEIR( Value( TypeType(), VEC( TSID( func ),
            TSID( builtin ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );
        return BfuncTypePat;
    }

    const Term& EagerBFuncTypePattern()
    {
        static auto EagerBfuncTypePat = ValueToEIR( Value( TypeType(), VEC( TSID( func ),

            TSID( builtin_eager ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );
        return EagerBfuncTypePat;
    }

    const BuiltinFuncWrapper& GetBuiltinFuncWrapper( const Value& func )
    {
        assert( IsBuiltinFunc( func ) );

        const auto& p = get< ptr< void > >( func.val() );
        return *static_pointer_cast< BuiltinFuncWrapper >( p );
    }
}










<
<
|
|
|
|
|
|
<





<
<
|
|
|
|
|
|
<










<
<
|
|
|
|
|
|
<










<
<
|
|
|
|
|
|
<






|
|





|
>
|










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

namespace goose::builtins
{
    bool IsBuiltinFunc( const Value& func )
    {
        auto funcType = EIRToValue( func.type() );
        assert( funcType );

        auto decomp = Decompose( funcType->val(),


            Vec( Lit( "func"_sid ), Lit( "builtin"_sid ),
                SubTerm(), // return type
                SubTerm(), // param types
                SubTerm(), // verif infos
                SubTerm() // Lowering infos
                ) );


        if( decomp )
            return true;

        decomp = Decompose( funcType->val(),


            Vec( Lit( "func"_sid ), Lit( "builtin_eager"_sid ),
                SubTerm(), // return type
                SubTerm(), // param types
                SubTerm(), // verif infos
                SubTerm() // Lowering infos
                ) );


        return !!decomp;
    }

    bool IsNonEagerBuiltinFunc( const Value& func )
    {
        auto funcType = EIRToValue( func.type() );
        assert( funcType );

        auto decomp = Decompose( funcType->val(),


            Vec( Lit( "func"_sid ), Lit( "builtin"_sid ),
                SubTerm(), // return type
                SubTerm(), // param types
                SubTerm(), // verif infos
                SubTerm() // Lowering infos
                ) );


        return !!decomp;
    }

    bool IsEagerBuiltinFunc( const Value& func )
    {
        auto funcType = EIRToValue( func.type() );
        assert( funcType );

        auto decomp = Decompose( funcType->val(),


            Vec( Lit( "func"_sid ), Lit( "builtin_eager"_sid ),
                SubTerm(), // return type
                SubTerm(), // param types
                SubTerm(), // verif infos
                SubTerm() // Lowering infos
                ) );


        return !!decomp;
    }

    const Term& BFuncTypePattern()
    {
        static auto BfuncTypePat = ValueToEIR( Value( TypeType(),
            VEC( TSID( func ), TSID( builtin ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );
        return BfuncTypePat;
    }

    const Term& EagerBFuncTypePattern()
    {
        static auto EagerBfuncTypePat = ValueToEIR( Value( TypeType(),
            VEC(
                TSID( func ), TSID( builtin_eager ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );
        return EagerBfuncTypePat;
    }

    const BuiltinFuncWrapper& GetBuiltinFuncWrapper( const Value& func )
    {
        assert( IsBuiltinFunc( func ) );

        const auto& p = get< ptr< void > >( func.val() );
        return *static_pointer_cast< BuiltinFuncWrapper >( p );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/func/bfunc.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

30
31
32
33

34
35
36
37

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

62
63
64
65
66
67

68
69
70
71
72
73
74
75


76
77
78

79
80
81
82
83

84

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122

123
124
#ifndef GOOSE_BUILTINS_TYPES_BFUNC_H
#define GOOSE_BUILTINS_TYPES_BFUNC_H

namespace goose::builtins
{
    class FuncVerificationInfos;

    using BuiltinFuncWrapper = function< Value ( const Term& argVec ) >;

    template< typename FT, typename F >
    void RegisterBuiltinFunc( Env& env, StringId name, F&& func, CURRENT_LOC );

    template< typename FT, typename F >
    void RegisterBuiltinFunc( Env& env, ptr< OverloadSet > pOvlSet, F&& func, CURRENT_LOC );

    extern bool IsBuiltinFunc( const Value& func );
    extern bool IsEagerBuiltinFunc( const Value& func );
    extern bool IsNonEagerBuiltinFunc( const Value& func );

    extern const Term& BFuncTypePattern();
    extern const Term& EagerBFuncTypePattern();

    extern const BuiltinFuncWrapper& GetBuiltinFuncWrapper( const Value& func );

    // If the return type of a builtin function is wrapped with this,
    // the function will be eargerly executed (if possible) if it is invoked
    // by the compiled code.
    template< typename T >
    struct Eager

    {};

    template< typename T >
    struct is_eager : public false_type

    {};

    template< typename T >
    struct is_eager< Eager< T > > : public true_type

    {};

    template< typename T >
    constexpr bool is_eager_v = is_eager< T >::value;

    template< typename T >
    struct remove_eager
    {
        using type = T;
    };

    template< typename T >
    struct remove_eager< Eager< T > >
    {
        using type = T;
    };

    template< typename T >
    using remove_eager_t = typename remove_eager< T >::type;

    // This wrapper provides a way to use a custom pattern
    // to match the type.
    template< typename T, typename PP >
    struct CustomPattern

    {};

    // This wrapper provides a way to use a custom pattern
    // to match the type, but only for a constant value.
    template< typename T, typename PP >
    struct CustomConstantPattern

    {};

    // This wrapper defines a builtin function param that takes
    // a type constant representing the given type.
    template< typename T >
    struct TypeParam
    {};



    // Same as above, but takes a type pattern provider.
    template< typename T, typename PP >
    struct TypePatternParam

    {};

    // Wrapper to define a constant valued parameter
    template< typename T, T val >
    struct ConstantParam

    {};

}

namespace goose::eir
{
    template< typename R, typename... T >
    struct Bridge< R ( T... ) >
    {
        using functype = function< R ( T... ) >;

        static const Term& Type();

        template< typename F >
        static Value ToValue( F&& func );

        template< typename F >

        static auto WrapFunc( F&& func );
    };

    template< typename T, typename PP >
    struct Bridge< builtins::CustomPattern< T, PP > >
    {
        static auto Type()
        {
            return GetValueType< T >();
        }

        template< typename TT >
        static auto ToValue( TT&& val )
        {
            return eir::ToValue( forward< TT >( val ) );
        }

        static auto FromValue( const Value& v )
        {
            return eir::FromValue< T >( v );
        }
    };
}


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

namespace goose::builtins
{
    class FuncVerificationInfos;

    using BuiltinFuncWrapper = function< Value( const Term& argVec ) >;

    template< typename FT, typename F >
    void RegisterBuiltinFunc( Env& env, StringId name, F&& func, CURRENT_LOC );

    template< typename FT, typename F >
    void RegisterBuiltinFunc( Env& env, ptr< OverloadSet > pOvlSet, F&& func, CURRENT_LOC );

    extern bool IsBuiltinFunc( const Value& func );
    extern bool IsEagerBuiltinFunc( const Value& func );
    extern bool IsNonEagerBuiltinFunc( const Value& func );

    extern const Term& BFuncTypePattern();
    extern const Term& EagerBFuncTypePattern();

    extern const BuiltinFuncWrapper& GetBuiltinFuncWrapper( const Value& func );

    // If the return type of a builtin function is wrapped with this,
    // the function will be eargerly executed (if possible) if it is invoked
    // by the compiled code.
    template< typename T > struct Eager

    {
    };

    template< typename T > struct is_eager : public false_type

    {
    };


    template< typename T > struct is_eager< Eager< T > > : public true_type
    {
    };


    template< typename T > constexpr bool is_eager_v = is_eager< T >::value;

    template< typename T > struct remove_eager

    {
        using type = T;
    };

    template< typename T > struct remove_eager< Eager< T > >

    {
        using type = T;
    };


    template< typename T > using remove_eager_t = typename remove_eager< T >::type;

    // This wrapper provides a way to use a custom pattern
    // to match the type.
    template< typename T, typename PP > struct CustomPattern

    {
    };

    // This wrapper provides a way to use a custom pattern
    // to match the type, but only for a constant value.
    template< typename T, typename PP > struct CustomConstantPattern

    {
    };

    // This wrapper defines a builtin function param that takes
    // a type constant representing the given type.
    template< typename T > struct TypeParam


    {
    };

    // Same as above, but takes a type pattern provider.
    template< typename T, typename PP > struct TypePatternParam

    {
    };

    // Wrapper to define a constant valued parameter
    template< typename T, T val > struct ConstantParam

    {
    };
} // namespace goose::builtins


namespace goose::eir
{
    template< typename R, typename... T > struct Bridge< R( T... ) >

    {
        using functype = function< R( T... ) >;

        static const Term& Type();




        template< typename F > static Value ToValue( F&& func );

        template< typename F > static auto WrapFunc( F&& func );
    };

    template< typename T, typename PP > struct Bridge< builtins::CustomPattern< T, PP > >

    {
        static auto Type() { return GetValueType< T >(); }




        template< typename TT > static auto ToValue( TT&& val )

        {
            return eir::ToValue( forward< TT >( val ) );
        }

        static auto FromValue( const Value& v ) { return eir::FromValue< T >( v ); }



    };

} // namespace goose::eir

#endif
Changes to bs/builtins/types/func/bfunc.inl.
10
11
12
13
14
15
16
17

18
19
20

21
22
23
24
25
26
27
28
29
30
31
32
        return RegisterBuiltinFunc< FT >( env, pOvlSet, forward< F >( func ), sloc );
    }

    template< typename FT, typename F >
    void RegisterBuiltinFunc( Env& env, ptr< OverloadSet > pOvlSet, F&& func, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        if( !pOvlSet->add( env, ToValue< FT >( forward< F >( func ) ).setLocationId( loc ), GetInvocationRule< FT >() ) )

            G_ERROR( "duplicate overload registered for builtin func." );
    }
}


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







|
>


<
>



|
<







10
11
12
13
14
15
16
17
18
19
20

21
22
23
24
25

26
27
28
29
30
31
32
        return RegisterBuiltinFunc< FT >( env, pOvlSet, forward< F >( func ), sloc );
    }

    template< typename FT, typename F >
    void RegisterBuiltinFunc( Env& env, ptr< OverloadSet > pOvlSet, F&& func, source_location sloc )
    {
        auto loc = Location::Create( sloc );
        if( !pOvlSet->add( env, ToValue< FT >( forward< F >( func ) ).setLocationId( loc ),
                GetInvocationRule< FT >() ) )
            G_ERROR( "duplicate overload registered for builtin func." );
    }

} // namespace goose::builtins

namespace goose::eir
{
    template< typename T > struct BuildBuiltinFuncParamPat

    {
        static const auto& GetParamPattern()
        {
            static auto pat = builtins::ParamPat( GetValueType< T >() );
            return pat;
        }
    };
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
        static const auto& GetParamPattern()
        {
            static auto pat = ValueToEIR( Value( PP::GetPattern(), HOLE( "_"_sid ) ) );
            return pat;
        }
    };

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







<
|







47
48
49
50
51
52
53

54
55
56
57
58
59
60
61
        static const auto& GetParamPattern()
        {
            static auto pat = ValueToEIR( Value( PP::GetPattern(), HOLE( "_"_sid ) ) );
            return pat;
        }
    };


    template< typename T > struct BuildBuiltinFuncParamPat< builtins::TypeParam< T > >
    {
        static const auto& GetParamPattern()
        {
            static auto pat = GetValueType< T >();
            return pat;
        }
    };
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
        static const auto& GetParamPattern()
        {
            static auto pat = ValueToEIR( ToValue( val ) );
            return pat;
        }
    };

    template< typename... T >
    Term BuildBuiltinFuncParamTypeList()
    {
        return VEC( BuildBuiltinFuncParamPat< T >::GetParamPattern()... );
    }

    template< typename R, typename... T >
    const Term& Bridge< R ( T... ) >::Type()
    {
        static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( func ),
            builtins::is_eager_v< R > ? TSID( builtin_eager ) : TSID( builtin ),
            GetValueType< builtins::remove_eager_t< R > >(),
            sema::Quote( BuildBuiltinFuncParamTypeList< T... >() ),
            ptr< void >(), ptr< void >()
        ) ) );
        return type;
    }

    template< typename R, typename... T >
    template< typename F >
    Value Bridge< R ( T... ) >::ToValue( F&& func )
    {

        ptr< void > pWrapper = make_shared< builtins::BuiltinFuncWrapper >( WrapFunc( forward< F >( func ) ) );
        return Value( Type(), TERM( move( pWrapper ) ) );
    }

    template< typename T >
    struct StripCustomPattern
    {
        using type = T;
    };

    template< typename T, typename PP >
    struct StripCustomPattern< builtins::CustomPattern< T, PP > >
    {
        using type = T;
    };

    template< typename T, typename PP >
    struct StripCustomPattern< builtins::CustomConstantPattern< T, PP > >
    {
        using type = T;
    };

    template< typename T >
    struct StripCustomPattern< builtins::TypeParam< T > >
    {
        using type = Value;
    };

    template< typename T, typename PP >
    struct StripCustomPattern< builtins::TypePatternParam< T, PP > >
    {
        using type = T;
    };

    template< typename T, T val >
    struct StripCustomPattern< builtins::ConstantParam< T, val > >
    {
        using type = T;
    };

    template< typename R, typename... T >
    template< typename F >
    auto Bridge< R ( T... ) >::WrapFunc( F&& func )
    {
        return [func]( const Term& argVec ) -> Value
        {

            auto params = Bridge< tuple< typename StripCustomPattern< T >::type... > >::FromVectorTerm( argVec );

            if( !params )
                return PoisonValue();

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

                return eir::ToValue( static_cast< builtins::remove_eager_t< R > >( apply( func, *params ) ) );
        };
    }

}

#endif







|
<




|
<

|
|
|
|
|
<





|

>
|



|
<
















<
|










<
|






|



>
|
>









>
|


>
|
<

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
        static const auto& GetParamPattern()
        {
            static auto pat = ValueToEIR( ToValue( val ) );
            return pat;
        }
    };

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

    {
        return VEC( BuildBuiltinFuncParamPat< T >::GetParamPattern()... );
    }

    template< typename R, typename... T > const Term& Bridge< R( T... ) >::Type()

    {
        static auto type = ValueToEIR( Value( TypeType(),
            VEC( TSID( func ), builtins::is_eager_v< R > ? TSID( builtin_eager ) : TSID( builtin ),
                GetValueType< builtins::remove_eager_t< R > >(),
                sema::Quote( BuildBuiltinFuncParamTypeList< T... >() ), ptr< void >(),
                ptr< void >() ) ) );

        return type;
    }

    template< typename R, typename... T >
    template< typename F >
    Value Bridge< R( T... ) >::ToValue( F&& func )
    {
        ptr< void > pWrapper =
            make_shared< builtins::BuiltinFuncWrapper >( WrapFunc( forward< F >( func ) ) );
        return Value( Type(), TERM( move( pWrapper ) ) );
    }

    template< typename T > struct StripCustomPattern

    {
        using type = T;
    };

    template< typename T, typename PP >
    struct StripCustomPattern< builtins::CustomPattern< T, PP > >
    {
        using type = T;
    };

    template< typename T, typename PP >
    struct StripCustomPattern< builtins::CustomConstantPattern< T, PP > >
    {
        using type = T;
    };


    template< typename T > struct StripCustomPattern< builtins::TypeParam< T > >
    {
        using type = Value;
    };

    template< typename T, typename PP >
    struct StripCustomPattern< builtins::TypePatternParam< T, PP > >
    {
        using type = T;
    };


    template< typename T, T val > struct StripCustomPattern< builtins::ConstantParam< T, val > >
    {
        using type = T;
    };

    template< typename R, typename... T >
    template< typename F >
    auto Bridge< R( T... ) >::WrapFunc( F&& func )
    {
        return [func]( const Term& argVec ) -> Value
        {
            auto params =
                Bridge< tuple< typename StripCustomPattern< T >::type... > >::FromVectorTerm(
                    argVec );
            if( !params )
                return PoisonValue();

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


#endif
Changes to bs/builtins/types/func/bintrinsic.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
#include "builtins/builtins.h"

namespace goose::builtins
{
    bool IsBuiltinIntrinsicFunc( const Value& func )
    {
        auto funcType = EIRToValue( func.type() );
        assert( funcType );

        auto decomp = Decompose( funcType->val(),
            Vec(
                Lit( "func"_sid ),
                Lit( "intrinsic_builtin"_sid ),
                SubTerm(),  // return type
                SubTerm(),  // param types
                SubTerm(),  // verif infos
                SubTerm()   // Lowering infos
            )
        );

        return !!decomp;
    }

    const BuiltinIntrinsicFuncWrapper& GetBuiltinIntrinsicFuncWrapper( const Value& func )
    {
        assert( IsBuiltinIntrinsicFunc( func ) );

        const auto& p = get< ptr< void > >( func.val() );
        return *static_pointer_cast< BuiltinIntrinsicFuncWrapper >( p );
    }
}










<
<
|
|
|
|
|
|
<











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

namespace goose::builtins
{
    bool IsBuiltinIntrinsicFunc( const Value& func )
    {
        auto funcType = EIRToValue( func.type() );
        assert( funcType );

        auto decomp = Decompose( funcType->val(),


            Vec( Lit( "func"_sid ), Lit( "intrinsic_builtin"_sid ),
                SubTerm(), // return type
                SubTerm(), // param types
                SubTerm(), // verif infos
                SubTerm() // Lowering infos
                ) );


        return !!decomp;
    }

    const BuiltinIntrinsicFuncWrapper& GetBuiltinIntrinsicFuncWrapper( const Value& func )
    {
        assert( IsBuiltinIntrinsicFunc( func ) );

        const auto& p = get< ptr< void > >( func.val() );
        return *static_pointer_cast< BuiltinIntrinsicFuncWrapper >( p );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/func/bintrinsic.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
#ifndef GOOSE_BUILTINS_TYPES_FUNC_BINTRINSIC_H
#define GOOSE_BUILTINS_TYPES_FUNC_BINTRINSIC_H

namespace goose::builtins
{
    extern bool IsBuiltinIntrinsicFunc( const Value& func );

    // A builtin intrinsic is defined on the C++ side by creating a builtin func
    // whose type is wrapped in the Intrinsic type.
    // The provided function should take and return only Values.
    template< typename F >
    struct Intrinsic

    {};

    using BuiltinIntrinsicFuncWrapper = function< Value ( Context& c, const Term& argVec ) >;
    extern const BuiltinIntrinsicFuncWrapper& GetBuiltinIntrinsicFuncWrapper( const Value& func );
}

namespace goose::eir
{
    template< typename R, typename... T >
    struct Bridge< builtins::Intrinsic< R ( T... ) > >
    {
        using functype = function< R ( T... ) >;

        static const Term& Type( const ptr< builtins::FuncVerificationInfos >& fvi = nullptr );

        template< typename F >

        static Value ToValue( F&& func, const ptr< builtins::FuncVerificationInfos >& fvi = nullptr );

        template< typename F >
        static auto WrapFunc( F&& func );
    };
}

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

namespace goose::builtins
{
    extern bool IsBuiltinIntrinsicFunc( const Value& func );

    // A builtin intrinsic is defined on the C++ side by creating a builtin func
    // whose type is wrapped in the Intrinsic type.
    // The provided function should take and return only Values.
    template< typename F > struct Intrinsic

    {
    };

    using BuiltinIntrinsicFuncWrapper = function< Value( Context& c, const Term& argVec ) >;
    extern const BuiltinIntrinsicFuncWrapper& GetBuiltinIntrinsicFuncWrapper( const Value& func );
} // namespace goose::builtins

namespace goose::eir
{
    template< typename R, typename... T > struct Bridge< builtins::Intrinsic< R( T... ) > >

    {
        using functype = function< R( T... ) >;

        static const Term& Type( const ptr< builtins::FuncVerificationInfos >& fvi = nullptr );

        template< typename F >
        static Value ToValue(
            F&& func, const ptr< builtins::FuncVerificationInfos >& fvi = nullptr );

        template< typename F > static auto WrapFunc( F&& func );

    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/func/bintrinsic.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
#ifndef GOOSE_BUILTINS_TYPES_FUNC_BINTRINSIC_INL
#define GOOSE_BUILTINS_TYPES_FUNC_BINTRINSIC_INL

namespace goose::eir
{
    template< typename R, typename... T >
    const Term& Bridge< builtins::Intrinsic< R ( T... ) > >::Type( const ptr< builtins::FuncVerificationInfos >& fvi )

    {
        static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( func ),
            TSID( intrinsic_builtin ),
            GetValueType< R >(),
            sema::Quote( BuildBuiltinFuncParamTypeList< T... >() ),
            static_pointer_cast< void >( fvi ), ptr< void >()
        ) ) );
        return type;
    }

    template< typename R, typename... T >
    template< typename F >

    Value Bridge< builtins::Intrinsic< R ( T... ) > >::ToValue( F&& func, const ptr< builtins::FuncVerificationInfos >& fvi )
    {
        ptr< void > pWrapper = make_shared< builtins::BuiltinIntrinsicFuncWrapper >( WrapFunc( forward< F >( func ) ) );

        return Value( Type( fvi ), TERM( move( pWrapper ) ) );
    }

    template< typename T > using AsValue = Value;

    template< typename R, typename... T >
    template< typename F >
    auto Bridge< builtins::Intrinsic< R ( T... ) > >::WrapFunc( F&& func )
    {
        return [func]( sema::Context& c, const Term& argVec ) -> Value
        {
            auto params = Bridge< tuple< AsValue< T >... > >::FromVectorTerm( argVec );
            assert( params );

            if constexpr( is_void_v< R > )
            {
                apply( func, tuple_cat( tuple< sema::Context& >( c ), *params ) );
                return Value( GetValueType< void >(), 0U );
            }
            else
                return apply( func, tuple_cat( make_tuple( c ), *params ) );
        };
    }
}

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

namespace goose::eir
{
    template< typename R, typename... T >
    const Term& Bridge< builtins::Intrinsic< R( T... ) > >::Type(
        const ptr< builtins::FuncVerificationInfos >& fvi )
    {
        static auto type = ValueToEIR( Value( TypeType(),
            VEC( TSID( func ), TSID( intrinsic_builtin ), GetValueType< R >(),

                sema::Quote( BuildBuiltinFuncParamTypeList< T... >() ),
                static_pointer_cast< void >( fvi ), ptr< void >() ) ) );

        return type;
    }

    template< typename R, typename... T >
    template< typename F >
    Value Bridge< builtins::Intrinsic< R( T... ) > >::ToValue(
        F&& func, const ptr< builtins::FuncVerificationInfos >& fvi )
    {
        ptr< void > pWrapper = make_shared< builtins::BuiltinIntrinsicFuncWrapper >(
            WrapFunc( forward< F >( func ) ) );
        return Value( Type( fvi ), TERM( move( pWrapper ) ) );
    }

    template< typename T > using AsValue = Value;

    template< typename R, typename... T >
    template< typename F >
    auto Bridge< builtins::Intrinsic< R( T... ) > >::WrapFunc( F&& func )
    {
        return [func]( sema::Context& c, const Term& argVec ) -> Value
        {
            auto params = Bridge< tuple< AsValue< T >... > >::FromVectorTerm( argVec );
            assert( params );

            if constexpr( is_void_v< R > )
            {
                apply( func, tuple_cat( tuple< sema::Context& >( c ), *params ) );
                return Value( GetValueType< void >(), 0U );
            }
            else
                return apply( func, tuple_cat( make_tuple( c ), *params ) );
        };
    }
} // namespace goose::eir

#endif
Changes to bs/builtins/types/func/build.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
#include "builtins/builtins.h"

namespace goose::builtins
{
    Term BuildParamPat( const Context& c, Value type, LocationId loc )
    {
        if( !type.isType() )
            type = ToType( c, type );

        return ValueToEIR( ValuePattern( TSID( param ), ValueToEIR( type ), HOLE( "_"_sid ) ).setLocationId( loc ) );

    }


    optional< FuncType > BuildFuncType( const Context& c, const Value& returnType, const Value& params )
    {
        auto verificationIdentity = VEC( Env::NewUniqueId() );
        c.env()->addVisibilityRule( c.identity(), verificationIdentity );

        auto paramCount = TupleSize( params );

        auto tv = make_shared< Vector >();
        tv->reserve( paramCount );

        FuncLoweringInfos fli;
        fli.loweredParamTypes.reserve( paramCount );

        bool failed = false;

        uint32_t varId = 0;
        ForEachInTuple( params, [&]( auto&& param )

        {
            if( IsDecl( param ) )
            {
                auto decl = *FromValue< Decl >( param );
                auto declType = *EIRToValue( decl.type() );
                auto loweredDeclType = LowerType( c, declType );
                if( !loweredDeclType )
                {
                    failed = true;
                    return false;
                }

                // Handle parameter packs (open tuples)
                if( IsOpenTuple( declType ) )
                {
                    ForEachInTuples( declType, *loweredDeclType, [&]( auto&& type, auto&& loweredType )

                    {
                        fli.loweredParamTypes.emplace_back( loweredType );
                        tv->append( BuildParamPat( c, type, param.locationId() ) );
                        return true;
                    } );

                    if( failed )
                        return false;
                }
                else
                {
                    fli.loweredParamTypes.emplace_back( *loweredDeclType );
                    tv->append( BuildParamPat( c, declType, param.locationId() ) );
                }

                // Bind a stand-in value with the parameters name to be used inside of verification expressions.

                auto paramVerificationIdentity = AppendToVectorTerm( verificationIdentity,
                    TERM( decl.name() ) );

                c.env()->storeValue( paramVerificationIdentity, ANYTERM( _ ),
                    ValueToEIR( BuildComputedValue( decl.type(),
                        cir::DataPathOf( varId++, param.locationId() ),
                        cir::Load( ValueToEIR( *loweredDeclType ), param.locationId() ) ) ) );
            }
            else if( param.isConstant() )
                tv->append( ValueToEIR( param ) );

            return true;
        } );

        if( failed )
            return nullopt;

        auto rtType = ToType( c, returnType );

        auto loweredRTType = LowerType( c, rtType );
        if( !loweredRTType )
            return nullopt;

        fli.loweredReturnType = move( *loweredRTType );

        // 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 cir instruction. This will allow verification

        // expressions to refer to the current function's return value as a value of the correct type.
        auto rtTerm = ValueToEIR( rtType );
        if( rtTerm != GetValueType< void >() )
        {
            auto name = "@result"_sid;
            auto retValVerificationIdentity = AppendToVectorTerm( verificationIdentity, TERM( name ) );


            c.env()->storeValue( retValVerificationIdentity, ANYTERM( _ ),

                ValueToEIR( BuildComputedValue( rtTerm, cir::Placeholder( rtTerm, name, returnType.locationId() ) ) ) );
        }

        auto pVerifInfos = make_shared< FuncVerificationInfos >( move( verificationIdentity ) );

        return FuncType( rtTerm, tv, move( pVerifInfos ), make_shared< FuncLoweringInfos >( move( fli ) ) );
    }

    Func BuildExternalFunc( FuncType funcType, const string& symbol, bool varArg )
    {
        if( varArg )
            funcType.setKind( FuncType::Kind::VarArg );
        return Func( funcType, symbol );
    }

    optional< Func > BuildFunc( const Context& c,
        const Term& parentIdentity, const Term& funcIdentity, const Value& returnType,
        const Value& paramsDecl, const ptr< void >& unparsedBody, Context& out_bodyContext )
    {
        auto funcType = BuildFuncType( c, returnType, paramsDecl );
        if( !funcType )
            return nullopt;

        return BuildFunc( c, *funcType, parentIdentity, funcIdentity, paramsDecl, unparsedBody, out_bodyContext );
    }

    Func BuildFunc( const Context& c,
        const FuncType& funcType, const Term& parentIdentity, const Term& funcIdentity,
        const Value& paramsDecl, const ptr< void >& unparsedBody, Context& out_bodyContext )
    {
        // TODO: instead of a normal import rule from the parent scope, we should use
        // a custom visibility rule that deals with variables from the parent context
        // in a special way:
        // If the function body tries to access variables from the parent function,
        // we should invoke an overridable global function that can transform both the
        // variable object and the function type. This way, we can implement/customize









|
>


>
|















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

|
|
|
|
>
|
|
|
|
|

|
|
|
|
|
|
|
|

|
>
|
|

|
|
|
|
|
|
|

|
|












|
|
>
|




|
>


>
|



>
|









|
|
|




>
|


|
|
|







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

namespace goose::builtins
{
    Term BuildParamPat( const Context& c, Value type, LocationId loc )
    {
        if( !type.isType() )
            type = ToType( c, type );

        return ValueToEIR( ValuePattern( TSID( param ), ValueToEIR( type ), HOLE( "_"_sid ) )
                               .setLocationId( loc ) );
    }

    optional< FuncType > BuildFuncType(
        const Context& c, const Value& returnType, const Value& params )
    {
        auto verificationIdentity = VEC( Env::NewUniqueId() );
        c.env()->addVisibilityRule( c.identity(), verificationIdentity );

        auto paramCount = TupleSize( params );

        auto tv = make_shared< Vector >();
        tv->reserve( paramCount );

        FuncLoweringInfos fli;
        fli.loweredParamTypes.reserve( paramCount );

        bool failed = false;

        uint32_t varId = 0;
        ForEachInTuple( params,
            [&]( auto&& param )
            {
                if( IsDecl( param ) )
                {
                    auto decl = *FromValue< Decl >( param );
                    auto declType = *EIRToValue( decl.type() );
                    auto loweredDeclType = LowerType( c, declType );
                    if( !loweredDeclType )
                    {
                        failed = true;
                        return false;
                    }

                    // Handle parameter packs (open tuples)
                    if( IsOpenTuple( declType ) )
                    {
                        ForEachInTuples( declType, *loweredDeclType,
                            [&]( auto&& type, auto&& loweredType )
                            {
                                fli.loweredParamTypes.emplace_back( loweredType );
                                tv->append( BuildParamPat( c, type, param.locationId() ) );
                                return true;
                            } );

                        if( failed )
                            return false;
                    }
                    else
                    {
                        fli.loweredParamTypes.emplace_back( *loweredDeclType );
                        tv->append( BuildParamPat( c, declType, param.locationId() ) );
                    }

                    // Bind a stand-in value with the parameters name to be used inside of
                    // verification expressions.
                    auto paramVerificationIdentity =
                        AppendToVectorTerm( verificationIdentity, TERM( decl.name() ) );

                    c.env()->storeValue( paramVerificationIdentity, ANYTERM( _ ),
                        ValueToEIR( BuildComputedValue( decl.type(),
                            cir::DataPathOf( varId++, param.locationId() ),
                            cir::Load( ValueToEIR( *loweredDeclType ), param.locationId() ) ) ) );
                }
                else if( param.isConstant() )
                    tv->append( ValueToEIR( param ) );

                return true;
            } );

        if( failed )
            return nullopt;

        auto rtType = ToType( c, returnType );

        auto loweredRTType = LowerType( c, rtType );
        if( !loweredRTType )
            return nullopt;

        fli.loweredReturnType = move( *loweredRTType );

        // 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
        // cir instruction. This will allow verification expressions to refer to the current
        // function's return value as a value of the correct type.
        auto rtTerm = ValueToEIR( rtType );
        if( rtTerm != GetValueType< void >() )
        {
            auto name = "@result"_sid;
            auto retValVerificationIdentity =
                AppendToVectorTerm( verificationIdentity, TERM( name ) );

            c.env()->storeValue( retValVerificationIdentity, ANYTERM( _ ),
                ValueToEIR( BuildComputedValue(
                    rtTerm, cir::Placeholder( rtTerm, name, returnType.locationId() ) ) ) );
        }

        auto pVerifInfos = make_shared< FuncVerificationInfos >( move( verificationIdentity ) );
        return FuncType(
            rtTerm, tv, move( pVerifInfos ), make_shared< FuncLoweringInfos >( move( fli ) ) );
    }

    Func BuildExternalFunc( FuncType funcType, const string& symbol, bool varArg )
    {
        if( varArg )
            funcType.setKind( FuncType::Kind::VarArg );
        return Func( funcType, symbol );
    }

    optional< Func > BuildFunc( const Context& c, const Term& parentIdentity,
        const Term& funcIdentity, const Value& returnType, const Value& paramsDecl,
        const ptr< void >& unparsedBody, Context& out_bodyContext )
    {
        auto funcType = BuildFuncType( c, returnType, paramsDecl );
        if( !funcType )
            return nullopt;
        return BuildFunc(
            c, *funcType, parentIdentity, funcIdentity, paramsDecl, unparsedBody, out_bodyContext );
    }

    Func BuildFunc( const Context& c, const FuncType& funcType, const Term& parentIdentity,
        const Term& funcIdentity, const Value& paramsDecl, const ptr< void >& unparsedBody,
        Context& out_bodyContext )
    {
        // TODO: instead of a normal import rule from the parent scope, we should use
        // a custom visibility rule that deals with variables from the parent context
        // in a special way:
        // If the function body tries to access variables from the parent function,
        // we should invoke an overridable global function that can transform both the
        // variable object and the function type. This way, we can implement/customize
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

        out_bodyContext = Context( c.env(), identity, funcType.returnType() );
        auto pFuncCIR = Env::CreateCIRFunc( identity );

        return Func( funcType, unparsedBody, get< pvec >( paramsDecl.val() ), pFuncCIR.get() );
    }

    optional< Func > BuildFunc( const Context& c,
        const Term& funcIdentity, const Value& returnType,
        const Value& paramsDecl, const ptr< cir::CFG >& cfg )
    {
        auto funcType = BuildFuncType( c, returnType, paramsDecl );
        if( !funcType )
            return nullopt;

        auto funcTypeTerm = ValueToEIR( ToValue( *funcType ) );







|
<







148
149
150
151
152
153
154
155

156
157
158
159
160
161
162

        out_bodyContext = Context( c.env(), identity, funcType.returnType() );
        auto pFuncCIR = Env::CreateCIRFunc( identity );

        return Func( funcType, unparsedBody, get< pvec >( paramsDecl.val() ), pFuncCIR.get() );
    }

    optional< Func > BuildFunc( const Context& c, const Term& funcIdentity, const Value& returnType,

        const Value& paramsDecl, const ptr< cir::CFG >& cfg )
    {
        auto funcType = BuildFuncType( c, returnType, paramsDecl );
        if( !funcType )
            return nullopt;

        auto funcTypeTerm = ValueToEIR( ToValue( *funcType ) );
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

        if( !ftype )
            return ANYTERM( _ );

        auto apv = make_shared< Vector >();
        apv->reserve( VecSize( ftype->params() ) + 1 );
        apv->append( ftype->returnType() );

        ForEachInVectorTerm( ftype->params(), [&]( auto&& param )

        {
            auto result = Decompose( param,
                Vec(
                    Lit( "value"_sid ),
                    SubTerm(),
                    SubTerm(),
                    SubTerm(),
                    Val< LocationId >()
                )
            );
            assert( result );

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

            if( sort == TSID( constant ) )
                apv->append( param );
            else

                apv->append( ValueToEIR( ValuePattern( TSID( computed ), type, TERM( ptr< void >() ) ) ) );

            return true;
        } );

        return TERM( apv );
    }
}








|
>
|
|
<
|
<
<
<
|
<
<
|

|

|
|
|
>
|

|
|



<
>
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
        if( !ftype )
            return ANYTERM( _ );

        auto apv = make_shared< Vector >();
        apv->reserve( VecSize( ftype->params() ) + 1 );
        apv->append( ftype->returnType() );

        ForEachInVectorTerm( ftype->params(),
            [&]( auto&& param )
            {
                auto result = Decompose( param,

                    Vec( Lit( "value"_sid ), SubTerm(), SubTerm(), SubTerm(),



                        Val< LocationId >() ) );


                assert( result );

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

                if( sort == TSID( constant ) )
                    apv->append( param );
                else
                    apv->append( ValueToEIR(
                        ValuePattern( TSID( computed ), type, TERM( ptr< void >() ) ) ) );

                return true;
            } );

        return TERM( apv );
    }

} // namespace goose::builtins
Changes to bs/builtins/types/func/build.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_FUNC_BUILD_H
#define GOOSE_BUILTINS_FUNC_BUILD_H

namespace goose::builtins
{
    extern Term BuildParamPat( const Context& c, Value type, LocationId loc );

    extern optional< FuncType > BuildFuncType( const Context& c, const Value& returnType, const Value& params );


    extern Func BuildExternalFunc( FuncType funcType, const string& symbol, bool varArg = false );

    extern optional< Func > BuildFunc( const Context& c,
        const Term& parentIdentity, const Term& funcIdentity, const Value& returnType,
        const Value& paramsDecl, const ptr< void >& unparsedBody, Context& out_bodyContext );

    extern Func BuildFunc( const Context& c,
        const FuncType& funcType, const Term& parentIdentity, const Term& funcIdentity,
        const Value& paramsDecl, const ptr< void >& unparsedBody, Context& out_bodyContext );

    extern optional< Func > BuildFunc( const Context& c,
        const Term& funcIdentity, const Value& returnType,
        const Value& paramsDecl, const ptr< cir::CFG >& cfg );

    extern Term BuildCallPatternFromFuncType( const Value& funcType );
}

#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_BUILTINS_FUNC_BUILD_H
#define GOOSE_BUILTINS_FUNC_BUILD_H

namespace goose::builtins
{
    extern Term BuildParamPat( const Context& c, Value type, LocationId loc );

    extern optional< FuncType > BuildFuncType(
        const Context& c, const Value& returnType, const Value& params );

    extern Func BuildExternalFunc( FuncType funcType, const string& symbol, bool varArg = false );

    extern optional< Func > BuildFunc( const Context& c, const Term& parentIdentity,
        const Term& funcIdentity, const Value& returnType, const Value& paramsDecl,
        const ptr< void >& unparsedBody, Context& out_bodyContext );

    extern Func BuildFunc( const Context& c, const FuncType& funcType, const Term& parentIdentity,
        const Term& funcIdentity, const Value& paramsDecl, const ptr< void >& unparsedBody,
        Context& out_bodyContext );

    extern optional< Func > BuildFunc( const Context& c, const Term& funcIdentity,

        const Value& returnType, const Value& paramsDecl, const ptr< cir::CFG >& cfg );

    extern Term BuildCallPatternFromFuncType( const Value& funcType );
} // namespace goose::builtins

#endif
Changes to bs/builtins/types/func/compilation/common.cpp.
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

        if( !fvi->parse( c ) )
            return false;

        // Perform lazy parsing on param types' predicates
        bool predsOk = true;

        ForEachInVectorTerm( f.type().params(), [&]( auto&& param )

        {
            auto result = Decompose( param,
                Vec(
                    Lit( "value"_sid ),
                    SubTerm(),
                    SubTerm(),
                    SubTerm(),
                    Val< LocationId >()
                )
            );
            assert( result );

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

            if( !ParseTypePredicates( c, *EIRToValue( type ) ) )
                predsOk = false;

            return true;
        } );

        if( !predsOk )
            return false;

        // Perform lazy parsing on return type predicates
        return ParseTypePredicates( c, *EIRToValue( f.type().returnType() ) );
    }

    Term CreateFuncBodyIdentity( const Context& c, const Term& funcIdentity )
    {
        auto bodyIdentity = AppendToVectorTerm( funcIdentity, TSID( 0_locvars ) );
        c.env()->addVisibilityRule( funcIdentity, bodyIdentity );
        return bodyIdentity;
    }

    bool ParseFunctionBody( const Context& localContext, CodeBuilder& cb, const Func& f )
    {
        const auto& pFuncCIR = f.cir();
        const auto& cfg = cb.cfg();


        auto tokProvider = lex::MakeVectorAdapter( *static_pointer_cast< vector< TermLoc > >( f.tokens() ) );
        auto r = make_shared< parse::Resolver >( tokProvider, localContext );
        Parser p( r );

        bool success = false;
        {
            LifetimeScopeGuard lsg( localContext );
            success = p.parseBraceBlock();







|
>
|
|
<
|
<
<
<
|
<
<
|

|

|
|

|
|




















>
|







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

        if( !fvi->parse( c ) )
            return false;

        // Perform lazy parsing on param types' predicates
        bool predsOk = true;

        ForEachInVectorTerm( f.type().params(),
            [&]( auto&& param )
            {
                auto result = Decompose( param,

                    Vec( Lit( "value"_sid ), SubTerm(), SubTerm(), SubTerm(),



                        Val< LocationId >() ) );


                assert( result );

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

                if( !ParseTypePredicates( c, *EIRToValue( type ) ) )
                    predsOk = false;

                return true;
            } );

        if( !predsOk )
            return false;

        // Perform lazy parsing on return type predicates
        return ParseTypePredicates( c, *EIRToValue( f.type().returnType() ) );
    }

    Term CreateFuncBodyIdentity( const Context& c, const Term& funcIdentity )
    {
        auto bodyIdentity = AppendToVectorTerm( funcIdentity, TSID( 0_locvars ) );
        c.env()->addVisibilityRule( funcIdentity, bodyIdentity );
        return bodyIdentity;
    }

    bool ParseFunctionBody( const Context& localContext, CodeBuilder& cb, const Func& f )
    {
        const auto& pFuncCIR = f.cir();
        const auto& cfg = cb.cfg();

        auto tokProvider =
            lex::MakeVectorAdapter( *static_pointer_cast< vector< TermLoc > >( f.tokens() ) );
        auto r = make_shared< parse::Resolver >( tokProvider, localContext );
        Parser p( r );

        bool success = false;
        {
            LifetimeScopeGuard lsg( localContext );
            success = p.parseBraceBlock();
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
                    "missing return statement in a function with non-void return type." );
                return false;
            }

            // Implicit return at the end of a void function:
            // Emit the cleanups, and the terminator.
            // TODO: at some point we'll want to check for reachability in the static verifier,
            // and either emit the implicit return or declare the code unreachable depending on the result.
            // The reachability analysis will have to be done before contract validation, as the
            // calls to DestroyValue() may also have requirements to enforce, so we'll need to emit
            // the eventual implicit return first.
            p.flushValue();
            cb.destroyAllLiveValues( localContext );
            cfg->emitTerminator( r->currentLocation(), cir::RetVoid( r->currentLocation() ) );
        }

        return true;
    }
}







|
|
|
|







|
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
                    "missing return statement in a function with non-void return type." );
                return false;
            }

            // Implicit return at the end of a void function:
            // Emit the cleanups, and the terminator.
            // TODO: at some point we'll want to check for reachability in the static verifier,
            // and either emit the implicit return or declare the code unreachable depending on the
            // result. The reachability analysis will have to be done before contract validation, as
            // the calls to DestroyValue() may also have requirements to enforce, so we'll need to
            // emit the eventual implicit return first.
            p.flushValue();
            cb.destroyAllLiveValues( localContext );
            cfg->emitTerminator( r->currentLocation(), cir::RetVoid( r->currentLocation() ) );
        }

        return true;
    }
} // namespace goose::builtins
Changes to bs/builtins/types/func/compilation/common.h.
1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
19
#ifndef GOOSE_BUILTINS_TYPES_FUNC_COMPILATION_COMMON_H
#define GOOSE_BUILTINS_TYPES_FUNC_COMPILATION_COMMON_H

namespace goose::builtins
{
    extern bool ParseAllFuncPredicates( const Context& c, const Func& f );
    extern Term CreateFuncBodyIdentity( const Context& c, const Term& funcIdentity );
    extern bool ParseFunctionBody( const Context& localContext, CodeBuilder& cb, const Func& f );

    template< typename MPARAMF, typename MPACKPARAMF >
    void MaterializeFuncParams( const Context& c, CodeBuilder& cb, const Func& f, const Term& bodyIdentity, uint32_t startVarId,

        MPARAMF&& matParamFunc, MPACKPARAMF&& matPackParamFunc )
    {
        uint32_t actualParamIndex = 0;
        uint32_t varId = startVarId;

        for( auto&& t : f.paramsDecl()->terms() )
        {
            auto param = *EIRToValue( t );










|
>
|







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

namespace goose::builtins
{
    extern bool ParseAllFuncPredicates( const Context& c, const Func& f );
    extern Term CreateFuncBodyIdentity( const Context& c, const Term& funcIdentity );
    extern bool ParseFunctionBody( const Context& localContext, CodeBuilder& cb, const Func& f );

    template< typename MPARAMF, typename MPACKPARAMF >
    void MaterializeFuncParams( const Context& c, CodeBuilder& cb, const Func& f,
        const Term& bodyIdentity, uint32_t startVarId, MPARAMF&& matParamFunc,
        MPACKPARAMF&& matPackParamFunc )
    {
        uint32_t actualParamIndex = 0;
        uint32_t varId = startVarId;

        for( auto&& t : f.paramsDecl()->terms() )
        {
            auto param = *EIRToValue( t );
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
            // Handle parameter packs (open tuples)
            auto declType = *EIRToValue( decl.type() );
            if( IsOpenTuple( declType ) )
            {
                auto packId = varId;

                auto pack = EmptyClosedTuple();
                ForEachInTuple( declType, [&]( auto&& type )

                {
                    auto val = matPackParamFunc( type, varId++ );
                    pack = AppendToTuple( pack, move( val ) );
                    return true;
                } );

                auto paramIdentity = AppendToVectorTerm( bodyIdentity, TERM( decl.name() ) );
                c.env()->storeValue( paramIdentity, ANYTERM( _ ),
                    ValueToEIR( pack ) );

                cb.declareValue( c, pack, packId );
            }
            else
            {

                auto type = EIRToValuePattern( get< pvec >( f.type().params() )->terms()[actualParamIndex++] )->type();

                if( IsWrappedType( type ) )
                    type = GetValueType< TypeWrapper< Value > >();

                matParamFunc( move( type ), decl.name(), varId++, param.locationId() );
            }
        }
    }
}

#endif







|
>
|
|
|
|
|


|
<





>
|
>







|


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
            // Handle parameter packs (open tuples)
            auto declType = *EIRToValue( decl.type() );
            if( IsOpenTuple( declType ) )
            {
                auto packId = varId;

                auto pack = EmptyClosedTuple();
                ForEachInTuple( declType,
                    [&]( auto&& type )
                    {
                        auto val = matPackParamFunc( type, varId++ );
                        pack = AppendToTuple( pack, move( val ) );
                        return true;
                    } );

                auto paramIdentity = AppendToVectorTerm( bodyIdentity, TERM( decl.name() ) );
                c.env()->storeValue( paramIdentity, ANYTERM( _ ), ValueToEIR( pack ) );


                cb.declareValue( c, pack, packId );
            }
            else
            {
                auto type = EIRToValuePattern(
                    get< pvec >( f.type().params() )->terms()[actualParamIndex++] )
                                ->type();
                if( IsWrappedType( type ) )
                    type = GetValueType< TypeWrapper< Value > >();

                matParamFunc( move( type ), decl.name(), varId++, param.locationId() );
            }
        }
    }
} // namespace goose::builtins

#endif
Changes to bs/builtins/types/func/compilation/compilation.h.
1
2
3
4
5
6
7
8
9
10
11
#ifndef GOOSE_BUILTINS_TYPES_FUNC_COMPILATION_H
#define GOOSE_BUILTINS_TYPES_FUNC_COMPILATION_H

namespace goose::builtins
{
    extern bool CompileRegularFunc( const Context& c, const Func& f );
    extern bool CompileIntrinsicFunc( const Context& c, const Func& f );
    extern bool CompileInlineFunc( const Context& c, const Func& f );
}

#endif








|


1
2
3
4
5
6
7
8
9
10
11
#ifndef GOOSE_BUILTINS_TYPES_FUNC_COMPILATION_H
#define GOOSE_BUILTINS_TYPES_FUNC_COMPILATION_H

namespace goose::builtins
{
    extern bool CompileRegularFunc( const Context& c, const Func& f );
    extern bool CompileIntrinsicFunc( const Context& c, const Func& f );
    extern bool CompileInlineFunc( const Context& c, const Func& f );
} // namespace goose::builtins

#endif
Changes to bs/builtins/types/func/compilation/inline.cpp.
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
        cfg->createBB();
        cfg->setCurrentBB( cfg->entryBB() );

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

        // TODO_SSA rewrite this
       /* uint32_t argId = 0;

        auto matParamFunc = [&]( Term&& type, StringId name, uint32_t index, LocationId loc )
        {
            // Materialize the params as temporaries with incremental indices.
            // The caller will have to emit the creation of those temporaries to pass the params,
            // allocaitng them new UIDs, and then replacing those indices by the correpsonding UIDs
            //when inlining the function.

            auto paramVal = BuildComputedValue( type, cir::GetTemporary( type, argId++, loc ) )
                .setLocationId( loc );

            auto paramIdentity = AppendToVectorTerm( bodyIdentity, TERM( name ) );
            c.env()->storeValue( paramIdentity, ANYTERM( _ ),
                ValueToEIR( paramVal ) );
        };*/

        // TODO_SSA rewrite
      /*  auto matPackParamFunc = [&]( const Value& type, uint32_t index )
        {
            auto t = ValueToEIR( ToType( c, type ) );

            // Materialize varargs as temporaries with incremental indices, like regular args.
            return BuildComputedValue( t, cir::GetTemporary( move( t ), argId++, type.locationId() ) )
                .setLocationId( type.locationId() );
        };

        MaterializeFuncParams( c, *cb, f, bodyIdentity, 0, matParamFunc, matPackParamFunc );*/

        if( !ParseFunctionBody( localContext, *cb, f ) )
            return false;

        pFuncCIR->body() = cfg;

        if( f.haveUnmetDependencies() )
        {
            Env::DepsGraph().setCallBack( pFuncCIR, [localContext,pFuncCIR,f]()

            {
                ExpandInlineCalls( *pFuncCIR->body() );
                pFuncCIR->setState( cir::Func::State::Complete );
            } );

            return true;
        }

        pFuncCIR->setState( cir::Func::State::Complete );

        Env::DepsGraph().satisfy( pFuncCIR );
        return true;
    }
}







|

|
|
|
|
|
|

|
|

|
|
|
|


|
|
|

|
|
|
|

|








|
>
|
|
|
|









|
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
        cfg->createBB();
        cfg->setCurrentBB( cfg->entryBB() );

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

        // TODO_SSA rewrite this
        /* uint32_t argId = 0;

         auto matParamFunc = [&]( Term&& type, StringId name, uint32_t index, LocationId loc )
         {
             // Materialize the params as temporaries with incremental indices.
             // The caller will have to emit the creation of those temporaries to pass the params,
             // allocaitng them new UIDs, and then replacing those indices by the correpsonding UIDs
             //when inlining the function.

             auto paramVal = BuildComputedValue( type, cir::GetTemporary( type, argId++, loc ) )
                 .setLocationId( loc );

             auto paramIdentity = AppendToVectorTerm( bodyIdentity, TERM( name ) );
             c.env()->storeValue( paramIdentity, ANYTERM( _ ),
                 ValueToEIR( paramVal ) );
         };*/

        // TODO_SSA rewrite
        /*  auto matPackParamFunc = [&]( const Value& type, uint32_t index )
          {
              auto t = ValueToEIR( ToType( c, type ) );

              // Materialize varargs as temporaries with incremental indices, like regular args.
              return BuildComputedValue( t, cir::GetTemporary( move( t ), argId++, type.locationId()
          ) ) .setLocationId( type.locationId() );
          };

          MaterializeFuncParams( c, *cb, f, bodyIdentity, 0, matParamFunc, matPackParamFunc );*/

        if( !ParseFunctionBody( localContext, *cb, f ) )
            return false;

        pFuncCIR->body() = cfg;

        if( f.haveUnmetDependencies() )
        {
            Env::DepsGraph().setCallBack( pFuncCIR,
                [localContext, pFuncCIR, f]()
                {
                    ExpandInlineCalls( *pFuncCIR->body() );
                    pFuncCIR->setState( cir::Func::State::Complete );
                } );

            return true;
        }

        pFuncCIR->setState( cir::Func::State::Complete );

        Env::DepsGraph().satisfy( pFuncCIR );
        return true;
    }
} // namespace goose::builtins
Changes to bs/builtins/types/func/compilation/intrinsic.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
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

        if( !f.tokens() )
            return false;

        auto bodyIdentity = CreateFuncBodyIdentity( c, pFuncCIR->identity() );

        Context localContext( c.env(), bodyIdentity,
            IsWrappedType( f.type().returnType() ) ?
                GetValueType< TypeWrapper< Value > >() :
                f.type().returnType() );

        auto paramCount = VecSize( f.type().params() ) + 1;

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

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

        // Intrinsic have an implicit extra context parameter, materialize it explicitely since it doesn't
        // appear in the params list
        LocalVar lv( "context"_sid, GetValueType< TypeWrapper< ptr< Context > > >(), 0 );
        auto locVar = ToValue( lv );
        c.env()->storeValue( AppendToVectorTerm( bodyIdentity, TSID( context ) ), ANYTERM( _ ), ValueToEIR( locVar ) );


        auto matParamFunc = [&]( Term&& type, StringId name, uint32_t index, LocationId loc )
        {
            // Create a locvar to hold the param.
            LocalVar lv( name, move( type ), index );
            auto locVar = ToValue( lv ).setLocationId( loc );

            auto paramIdentity = AppendToVectorTerm( bodyIdentity, TERM( name ) );
            c.env()->storeValue( paramIdentity, ANYTERM( _ ),
                ValueToEIR( locVar ) );

            cb->declareValue( c, locVar, lv.index() );
        };

        auto matPackParamFunc = [&]( const Value& type, uint32_t index )
        {
            auto t = ValueToEIR( ToType( c, type ) );
            return BuildComputedValue( t,
                cir::DataPathOf( index, type.locationId() ),
                cir::Load( t, type.locationId() ) );
        };

        MaterializeFuncParams( c, *cb, f, bodyIdentity, 1, matParamFunc, matPackParamFunc );

        if( !ParseFunctionBody( localContext, *cb, f ) )
            return false;

        ReindexVars( cfg );
        pFuncCIR->body() = cfg;

        pFuncCIR->setState( cir::Func::State::Complete );

        // TODO_SSA reenable
        //verify::Func fv( localContext, f );
        //return fv.verify();
        return true;
    }
}







|
<
|










|
|


|
>








|
<







|
|
<













|
|


|
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

        if( !f.tokens() )
            return false;

        auto bodyIdentity = CreateFuncBodyIdentity( c, pFuncCIR->identity() );

        Context localContext( c.env(), bodyIdentity,
            IsWrappedType( f.type().returnType() ) ? GetValueType< TypeWrapper< Value > >() :

                                                     f.type().returnType() );

        auto paramCount = VecSize( f.type().params() ) + 1;

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

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

        // Intrinsic have an implicit extra context parameter, materialize it explicitely since it
        // doesn't appear in the params list
        LocalVar lv( "context"_sid, GetValueType< TypeWrapper< ptr< Context > > >(), 0 );
        auto locVar = ToValue( lv );
        c.env()->storeValue( AppendToVectorTerm( bodyIdentity, TSID( context ) ), ANYTERM( _ ),
            ValueToEIR( locVar ) );

        auto matParamFunc = [&]( Term&& type, StringId name, uint32_t index, LocationId loc )
        {
            // Create a locvar to hold the param.
            LocalVar lv( name, move( type ), index );
            auto locVar = ToValue( lv ).setLocationId( loc );

            auto paramIdentity = AppendToVectorTerm( bodyIdentity, TERM( name ) );
            c.env()->storeValue( paramIdentity, ANYTERM( _ ), ValueToEIR( locVar ) );


            cb->declareValue( c, locVar, lv.index() );
        };

        auto matPackParamFunc = [&]( const Value& type, uint32_t index )
        {
            auto t = ValueToEIR( ToType( c, type ) );
            return BuildComputedValue(
                t, cir::DataPathOf( index, type.locationId() ), cir::Load( t, type.locationId() ) );

        };

        MaterializeFuncParams( c, *cb, f, bodyIdentity, 1, matParamFunc, matPackParamFunc );

        if( !ParseFunctionBody( localContext, *cb, f ) )
            return false;

        ReindexVars( cfg );
        pFuncCIR->body() = cfg;

        pFuncCIR->setState( cir::Func::State::Complete );

        // TODO_SSA reenable
        // verify::Func fv( localContext, f );
        // return fv.verify();
        return true;
    }
} // namespace goose::builtins
Changes to bs/builtins/types/func/compilation/regular.cpp.
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
        auto matParamFunc = [&]( Term&& type, StringId name, uint32_t index, LocationId loc )
        {
            // Create a locvar to hold the param.
            LocalVar lv( name, move( type ), index );
            auto locVar = ToValue( lv ).setLocationId( loc );

            auto paramIdentity = AppendToVectorTerm( bodyIdentity, TERM( name ) );
            c.env()->storeValue( paramIdentity, ANYTERM( _ ),
                ValueToEIR( locVar ) );

            cb->declareValue( c, locVar, lv.index() );
        };

        auto matPackParamFunc = [&]( const Value& type, uint32_t index )
        {
            auto t = ValueToEIR( ToType( c, type ) );
            return BuildComputedValue( t,
                cir::DataPathOf( index, type.locationId() ),
                cir::Load( t, type.locationId() ) )
                .setLocationId( type.locationId() );
        };

        MaterializeFuncParams( c, *cb, f, bodyIdentity, 0, matParamFunc, matPackParamFunc );

        pFuncCIR->body() = cfg;

        if( !ParseFunctionBody( localContext, *cb, f ) )
            return false;

        if( f.haveUnmetDependencies() )
        {
            Env::DepsGraph().setCallBack( pFuncCIR, [localContext,pFuncCIR,f]()

            {
                ExpandInlineCalls( *pFuncCIR->body() );

                ReindexVars( pFuncCIR->body() );
                pFuncCIR->setState( cir::Func::State::Complete );

                // TODO_SSA reenable
                //verify::Func fv( localContext, f );
                //fv.verify();

                // TODO: perhaps store a "verified" bool to be able to
                // check that a function was actually verified before trying
                // to execute it or codegen it? Or perhaps unneeded since we won't try to
                // eaegr eval if unmet deps? But maybe we do want to know if verif failed, to skip
                // eager exec that will explode anyway? (at that point we're in dead man walking mode
                // trying to report additional meaningful errors before giving up anyway - so any
                // call that attemp to eager eval a func that failed verif would let us give up on
                // compiling the current func)
            } );

            return true;
        }

        // If all dependencies are already met, finish up right now.
        // Finishing this up as soon as possible is necessary because
        // we need to be able to greredily attempt to eagerly evaluate
        // function calls, as any function call might be intended to be
        // done at compilation time to construct something (for instance a type).
        ReindexVars( cfg );
        pFuncCIR->setState( cir::Func::State::Complete );

        // TODO_SSA reenable
        /*verify::Func fv( localContext, f );
        fv.verify();*/
        return true;
    }
}







|
<







|
|
<












|
>
|
|

|
|

|
|
|

|
|
|
|
|
|
|
|
|




|
<
|
|
|








|
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
        auto matParamFunc = [&]( Term&& type, StringId name, uint32_t index, LocationId loc )
        {
            // Create a locvar to hold the param.
            LocalVar lv( name, move( type ), index );
            auto locVar = ToValue( lv ).setLocationId( loc );

            auto paramIdentity = AppendToVectorTerm( bodyIdentity, TERM( name ) );
            c.env()->storeValue( paramIdentity, ANYTERM( _ ), ValueToEIR( locVar ) );


            cb->declareValue( c, locVar, lv.index() );
        };

        auto matPackParamFunc = [&]( const Value& type, uint32_t index )
        {
            auto t = ValueToEIR( ToType( c, type ) );
            return BuildComputedValue(
                t, cir::DataPathOf( index, type.locationId() ), cir::Load( t, type.locationId() ) )

                .setLocationId( type.locationId() );
        };

        MaterializeFuncParams( c, *cb, f, bodyIdentity, 0, matParamFunc, matPackParamFunc );

        pFuncCIR->body() = cfg;

        if( !ParseFunctionBody( localContext, *cb, f ) )
            return false;

        if( f.haveUnmetDependencies() )
        {
            Env::DepsGraph().setCallBack( pFuncCIR,
                [localContext, pFuncCIR, f]()
                {
                    ExpandInlineCalls( *pFuncCIR->body() );

                    ReindexVars( pFuncCIR->body() );
                    pFuncCIR->setState( cir::Func::State::Complete );

                    // TODO_SSA reenable
                    // verify::Func fv( localContext, f );
                    // fv.verify();

                    // TODO: perhaps store a "verified" bool to be able to check that a function was
                    // actually verified before trying to execute it or codegen it? Or perhaps
                    // unneeded since we won't try to eager eval if unmet deps? But maybe we do want
                    // to know if verif failed, to skip eager exec that will explode anyway? (at
                    // that point we're in dead man walking mode trying to report additional
                    // meaningful errors before giving up anyway - so any call that attemp to eager
                    // eval a func that failed verif would let us give up on compiling the current
                    // func)
                } );

            return true;
        }

        // If all dependencies are already met, finish up right now. Finishing this up as soon as

        // possible is necessary because  we need to be able to greredily attempt to eagerly
        // evaluate function calls, as any function call might be intended to be done at compilation
        // time to construct something (for instance a type).
        ReindexVars( cfg );
        pFuncCIR->setState( cir::Func::State::Complete );

        // TODO_SSA reenable
        /*verify::Func fv( localContext, f );
        fv.verify();*/
        return true;
    }
} // namespace goose::builtins
Changes to bs/builtins/types/func/compile.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "builtins/builtins.h"
#include "compilation/compilation.h"
#include "compilation/common.h"

#include "lex/lex.h"
#include "parse/parse.h"
// TODO_SSA reenable
//#include "verify/verify.h"

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

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







|







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

#include "lex/lex.h"
#include "parse/parse.h"
// TODO_SSA reenable
// #include "verify/verify.h"

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

namespace goose::builtins
{
    Value CompileFunc( const Context& c, const Value& f )
52
53
54
55
56
57
58
59
                // All the other cases for now are functions without bodies,
                // nothing more to do.
                break;
        }

        return f;
    }
}







|
52
53
54
55
56
57
58
59
                // All the other cases for now are functions without bodies,
                // nothing more to do.
                break;
        }

        return f;
    }
} // namespace goose::builtins
Changes to bs/builtins/types/func/func.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
#include "builtins/builtins.h"
#include "lex/lex.h"
#include "parse/parse.h"
// TODO_SSA reenable
//#include "verify/verify.h"

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

namespace goose::builtins
{
    llvm::SmallVector< Func::Summary, 16 > Func::ms_funcStack;

    cir::InstrSeq BuildArgsInstrSeq( const Term& args )
    {
        return BuildArgsInstrSeq( args, []( auto&&, auto&& ){} );
    }

    const Term& FuncPattern::GetPattern()
    {
        static auto pattern = ValueToEIR(
            Value( TypeType(), VEC( TSID( func ),
            HOLE( "_"_sid ), HOLE( "_"_sid ),
            HOLE( "_"_sid ), HOLE( "_"_sid ),
            HOLE( "_"_sid ) ) ) );

        return pattern;
    }

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

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

        return !!result;
    }

    bool IsGhostFunc( const Value& f )
    {
        if( !f.isConstant() )




|










|




|
<
|
<
|










|
<
<







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
#include "builtins/builtins.h"
#include "lex/lex.h"
#include "parse/parse.h"
// TODO_SSA reenable
// #include "verify/verify.h"

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

namespace goose::builtins
{
    llvm::SmallVector< Func::Summary, 16 > Func::ms_funcStack;

    cir::InstrSeq BuildArgsInstrSeq( const Term& args )
    {
        return BuildArgsInstrSeq( args, []( auto&&, auto&& ) {} );
    }

    const Term& FuncPattern::GetPattern()
    {
        static auto pattern = ValueToEIR( Value( TypeType(),

            VEC( TSID( func ), HOLE( "_"_sid ), HOLE( "_"_sid ), HOLE( "_"_sid ), HOLE( "_"_sid ),

                HOLE( "_"_sid ) ) ) );

        return pattern;
    }

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

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



        return !!result;
    }

    bool IsGhostFunc( const Value& f )
    {
        if( !f.isConstant() )
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
    optional< Term > GetFuncRType( const Value& func )
    {
        auto funcType = EIRToValue( func.type() );
        if( !funcType )
            return nullopt;

        auto typeDecomp = Decompose( funcType->val(),
            Vec(
                Lit( "func"_sid ),
                SubTerm(),  // kind
                SubTerm(),  // return type
                SubTerm(),  // param types
                SubTerm(),  // verif infos
                SubTerm()   // Lowering infos
            )
        );

        if( !typeDecomp )
            return nullopt;

        auto&& [kind, rtype, ptypes, vinf, li] = *typeDecomp;
        return rtype;
    }







<
|
|
|
|
|
|
|
<







85
86
87
88
89
90
91

92
93
94
95
96
97
98

99
100
101
102
103
104
105
    optional< Term > GetFuncRType( const Value& func )
    {
        auto funcType = EIRToValue( func.type() );
        if( !funcType )
            return nullopt;

        auto typeDecomp = Decompose( funcType->val(),

            Vec( Lit( "func"_sid ),
                SubTerm(), // kind
                SubTerm(), // return type
                SubTerm(), // param types
                SubTerm(), // verif infos
                SubTerm() // Lowering infos
                ) );


        if( !typeDecomp )
            return nullopt;

        auto&& [kind, rtype, ptypes, vinf, li] = *typeDecomp;
        return rtype;
    }
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
        assert( func );
        return func->cir();
    }

    optional< FuncVerifData > ExtractFuncVerifData( const Value& funcType )
    {
        auto typeDecomp = Decompose( funcType.val(),
            Vec(
                Lit( "func"_sid ),
                SubTerm(),  // kind
                SubTerm(),  // return type
                SubTerm(),  // param types
                Val< ptr< void > >(),  // verif infos
                SubTerm()   // Lowering infos
            )
        );

        if( !typeDecomp )
            return nullopt;

        auto&& [kind, rtype, params, vinf, li] = *typeDecomp;
        return FuncVerifData{ rtype, *Unquote( params ), static_pointer_cast< FuncVerificationInfos >( vinf ) };
    }
}


namespace goose::eir
{
    Term Bridge< Func >::Type( const builtins::Func& func )
    {
        return ValueToEIR( ::ToValue( func.type() ) );
    }

    Value Bridge< Func >::ToValue( const builtins::Func& func )
    {
        if( func.isExternal() )
            return Value( Type( func ), TERM( *func.symbol() ) );

        return Value( Type( func ), VEC(
            TERM( func.tokens() ), TERM( func.paramsDecl() ), TERM( func.cir() ) ) );
    }

    optional< Func > Bridge< Func >::FromValue( const Value& v )
    {
        if( !v.isConstant() || v.isPoison() )
            return nullopt;

        auto funcTypeVal = EIRToValue( v.type() );
        if( !funcTypeVal )
            return nullopt;

        auto funcType = ::FromValue< FuncType >( *funcTypeVal );
        if( !funcType )
            return nullopt;

        auto result = Decompose( v.val(),
            Vec(
                Val< ptr< void > >(),   // body toks
                Val< pvec >(),          // param decls
                Val< void* >()          // cir
            )
        );

        if( result )
        {
            auto&& [toks,paramDecls,cir] = *result;
            return Func( move( *funcType ), toks, paramDecls, static_cast< cir::Func* >( cir ) );
        }

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

        if( !result2 )
            return nullopt;

        return Func( move( *funcType ), *result2 );
    }
}







<
|
|
|
|
|
|
|
<





|
|
|
>













|
|
















<
|
|
|
|
<



|




|
<
<






|
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
        assert( func );
        return func->cir();
    }

    optional< FuncVerifData > ExtractFuncVerifData( const Value& funcType )
    {
        auto typeDecomp = Decompose( funcType.val(),

            Vec( Lit( "func"_sid ),
                SubTerm(), // kind
                SubTerm(), // return type
                SubTerm(), // param types
                Val< ptr< void > >(), // verif infos
                SubTerm() // Lowering infos
                ) );


        if( !typeDecomp )
            return nullopt;

        auto&& [kind, rtype, params, vinf, li] = *typeDecomp;
        return FuncVerifData{ rtype, *Unquote( params ),
            static_pointer_cast< FuncVerificationInfos >( vinf ) };
    }
} // namespace goose::builtins

namespace goose::eir
{
    Term Bridge< Func >::Type( const builtins::Func& func )
    {
        return ValueToEIR( ::ToValue( func.type() ) );
    }

    Value Bridge< Func >::ToValue( const builtins::Func& func )
    {
        if( func.isExternal() )
            return Value( Type( func ), TERM( *func.symbol() ) );

        return Value( Type( func ),
            VEC( TERM( func.tokens() ), TERM( func.paramsDecl() ), TERM( func.cir() ) ) );
    }

    optional< Func > Bridge< Func >::FromValue( const Value& v )
    {
        if( !v.isConstant() || v.isPoison() )
            return nullopt;

        auto funcTypeVal = EIRToValue( v.type() );
        if( !funcTypeVal )
            return nullopt;

        auto funcType = ::FromValue< FuncType >( *funcTypeVal );
        if( !funcType )
            return nullopt;

        auto result = Decompose( v.val(),

            Vec( Val< ptr< void > >(), // body toks
                Val< pvec >(), // param decls
                Val< void* >() // cir
                ) );


        if( result )
        {
            auto&& [toks, paramDecls, cir] = *result;
            return Func( move( *funcType ), toks, paramDecls, static_cast< cir::Func* >( cir ) );
        }

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



        if( !result2 )
            return nullopt;

        return Func( move( *funcType ), *result2 );
    }
} // namespace goose::eir
Changes to bs/builtins/types/func/func.h.
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
        ptr< FuncVerificationInfos > verifInfos;
    };

    // Extracts the return type, param types and verfif infos of any function,
    // either builtin or not. (these parts are encoded in a common location for all function types)
    extern optional< FuncVerifData > ExtractFuncVerifData( const Value& func );

    template< typename F >
    void ForEachDeclInTuple( const Value& tup, F&& func );

    extern bool IsExternalFunc( const Value& f );
    extern bool IsGhostFunc( const Value& f );
    extern bool IsG0Func( const Value& f );

    extern bool HaveUnmetDependencies( const Value& f );

    class Func
    {
        public:
            template< typename T, typename B, typename P >
            Func( T&& funcType, B&& toks, P&& paramsDecls, cir::Func* cir ) :
                m_type( forward< T >( funcType ) ),
                m_tokens( forward< B >( toks ) ),
                m_paramsDecl( forward< P >( paramsDecls ) ),
                m_cir( cir )
            {}



            template< typename T, typename P >
            Func( T&& funcType, P&& paramsDecls, cir::Func* cir ) :
                m_type( forward< T >( funcType ) ),
                m_paramsDecl( forward< P >( paramsDecls ) ),
                m_cir( cir )
            {}



            template< typename T, typename B, typename P >
            Func( T&& funcType, B&& toks, P&& paramsDecls ) :
                m_type( forward< T >( funcType ) ),
                m_tokens( forward< B >( toks ) ),
                m_paramsDecl( forward< P >( paramsDecls ) )
            {}



            template< typename T >
            Func( T&& funcType, const string& symbol ) :
                m_type( forward< T >( funcType ) ),
                m_symbol( symbol )
            {}



            bool isExternal() const { return m_symbol.has_value(); }

            const auto& type() const { return m_type; }

            auto& type() { return m_type; }

            const auto& tokens() const { return m_tokens; }

            const auto& paramsDecl() const { return m_paramsDecl; }

            const auto& cir() const { return m_cir; }

            const auto& symbol() const { return m_symbol; }

            void setCIR( cir::Func* cir )
            {
                m_cir = cir;
            }

            void setTokens( const ptr< void >& pToks )
            {
                m_tokens = pToks;
            }

            bool haveUnmetDependencies() const
            {
                if( !m_cir )
                    return false;
                return Env::DepsGraph().haveUnmetDependencies( m_cir );
            }

            struct Summary
            {
                cir::Func* cir = nullptr;
                FuncType::Kind kind = FuncType::Kind::Regular;
            };

            struct StackHelper
            {
                StackHelper( const Func& func )
                {
                    Func::PushFunc( Summary( func.m_cir, func.type().kind() ) );
                }

                ~StackHelper()
                {
                    Func::PopFunc();
                }
            };

            static inline const Summary& CurrentCaller() { return ms_funcStack.back(); }

            static inline const auto& FuncStack() { return ms_funcStack; }

        private:
            static inline void PushFunc( Summary fs ) { ms_funcStack.emplace_back( fs ); }

            static inline void PopFunc() { ms_funcStack.resize( ms_funcStack.size() - 1 ); }

            FuncType m_type;

            ptr< void > m_tokens;  // the unparsed body

            pvec m_paramsDecl;
            cir::Func* m_cir = nullptr;

            // The function's symbol, if this is an external function.
            optional< string > m_symbol;

            // The current callstack that we are compiling, used to detect
            // invalid recursive inline calls
            static llvm::SmallVector< Summary, 16 > ms_funcStack;
    };

    extern Value CompileFunc( const Context& c, const Value& f );

    template< typename F >
    cir::InstrSeq BuildArgsInstrSeq( const Term& args, F&& argPostProc );

    extern cir::InstrSeq BuildArgsInstrSeq( const Term& args );
}


namespace goose::eir
{
    template<>
    struct Bridge< builtins::Func >
    {
        static Term Type( const builtins::Func& func );
        static Value ToValue( const builtins::Func& func );
        static optional< builtins::Func > FromValue( const Value& v );
    };
}

#endif







<
|









|
|
|
|
|
|
|
<
|
>
>
|
|
|
|
|
<
>
>

|
|
|
|
|
<
|
>
>
|
|
|
|
<
|
>
>
|

|
>
|
>
|
>
|
>
|
>
|

|
|
<
<
<
|
<
<
<

|
|
|
|
|
|

|
|
|
|
|

|
|
|
|
|
|

|
<
<
<
|

|
>
|

|
|
>
|

|

|

|
|

|
|

|
|
|




<
|


<
>



<
|





|


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
        ptr< FuncVerificationInfos > verifInfos;
    };

    // Extracts the return type, param types and verfif infos of any function,
    // either builtin or not. (these parts are encoded in a common location for all function types)
    extern optional< FuncVerifData > ExtractFuncVerifData( const Value& func );


    template< typename F > void ForEachDeclInTuple( const Value& tup, F&& func );

    extern bool IsExternalFunc( const Value& f );
    extern bool IsGhostFunc( const Value& f );
    extern bool IsG0Func( const Value& f );

    extern bool HaveUnmetDependencies( const Value& f );

    class Func
    {
      public:
        template< typename T, typename B, typename P >
        Func( T&& funcType, B&& toks, P&& paramsDecls, cir::Func* cir ) :
            m_type( forward< T >( funcType ) ),
            m_tokens( forward< B >( toks ) ),
            m_paramsDecl( forward< P >( paramsDecls ) ),
            m_cir( cir )

        {
        }

        template< typename T, typename P >
        Func( T&& funcType, P&& paramsDecls, cir::Func* cir ) :
            m_type( forward< T >( funcType ) ),
            m_paramsDecl( forward< P >( paramsDecls ) ),
            m_cir( cir )

        {
        }

        template< typename T, typename B, typename P >
        Func( T&& funcType, B&& toks, P&& paramsDecls ) :
            m_type( forward< T >( funcType ) ),
            m_tokens( forward< B >( toks ) ),
            m_paramsDecl( forward< P >( paramsDecls ) )

        {
        }

        template< typename T >
        Func( T&& funcType, const string& symbol ) :
            m_type( forward< T >( funcType ) ),
            m_symbol( symbol )

        {
        }

        bool isExternal() const { return m_symbol.has_value(); }

        const auto& type() const { return m_type; }

        auto& type() { return m_type; }

        const auto& tokens() const { return m_tokens; }

        const auto& paramsDecl() const { return m_paramsDecl; }

        const auto& cir() const { return m_cir; }

        const auto& symbol() const { return m_symbol; }

        void setCIR( cir::Func* cir ) { m_cir = cir; }




        void setTokens( const ptr< void >& pToks ) { m_tokens = pToks; }




        bool haveUnmetDependencies() const
        {
            if( !m_cir )
                return false;
            return Env::DepsGraph().haveUnmetDependencies( m_cir );
        }

        struct Summary
        {
            cir::Func* cir = nullptr;
            FuncType::Kind kind = FuncType::Kind::Regular;
        };

        struct StackHelper
        {
            StackHelper( const Func& func )
            {
                Func::PushFunc( Summary( func.m_cir, func.type().kind() ) );
            }

            ~StackHelper() { Func::PopFunc(); }



        };

        static inline const Summary& CurrentCaller() { return ms_funcStack.back(); }

        static inline const auto& FuncStack() { return ms_funcStack; }

      private:
        static inline void PushFunc( Summary fs ) { ms_funcStack.emplace_back( fs ); }

        static inline void PopFunc() { ms_funcStack.resize( ms_funcStack.size() - 1 ); }

        FuncType m_type;

        ptr< void > m_tokens; // the unparsed body

        pvec m_paramsDecl;
        cir::Func* m_cir = nullptr;

        // The function's symbol, if this is an external function.
        optional< string > m_symbol;

        // The current callstack that we are compiling, used to detect
        // invalid recursive inline calls
        static llvm::SmallVector< Summary, 16 > ms_funcStack;
    };

    extern Value CompileFunc( const Context& c, const Value& f );


    template< typename F > cir::InstrSeq BuildArgsInstrSeq( const Term& args, F&& argPostProc );

    extern cir::InstrSeq BuildArgsInstrSeq( const Term& args );

} // namespace goose::builtins

namespace goose::eir
{

    template<> struct Bridge< builtins::Func >
    {
        static Term Type( const builtins::Func& func );
        static Value ToValue( const builtins::Func& func );
        static optional< builtins::Func > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/func/func.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
#ifndef GOOSE_BUILTINS_TYPES_FUNC_INL
#define GOOSE_BUILTINS_TYPES_FUNC_INL

namespace goose::builtins
{
    template< typename F >
    void ForEachDeclInTuple( const Value& tup, F&& func )
    {
        ForEachInTuple( tup, [&]( auto&& tupVal )
        {
            func( *FromValue< Decl >( tupVal ) );
        } );
    }

    template< typename F >
    cir::InstrSeq BuildArgsInstrSeq( const Term& args, F&& argPostProc )
    {
        cir::InstrSeq result;

        ForEachInVectorTerm( args, [&]( auto&& arg )

        {
            auto argVal = *EIRToValue( arg );
            cir::AppendToInstrSeq( result, argVal );
            argPostProc( argVal, result );
            return true;
        } );

        return result;
    }
}

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

namespace goose::builtins
{

    template< typename F > void ForEachDeclInTuple( const Value& tup, F&& func )
    {
        ForEachInTuple( tup, [&]( auto&& tupVal ) { func( *FromValue< Decl >( tupVal ) ); } );



    }


    template< typename F > cir::InstrSeq BuildArgsInstrSeq( const Term& args, F&& argPostProc )
    {
        cir::InstrSeq result;

        ForEachInVectorTerm( args,
            [&]( auto&& arg )
            {
                auto argVal = *EIRToValue( arg );
                cir::AppendToInstrSeq( result, argVal );
                argPostProc( argVal, result );
                return true;
            } );

        return result;
    }
} // namespace goose::builtins

#endif
Changes to bs/builtins/types/func/functype.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
#include "builtins/builtins.h"
#include "lex/lex.h"
#include "parse/parse.h"
// TODO_SSA reenable
//#include "verify/verify.h"

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

namespace goose::builtins
{
    void FuncType::setKind( Kind k )
    {
        m_kind = k;

        // When converting a function to an intrinsic function,
        // we need to convert the param signatures to constants,
        // unless they are wrapped.
        if( m_kind == FuncType::Kind::Intrinsic )
        {
            ForEachInVectorTerm( m_params, []( auto& t )

            {
                auto p = *EIRToValuePattern( t );
                if( !IsWrappedType( p.type() ) )
                {
                    p.sort() = TSID( constant );
                    t = ValueToEIR( p );
                }

                return true;
            } );
        }
    }

    const Term& FuncTypePattern()
    {
        static auto funcTypePat = ValueToEIR( Value( TypeType(), VEC( TSID( func ),
            ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );

        return funcTypePat;
    }

    bool IsFuncType( const Value& t )
    {
        auto result = Decompose( t.val(),
            Vec(
                Lit( "func"_sid ),
                SubTerm(),  // kind
                SubTerm(),  // return type
                SubTerm(),  // param types
                SubTerm(),  // verif infos
                SubTerm()   // Lowering infos
            )
        );

        return !!result;
    }

    Term GetFuncSigFromType( const Value& funcType )
    {
      //  G_TRACE_VAL( funcType );
        auto typeDecomp = Decompose( funcType.val(),
            Vec(
                Lit( "func"_sid ),
                SubTerm(),  // kind
                SubTerm(),  // return type
                SubTerm(),  // param types
                SubTerm(),  // verif infos
                SubTerm()   // Lowering infos
            )
        );
        assert( typeDecomp );
        auto&& [kind, rtype, ptypes, vinf, li] = *typeDecomp;

        return PrependToVectorTerm( *Unquote( ptypes ), rtype );
    }

    ParamListKind CheckParamListKind( const Value& tup )




|















|
>
|
|
|
|
|
|
|

|
|





|
|
>






<
|
|
|
|
|
|
|
<






|

<
|
|
|
|
|
|
|
<







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
#include "builtins/builtins.h"
#include "lex/lex.h"
#include "parse/parse.h"
// TODO_SSA reenable
// #include "verify/verify.h"

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

namespace goose::builtins
{
    void FuncType::setKind( Kind k )
    {
        m_kind = k;

        // When converting a function to an intrinsic function,
        // we need to convert the param signatures to constants,
        // unless they are wrapped.
        if( m_kind == FuncType::Kind::Intrinsic )
        {
            ForEachInVectorTerm( m_params,
                []( auto& t )
                {
                    auto p = *EIRToValuePattern( t );
                    if( !IsWrappedType( p.type() ) )
                    {
                        p.sort() = TSID( constant );
                        t = ValueToEIR( p );
                    }

                    return true;
                } );
        }
    }

    const Term& FuncTypePattern()
    {
        static auto funcTypePat = ValueToEIR( Value( TypeType(),
            VEC( TSID( func ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ),
                ANYTERM( _ ) ) ) );
        return funcTypePat;
    }

    bool IsFuncType( const Value& t )
    {
        auto result = Decompose( t.val(),

            Vec( Lit( "func"_sid ),
                SubTerm(), // kind
                SubTerm(), // return type
                SubTerm(), // param types
                SubTerm(), // verif infos
                SubTerm() // Lowering infos
                ) );


        return !!result;
    }

    Term GetFuncSigFromType( const Value& funcType )
    {
        //  G_TRACE_VAL( funcType );
        auto typeDecomp = Decompose( funcType.val(),

            Vec( Lit( "func"_sid ),
                SubTerm(), // kind
                SubTerm(), // return type
                SubTerm(), // param types
                SubTerm(), // verif infos
                SubTerm() // Lowering infos
                ) );

        assert( typeDecomp );
        auto&& [kind, rtype, ptypes, vinf, li] = *typeDecomp;

        return PrependToVectorTerm( *Unquote( ptypes ), rtype );
    }

    ParamListKind CheckParamListKind( const Value& tup )
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

            else if( !IsDecl( *v ) && !v->isConstant() )
                return ParamListKind::Invalid;
        }

        return result;
    }

    optional< Term > BuildArgListForCall( const Context& c, const Value& func, const Term& typeCheckedArgs )

    {
        pvec argVec = make_shared< Vector >();
        pvec paramVec = make_shared< Vector >();

        auto count = VecSize( typeCheckedArgs );
        argVec->reserve( count );
        paramVec->reserve( count );

        auto ft = *FromValue< FuncType >( *EIRToValue( func.type() ) );

        // Filter out constant params
        ForEachInVectorTerms( ft.params(), typeCheckedArgs, [&]( auto&& p, auto&& a )

        {
            auto vp = EIRToValuePattern( p );
            if( vp->val() == HOLE( "_"_sid ) )
            {
                argVec->append( a );
                paramVec->append( EIRToValuePattern( p )->type() );
            }
            return true;
        } );

        // Do not perform arg conversion on g0 funcs themselves
        // because we don't want to infinitely recurse into the
        // extension points implementations
        if( IsG0Func( func ) || IsGhostFunc( func ) )
            return TERM( argVec );

        auto result = InvokeOverloadSet( c, c.env()->extConvertFuncArgs(),
            MakeClosedTuple( Wrap( argVec ), Wrap( paramVec ) ) );

        auto resultVec = FromValue< TypeWrapper< pvec > >( result );
        if( !resultVec )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( func.locationId(),
                "_ConvertArgs returned an invalid value." );
            return nullopt;
        }

        ForEachInVectorTerms( ft.params(), TERM( *resultVec ), [&]( auto&& p, Term& a )

        {
            auto vp = EIRToValuePattern( p );
            if( vp->val() == HOLE( "_"_sid ) )
            {
                auto arg = *EIRToValue( a );
                if( IsWrappedType( arg.type() ) )
                {
                    auto argVal = *EIRToValue( a );

                    Value unwrappedArg( GetUnwrappedType( *EIRToValue( argVal.type() ) ), argVal.payload() );
                    unwrappedArg.setLocationId( argVal.locationId() );
                    auto wrappedArg = ToValue( TypeWrapper< Value >( unwrappedArg ) );
                    a = ValueToEIR( wrappedArg );
                }
            }

            return true;
        } );

        return TERM( *resultVec );
    }

    // Identical to BuildArgListForCall except for the insertion of the implicit context parameter
    // and not calling the parg conversion extension point.
    // A bit redundant but most of that stuff is going ot move to the prelude anyway
    // (single ext point to convert all the args versus doing each arg individually) so there will be
    // an opportunity to factorize this later
    optional< Term > BuildArgListForIntrinsicCall( const Context& c, const FuncType& ft, const Term& typeCheckedArgs )

    {
        auto av = make_shared< Vector >();
        av->reserve( VecSize( ft.params() ) + 1 );

        auto pCBWrapper = ToValue( TypeWrapper< ptr< Context > >( make_shared< Context >( c ) ) );
        av->append( ValueToEIR( pCBWrapper ) );

        bool failed = false;
        ForEachInVectorTerms( ft.params(), typeCheckedArgs, [&]( auto&& p, auto&& a )

        {
            auto vp = EIRToValuePattern( p );
            if( vp->val() == HOLE( "_"_sid ) )
            {
                if( IsWrappedType( vp->type() ) )
                {
                    auto argVal = *EIRToValue( a );
                    auto unwrappedArg = GetUnwrapped( argVal );
                    unwrappedArg.setLocationId( argVal.locationId() );
                    auto wrappedArg = ToValue( TypeWrapper< Value >( unwrappedArg ) );
                    av->append( ValueToEIR( wrappedArg ) );
                }
                else
                    av->append( a );
            }

            return true;
        } );

        if( failed )
            return nullopt;

        return av;
    }
}

namespace goose::eir
{
    const Term& Bridge< FuncType >::Type()
    {
        return TypeType();
    }

    Value Bridge< FuncType >::ToValue( const FuncType& ft )
    {
        return Value( Type(), VEC( TSID( func ), TERM( static_cast< uint64_t >( ft.kind() ) ),
            ft.returnType(), Quote( ft.params() ),
            static_pointer_cast< void >( ft.verifInfos() ),
            ft.loweringInfos() ) );
    }

    optional< FuncType > Bridge< FuncType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),
            Vec(
                Lit( "func"_sid ),
                Val< uint64_t >(),      // kind
                SubTerm(),  // return type
                SubTerm(),  // param types
                Val< ptr< void > >(),  // verif infos
                Val< ptr< void > >()   // Lowering infos
            )
        );

        if( !result )
            return nullopt;

        auto&& [kind, rtype, params, vinf, li] = *result;

        return FuncType( rtype, *Unquote( params ),
            static_pointer_cast< FuncVerificationInfos >( vinf ),
            static_pointer_cast< FuncLoweringInfos >( li ),
            static_cast< FuncType::Kind >( kind ) );
    }
}








|
>











|
>
|
|
|
|
|
|
|
|
|







|
|




|
|



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

|
|







|
|
|
>








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

|
|






|










|
|
|
|





<
|
|
|
|
|
|
|
<








|
<

<
>
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
            else if( !IsDecl( *v ) && !v->isConstant() )
                return ParamListKind::Invalid;
        }

        return result;
    }

    optional< Term > BuildArgListForCall(
        const Context& c, const Value& func, const Term& typeCheckedArgs )
    {
        pvec argVec = make_shared< Vector >();
        pvec paramVec = make_shared< Vector >();

        auto count = VecSize( typeCheckedArgs );
        argVec->reserve( count );
        paramVec->reserve( count );

        auto ft = *FromValue< FuncType >( *EIRToValue( func.type() ) );

        // Filter out constant params
        ForEachInVectorTerms( ft.params(), typeCheckedArgs,
            [&]( auto&& p, auto&& a )
            {
                auto vp = EIRToValuePattern( p );
                if( vp->val() == HOLE( "_"_sid ) )
                {
                    argVec->append( a );
                    paramVec->append( EIRToValuePattern( p )->type() );
                }
                return true;
            } );

        // Do not perform arg conversion on g0 funcs themselves
        // because we don't want to infinitely recurse into the
        // extension points implementations
        if( IsG0Func( func ) || IsGhostFunc( func ) )
            return TERM( argVec );

        auto result = InvokeOverloadSet(
            c, c.env()->extConvertFuncArgs(), MakeClosedTuple( Wrap( argVec ), Wrap( paramVec ) ) );

        auto resultVec = FromValue< TypeWrapper< pvec > >( result );
        if( !resultVec )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage(
                func.locationId(), "_ConvertArgs returned an invalid value." );
            return nullopt;
        }

        ForEachInVectorTerms( ft.params(), TERM( *resultVec ),
            [&]( auto&& p, Term& a )
            {
                auto vp = EIRToValuePattern( p );
                if( vp->val() == HOLE( "_"_sid ) )
                {
                    auto arg = *EIRToValue( a );
                    if( IsWrappedType( arg.type() ) )
                    {
                        auto argVal = *EIRToValue( a );
                        Value unwrappedArg(
                            GetUnwrappedType( *EIRToValue( argVal.type() ) ), argVal.payload() );
                        unwrappedArg.setLocationId( argVal.locationId() );
                        auto wrappedArg = ToValue( TypeWrapper< Value >( unwrappedArg ) );
                        a = ValueToEIR( wrappedArg );
                    }
                }

                return true;
            } );

        return TERM( *resultVec );
    }

    // Identical to BuildArgListForCall except for the insertion of the implicit context parameter
    // and not calling the parg conversion extension point.
    // A bit redundant but most of that stuff is going ot move to the prelude anyway
    // (single ext point to convert all the args versus doing each arg individually) so there will
    // be an opportunity to factorize this later
    optional< Term > BuildArgListForIntrinsicCall(
        const Context& c, const FuncType& ft, const Term& typeCheckedArgs )
    {
        auto av = make_shared< Vector >();
        av->reserve( VecSize( ft.params() ) + 1 );

        auto pCBWrapper = ToValue( TypeWrapper< ptr< Context > >( make_shared< Context >( c ) ) );
        av->append( ValueToEIR( pCBWrapper ) );

        bool failed = false;
        ForEachInVectorTerms( ft.params(), typeCheckedArgs,
            [&]( auto&& p, auto&& a )
            {
                auto vp = EIRToValuePattern( p );
                if( vp->val() == HOLE( "_"_sid ) )
                {
                    if( IsWrappedType( vp->type() ) )
                    {
                        auto argVal = *EIRToValue( a );
                        auto unwrappedArg = GetUnwrapped( argVal );
                        unwrappedArg.setLocationId( argVal.locationId() );
                        auto wrappedArg = ToValue( TypeWrapper< Value >( unwrappedArg ) );
                        av->append( ValueToEIR( wrappedArg ) );
                    }
                    else
                        av->append( a );
                }

                return true;
            } );

        if( failed )
            return nullopt;

        return av;
    }
} // namespace goose::builtins

namespace goose::eir
{
    const Term& Bridge< FuncType >::Type()
    {
        return TypeType();
    }

    Value Bridge< FuncType >::ToValue( const FuncType& ft )
    {
        return Value( Type(),
            VEC( TSID( func ), TERM( static_cast< uint64_t >( ft.kind() ) ), ft.returnType(),
                Quote( ft.params() ), static_pointer_cast< void >( ft.verifInfos() ),
                ft.loweringInfos() ) );
    }

    optional< FuncType > Bridge< FuncType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),

            Vec( Lit( "func"_sid ),
                Val< uint64_t >(), // kind
                SubTerm(), // return type
                SubTerm(), // param types
                Val< ptr< void > >(), // verif infos
                Val< ptr< void > >() // Lowering infos
                ) );


        if( !result )
            return nullopt;

        auto&& [kind, rtype, params, vinf, li] = *result;

        return FuncType( rtype, *Unquote( params ),
            static_pointer_cast< FuncVerificationInfos >( vinf ),
            static_pointer_cast< FuncLoweringInfos >( li ), static_cast< FuncType::Kind >( kind ) );

    }

} // namespace goose::eir
Changes to bs/builtins/types/func/functype.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
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
    extern Term GetFuncSigFromType( const Value& funcType );

    // Given a tuple, determines whether it is a valid param list, and of which kind.
    extern ParamListKind CheckParamListKind( const Value& tup );

    class FuncVerificationInfos
    {
        public:
            template< typename I >
            FuncVerificationInfos( I&& identity )
            {
                m_preConds->setIdentity( identity );
                m_postConds->setIdentity( forward< I >( identity ) );
            }

            bool parse( const Context& c )
            {
                bool b1 = m_preConds->parse( c );
                bool b2 = m_postConds->parse( c );
                return b1 && b2;
            }

            template< typename T >
            void setPreCondToks( T&& preCondToks )
            {
                m_preConds->setUnparsedProps( forward< T >( preCondToks ) );
            }

            template< typename T >
            void setPostCondToks( T&& postCondToks )
            {
                m_postConds->setUnparsedProps( forward< T >( postCondToks ) );
            }

            const auto& preConds() const { return m_preConds; }

            const auto& postConds() const { return m_postConds; }

            auto& preConds() { return m_preConds; }

            auto& postConds() { return m_postConds; }

        private:
            ptr< Propositions > m_preConds = make_shared< Propositions >();
            ptr< Propositions > m_postConds = make_shared< Propositions >();
    };

    struct FuncLoweringInfos
    {
        vector< Value > loweredParamTypes;
        Value loweredReturnType;
    };

    class FuncType
    {
        public:
            enum class Kind
            {
                Regular,
                Comptime,
                VarArg,
                Intrinsic,
                Inline,
                Ghost
            };

            template< typename R, typename P, typename VI >
            FuncType( R&& returnType, P&& params, VI&& verifInfos,
                Kind kind = Kind::Regular ) :
                m_returnType( forward< R >( returnType ) ),
                m_params( forward< P >( params ) ),
                m_verifInfos( forward< VI >( verifInfos ) ),
                m_kind( kind )
            {}



            template< typename R, typename P, typename VI, typename LI >
            FuncType( R&& returnType, P&& params, VI&& verifInfos, LI&& loweringInfos,
                Kind kind = Kind::Regular ) :
                m_returnType( forward< R >( returnType ) ),
                m_params( forward< P >( params ) ),
                m_verifInfos( forward< VI >( verifInfos ) ),
                m_loweringInfos( forward< LI >( loweringInfos ) ),
                m_kind( kind )
            {}



            template< typename R, typename P >
            FuncType( R&& returnType, P&& params ) :
                m_returnType( forward< R >( returnType ) ),
                m_params( forward< P >( params ) )
            {}



            const auto& returnType() const { return m_returnType; }

            const auto& params() const { return m_params; }

            const auto& verifInfos() const { return m_verifInfos; }

            const auto& loweringInfos() const { return m_loweringInfos; }

            auto kind() const { return m_kind; }

            void setKind( Kind k );

        private:
            Term m_returnType;
            Term m_params;

            ptr< FuncVerificationInfos > m_verifInfos;
            ptr< FuncLoweringInfos > m_loweringInfos;

            Kind m_kind = Kind::Regular;
    };

    extern bool IsFuncType( const Value& t );

    // The args for a function may include constants that are specialization args.
    // When building a call instruction, we need to strip those out and keep only
    // the args that actually exist at runtime / execution time.
    // We also need to run the args through the ConvertFuncArg() extension point.
    extern optional< Term > BuildArgListForCall( const Context& c, const Value& func, const Term& typeCheckedArgs );


    // Takes argument intended to call an intrinsic function and wraps them using TypeWrapper< Value >, so
    // that the intrinsic function code can manipulate the value representations rather than the actual values.

    extern optional< Term > BuildArgListForIntrinsicCall( const Context& c, const FuncType& ft, const Term& typeCheckedArgs );
}



namespace goose::eir
{
    template<>
    struct Bridge< builtins::FuncType >
    {
        static const Term& Type();
        static Value ToValue( const builtins::FuncType& ft );
        static optional< builtins::FuncType > FromValue( const Value& v );
    };
}

#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
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
    extern Term GetFuncSigFromType( const Value& funcType );

    // Given a tuple, determines whether it is a valid param list, and of which kind.
    extern ParamListKind CheckParamListKind( const Value& tup );

    class FuncVerificationInfos
    {
      public:
        template< typename I > FuncVerificationInfos( I&& identity )

        {
            m_preConds->setIdentity( identity );
            m_postConds->setIdentity( forward< I >( identity ) );
        }

        bool parse( const Context& c )
        {
            bool b1 = m_preConds->parse( c );
            bool b2 = m_postConds->parse( c );
            return b1 && b2;
        }

        template< typename T > void setPreCondToks( T&& preCondToks )

        {
            m_preConds->setUnparsedProps( forward< T >( preCondToks ) );
        }

        template< typename T > void setPostCondToks( T&& postCondToks )

        {
            m_postConds->setUnparsedProps( forward< T >( postCondToks ) );
        }

        const auto& preConds() const { return m_preConds; }

        const auto& postConds() const { return m_postConds; }

        auto& preConds() { return m_preConds; }

        auto& postConds() { return m_postConds; }

      private:
        ptr< Propositions > m_preConds = make_shared< Propositions >();
        ptr< Propositions > m_postConds = make_shared< Propositions >();
    };

    struct FuncLoweringInfos
    {
        vector< Value > loweredParamTypes;
        Value loweredReturnType;
    };

    class FuncType
    {
      public:
        enum class Kind
        {
            Regular,
            Comptime,
            VarArg,
            Intrinsic,
            Inline,
            Ghost
        };

        template< typename R, typename P, typename VI >
        FuncType( R&& returnType, P&& params, VI&& verifInfos, Kind kind = Kind::Regular ) :

            m_returnType( forward< R >( returnType ) ),
            m_params( forward< P >( params ) ),
            m_verifInfos( forward< VI >( verifInfos ) ),
            m_kind( kind )

        {
        }

        template< typename R, typename P, typename VI, typename LI >
        FuncType( R&& returnType, P&& params, VI&& verifInfos, LI&& loweringInfos,
            Kind kind = Kind::Regular ) :
            m_returnType( forward< R >( returnType ) ),
            m_params( forward< P >( params ) ),
            m_verifInfos( forward< VI >( verifInfos ) ),
            m_loweringInfos( forward< LI >( loweringInfos ) ),
            m_kind( kind )

        {
        }

        template< typename R, typename P >
        FuncType( R&& returnType, P&& params ) :
            m_returnType( forward< R >( returnType ) ),
            m_params( forward< P >( params ) )

        {
        }

        const auto& returnType() const { return m_returnType; }

        const auto& params() const { return m_params; }

        const auto& verifInfos() const { return m_verifInfos; }

        const auto& loweringInfos() const { return m_loweringInfos; }

        auto kind() const { return m_kind; }

        void setKind( Kind k );

      private:
        Term m_returnType;
        Term m_params;

        ptr< FuncVerificationInfos > m_verifInfos;
        ptr< FuncLoweringInfos > m_loweringInfos;

        Kind m_kind = Kind::Regular;
    };

    extern bool IsFuncType( const Value& t );

    // The args for a function may include constants that are specialization args.
    // When building a call instruction, we need to strip those out and keep only
    // the args that actually exist at runtime / execution time.
    // We also need to run the args through the ConvertFuncArg() extension point.
    extern optional< Term > BuildArgListForCall(
        const Context& c, const Value& func, const Term& typeCheckedArgs );

    // Takes argument intended to call an intrinsic function and wraps them using TypeWrapper< Value
    // >, so that the intrinsic function code can manipulate the value representations rather than
    // the actual values.
    extern optional< Term > BuildArgListForIntrinsicCall(

        const Context& c, const FuncType& ft, const Term& typeCheckedArgs );
} // namespace goose::builtins

namespace goose::eir
{

    template<> struct Bridge< builtins::FuncType >
    {
        static const Term& Type();
        static Value ToValue( const builtins::FuncType& ft );
        static optional< builtins::FuncType > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/func/invocation/beagerfunc.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 "builtins/builtins.h"
#include "common.h"

using namespace goose::sema;

namespace goose::builtins
{
    class BuiltinEagerFuncInvocationRule : public BaseFuncInvocationRule
    {
        public:
            Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final

            {
                auto callDecomp = Decompose( typeCheckedCallPat,
                    Val< pvec >()
                );

                const auto& typeCheckedRType = callDecomp->get()->terms().front();
                auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
                auto argCount = VecSize( typeCheckedArgs );
                auto argsInstrSeq = BuildArgsInstrSeq( typeCheckedArgs );

                auto call = BuildComputedValue( typeCheckedRType, argsInstrSeq, callee,
                    cir::Call( argCount, loc ) );

                if( !cir::CanValueBeEagerlyEvaluated( call ) )
                    return call;

                Context::DefaultContextGuard dfg( c );
                execute::VM vm;
                return execute::Evaluate( call, vm );
            }

            Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final

            {
                return callee;
            }
    };

    const ptr< InvocationRule >& GetBuiltinEagerFuncInvocationRule()
    {
        static ptr< InvocationRule > pRule = make_shared< BuiltinEagerFuncInvocationRule >();
        return pRule;
    }

    void SetupBuiltinEagerFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(), VEC( TSID( func ),
                TSID( builtin_eager ),
                ANYTERM( _ ), ANYTERM( _ ),
                ANYTERM( _ ), ANYTERM( _ ) ) ) ),

                ANYTERM( _ ) ) ),
            GetBuiltinEagerFuncInvocationRule() );
    }
}









|
|
>
|
|
<
<

|
|
|
|

|
|

|
|

|
|
|
|

|
>
|
|
|













|
<
|
|




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

using namespace goose::sema;

namespace goose::builtins
{
    class BuiltinEagerFuncInvocationRule : public BaseFuncInvocationRule
    {
      public:
        Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args,
            const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
        {
            auto callDecomp = Decompose( typeCheckedCallPat, Val< pvec >() );



            const auto& typeCheckedRType = callDecomp->get()->terms().front();
            auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
            auto argCount = VecSize( typeCheckedArgs );
            auto argsInstrSeq = BuildArgsInstrSeq( typeCheckedArgs );

            auto call = BuildComputedValue(
                typeCheckedRType, argsInstrSeq, callee, cir::Call( argCount, loc ) );

            if( !cir::CanValueBeEagerlyEvaluated( call ) )
                return call;

            Context::DefaultContextGuard dfg( c );
            execute::VM vm;
            return execute::Evaluate( call, vm );
        }

        Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee,
            const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
        {
            return callee;
        }
    };

    const ptr< InvocationRule >& GetBuiltinEagerFuncInvocationRule()
    {
        static ptr< InvocationRule > pRule = make_shared< BuiltinEagerFuncInvocationRule >();
        return pRule;
    }

    void SetupBuiltinEagerFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(),

                    VEC( TSID( func ), TSID( builtin_eager ), ANYTERM( _ ), ANYTERM( _ ),
                        ANYTERM( _ ), ANYTERM( _ ) ) ) ),

                ANYTERM( _ ) ) ),
            GetBuiltinEagerFuncInvocationRule() );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/func/invocation/bfunc.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

#include "builtins/builtins.h"
#include "common.h"

using namespace goose::sema;

namespace goose::builtins
{
    class BuiltinFuncInvocationRule : public BaseFuncInvocationRule
    {
        public:
            Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final

            {
                auto callDecomp = Decompose( typeCheckedCallPat,
                    Val< pvec >()
                );

                const auto& typeCheckedRType = callDecomp->get()->terms().front();
                auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
                auto argCount = VecSize( typeCheckedArgs );
                auto argsInstrSeq = BuildArgsInstrSeq( typeCheckedArgs );


                return BuildComputedValue( typeCheckedRType, move( argsInstrSeq ), callee, cir::Call( argCount, loc ) );
            }

            Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final

            {
                return callee;
            }
    };

    const ptr< InvocationRule >& GetBuiltinFuncInvocationRule()
    {
        static ptr< InvocationRule > pRule = make_shared< BuiltinFuncInvocationRule >();
        return pRule;
    }

    void SetupBuiltinFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(), VEC( TSID( func ),
                TSID( builtin ),
                ANYTERM( _ ), ANYTERM( _ ),
                ANYTERM( _ ), ANYTERM( _ ) ) ) ),

                ANYTERM( _ ) ) ),
            GetBuiltinFuncInvocationRule() );
    }
}










|
|
>
|
|
<
<

|
|
|
|

>
|
|

|
>
|
|
|










|
<

|
|
<
|

|


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

using namespace goose::sema;

namespace goose::builtins
{
    class BuiltinFuncInvocationRule : public BaseFuncInvocationRule
    {
      public:
        Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args,
            const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
        {
            auto callDecomp = Decompose( typeCheckedCallPat, Val< pvec >() );



            const auto& typeCheckedRType = callDecomp->get()->terms().front();
            auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
            auto argCount = VecSize( typeCheckedArgs );
            auto argsInstrSeq = BuildArgsInstrSeq( typeCheckedArgs );

            return BuildComputedValue(
                typeCheckedRType, move( argsInstrSeq ), callee, cir::Call( argCount, loc ) );
        }

        Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee,
            const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
        {
            return callee;
        }
    };

    const ptr< InvocationRule >& GetBuiltinFuncInvocationRule()
    {
        static ptr< InvocationRule > pRule = make_shared< BuiltinFuncInvocationRule >();
        return pRule;
    }

    void SetupBuiltinFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule( ValueToEIR( ValuePattern( ANYTERM( _ ),


                                            ValueToEIR( Value( TypeType(),
                                                VEC( TSID( func ), TSID( builtin ), ANYTERM( _ ),

                                                    ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) ),

                                            ANYTERM( _ ) ) ),
            GetBuiltinFuncInvocationRule() );
    }

} // namespace goose::builtins
Changes to bs/builtins/types/func/invocation/bintrinsic.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 "builtins/builtins.h"
#include "common.h"

using namespace goose::sema;

namespace goose::builtins
{
    class BuiltinIntrinsicFuncInvocationRule : public BaseFuncInvocationRule
    {
        public:
            Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final

            {
                auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
                return GetBuiltinIntrinsicFuncWrapper( callee )( c, move( typeCheckedArgs ) );
            }

            Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final

            {
                return callee;
            }
    };

    const ptr< InvocationRule >& GetBuiltinIntrinsicFuncInvocationRule()
    {
        static ptr< InvocationRule > pRule = make_shared< BuiltinIntrinsicFuncInvocationRule >();
        return pRule;
    }

    void SetupBuiltinIntrinsicFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(), VEC( TSID( func ),
                TSID( intrinsic_builtin ),
                ANYTERM( _ ), ANYTERM( _ ),
                ANYTERM( _ ), ANYTERM( _ ) ) ) ),

                ANYTERM( _ ) ) ),
            GetBuiltinIntrinsicFuncInvocationRule() );
    }
}









|
|
>
|
|
|
|

|
>
|
|
|













|
<
|
|




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

using namespace goose::sema;

namespace goose::builtins
{
    class BuiltinIntrinsicFuncInvocationRule : public BaseFuncInvocationRule
    {
      public:
        Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args,
            const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
        {
            auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
            return GetBuiltinIntrinsicFuncWrapper( callee )( c, move( typeCheckedArgs ) );
        }

        Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee,
            const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
        {
            return callee;
        }
    };

    const ptr< InvocationRule >& GetBuiltinIntrinsicFuncInvocationRule()
    {
        static ptr< InvocationRule > pRule = make_shared< BuiltinIntrinsicFuncInvocationRule >();
        return pRule;
    }

    void SetupBuiltinIntrinsicFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(),

                    VEC( TSID( func ), TSID( intrinsic_builtin ), ANYTERM( _ ), ANYTERM( _ ),
                        ANYTERM( _ ), ANYTERM( _ ) ) ) ),

                ANYTERM( _ ) ) ),
            GetBuiltinIntrinsicFuncInvocationRule() );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/func/invocation/common.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
#include "builtins/builtins.h"
#include "common.h"

using namespace goose::sema;
using namespace goose::cir;

namespace goose::builtins
{
    Value BaseFuncInvocationRule::resolveInvocation( Context& c, LocationId loc, const Value& callee, const Term& args ) const

    {
        optional< TypeCheckingContext > bestTCC;
        optional< Term > bestSol;

        auto sig = GetFuncSig( callee );
        auto callPat = PrependToVectorTerm( args, HOLE( "_"_sid, "fwd"_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 );
    }

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

    Value BaseFuncInvocationRule::EmitTypePredicatesCheck( CFG& cfg, const Value& val, const ptr< Propositions >& props )

    {
        // TODO_SSA reenable
        /*if( props->empty() )
            return val;

        auto uid = util::GenerateNewUID();
        auto bb = cfg.currentBB();








|
>












|
|






|
<



|









|
>







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

using namespace goose::sema;
using namespace goose::cir;

namespace goose::builtins
{
    Value BaseFuncInvocationRule::resolveInvocation(
        Context& c, LocationId loc, const Value& callee, const Term& args ) const
    {
        optional< TypeCheckingContext > bestTCC;
        optional< Term > bestSol;

        auto sig = GetFuncSig( callee );
        auto callPat = PrependToVectorTerm( args, HOLE( "_"_sid, "fwd"_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 );
    }

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

    Value BaseFuncInvocationRule::EmitTypePredicatesCheck(
        CFG& cfg, const Value& val, const ptr< Propositions >& props )
    {
        // TODO_SSA reenable
        /*if( props->empty() )
            return val;

        auto uid = util::GenerateNewUID();
        auto bb = cfg.currentBB();
87
88
89
90
91
92
93
94
            return true;
        }*/

        execute::VM vm;
        call = execute::Evaluate( call, vm );
        return call.isConstant();
    }
}







|
88
89
90
91
92
93
94
95
            return true;
        }*/

        execute::VM vm;
        call = execute::Evaluate( call, vm );
        return call.isConstant();
    }
} // namespace goose::builtins
Changes to bs/builtins/types/func/invocation/common.h.
1
2
3
4
5
6
7
8

9
10
11

12
13
14
15

16
17
#ifndef GOOSE_BUILTINS_TYPES_FUNC_INVOCATION_COMMON_H
#define GOOSE_BUILTINS_TYPES_FUNC_INVOCATION_COMMON_H

namespace goose::builtins
{
    class BaseFuncInvocationRule : public InvocationRule
    {
        public:

            Value resolveInvocation( Context& c, LocationId loc, const Value& callee, const Term& args ) const final;
            optional< Term > getSignature( const Value& callee ) const final;


            static Value EmitTypePredicatesCheck( cir::CFG& cfg, const Value& val, const ptr< Propositions >& props );
            static bool EvaluateComptimeCall( const Context& c, Value& call );
    };
}


#endif







|
>
|
|

>
|
|

<
>


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

17
18
19
#ifndef GOOSE_BUILTINS_TYPES_FUNC_INVOCATION_COMMON_H
#define GOOSE_BUILTINS_TYPES_FUNC_INVOCATION_COMMON_H

namespace goose::builtins
{
    class BaseFuncInvocationRule : public InvocationRule
    {
      public:
        Value resolveInvocation(
            Context& c, LocationId loc, const Value& callee, const Term& args ) const final;
        optional< Term > getSignature( const Value& callee ) const final;

        static Value EmitTypePredicatesCheck(
            cir::CFG& cfg, const Value& val, const ptr< Propositions >& props );
        static bool EvaluateComptimeCall( const Context& c, Value& call );
    };

} // namespace goose::builtins

#endif
Changes to bs/builtins/types/func/invocation/comptime.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
#include "builtins/builtins.h"
#include "common.h"

using namespace goose::sema;

namespace goose::builtins
{
    class ComptimeFuncInvocationRule : public BaseFuncInvocationRule
    {
        public:
            Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final

            {
                auto preparedCallee = prepareFunc( c, 0, callee, typeCheckedCallPat, tcc );
                if( preparedCallee.isPoison() )
                    return PoisonValue();

                auto callDecomp = Decompose( typeCheckedCallPat,
                    Val< pvec >()
                );

                const auto& typeCheckedRType = callDecomp->get()->terms().front();
                auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );

                preparedCallee.setLocationId( loc );

                auto argList = BuildArgListForCall( c, preparedCallee, typeCheckedArgs );
                if( !argList )
                    return PoisonValue();
                auto argsInstrSeq = BuildArgsInstrSeq( *argList );
                auto argCount = VecSize( *argList );

                auto result = BuildComputedValue( typeCheckedRType, argsInstrSeq, preparedCallee,

                    cir::Call( argCount, loc ) ).setLocationId( loc );

                if( HaveUnmetDependencies( preparedCallee ) || !EvaluateComptimeCall( c, result ) )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                        "this call couldn't be evaluated at compilation time." );
                    return PoisonValue();
                }

                return result;
            }

            Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final

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

                return CompileFunc( c, callee );
            }
    };

    void SetupComptimeFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(), VEC( TSID( func ),
                TERM( static_cast< uint64_t >( FuncType::Kind::Comptime ) ),
                ANYTERM( _ ), ANYTERM( _ ),
                ANYTERM( _ ), ANYTERM( _ ) ) ) ),

                ANYTERM( _ ) ) ),
            make_shared< ComptimeFuncInvocationRule >() );
    }
}









|
|
>
|
|
|
|

|
<
<

|
|

|

|
|
|
|
|

|
>
|

|
|
|
|
|
|

|
|

|
>
|
|
>
|
|

|
|







|
|
|
<




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

using namespace goose::sema;

namespace goose::builtins
{
    class ComptimeFuncInvocationRule : public BaseFuncInvocationRule
    {
      public:
        Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args,
            const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
        {
            auto preparedCallee = prepareFunc( c, 0, callee, typeCheckedCallPat, tcc );
            if( preparedCallee.isPoison() )
                return PoisonValue();

            auto callDecomp = Decompose( typeCheckedCallPat, Val< pvec >() );



            const auto& typeCheckedRType = callDecomp->get()->terms().front();
            auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );

            preparedCallee.setLocationId( loc );

            auto argList = BuildArgListForCall( c, preparedCallee, typeCheckedArgs );
            if( !argList )
                return PoisonValue();
            auto argsInstrSeq = BuildArgsInstrSeq( *argList );
            auto argCount = VecSize( *argList );

            auto result = BuildComputedValue(
                typeCheckedRType, argsInstrSeq, preparedCallee, cir::Call( argCount, loc ) )
                              .setLocationId( loc );

            if( HaveUnmetDependencies( preparedCallee ) || !EvaluateComptimeCall( c, result ) )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage(
                    loc, "this call couldn't be evaluated at compilation time." );
                return PoisonValue();
            }

            return result;
        }

        Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee,
            const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
        {
            // 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 );

            return CompileFunc( c, callee );
        }
    };

    void SetupComptimeFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(),
                    VEC( TSID( func ), TERM( static_cast< uint64_t >( FuncType::Kind::Comptime ) ),
                        ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) ),


                ANYTERM( _ ) ) ),
            make_shared< ComptimeFuncInvocationRule >() );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/func/invocation/ghostfunc.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
#include "builtins/builtins.h"
#include "common.h"

using namespace goose::sema;

namespace goose::builtins
{
    class GhostFuncInvocationRule : public BaseFuncInvocationRule
    {
        public:
            Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final

            {
                if( !InVerificationCode( c ) )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                        "ghost functions can't be called in this context." );
                    return PoisonValue();
                }

                auto callDecomp = Decompose( typeCheckedCallPat,
                    Val< pvec >()
                );

                const auto& typeCheckedRType = callDecomp->get()->terms().front();
                auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
                auto argCount = VecSize( typeCheckedArgs );

                auto argList = BuildArgListForCall( c, callee, typeCheckedArgs );
                if( !argList )
                    return PoisonValue();

                auto argsInstrSeq = BuildArgsInstrSeq( *argList );

                // A ghost call is a mutable reference to a ghost closure, using the special "GhostCall"
                // as the instruction to compute its "address"

                auto rt = ValueToEIR( ToValue( ReferenceType{ typeCheckedRType, MutAccessSpecifier() } ) );
                return BuildComputedValue( rt,
                    argsInstrSeq, callee, cir::CallCheck( argCount, loc ),
                    move( argsInstrSeq ), callee, cir::GhostCall( argCount, loc ) );
            }

            Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final

            {
                return callee;
            }
    };

    void SetupGhostFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(), VEC( TSID( func ),
                TERM( static_cast< uint64_t >( FuncType::Kind::Ghost ) ),
                ANYTERM( _ ), ANYTERM( _ ),
                ANYTERM( _ ), ANYTERM( _ ) ) ) ),

                ANYTERM( _ ) ) ),
            make_shared< GhostFuncInvocationRule >() );
    }
}









|
|
>
|
|
|
|
|
|
|

|
<
<

|
|
|

|
|
|

|

|
|
>
|
|
<
|
|

|
>
|
|
|







|
|
|
<




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

using namespace goose::sema;

namespace goose::builtins
{
    class GhostFuncInvocationRule : public BaseFuncInvocationRule
    {
      public:
        Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args,
            const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
        {
            if( !InVerificationCode( c ) )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage(
                    loc, "ghost functions can't be called in this context." );
                return PoisonValue();
            }

            auto callDecomp = Decompose( typeCheckedCallPat, Val< pvec >() );



            const auto& typeCheckedRType = callDecomp->get()->terms().front();
            auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
            auto argCount = VecSize( typeCheckedArgs );

            auto argList = BuildArgListForCall( c, callee, typeCheckedArgs );
            if( !argList )
                return PoisonValue();

            auto argsInstrSeq = BuildArgsInstrSeq( *argList );

            // A ghost call is a mutable reference to a ghost closure, using the special "GhostCall"
            // as the instruction to compute its "address"
            auto rt =
                ValueToEIR( ToValue( ReferenceType{ typeCheckedRType, MutAccessSpecifier() } ) );
            return BuildComputedValue( rt, argsInstrSeq, callee, cir::CallCheck( argCount, loc ),

                move( argsInstrSeq ), callee, cir::GhostCall( argCount, loc ) );
        }

        Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee,
            const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
        {
            return callee;
        }
    };

    void SetupGhostFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(),
                    VEC( TSID( func ), TERM( static_cast< uint64_t >( FuncType::Kind::Ghost ) ),
                        ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) ),


                ANYTERM( _ ) ) ),
            make_shared< GhostFuncInvocationRule >() );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/func/invocation/inline.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
#include "builtins/builtins.h"
#include "common.h"

using namespace goose::sema;

namespace goose::builtins
{
    class InlineFuncInvocationRule : public BaseFuncInvocationRule
    {
        public:
            Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final

            {
                if( !callee.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc, "indirect inline function call." );

                    return PoisonValue().setLocationId( loc );
                }

                auto funcCIR = GetFuncCIR( callee );

                if( !Func::FuncStack().empty() )    // TODO: can happen today when called from toplevel. Remove after factoring toplevel parsing into a function wrapper.

                {
                    // Check for invalid recursive inline calls.
                    for( auto&& fi : Func::FuncStack() | views::reverse )
                    {
                        if( fi.kind == FuncType::Kind::Regular )
                            break;

                        if( fi.cir == funcCIR )
                        {
                            DiagnosticsManager::GetInstance().emitErrorMessage( loc, "recursive inline call." );

                            return PoisonValue().setLocationId( loc );
                        }
                    }
                }

                auto cfg = GetCFG( c );
                if( !cfg )
                {
                    // TODO: currently this mean among other things "not from proposition lists aka type predicates",
                    // which sucks. Need to once again revisit the idea of getting rid of proposition lists and replace them with just CFGs.

                    DiagnosticsManager::GetInstance().emitErrorMessage( loc, "inline functions can't be called here." );

                    return PoisonValue().setLocationId( loc );
                }

                auto callDecomp = Decompose( typeCheckedCallPat,
                    Val< pvec >()
                );

                const auto& typeCheckedRType = callDecomp->get()->terms().front();
                auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );

                auto argList = BuildArgListForCall( c, callee, typeCheckedArgs );
                if( !argList )
                    return PoisonValue();
                auto argCount = VecSize( *argList );

                cir::InlineCall::ArgUidArray argUids( argCount );

                uint32_t index = 0;
                auto argsInstrSeq = BuildArgsInstrSeq( *argList, [&]( auto&& argVal, auto&& is )

                {
                    auto uid = util::GenerateNewUID();
                    argUids[index++] = uid;
                  /*  AppendToInstrSeq( is, cir::CreateTemporary( uid, false,
                        argVal.locationId() ) );*/
                } );

                // If the function we want to call have unmet dependencies, or if the function we are calling
                // is currently being compiled (it it's present in the func stack), then we can't perform the inlining eagerly.

                // So we emit an InlineCall instr that will be expanded once the dependencies are met, and we register
                // that dependendy in the DepsGraph so it can make it happen.
                bool shouldDeferInlining = HaveUnmetDependencies( callee );
                if( !shouldDeferInlining )
                {
                    for( auto&& fi : Func::FuncStack() | views::reverse )
                    {
                        if( fi.cir == funcCIR )
                        {
                            shouldDeferInlining = true;
                            break;
                        }
                    }
                }

                if( shouldDeferInlining )
                {
                    const auto& caller = Func::CurrentCaller();
                    auto result = BuildComputedValue( typeCheckedRType, argsInstrSeq, callee,
                        cir::InlineCall( move( argUids ), funcCIR, typeCheckedRType, loc ) );

                    Env::DepsGraph().add( caller.cir, funcCIR );
                    return result;
                }

                auto preparedCallee = prepareFunc( c, 0, callee, typeCheckedCallPat, tcc );
                if( preparedCallee.isPoison() )
                    return PoisonValue();

                preparedCallee.setLocationId( loc );

                // no unmet dependencies: eagerly inline the function, so that the function have a chance to
                // be eagerly evaluated later
                cfg->currentBB()->append( move( argsInstrSeq ) );
                auto retIs = AppendCFG( *cfg, *funcCIR->body(), typeCheckedRType, argUids );

                // After AppendCFG, the resulting code could push the return value in a way
                // or another, or give us an instruction sequence to load it from some var slot.
                // Since we need to package it into a computed value that may not necessarily
                // be appended right away, if we are receiving an empty instr sequence,
                // we need to store it into a temporary.
                if( retIs->empty() )
                {
                   /* auto retValUid = util::GenerateNewUID();
                    cfg->currentBB()->append(
                        cir::CreateTemporary( retValUid, false, loc ) );

                    AppendToInstrSeq( *retIs, cir::GetTemporary{ typeCheckedRType, retValUid, loc } );*/

                }

                return BuildComputedValue( typeCheckedRType, move( retIs ) );

                // TODO: we need to support eager evaluation of inline function calls. (Otherwise whenever
                // we make generic inline wrapper functions they will not really work for types and it will be
                // annoying)
                // If all dependencies are met, and if a regular call to it could be eagerly evaluated,
                // Then attempt to perform an eager evaluation like regular function does (move that code from regular
                // func invoke to a helper func in common)
                // The current inline func tests may need to be rewritten to prevent eager evaluation.
                // (or perhaps add a directive for test purpose only to disable eager evaluation of inline funcs?)

            }

            Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final

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

                return CompileFunc( c, callee );
            }
    };

    void SetupInlineFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(), VEC( TSID( func ),
                TERM( static_cast< uint64_t >( FuncType::Kind::Inline ) ),
                ANYTERM( _ ), ANYTERM( _ ),
                ANYTERM( _ ), ANYTERM( _ ) ) ) ),

                ANYTERM( _ ) ) ),
            make_shared< InlineFuncInvocationRule >() );
    }
}









|
|
>
|
|
|
|
>
|
|

|
>
|
>
|
|
|
|
|
|

|
|
|
>
|
|
|
|

|
|
|
|
|
>
|
>
|
|

|
<
<

|
|

|
|
|
|

|

|
|
>



|
|


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

|
|
|
|
|

|
|
|

|
|
|

|

|
|
|
|

|
|
|
|
|
|
|
|
|
|

|
>
|

|

|
|
<
|
|
|
|
|
>
|

|
>
|
|
>
|
|

|
|







|
|
|
<




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

using namespace goose::sema;

namespace goose::builtins
{
    class InlineFuncInvocationRule : public BaseFuncInvocationRule
    {
      public:
        Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args,
            const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
        {
            if( !callee.isConstant() )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage(
                    loc, "indirect inline function call." );
                return PoisonValue().setLocationId( loc );
            }

            auto funcCIR = GetFuncCIR( callee );
            if( !Func::FuncStack()
                     .empty() ) // TODO: can happen today when called from toplevel. Remove after
                                // factoring toplevel parsing into a function wrapper.
            {
                // Check for invalid recursive inline calls.
                for( auto&& fi : Func::FuncStack() | views::reverse )
                {
                    if( fi.kind == FuncType::Kind::Regular )
                        break;

                    if( fi.cir == funcCIR )
                    {
                        DiagnosticsManager::GetInstance().emitErrorMessage(
                            loc, "recursive inline call." );
                        return PoisonValue().setLocationId( loc );
                    }
                }
            }

            auto cfg = GetCFG( c );
            if( !cfg )
            {
                // TODO: currently this mean among other things "not from proposition lists aka type
                // predicates", which sucks. Need to once again revisit the idea of getting rid of
                // proposition lists and replace them with just CFGs.
                DiagnosticsManager::GetInstance().emitErrorMessage(
                    loc, "inline functions can't be called here." );
                return PoisonValue().setLocationId( loc );
            }

            auto callDecomp = Decompose( typeCheckedCallPat, Val< pvec >() );



            const auto& typeCheckedRType = callDecomp->get()->terms().front();
            auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );

            auto argList = BuildArgListForCall( c, callee, typeCheckedArgs );
            if( !argList )
                return PoisonValue();
            auto argCount = VecSize( *argList );

            cir::InlineCall::ArgUidArray argUids( argCount );

            uint32_t index = 0;
            auto argsInstrSeq = BuildArgsInstrSeq( *argList,
                [&]( auto&& argVal, auto&& is )
                {
                    auto uid = util::GenerateNewUID();
                    argUids[index++] = uid;
                    /*  AppendToInstrSeq( is, cir::CreateTemporary( uid, false,
                          argVal.locationId() ) );*/
                } );

            // If the function we want to call have unmet dependencies, or if the function we are
            // calling is currently being compiled (it it's present in the func stack), then we
            // can't perform the inlining eagerly. So we emit an InlineCall instr that will be
            // expanded once the dependencies are met, and we register that dependendy in the
            // DepsGraph so it can make it happen.
            bool shouldDeferInlining = HaveUnmetDependencies( callee );
            if( !shouldDeferInlining )
            {
                for( auto&& fi : Func::FuncStack() | views::reverse )
                {
                    if( fi.cir == funcCIR )
                    {
                        shouldDeferInlining = true;
                        break;
                    }
                }
            }

            if( shouldDeferInlining )
            {
                const auto& caller = Func::CurrentCaller();
                auto result = BuildComputedValue( typeCheckedRType, argsInstrSeq, callee,
                    cir::InlineCall( move( argUids ), funcCIR, typeCheckedRType, loc ) );

                Env::DepsGraph().add( caller.cir, funcCIR );
                return result;
            }

            auto preparedCallee = prepareFunc( c, 0, callee, typeCheckedCallPat, tcc );
            if( preparedCallee.isPoison() )
                return PoisonValue();

            preparedCallee.setLocationId( loc );

            // no unmet dependencies: eagerly inline the function, so that the function have a
            // chance to be eagerly evaluated later
            cfg->currentBB()->append( move( argsInstrSeq ) );
            auto retIs = AppendCFG( *cfg, *funcCIR->body(), typeCheckedRType, argUids );

            // After AppendCFG, the resulting code could push the return value in a way
            // or another, or give us an instruction sequence to load it from some var slot.
            // Since we need to package it into a computed value that may not necessarily
            // be appended right away, if we are receiving an empty instr sequence,
            // we need to store it into a temporary.
            if( retIs->empty() )
            {
                /* auto retValUid = util::GenerateNewUID();
                 cfg->currentBB()->append(
                     cir::CreateTemporary( retValUid, false, loc ) );

                 AppendToInstrSeq( *retIs, cir::GetTemporary{ typeCheckedRType, retValUid, loc }
                 );*/
            }

            return BuildComputedValue( typeCheckedRType, move( retIs ) );

            // TODO: we need to support eager evaluation of inline function calls. (Otherwise
            // whenever we make generic inline wrapper functions they will not really work for types

            // and it will be annoying) If all dependencies are met, and if a regular call to it
            // could be eagerly evaluated, Then attempt to perform an eager evaluation like regular
            // function does (move that code from regular func invoke to a helper func in common)
            // The current inline func tests may need to be rewritten to prevent eager evaluation.
            // (or perhaps add a directive for test purpose only to disable eager evaluation of
            // inline funcs?)
        }

        Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee,
            const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
        {
            // 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 );

            return CompileFunc( c, callee );
        }
    };

    void SetupInlineFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(),
                    VEC( TSID( func ), TERM( static_cast< uint64_t >( FuncType::Kind::Inline ) ),
                        ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) ),


                ANYTERM( _ ) ) ),
            make_shared< InlineFuncInvocationRule >() );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/func/invocation/intrinsic.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
#include "builtins/builtins.h"
#include "builtins/helpers.h"
#include "common.h"

using namespace goose::sema;
using namespace goose::cir;

namespace goose::builtins
{
    class IntrinsicFuncInvocationRule : public BaseFuncInvocationRule
    {
        public:
            Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final

            {
                if( !callee.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc, "indirect intrinsic function call." );

                    return PoisonValue().setLocationId( loc );
                }

                auto funcCIR = GetFuncCIR( callee );

                if( !Func::FuncStack().empty() )    // TODO: can happen today when called from toplevel. Remove after factoring toplevel parsing into a function wrapper.

                {
                    // Check for invalid recursive intrinsic calls.
                    for( auto&& fi : Func::FuncStack() | views::reverse )
                    {
                        if( fi.kind == FuncType::Kind::Regular )
                            break;

                        if( fi.cir == funcCIR )
                        {
                            DiagnosticsManager::GetInstance().emitErrorMessage( loc, "recursive intrinsic call." );

                            return PoisonValue().setLocationId( loc );
                        }
                    }
                }

                // TODO: if the callee have unmet dependencies, error out. intrinsic functions
                // can only be eagerly evaluated.

                auto preparedCallee = prepareFunc( c, 0, callee, typeCheckedCallPat, tcc );
                if( preparedCallee.isPoison() )
                    return PoisonValue();

                auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
                preparedCallee.setLocationId( loc );

                auto ft = *FromValue< FuncType >( *EIRToValue( preparedCallee.type() ) );

                // Intrinsic call: we insert the context wrapper as first param,
                // wrap all args with TypeWrapper< Value >, and execute the function directly.
                auto argList = BuildArgListForIntrinsicCall( c, ft, typeCheckedArgs );
                if( !argList )
                    return PoisonValue();

                auto argsInstrSeq = BuildArgsInstrSeq( *argList );
                auto argCount = VecSize( *argList );

                execute::VM vm;
                cir::InstrSeq is;

                AppendToInstrSeq( is, move( argsInstrSeq ), preparedCallee, cir::Call( argCount, loc ) );

                if( !vm.execute( is ) )
                    return PoisonValue();
                if( ft.returnType() == GetValueType< void >() )
                    return Value( GetValueType< void >(), 0U );

                auto result = vm.pop();
                if( !result )
                    return PoisonValue();

                if( result->type() != GetValueType< TypeWrapper< Value > >() )
                    return *result;

                auto& dm = DiagnosticsManager::GetInstance();

                auto unwrapped = *FromValue< TypeWrapper< Value > >( *result );
                auto converted = ConvertValueToType( c, unwrapped,
                    GetUnwrappedType( *EIRToValue( ft.returnType() ) ) );
                if( holds_alternative< ValUnifyError >( converted ) )
                {
                    switch( get< ValUnifyError >( converted ) )
                    {
                        case ValUnifyError::NoSolution:
                            dm.emitErrorMessage( loc, "return value type mismatch." );
                            break;

                        case ValUnifyError::Ambiguous:
                            dm.emitErrorMessage( loc, "ambiguous return value conversion." );
                            break;
                    }
                }

                const auto& convVal = get< Value >( converted );

                // TODO_SSA reenable
                // If the type have predicates, emit assertion checks.
                /*auto preds = GetTypePredicates( *EIRToValue( convVal.type() ) );
                if( !preds || !*preds )
                    return convVal;

                if( ( *preds )->props().empty() )
                    return convVal;

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

                return EmitTypePredicatesCheck( *cfg, convVal, *preds );*/
                return convVal;
            }

            Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final

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

                return CompileFunc( c, callee );
            }
    };

    void SetupIntrinsicFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(), VEC( TSID( func ),
                TERM( static_cast< uint64_t >( FuncType::Kind::Intrinsic ) ),
                ANYTERM( _ ), ANYTERM( _ ),
                ANYTERM( _ ), ANYTERM( _ ) ) ) ),

                ANYTERM( _ ) ) ),
            make_shared< IntrinsicFuncInvocationRule >() );
    }
}











|
|
>
|
|
|
|
>
|
|

|
>
|
>
|
|
|
|
|
|

|
|
|
>
|
|
|
|

|
|

|
|
|

|
|

|

|
|
|
|
|

|
|

|
|
>
|

|
|
|
|

|
|
|

|
|

|

|
|
|
|
|
|
|
|
|
|

|
|
|
|
|

|

|
|
|
|
|

|
|

|
|
|

|
|
|

|
>
|
|
>
|
|

|
|







|
|
|
<




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

using namespace goose::sema;
using namespace goose::cir;

namespace goose::builtins
{
    class IntrinsicFuncInvocationRule : public BaseFuncInvocationRule
    {
      public:
        Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args,
            const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
        {
            if( !callee.isConstant() )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage(
                    loc, "indirect intrinsic function call." );
                return PoisonValue().setLocationId( loc );
            }

            auto funcCIR = GetFuncCIR( callee );
            if( !Func::FuncStack()
                     .empty() ) // TODO: can happen today when called from toplevel. Remove after
                                // factoring toplevel parsing into a function wrapper.
            {
                // Check for invalid recursive intrinsic calls.
                for( auto&& fi : Func::FuncStack() | views::reverse )
                {
                    if( fi.kind == FuncType::Kind::Regular )
                        break;

                    if( fi.cir == funcCIR )
                    {
                        DiagnosticsManager::GetInstance().emitErrorMessage(
                            loc, "recursive intrinsic call." );
                        return PoisonValue().setLocationId( loc );
                    }
                }
            }

            // TODO: if the callee have unmet dependencies, error out. intrinsic functions
            // can only be eagerly evaluated.

            auto preparedCallee = prepareFunc( c, 0, callee, typeCheckedCallPat, tcc );
            if( preparedCallee.isPoison() )
                return PoisonValue();

            auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
            preparedCallee.setLocationId( loc );

            auto ft = *FromValue< FuncType >( *EIRToValue( preparedCallee.type() ) );

            // Intrinsic call: we insert the context wrapper as first param,
            // wrap all args with TypeWrapper< Value >, and execute the function directly.
            auto argList = BuildArgListForIntrinsicCall( c, ft, typeCheckedArgs );
            if( !argList )
                return PoisonValue();

            auto argsInstrSeq = BuildArgsInstrSeq( *argList );
            auto argCount = VecSize( *argList );

            execute::VM vm;
            cir::InstrSeq is;
            AppendToInstrSeq(
                is, move( argsInstrSeq ), preparedCallee, cir::Call( argCount, loc ) );

            if( !vm.execute( is ) )
                return PoisonValue();
            if( ft.returnType() == GetValueType< void >() )
                return Value( GetValueType< void >(), 0U );

            auto result = vm.pop();
            if( !result )
                return PoisonValue();

            if( result->type() != GetValueType< TypeWrapper< Value > >() )
                return *result;

            auto& dm = DiagnosticsManager::GetInstance();

            auto unwrapped = *FromValue< TypeWrapper< Value > >( *result );
            auto converted = ConvertValueToType(
                c, unwrapped, GetUnwrappedType( *EIRToValue( ft.returnType() ) ) );
            if( holds_alternative< ValUnifyError >( converted ) )
            {
                switch( get< ValUnifyError >( converted ) )
                {
                    case ValUnifyError::NoSolution:
                        dm.emitErrorMessage( loc, "return value type mismatch." );
                        break;

                    case ValUnifyError::Ambiguous:
                        dm.emitErrorMessage( loc, "ambiguous return value conversion." );
                        break;
                }
            }

            const auto& convVal = get< Value >( converted );

            // TODO_SSA reenable
            // If the type have predicates, emit assertion checks.
            /*auto preds = GetTypePredicates( *EIRToValue( convVal.type() ) );
            if( !preds || !*preds )
                return convVal;

            if( ( *preds )->props().empty() )
                return convVal;

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

            return EmitTypePredicatesCheck( *cfg, convVal, *preds );*/
            return convVal;
        }

        Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee,
            const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
        {
            // 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 );

            return CompileFunc( c, callee );
        }
    };

    void SetupIntrinsicFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(),
                    VEC( TSID( func ), TERM( static_cast< uint64_t >( FuncType::Kind::Intrinsic ) ),
                        ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) ),


                ANYTERM( _ ) ) ),
            make_shared< IntrinsicFuncInvocationRule >() );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/func/invocation/invocation.h.
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
    extern void SetupIntrinsicFuncInvocationRule( Env& e );
    extern void SetupInlineFuncInvocationRule( Env& e );

    extern const ptr< InvocationRule >& GetBuiltinFuncInvocationRule();
    extern const ptr< InvocationRule >& GetBuiltinEagerFuncInvocationRule();
    extern const ptr< InvocationRule >& GetBuiltinIntrinsicFuncInvocationRule();

    template< typename F >
    struct InvocationRuleSelector
    {
        static const auto& Get() { return GetBuiltinFuncInvocationRule(); }
    };

    template< typename R, typename... T >
    struct InvocationRuleSelector< Eager< R >( T... ) >
    {
        static const auto& Get() { return GetBuiltinEagerFuncInvocationRule(); }
    };

    template< typename F >
    struct InvocationRuleSelector< Intrinsic< F > >
    {
        static const auto& Get() { return GetBuiltinIntrinsicFuncInvocationRule(); }
    };

    template< typename F >
    const ptr< InvocationRule >& GetInvocationRule()
    {
        return InvocationRuleSelector< F >::Get();
    }

    static inline void SetupFuncInvocationRules( Env& e )
    {
        SetupBuiltinEagerFuncInvocationRule( e );
        SetupBuiltinFuncInvocationRule( e );
        SetupBuiltinIntrinsicFuncInvocationRule( e );
        SetupRegularFuncInvocationRule( e );
        SetupComptimeFuncInvocationRule( e );
        SetupGhostFuncInvocationRule( e );
        SetupIntrinsicFuncInvocationRule( e );
        SetupInlineFuncInvocationRule( e );
    }
}

#endif







|
<




|
<




<
|




<
|















|


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
    extern void SetupIntrinsicFuncInvocationRule( Env& e );
    extern void SetupInlineFuncInvocationRule( Env& e );

    extern const ptr< InvocationRule >& GetBuiltinFuncInvocationRule();
    extern const ptr< InvocationRule >& GetBuiltinEagerFuncInvocationRule();
    extern const ptr< InvocationRule >& GetBuiltinIntrinsicFuncInvocationRule();

    template< typename F > struct InvocationRuleSelector

    {
        static const auto& Get() { return GetBuiltinFuncInvocationRule(); }
    };

    template< typename R, typename... T > struct InvocationRuleSelector< Eager< R >( T... ) >

    {
        static const auto& Get() { return GetBuiltinEagerFuncInvocationRule(); }
    };


    template< typename F > struct InvocationRuleSelector< Intrinsic< F > >
    {
        static const auto& Get() { return GetBuiltinIntrinsicFuncInvocationRule(); }
    };


    template< typename F > const ptr< InvocationRule >& GetInvocationRule()
    {
        return InvocationRuleSelector< F >::Get();
    }

    static inline void SetupFuncInvocationRules( Env& e )
    {
        SetupBuiltinEagerFuncInvocationRule( e );
        SetupBuiltinFuncInvocationRule( e );
        SetupBuiltinIntrinsicFuncInvocationRule( e );
        SetupRegularFuncInvocationRule( e );
        SetupComptimeFuncInvocationRule( e );
        SetupGhostFuncInvocationRule( e );
        SetupIntrinsicFuncInvocationRule( e );
        SetupInlineFuncInvocationRule( e );
    }
} // namespace goose::builtins

#endif
Changes to bs/builtins/types/func/invocation/regular.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

#include "builtins/builtins.h"
#include "common.h"

using namespace goose::sema;

namespace goose::builtins
{
    class RegularFuncInvocationRule : public BaseFuncInvocationRule
    {
        public:
            Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final

            {
                auto preparedCallee = prepareFunc( c, 0, callee, typeCheckedCallPat, tcc );
                if( preparedCallee.isPoison() )
                    return PoisonValue();

                auto callDecomp = Decompose( typeCheckedCallPat,
                    Val< pvec >()
                );

                const auto& typeCheckedRType = callDecomp->get()->terms().front();
                auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );

                preparedCallee.setLocationId( loc );

                auto argList = BuildArgListForCall( c, preparedCallee, typeCheckedArgs );
                if( !argList )
                    return PoisonValue();
                auto argsInstrSeq = BuildArgsInstrSeq( *argList );
                auto argCount = VecSize( *argList );

                auto result = BuildComputedValue( typeCheckedRType, argsInstrSeq, preparedCallee,

                    cir::Call( argCount, loc ) ).setLocationId( loc );

                if( IsExternalFunc( callee ) )
                {
                    // No dependency handling or eager evaluation on external funcs,
                    // so we're done.
                    return result;
                }

                bool comptime = false;

                // If we are part of call chain involving a comptime function, we need the call to be comptime as well.

                for( auto&& fi : Func::FuncStack() | views::reverse )
                {
                    if( fi.kind == FuncType::Kind::Comptime )
                    {
                        comptime = true;
                        break;
                    }
                }

                if( !comptime )
                    return result;

                if( HaveUnmetDependencies( preparedCallee ) || !EvaluateComptimeCall( c, result ) )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( loc,
                        "this call couldn't be evaluated at compilation time." );
                    return PoisonValue();
                }

                return result;
            }

            Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final

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

                return CompileFunc( c, callee );
            }
    };

    void SetupRegularFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( Value( TypeType(), VEC( TSID( func ),
                ANYTERM( _ ), ANYTERM( _ ),
                ANYTERM( _ ), ANYTERM( _ ),
                ANYTERM( _ ) ) ) ),

                ANYTERM( _ ) ) ),
            make_shared< RegularFuncInvocationRule >() );
    }
}










|
|
>
|
|
|
|

|
<
<

|
|

|

|
|
|
|
|

|
>
|

|
|
|
|
|
|

|

|
>
|
|
|
|
|
|
|
|

|
|

|
|
|
|
|
|

|
|

|
>
|
|
>
|
|

|
|




|
<

|
|
|
<

|


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

using namespace goose::sema;

namespace goose::builtins
{
    class RegularFuncInvocationRule : public BaseFuncInvocationRule
    {
      public:
        Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args,
            const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
        {
            auto preparedCallee = prepareFunc( c, 0, callee, typeCheckedCallPat, tcc );
            if( preparedCallee.isPoison() )
                return PoisonValue();

            auto callDecomp = Decompose( typeCheckedCallPat, Val< pvec >() );



            const auto& typeCheckedRType = callDecomp->get()->terms().front();
            auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );

            preparedCallee.setLocationId( loc );

            auto argList = BuildArgListForCall( c, preparedCallee, typeCheckedArgs );
            if( !argList )
                return PoisonValue();
            auto argsInstrSeq = BuildArgsInstrSeq( *argList );
            auto argCount = VecSize( *argList );

            auto result = BuildComputedValue(
                typeCheckedRType, argsInstrSeq, preparedCallee, cir::Call( argCount, loc ) )
                              .setLocationId( loc );

            if( IsExternalFunc( callee ) )
            {
                // No dependency handling or eager evaluation on external funcs,
                // so we're done.
                return result;
            }

            bool comptime = false;

            // If we are part of call chain involving a comptime function, we need the call to be
            // comptime as well.
            for( auto&& fi : Func::FuncStack() | views::reverse )
            {
                if( fi.kind == FuncType::Kind::Comptime )
                {
                    comptime = true;
                    break;
                }
            }

            if( !comptime )
                return result;

            if( HaveUnmetDependencies( preparedCallee ) || !EvaluateComptimeCall( c, result ) )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage(
                    loc, "this call couldn't be evaluated at compilation time." );
                return PoisonValue();
            }

            return result;
        }

        Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee,
            const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
        {
            // 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 );

            return CompileFunc( c, callee );
        }
    };

    void SetupRegularFuncInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule( ValueToEIR( ValuePattern( ANYTERM( _ ),


                                            ValueToEIR( Value( TypeType(),
                                                VEC( TSID( func ), ANYTERM( _ ), ANYTERM( _ ),
                                                    ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) ),


                                            ANYTERM( _ ) ) ),
            make_shared< RegularFuncInvocationRule >() );
    }

} // namespace goose::builtins
Changes to bs/builtins/types/func/lower.cpp.
32
33
34
35
36
37
38
39
            paramTypes.emplace_back( pCGType );
        }

        return codegen::FunctionType::Get( pRTType, paramTypes,
            ft.kind() == FuncType::Kind::VarArg );*/
        return nullptr;
    }
}







|
32
33
34
35
36
37
38
39
            paramTypes.emplace_back( pCGType );
        }

        return codegen::FunctionType::Get( pRTType, paramTypes,
            ft.kind() == FuncType::Kind::VarArg );*/
        return nullptr;
    }
} // namespace goose::builtins
Changes to bs/builtins/types/func/typecheck.cpp.
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
    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(

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

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

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

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

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

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

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

            // Check the types
            for( auto&& [ut,tcc] : TypeCheck( lhsVal.type(), rhsVal.type(), tcc ) )
            {
                // Check the contents
                for( auto&& [uv,tcc] : TypeCheck( lhsVal.val(), rhsVal.val(), tcc ) )
                {

                    ValuePattern result( TSID( constant ), move( ut ), move( uv ), rhsVal.locationId() );
                    co_yield { ValueToEIR( move( result ) ), tcc };
                }
            }
        } );

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

            ParamPat( FuncTypePattern() ),

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

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            auto rhsVal = *EIRToValue( 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 ValueToEIR( func );
            } );

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

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

            ParamPat( TFuncTypeSigPattern( ANYTERM( _ ), ANYTERM( _ ) ) ),

            ValueToEIR( ValuePattern(
                TSID( constant ),
                FuncTypePattern(),
                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 [tfType,_] = DecomposeTFTSig( type );
            auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *EIRToValue( tfType ) );
            assert( callPat );

            auto rhsVal = *EIRToValue( rhs );
            auto sig = GetFuncSig( rhsVal );

            for( auto&& [s, tcc] : TypeCheck( sig, *callPat, tcc ) )
            {
                if( IsBuiltinFunc( rhsVal ) )
                {
                    co_yield { ValueToEIR( 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 ValueToEIR( func );
                } );

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







|
<
<
<

|
<
<
<

|
|
|
|
|
|




|
<
<
<

|
<
<
<

|
|
|
|

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






|
<
<
<

|
|
|

|
|
|
|
|

|
|
|
|
|

|
|
|

|
|

|
|






|
<
<
<

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

|
|
|
|

|
|

|
|
|
|
|
|
|

|
>
|
|
|
|

|
|
|

|
|

|
|
|

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

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




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




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

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

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




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




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

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

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

            ParamPat( FuncTypePattern() ),

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




            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                auto rhsVal = *EIRToValue( 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 ValueToEIR( func );
                    } );

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

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

            ParamPat( TFuncTypeSigPattern( ANYTERM( _ ), ANYTERM( _ ) ) ),

            ValueToEIR( ValuePattern( TSID( constant ), FuncTypePattern(), 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 [tfType, _] = DecomposeTFTSig( type );
                auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *EIRToValue( tfType ) );
                assert( callPat );

                auto rhsVal = *EIRToValue( rhs );
                auto sig = GetFuncSig( rhsVal );

                for( auto&& [s, tcc] : TypeCheck( sig, *callPat, tcc ) )
                {
                    if( IsBuiltinFunc( rhsVal ) )
                    {
                        co_yield { ValueToEIR( 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 ValueToEIR( func );
                        } );

                    co_yield { move( wrapped ), tcc };
                }
            } );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/func/wrapped.h.
8
9
10
11
12
13
14
15
16
17
    extern Term GetUnwrappedType( const Value& wrapppedArgType );

    extern Value GetWrapped( const Value& arg );
    extern Value GetUnwrapped( const Value& wrappedArg );

    extern bool IsWrappedType( const Value& type );
    extern bool IsWrappedType( const Term& type );
}

#endif







|


8
9
10
11
12
13
14
15
16
17
    extern Term GetUnwrappedType( const Value& wrapppedArgType );

    extern Value GetWrapped( const Value& arg );
    extern Value GetUnwrapped( const Value& wrappedArg );

    extern bool IsWrappedType( const Value& type );
    extern bool IsWrappedType( const Term& type );
} // namespace goose::builtins

#endif
Changes to bs/builtins/types/ghostcode/drop.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 "builtins/builtins.h"
#include "parse/parse.h"

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

namespace goose::builtins
{
    void SetupGhostCodeDropValue( Env& e )
    {
        // When a boolean is dropped into a ghost code block, it is appended as an assertion check.
        RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< GhostCode > >, bool ) > >( e, e.extDropValue(),

            []( const Context& c, const Value& gcv, const Value& b )
            {
                auto gc = *FromValue< TypeWrapper< ptr< GhostCode > > >( gcv );

                gc->cfg()->currentBB()->append(
                    b, cir::Assert( b.locationId() )
                );
            } );

        // When a ghost code block is dropped into a code builder, we append it using a GhostBranch terminator.


        RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< CodeBuilder > >, TypeWrapper< ptr< GhostCode > > ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& cbv, const Value& gcv )
            {
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( cbv );
                auto gc = *FromValue< TypeWrapper< ptr< GhostCode > > >( gcv );

                assert( cb->cfg() == gc->cfg() );

                auto contBB = cb->cfg()->createBB();

                gc->fixUpExitBranches( contBB );

                cb->cfg()->emitTerminator( cbv.locationId(), cir::GhostBranch( gc->entryBB(), contBB ) );
                cb->cfg()->setCurrentBB( contBB );
            } );
    }
}











|
>




|
<
<


|
>
>
|










>
|



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

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

namespace goose::builtins
{
    void SetupGhostCodeDropValue( Env& e )
    {
        // When a boolean is dropped into a ghost code block, it is appended as an assertion check.
        RegisterBuiltinFunc< Intrinsic< void( TypeWrapper< ptr< GhostCode > >, bool ) > >( e,
            e.extDropValue(),
            []( const Context& c, const Value& gcv, const Value& b )
            {
                auto gc = *FromValue< TypeWrapper< ptr< GhostCode > > >( gcv );

                gc->cfg()->currentBB()->append( b, cir::Assert( b.locationId() ) );


            } );

        // When a ghost code block is dropped into a code builder, we append it using a GhostBranch
        // terminator.
        RegisterBuiltinFunc< Intrinsic< void( TypeWrapper< ptr< CodeBuilder > >,
            TypeWrapper< ptr< GhostCode > > ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& cbv, const Value& gcv )
            {
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( cbv );
                auto gc = *FromValue< TypeWrapper< ptr< GhostCode > > >( gcv );

                assert( cb->cfg() == gc->cfg() );

                auto contBB = cb->cfg()->createBB();

                gc->fixUpExitBranches( contBB );
                cb->cfg()->emitTerminator(
                    cbv.locationId(), cir::GhostBranch( gc->entryBB(), contBB ) );
                cb->cfg()->setCurrentBB( contBB );
            } );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/ghostcode/ghostcode.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
#ifndef GOOSE_BUILTINS_TYPE_GHOSTCODE_H
#define GOOSE_BUILTINS_TYPE_GHOSTCODE_H

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

    class GhostCode : public LifeCycleManager< GhostCode >
    {
        public:
            GhostCode( const ptr< cir::CFG >& cfg ) :
                m_cfg( cfg )
            {
                m_entryBB = m_cfg->createBB();
            }

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

            const auto& entryBB() const { return m_entryBB; }

            bool branchingAllowed() const { return m_branchingAllowed; }

            void setBranchingAllowed( bool b ) { m_branchingAllowed = b; }

            template< typename V >
            void append( V&& v )
            {
                cfg()->currentBB()->append( forward< V >( v ) );
            }

            // Used to mark the ghost code as poisoned if something goes wrong during
            // its parsing.
            void poison()
            {
                m_poison = true;
            }

            void fixUpExitBranches( const ptr< cir::BasicBlock >& contBB )
            {
                m_cfg->forEachReachableBlock( m_entryBB, [&]( auto&& bb )

                {
                    if( !bb->terminator() )
                        bb->setTerminator( cir::Branch( contBB ) );
                } );
            }

        private:
            ptr< cir::CFG > m_cfg;
            ptr< cir::BasicBlock > m_entryBB;

            bool m_branchingAllowed = true;
            bool m_poison = false;
    };
}

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

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

    class GhostCode : public LifeCycleManager< GhostCode >
    {
      public:
        GhostCode( const ptr< cir::CFG >& cfg ) :
            m_cfg( cfg )
        {
            m_entryBB = m_cfg->createBB();
        }

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

        const auto& entryBB() const { return m_entryBB; }

        bool branchingAllowed() const { return m_branchingAllowed; }

        void setBranchingAllowed( bool b ) { m_branchingAllowed = b; }

        template< typename V > void append( V&& v )

        {
            cfg()->currentBB()->append( forward< V >( v ) );
        }

        // Used to mark the ghost code as poisoned if something goes wrong during
        // its parsing.
        void poison() { m_poison = true; }




        void fixUpExitBranches( const ptr< cir::BasicBlock >& contBB )
        {
            m_cfg->forEachReachableBlock( m_entryBB,
                [&]( auto&& bb )
                {
                    if( !bb->terminator() )
                        bb->setTerminator( cir::Branch( contBB ) );
                } );
        }

      private:
        ptr< cir::CFG > m_cfg;
        ptr< cir::BasicBlock > m_entryBB;

        bool m_branchingAllowed = true;
        bool m_poison = false;
    };
} // namespace goose::builtins

#endif
Changes to bs/builtins/types/infer.cpp.
1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
#include "builtins/builtins.h"

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

namespace goose::builtins
{
    Value InferTypeFromTExprAndInitializer( const Context& c, const Term& typeTExpr, const Value& initVal )

    {
        // To infer the type and obtain a suitable initialization function, we typecheck
        //          ( $$T, _ ( MutRef[$$T], initValType ) initFunc )
        // against  ( pat, Initialize                              )
        //
        // Where:
        //   pat is the type pattern provided for the declaration,








|
>







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

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

namespace goose::builtins
{
    Value InferTypeFromTExprAndInitializer(
        const Context& c, const Term& typeTExpr, const Value& initVal )
    {
        // To infer the type and obtain a suitable initialization function, we typecheck
        //          ( $$T, _ ( MutRef[$$T], initValType ) initFunc )
        // against  ( pat, Initialize                              )
        //
        // Where:
        //   pat is the type pattern provided for the declaration,
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
        // Create the _ texpr.
        static auto TVarAny = ValueToEIR( ToValue( TVar( "_"_sid ) ) );

        // The $$T texpr.
        static auto TTVarT = ValueToEIR( ToValue( TTVar( "T"_sid ) ) );

        // Create the MutRef[$$T] param.
        static auto mutRefParam = ValueToEIR( ToValue( TNamedDecl(


            ValueToEIR( ToDeclValue( ReferenceType{ TTVarT, MutAccessSpecifier(), } ) ), "ref"_sid ) ) );



        auto initValParam = ValueToEIR( ToValue( Decl( initVal.type(), "initVal"_sid ) ) );

        // Create the _ ( MutRef[$$T], initType ) initFunc param.
        auto initFuncTFTParam = ValueToEIR( ToValue(

            TNamedDecl( ValueToEIR( ToValue( TFuncType( TVarAny, VEC( move( mutRefParam ), move( initValParam ) ) ) ) ),
                "initFunc"_sid ) ) );

        TemplateContext tc( c );

        // Create our parameter list.
        auto paramPat = VEC(
            *BuildTemplateSignature( tc, TTVarT ),
            *BuildTemplateSignature( tc, initFuncTFTParam )
        );

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

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

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

        if( holds_alternative< AmbiguousTypeCheck >( us ) )
        {
            // TODO display details
            DiagnosticsManager::GetInstance().emitErrorMessage( 0,
                "ambiguous 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 = TakeVectorTerm( c.identity(), VecSize( c.identity() ) - 2 );
        Context ctxt( c.env(), parentIdentity );

        // The TVar setup expects to find its name on the left hand side of the type checking,
        // But in our case we have put it on the rhs, so we need to flip the context around
        // before setup.
        TemplateSetup( ctxt, tcc.flip(), typeTExpr );

        auto callDecomp = Decompose( s,
            Vec(
                SubTerm(),  // Infered type
                SubTerm()   // Infered initialize overload (can't use it anyway
            )
        );

        auto&& [type, _] = *callDecomp;

        auto typeVal = *EIRToValue( type );

        // Retrieve the texpr's location and set it on the inferred type. This way if an
        // error occurs later with it, for instance when calling LowerTypeForRuntime on it during codegen,
        // it will have a meaningful location for the error message to attach itself on.
        auto typeLoc = EIRToValue( typeTExpr )->locationId();
        typeVal.setLocationId( typeLoc );

        // TODO_SSA reenable
        //if( !ParseTypePredicates( c, typeVal  ) )
        //    return PoisonValue();

        return typeVal;
    }
}







|
>
>
|
>
>




|
>
|
|




<
|
|
<


<
|
|
<






|
|






|
<



|















<
|
|
|
<






|
|




|




|
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
        // Create the _ texpr.
        static auto TVarAny = ValueToEIR( ToValue( TVar( "_"_sid ) ) );

        // The $$T texpr.
        static auto TTVarT = ValueToEIR( ToValue( TTVar( "T"_sid ) ) );

        // Create the MutRef[$$T] param.
        static auto mutRefParam =
            ValueToEIR( ToValue( TNamedDecl( ValueToEIR( ToDeclValue( ReferenceType{
                                                 TTVarT,
                                                 MutAccessSpecifier(),
                                             } ) ),
                "ref"_sid ) ) );

        auto initValParam = ValueToEIR( ToValue( Decl( initVal.type(), "initVal"_sid ) ) );

        // Create the _ ( MutRef[$$T], initType ) initFunc param.
        auto initFuncTFTParam = ValueToEIR( ToValue( TNamedDecl(
            ValueToEIR(
                ToValue( TFuncType( TVarAny, VEC( move( mutRefParam ), move( initValParam ) ) ) ) ),
            "initFunc"_sid ) ) );

        TemplateContext tc( c );

        // Create our parameter list.

        auto paramPat = VEC( *BuildTemplateSignature( tc, TTVarT ),
            *BuildTemplateSignature( tc, initFuncTFTParam ) );


        // Create our arg list.

        auto args = VEC( *BuildTemplateArgPattern( c, typeTExpr ),
            ValueToEIR( ToValue( c.env()->extInitialize() ) ) );


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

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

        if( holds_alternative< AmbiguousTypeCheck >( us ) )
        {
            // TODO display details
            DiagnosticsManager::GetInstance().emitErrorMessage( 0, "ambiguous 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 = TakeVectorTerm( c.identity(), VecSize( c.identity() ) - 2 );
        Context ctxt( c.env(), parentIdentity );

        // The TVar setup expects to find its name on the left hand side of the type checking,
        // But in our case we have put it on the rhs, so we need to flip the context around
        // before setup.
        TemplateSetup( ctxt, tcc.flip(), typeTExpr );

        auto callDecomp = Decompose( s,

            Vec( SubTerm(), // Infered type
                SubTerm() // Infered initialize overload (can't use it anyway
                ) );


        auto&& [type, _] = *callDecomp;

        auto typeVal = *EIRToValue( type );

        // Retrieve the texpr's location and set it on the inferred type. This way if an
        // error occurs later with it, for instance when calling LowerTypeForRuntime on it during
        // codegen, it will have a meaningful location for the error message to attach itself on.
        auto typeLoc = EIRToValue( typeTExpr )->locationId();
        typeVal.setLocationId( typeLoc );

        // TODO_SSA reenable
        // if( !ParseTypePredicates( c, typeVal  ) )
        //    return PoisonValue();

        return typeVal;
    }
} // namespace goose::builtins
Changes to bs/builtins/types/init.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

#include "builtins/builtins.h"
#include "parse/parse.h"

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

namespace goose::builtins
{
    void SetupInitialize( Env& e )
    {
        using ValueOfTypeT = CustomPattern< Value, ValuePatternT >;

        using MutRefOfTypeT =
            CustomPattern< Value, ReferenceType::PatternAnyMutableOfTypeT >;

        using CTIntMutRefType =
            CustomPattern< Value, ReferenceType::PatternMutableOfType< BigInt > >;

        using CTStringMutRefType =
            CustomPattern< Value, ReferenceType::PatternMutableOfType< string > >;

        // Initialization for any builtin type. Only allowed at compilation time,
        // all types at runtime will have to go through their own specialization
        // or partial specialization of this, since it will have to emit specialized
        // binary runtime code.
        RegisterBuiltinFunc< Intrinsic< Value ( MutRefOfTypeT, ValueOfTypeT ) > >( e, e.extInitialize(),

            []( auto&& c, const Value& r, const Value& initVal )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(),
                    initVal, r, Store( refType.type(), initVal.locationId(), r.locationId() ) );
            } );

        // Default initialization for ct_int vars
        RegisterBuiltinFunc< Intrinsic< Value ( CTIntMutRefType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(),
                    ToValue( BigInt() ), r, Store( refType.type(), {}, r.locationId() ) );
            } );

        // Default initialization for ct_string vars
        RegisterBuiltinFunc< Intrinsic< Value ( CTStringMutRefType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(),
                    ToValue( ""s ), r, Store( refType.type(), {}, r.locationId() ) );
            } );
    }
}













<
|











|
>




|
|



|




|
|



|




|
|


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

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

namespace goose::builtins
{
    void SetupInitialize( Env& e )
    {
        using ValueOfTypeT = CustomPattern< Value, ValuePatternT >;


        using MutRefOfTypeT = CustomPattern< Value, ReferenceType::PatternAnyMutableOfTypeT >;

        using CTIntMutRefType =
            CustomPattern< Value, ReferenceType::PatternMutableOfType< BigInt > >;

        using CTStringMutRefType =
            CustomPattern< Value, ReferenceType::PatternMutableOfType< string > >;

        // Initialization for any builtin type. Only allowed at compilation time,
        // all types at runtime will have to go through their own specialization
        // or partial specialization of this, since it will have to emit specialized
        // binary runtime code.
        RegisterBuiltinFunc< Intrinsic< Value( MutRefOfTypeT, ValueOfTypeT ) > >( e,
            e.extInitialize(),
            []( auto&& c, const Value& r, const Value& initVal )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(), initVal, r,
                    Store( refType.type(), initVal.locationId(), r.locationId() ) );
            } );

        // Default initialization for ct_int vars
        RegisterBuiltinFunc< Intrinsic< Value( CTIntMutRefType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(), ToValue( BigInt() ), r,
                    Store( refType.type(), {}, r.locationId() ) );
            } );

        // Default initialization for ct_string vars
        RegisterBuiltinFunc< Intrinsic< Value( CTStringMutRefType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(), ToValue( ""s ), r,
                    Store( refType.type(), {}, r.locationId() ) );
            } );
    }

} // namespace goose::builtins
Changes to bs/builtins/types/localvar/drop.cpp.
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
{
    void SetupLocalVarDropValue( Env& e )
    {
        using AnyLocVarType = CustomPattern< LocalVar, LocalVar::PatternAny >;

        // When a localvar is dropped, it means it has escaped from the scope of its statement:
        // Make it visible from outside its statement, and extends its lifetime to its parent scope.

        RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< CodeBuilder > >, AnyLocVarType ) > >( e, e.extDropValue(),

            []( const Context& c, const Value& b, const Value& v )
            {
                auto lv = *FromValue< LocalVar >( v );
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( b );

                if( lv.name() == ""_sid )
                    return;

                // Make the variable visible from the current scope (the parent scope of the statement)

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

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

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

        // When a localvar is dropped in any context that doesn't explicitely allow for variable declarations,
        // emit an error.
        RegisterBuiltinFunc< Intrinsic< void ( Value, AnyLocVarType ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& b, const Value& v )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( v.locationId(), "variable declarations are not allowed here." );

                PoisonBuilder( c );
            } );
    }
}







>
|
>








|
>


|
<

|
|



|
|
|


|
>



|
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
{
    void SetupLocalVarDropValue( Env& e )
    {
        using AnyLocVarType = CustomPattern< LocalVar, LocalVar::PatternAny >;

        // When a localvar is dropped, it means it has escaped from the scope of its statement:
        // Make it visible from outside its statement, and extends its lifetime to its parent scope.
        RegisterBuiltinFunc<
            Intrinsic< void( TypeWrapper< ptr< CodeBuilder > >, AnyLocVarType ) > >( e,
            e.extDropValue(),
            []( const Context& c, const Value& b, const Value& v )
            {
                auto lv = *FromValue< LocalVar >( v );
                auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( b );

                if( lv.name() == ""_sid )
                    return;

                // Make the variable visible from the current scope (the parent scope of the
                // statement)
                auto identity = AppendToVectorTerm( c.identity(), lv.name() );

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


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

        // When a localvar is dropped in any context that doesn't explicitely allow for variable
        // declarations, emit an error.
        RegisterBuiltinFunc< Intrinsic< void( Value, AnyLocVarType ) > >( e, e.extDropValue(),
            []( const Context& c, const Value& b, const Value& v )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                    v.locationId(), "variable declarations are not allowed here." );
                PoisonBuilder( c );
            } );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/localvar/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

#include "builtins/builtins.h"

using namespace goose::sema;

namespace goose::builtins
{
    class LocVarInvocationRule : public InvocationRule
    {
        public:
            bool canBeInvoked( const Context& c, const Value& callee ) const final
            {
                auto lv = *FromValue< LocalVar >( callee );

                auto val = BuildComputedValue( lv.type(),
                    GetAddrFromLocalVar( lv ),
                    cir::Load( lv.type(), {} ) );

                return sema::CanBeInvoked( c, val );
            }

            Value resolveInvocation( Context& c, LocationId locationId, const Value& callee, const Term& args ) const final

            {
                auto lv = *FromValue< LocalVar >( callee );

                auto val = BuildComputedValue( lv.type(),
                    GetAddrFromLocalVar( lv ),
                    cir::Load( lv.type(), locationId ) );

                return sema::GetInvocationRule( *c.env(), val )->resolveInvocation( c, locationId, val, args );

            }
    };

    void SetupLocalVarInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( Value(
                GetValueType< LocalVar >( ANYTERM( _ ) ),
                ANYTERM( _ ) ) ),
            make_shared< LocVarInvocationRule >() );
    }
}














|
<
|




|
>



|
<
|

|
>






<
|
<


<
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

15
16
17
18
19
20
21
22
23
24
25

26
27
28
29
30
31
32
33
34
35

36

37
38

39
#include "builtins/builtins.h"

using namespace goose::sema;

namespace goose::builtins
{
    class LocVarInvocationRule : public InvocationRule
    {
        public:
            bool canBeInvoked( const Context& c, const Value& callee ) const final
            {
                auto lv = *FromValue< LocalVar >( callee );

                auto val = BuildComputedValue(

                    lv.type(), GetAddrFromLocalVar( lv ), cir::Load( lv.type(), {} ) );

                return sema::CanBeInvoked( c, val );
            }

            Value resolveInvocation( Context& c, LocationId locationId, const Value& callee,
                const Term& args ) const final
            {
                auto lv = *FromValue< LocalVar >( callee );

                auto val = BuildComputedValue(

                    lv.type(), GetAddrFromLocalVar( lv ), cir::Load( lv.type(), locationId ) );

                return sema::GetInvocationRule( *c.env(), val )
                    ->resolveInvocation( c, locationId, val, args );
            }
    };

    void SetupLocalVarInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(

            ValueToEIR( Value( GetValueType< LocalVar >( ANYTERM( _ ) ), ANYTERM( _ ) ) ),

            make_shared< LocVarInvocationRule >() );
    }

} // namespace goose::builtins
Changes to bs/builtins/types/localvar/localvar.cpp.
15
16
17
18
19
20
21
22

23
24
25
26
27
28
29

    const Term& LocalVar::PatternAnyOfTypeT::GetPattern()
    {
        static auto pattern = GetValueType< LocalVar >( HOLE( "T"_sid ) );
        return pattern;
    }

    Value DeclareLocalVar( const Context& c, const Term& type, StringId name, const optional< Value >& initializer, LocationId locId )

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

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







|
>







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

    const Term& LocalVar::PatternAnyOfTypeT::GetPattern()
    {
        static auto pattern = GetValueType< LocalVar >( HOLE( "T"_sid ) );
        return pattern;
    }

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

        auto bb = cfg->currentBB();
        if( !bb )
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
        Value initResult;

        {
            DiagnosticsContext dc( 0, "When invoking _Initialize." );

            if( initializer )
            {
                initResult = InvokeOverloadSet( c,
                    c.env()->extInitialize(),
                    MakeClosedTuple( ToValue( lv ).setLocationId( locId ), *initializer ), locId );
            }
            else
            {
                initResult = InvokeOverloadSet( c,
                    c.env()->extInitialize(),
                    MakeClosedTuple( ToValue( lv ).setLocationId( locId ) ), locId );
            }
        }

        if( !initResult.isPoison() )
        {
            DiagnosticsContext dc2( initResult.locationId(), "When invoking _DropValue." );
            InvokeOverloadSet( c, c.env()->extDropValue(),
                MakeClosedTuple( c.builder(), move( initResult ) ), initResult.locationId() );
        }

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

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

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

    Value DeclareLocalVarWithTypeInference( const Context& c, const Term& typeTExpr, StringId name, const Value& initVal, LocationId locId )

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

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

        auto type = InferTypeFromTExprAndInitializer( c, typeTExpr, initVal );
        if( type.isPoison() )
            return PoisonValue();

        auto index = util::GenerateNewUID();

        // Retrieve the texpr's location and set it on the inferred type. This way if an
        // error occurs later with it, for instance when calling LowerTypeForRuntime on it during codegen,
        // it will have a meaningful location for the error message to attach itself on.
        LocalVar lv( name, ValueToEIR( type ), index );
        bb->append( AllocVar( lv.type(), index, locId ) );

        DiagnosticsContext dc( 0, "When invoking _Initialize." );

        auto initResult = InvokeOverloadSet( c,
            c.env()->extInitialize(),
            MakeClosedTuple( ToValue( lv ).setLocationId( locId ), initVal ) );

        if( !initResult.isPoison() )
        {
            DiagnosticsContext dc2( initResult.locationId(), "When invoking _DropValue." );
            InvokeOverloadSet( c, c.env()->extDropValue(),
                MakeClosedTuple( c.builder(), move( initResult ) ) );
        }

        auto locVar = ToValue( lv );

        if( name != ""_sid )
        {
            auto identity = AppendToVectorTerm( c.identity(), name );
            c.env()->storeValue( identity, ANYTERM( _ ),
                ValueToEIR( locVar ) );
        }

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

namespace goose::eir
{
    const Term& Bridge< LocalVarType >::Type()
    {
        return TypeType();
    }

    Value Bridge< LocalVarType >::ToValue( const LocalVarType& t )
    {
        return Value( Type(), TVEC( TSID( ct_type ), TSID( local_var ), t.type() ) );
    }

    Value Bridge< LocalVarType >::ToValue( const Term& type )
    {
        return Value( Type(), TVEC( TSID( ct_type ), TSID( local_var ), type ) );
    }

    optional< LocalVarType > Bridge< LocalVarType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),
            Vec(
                Lit( "ct_type"_sid ),
                Lit( "local_var"_sid ),
                SubTerm()
            )
        );

        if( !result )
            return nullopt;

        auto&& [type] = *result;
        return LocalVarType( move( type ) );
    }







|
<




|
<














|
<





|
>
















|
|





|
<





|
|







|
<





|




















|
<
<
|
<
<
<







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
        Value initResult;

        {
            DiagnosticsContext dc( 0, "When invoking _Initialize." );

            if( initializer )
            {
                initResult = InvokeOverloadSet( c, c.env()->extInitialize(),

                    MakeClosedTuple( ToValue( lv ).setLocationId( locId ), *initializer ), locId );
            }
            else
            {
                initResult = InvokeOverloadSet( c, c.env()->extInitialize(),

                    MakeClosedTuple( ToValue( lv ).setLocationId( locId ) ), locId );
            }
        }

        if( !initResult.isPoison() )
        {
            DiagnosticsContext dc2( initResult.locationId(), "When invoking _DropValue." );
            InvokeOverloadSet( c, c.env()->extDropValue(),
                MakeClosedTuple( c.builder(), move( initResult ) ), initResult.locationId() );
        }

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

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


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

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

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

        auto type = InferTypeFromTExprAndInitializer( c, typeTExpr, initVal );
        if( type.isPoison() )
            return PoisonValue();

        auto index = util::GenerateNewUID();

        // Retrieve the texpr's location and set it on the inferred type. This way if an
        // error occurs later with it, for instance when calling LowerTypeForRuntime on it during
        // codegen, it will have a meaningful location for the error message to attach itself on.
        LocalVar lv( name, ValueToEIR( type ), index );
        bb->append( AllocVar( lv.type(), index, locId ) );

        DiagnosticsContext dc( 0, "When invoking _Initialize." );

        auto initResult = InvokeOverloadSet( c, c.env()->extInitialize(),

            MakeClosedTuple( ToValue( lv ).setLocationId( locId ), initVal ) );

        if( !initResult.isPoison() )
        {
            DiagnosticsContext dc2( initResult.locationId(), "When invoking _DropValue." );
            InvokeOverloadSet(
                c, c.env()->extDropValue(), MakeClosedTuple( c.builder(), move( initResult ) ) );
        }

        auto locVar = ToValue( lv );

        if( name != ""_sid )
        {
            auto identity = AppendToVectorTerm( c.identity(), name );
            c.env()->storeValue( identity, ANYTERM( _ ), ValueToEIR( locVar ) );

        }

        DeclareValue( c, locVar, lv.index() );
        return locVar;
    }
} // namespace goose::builtins

namespace goose::eir
{
    const Term& Bridge< LocalVarType >::Type()
    {
        return TypeType();
    }

    Value Bridge< LocalVarType >::ToValue( const LocalVarType& t )
    {
        return Value( Type(), TVEC( TSID( ct_type ), TSID( local_var ), t.type() ) );
    }

    Value Bridge< LocalVarType >::ToValue( const Term& type )
    {
        return Value( Type(), TVEC( TSID( ct_type ), TSID( local_var ), type ) );
    }

    optional< LocalVarType > Bridge< LocalVarType >::FromValue( const Value& v )
    {
        auto result =


            Decompose( v.val(), Vec( Lit( "ct_type"_sid ), Lit( "local_var"_sid ), SubTerm() ) );




        if( !result )
            return nullopt;

        auto&& [type] = *result;
        return LocalVarType( move( type ) );
    }
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
        if( !v.isConstant() )
        {
            // This is an abstract local variable value, this may happen when
            // typechecking abstract arguments while resolving function typed parameters
            return LocalVar( move( t->type() ) );
        }

        auto result = Decompose( v.val(),
            Vec(
                Val< StringId >(),
                Val< uint64_t >()
            )
        );

        if( !result )
            return nullopt;

        auto&& [name,index] = *result;
        return LocalVar( name, move( t->type() ), index );
    }
}







|
<
<
<
<
<




|


|
170
171
172
173
174
175
176
177





178
179
180
181
182
183
184
185
        if( !v.isConstant() )
        {
            // This is an abstract local variable value, this may happen when
            // typechecking abstract arguments while resolving function typed parameters
            return LocalVar( move( t->type() ) );
        }

        auto result = Decompose( v.val(), Vec( Val< StringId >(), Val< uint64_t >() ) );






        if( !result )
            return nullopt;

        auto&& [name, index] = *result;
        return LocalVar( name, move( t->type() ), index );
    }
} // namespace goose::eir
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
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
#ifndef GOOSE_BUILTINS_TYPES_LOCALVAR_H
#define GOOSE_BUILTINS_TYPES_LOCALVAR_H

namespace goose::builtins
{
    class LocalVar;

    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, LocationId locId );

    Value DeclareLocalVarWithTypeInference( const Context& c, const Term& typeTExpr, StringId name, const Value& initVal, LocationId locId );


    class LocalVarType
    {
        public:
            template< typename T >
            LocalVarType( T&& type ) :
                m_type( forward< T >( type ) )
            {}



            const auto& type() const { return m_type; }

        private:
            Term m_type;
    };

    class LocalVar
    {
        public:
            template< typename T >
            LocalVar( StringId name, T&& type, uint32_t index ) :
                m_name( name ),
                m_type( forward< T >( type ) ),
                m_index( index )
            {}



            template< typename T >

            LocalVar( T&& type ) : LocalVar( "<unknown>"_sid, forward< T >( type ), ~0U )
            {}



            auto name() const { return m_name; }

            const auto& type() const { return m_type; }

            auto& type() { return m_type; }

            const auto& index() const { return m_index; }

            struct PatternAny
            {
                static const Term& GetPattern();
            };

            struct PatternAnyOfTypeT
            {
                static const Term& GetPattern();
            };

            template< typename T >
            struct PatternOfType
            {
                static const Term& GetPattern()
                {
                    static auto pattern = GetValueType< LocalVar >( GetValueType< T >() );
                    return pattern;
                }
            };

            template< typename PP >
            struct Pattern
            {
                static auto GetPattern()
                {
                    static auto pattern = GetValueType< LocalVar >( PP::GetPattern() );
                    return pattern;
                }
            };

        private:
            StringId m_name;
            Term m_type;
            uint32_t m_index = 0;
    };
}

namespace goose::eir
{
    template<>
    struct Bridge< builtins::LocalVarType >
    {
        static const Term& Type();
        static Value ToValue( const builtins::LocalVarType& t );
        static Value ToValue( const Term& type );
        static optional< builtins::LocalVarType > FromValue( const Value& v );
    };

    template<>
    struct Bridge< builtins::LocalVar >
    {
        static Term Type( const Term& type );
        static Value ToValue( const builtins::LocalVar& lv );
        static optional< builtins::LocalVar > FromValue( const Value& v );
    };
}

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

namespace goose::builtins
{
    class LocalVar;

    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, LocationId locId );
    Value DeclareLocalVarWithTypeInference( const Context& c, const Term& typeTExpr, StringId name,
        const Value& initVal, LocationId locId );

    class LocalVarType
    {
      public:
        template< typename T >
        LocalVarType( T&& type ) :
            m_type( forward< T >( type ) )

        {
        }

        const auto& type() const { return m_type; }

      private:
        Term m_type;
    };

    class LocalVar
    {
      public:
        template< typename T >
        LocalVar( StringId name, T&& type, uint32_t index ) :
            m_name( name ),
            m_type( forward< T >( type ) ),
            m_index( index )

        {
        }

        template< typename T >
        LocalVar( T&& type ) :
            LocalVar( "<unknown>"_sid, forward< T >( type ), ~0U )

        {
        }

        auto name() const { return m_name; }

        const auto& type() const { return m_type; }

        auto& type() { return m_type; }

        const auto& index() const { return m_index; }

        struct PatternAny
        {
            static const Term& GetPattern();
        };

        struct PatternAnyOfTypeT
        {
            static const Term& GetPattern();
        };

        template< typename T > struct PatternOfType

        {
            static const Term& GetPattern()
            {
                static auto pattern = GetValueType< LocalVar >( GetValueType< T >() );
                return pattern;
            }
        };

        template< typename PP > struct Pattern

        {
            static auto GetPattern()
            {
                static auto pattern = GetValueType< LocalVar >( PP::GetPattern() );
                return pattern;
            }
        };

      private:
        StringId m_name;
        Term m_type;
        uint32_t m_index = 0;
    };
} // namespace goose::builtins

namespace goose::eir
{

    template<> struct Bridge< builtins::LocalVarType >
    {
        static const Term& Type();
        static Value ToValue( const builtins::LocalVarType& t );
        static Value ToValue( const Term& type );
        static optional< builtins::LocalVarType > FromValue( const Value& v );
    };


    template<> struct Bridge< builtins::LocalVar >
    {
        static Term Type( const Term& type );
        static Value ToValue( const builtins::LocalVar& lv );
        static optional< builtins::LocalVar > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/localvar/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
        auto localVarPattern = GetValueType< LocalVar >( ANYTERM( _ ) );

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

            ParamPat( localVarPattern ),

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

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

                auto lvarType = *FromValue< LocalVarType >( *EIRToValue( EIRToValuePattern( lhs )->type() ) );

                auto rhsVal = *EIRToValuePattern( rhs );
                auto rvarType = *FromValue< LocalVarType >( *EIRToValue( rhsVal.type() ) );

                for( auto&& [s, tcc] : Unify( lvarType.type(), rvarType.type(), tcc ) )
                {

                    co_yield { ValueToEIR( Value( ValueToEIR( ToValue( LocalVarType( s ) ) ),
                        rhsVal.val() ).setLocationId( rhsVal.locationId() ) ), tcc };

                }
            } );
    }
}







|
<
<
<



>
|






>
|
|
>



|
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
        auto localVarPattern = GetValueType< LocalVar >( ANYTERM( _ ) );

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

            ParamPat( localVarPattern ),

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




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

                auto rhsVal = *EIRToValuePattern( rhs );
                auto rvarType = *FromValue< LocalVarType >( *EIRToValue( rhsVal.type() ) );

                for( auto&& [s, tcc] : Unify( lvarType.type(), rvarType.type(), tcc ) )
                {
                    co_yield { ValueToEIR(
                                   Value( ValueToEIR( ToValue( LocalVarType( s ) ) ), rhsVal.val() )
                                       .setLocationId( rhsVal.locationId() ) ),
                        tcc };
                }
            } );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/lower.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

#include "builtins/builtins.h"
#include "parse/parse.h"

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

namespace goose::builtins
{
    void SetupLower( Env& e )
    {
        // Default implementation of GetUnderlyingType():
        // Do nothing.
        RegisterBuiltinFunc< Intrinsic< Value ( Value ) > >( e, e.extGetUnderlyingType(),
            []( const Context& c, const Value& v )
            {
                return v;
            } );

        // Default implementation of LowerType():
        // Do nothing.
        RegisterBuiltinFunc< Intrinsic< Value ( Value ) > >( e, e.extLowerType(),
            []( const Context& c, const Value& v )
            {
                return v;
            } );

        // Default implementation of LowerValue():
        // Do nothing.
        RegisterBuiltinFunc< Intrinsic< Value ( Value ) > >( e, e.extLowerValue(),
            []( const Context& c, const Value& v )
            {
                return v;
            } );
    }
}













|
|
<
<
<



|
|
<
<
<



|
|
<
<
<

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

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

namespace goose::builtins
{
    void SetupLower( Env& e )
    {
        // Default implementation of GetUnderlyingType():
        // Do nothing.
        RegisterBuiltinFunc< Intrinsic< Value( Value ) > >(
            e, e.extGetUnderlyingType(), []( const Context& c, const Value& v ) { return v; } );




        // Default implementation of LowerType():
        // Do nothing.
        RegisterBuiltinFunc< Intrinsic< Value( Value ) > >(
            e, e.extLowerType(), []( const Context& c, const Value& v ) { return v; } );




        // Default implementation of LowerValue():
        // Do nothing.
        RegisterBuiltinFunc< Intrinsic< Value( Value ) > >(
            e, e.extLowerValue(), []( const Context& c, const Value& v ) { return v; } );



    }

} // namespace goose::builtins
Changes to bs/builtins/types/overloadset/helpers.cpp.
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
            case sema::Env::Status::AmbiguousMatch:
                G_ERROR( format( "fatal: ambiguous match for overload set {}", name.str() ) );
        }

        return nullptr;
    }


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

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

        execute::VM vm;

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


        auto val = ResolveInvocation( localC, GetOverloadSetInvocationRule(), ToValue( pOvlSet ).setLocationId( loc ), args );

        if( val.isConstant() || !cir::CanValueBeEagerlyEvaluated( val ) )
            return val;

        return execute::Evaluate( val, vm );
    }


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

        execute::VM vm;

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


        auto val = ResolveInvocation( c, GetOverloadSetInvocationRule(), ToValue( pOvlSet ).setLocationId( loc ), args );

        if( val.isConstant() || !cir::CanValueBeEagerlyEvaluated( val ) )
            return val;

        return execute::Evaluate( val, vm );
    }
}







>
|













>
|







>
|










>
|






|
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
            case sema::Env::Status::AmbiguousMatch:
                G_ERROR( format( "fatal: ambiguous match for overload set {}", name.str() ) );
        }

        return nullptr;
    }

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

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

        execute::VM vm;

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

        auto val = ResolveInvocation(
            localC, GetOverloadSetInvocationRule(), ToValue( pOvlSet ).setLocationId( loc ), args );

        if( val.isConstant() || !cir::CanValueBeEagerlyEvaluated( val ) )
            return val;

        return execute::Evaluate( val, vm );
    }

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

        execute::VM vm;

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

        auto val = ResolveInvocation(
            c, GetOverloadSetInvocationRule(), ToValue( pOvlSet ).setLocationId( loc ), args );

        if( val.isConstant() || !cir::CanValueBeEagerlyEvaluated( val ) )
            return val;

        return execute::Evaluate( val, vm );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/overloadset/helpers.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef GOOSE_BUILTINS_TYPES_OVERLOADSET_HELPERS_H
#define GOOSE_BUILTINS_TYPES_OVERLOADSET_HELPERS_H

namespace goose::builtins
{
    extern ptr< OverloadSet > CreateOverloadSet( Env& env, StringId name );
    extern ptr< OverloadSet > GetOverloadSet( Env& env, StringId name );
    extern ptr< OverloadSet > GetOrCreateOverloadSet( Env& env, StringId name );
    extern Value InvokeOverloadSet( const Context& c, const ptr< OverloadSet >& pOvlSet, Value args,
        LocationId loc = Location::Create( source_location::current() ) );
    extern Value InvokeOverloadSet( Context& c, const ptr< OverloadSet >& pOvlSet, Value args,
        LocationId loc = Location::Create( source_location::current() ) );
}

#endif












|


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

namespace goose::builtins
{
    extern ptr< OverloadSet > CreateOverloadSet( Env& env, StringId name );
    extern ptr< OverloadSet > GetOverloadSet( Env& env, StringId name );
    extern ptr< OverloadSet > GetOrCreateOverloadSet( Env& env, StringId name );
    extern Value InvokeOverloadSet( const Context& c, const ptr< OverloadSet >& pOvlSet, Value args,
        LocationId loc = Location::Create( source_location::current() ) );
    extern Value InvokeOverloadSet( Context& c, const ptr< OverloadSet >& pOvlSet, Value args,
        LocationId loc = Location::Create( source_location::current() ) );
} // namespace goose::builtins

#endif
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
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

#include "builtins/builtins.h"

//#define OVL_TC_DEBUG

using namespace goose::sema;

namespace goose::builtins
{
    class OverloadSetInvocationRule : public InvocationRule
    {
        public:

            Value resolveInvocation( Context& c, LocationId loc, const Value& callee, const Term& args ) const final
            {
                ProfileZoneScoped;

                auto pOvlSet = *FromValue< ptr< OverloadSet > >( callee );

                #if TRACY_ENABLE
                    stringstream sstr;
                    sstr << pOvlSet->identity();
                    ProfileZoneName( sstr.str().c_str(), sstr.str().size() );
                #endif

                if( auto ovl = pOvlSet->getResolutionFromCache( args ) )
                    return ovl->pInvRule->resolveInvocation( c, loc, *ovl->callee, args );
                else
                    return resolve( c, loc, pOvlSet, args );
            }

        private:

            Value resolve( Context& c, LocationId loc, const ptr< OverloadSet >& pOvlSet, const Term& args ) const
            {
                const OverloadSet::Overload* bestOvl = nullptr;
                optional< TypeCheckingContext > bestTCC;
                optional< Term > bestSol;

                {
                    ProfileZoneScopedN( "Overload resolution" );

                    bool ambiguous = false;

                    if( pOvlSet->verboseResolution() )
                    {
                        DiagnosticsManager::GetInstance().emitTraceMessage( loc,
                            std::format( "invoking overload set: {}", pOvlSet->identity() ) );
                    }

                    auto callPat = PrependToVectorTerm( args, HOLE( "_"_sid, "fwd"_sid ) );
                    TypeCheckingContext tcc( c );
                    for( auto&& [s,ovl,tcc] : pOvlSet->typeCheck( callPat, tcc ) )
                    {
                        if( tcc.numUnknownValues() )
                        {
                            if( pOvlSet->verboseResolution() )
                            {
                                DiagnosticsManager::GetInstance().emitTraceMessage(
                                    ovl.callee ? ovl.callee->locationId() : 0,
                                    format( "rejected candidate", tcc.score() ) );
                            }
                            continue;
                        }

                        auto subs = Substitute( s, tcc );

                        // Typechecking rules often end up stripping part of the original type,
                        // and we want to invoke the overload where these removals are minimized.
                        //
                        // Obvious example: if there is an overload that accepts a reference
                        // and one that accepts a value of the same type and we started with a
                        // reference, then we want to call the overload where the typechecking
                        // solution didn't strip the reference.
                        //
                        // So we add the weight of the original arguments to the cost,
                        // and remove the cost of the typechecking solution to account for that.
                        int32_t cost = tcc.cost();
                        cost += GetWeight( callPat );
                        cost -= GetWeight( subs );
                        tcc.setCost( cost );

                        if( pOvlSet->verboseResolution() )
                        {
                            DiagnosticsManager::GetInstance().emitTraceMessage(
                                ovl.callee ? ovl.callee->locationId() : 0,
                                format( "candidate score: {}", tcc.score() ) );
                        }

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

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

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

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

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

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

                #if defined( OVL_TC_DEBUG ) && !defined( NDEBUG )
                    bestTCC->DumpParamsTraces( cout );
                    cout << endl;
                #endif
                }

                pOvlSet->addResolutionToCache( args, *bestOvl );

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

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

    void SetupOverloadSetInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( Value(
                GetValueType< ptr< OverloadSet > >(),
                ANYTERM( _ ) ) ),
            GetOverloadSetInvocationRule() );
    }
}



|







|
>
|
|
|

|

|
|
|
|
|

|
|
|
|
|

|
>
|
|
|
|
|

|
|

|

|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|

|

|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|

|
|
|

|
|
|

|
|
|
|
|

|
|
|
|
|

|
|
|
|
|
|
|

|
|
|
|
|
|
|

|
|
|
|
|

|

|
|











<
|
<


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

// #define OVL_TC_DEBUG

using namespace goose::sema;

namespace goose::builtins
{
    class OverloadSetInvocationRule : public InvocationRule
    {
      public:
        Value resolveInvocation(
            Context& c, LocationId loc, const Value& callee, const Term& args ) const final
        {
            ProfileZoneScoped;

            auto pOvlSet = *FromValue< ptr< OverloadSet > >( callee );

#if TRACY_ENABLE
            stringstream sstr;
            sstr << pOvlSet->identity();
            ProfileZoneName( sstr.str().c_str(), sstr.str().size() );
#endif

            if( auto ovl = pOvlSet->getResolutionFromCache( args ) )
                return ovl->pInvRule->resolveInvocation( c, loc, *ovl->callee, args );
            else
                return resolve( c, loc, pOvlSet, args );
        }

      private:
        Value resolve(
            Context& c, LocationId loc, const ptr< OverloadSet >& pOvlSet, const Term& args ) const
        {
            const OverloadSet::Overload* bestOvl = nullptr;
            optional< TypeCheckingContext > bestTCC;
            optional< Term > bestSol;

            {
                ProfileZoneScopedN( "Overload resolution" );

                bool ambiguous = false;

                if( pOvlSet->verboseResolution() )
                {
                    DiagnosticsManager::GetInstance().emitTraceMessage(
                        loc, std::format( "invoking overload set: {}", pOvlSet->identity() ) );
                }

                auto callPat = PrependToVectorTerm( args, HOLE( "_"_sid, "fwd"_sid ) );
                TypeCheckingContext tcc( c );
                for( auto&& [s, ovl, tcc] : pOvlSet->typeCheck( callPat, tcc ) )
                {
                    if( tcc.numUnknownValues() )
                    {
                        if( pOvlSet->verboseResolution() )
                        {
                            DiagnosticsManager::GetInstance().emitTraceMessage(
                                ovl.callee ? ovl.callee->locationId() : 0,
                                format( "rejected candidate", tcc.score() ) );
                        }
                        continue;
                    }

                    auto subs = Substitute( s, tcc );

                    // Typechecking rules often end up stripping part of the original type,
                    // and we want to invoke the overload where these removals are minimized.
                    //
                    // Obvious example: if there is an overload that accepts a reference
                    // and one that accepts a value of the same type and we started with a
                    // reference, then we want to call the overload where the typechecking
                    // solution didn't strip the reference.
                    //
                    // So we add the weight of the original arguments to the cost,
                    // and remove the cost of the typechecking solution to account for that.
                    int32_t cost = tcc.cost();
                    cost += GetWeight( callPat );
                    cost -= GetWeight( subs );
                    tcc.setCost( cost );

                    if( pOvlSet->verboseResolution() )
                    {
                        DiagnosticsManager::GetInstance().emitTraceMessage(
                            ovl.callee ? ovl.callee->locationId() : 0,
                            format( "candidate score: {}", tcc.score() ) );
                    }

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

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

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

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

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

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

#if defined( OVL_TC_DEBUG ) && !defined( NDEBUG )
                bestTCC->DumpParamsTraces( cout );
                cout << endl;
#endif
            }

            pOvlSet->addResolutionToCache( args, *bestOvl );

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

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

    void SetupOverloadSetInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(

            ValueToEIR( Value( GetValueType< ptr< OverloadSet > >(), ANYTERM( _ ) ) ),

            GetOverloadSetInvocationRule() );
    }

} // namespace goose::builtins
Changes to bs/builtins/types/overloadset/overloadset.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::builtins;

bool goose::builtins::IsOverloadSet( const Value& os )
{
    return os.type() == GetValueType< ptr< OverloadSet > >();
}

namespace goose::eir
{
    const Term& Bridge< ptr< sema::OverloadSet > >::Type()
    {

        static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( ct_type ), TSID( overloadset ) ) ) );
        return type;
    }

    Value Bridge< ptr< sema::OverloadSet > >::ToValue( const ptr< sema::OverloadSet >& os )
    {
        return Value( Type(), TERM( static_pointer_cast< void >( os ) ) );
    }

    optional< ptr< sema::OverloadSet > > Bridge< ptr< sema::OverloadSet > >::FromValue( const Value& v )

    {
        if( !IsOverloadSet( v ) )
            return nullopt;

        auto result = Decompose( v.val(),
            Val< ptr< void > >()
        );

        if( !result )
            return nullopt;

        return static_pointer_cast< sema::OverloadSet >( result->get() );
    }
}













>
|








|
>




|
<
<






|
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::builtins;

bool goose::builtins::IsOverloadSet( const Value& os )
{
    return os.type() == GetValueType< ptr< OverloadSet > >();
}

namespace goose::eir
{
    const Term& Bridge< ptr< sema::OverloadSet > >::Type()
    {
        static auto type =
            ValueToEIR( Value( TypeType(), VEC( TSID( ct_type ), TSID( overloadset ) ) ) );
        return type;
    }

    Value Bridge< ptr< sema::OverloadSet > >::ToValue( const ptr< sema::OverloadSet >& os )
    {
        return Value( Type(), TERM( static_pointer_cast< void >( os ) ) );
    }

    optional< ptr< sema::OverloadSet > > Bridge< ptr< sema::OverloadSet > >::FromValue(
        const Value& v )
    {
        if( !IsOverloadSet( v ) )
            return nullopt;

        auto result = Decompose( v.val(), Val< ptr< void > >() );



        if( !result )
            return nullopt;

        return static_pointer_cast< sema::OverloadSet >( result->get() );
    }
} // namespace goose::eir
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
18
19
20
21
22
23
24
25
26
#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::eir
{
    template<>
    struct Bridge< ptr< sema::OverloadSet > >
    {
        static const Term& Type();
        static Value ToValue( const ptr< sema::OverloadSet >& os );
        static optional< ptr< sema::OverloadSet > > FromValue( const Value& v );
    };
}

#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
#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::builtins

namespace goose::eir
{

    template<> struct Bridge< ptr< sema::OverloadSet > >
    {
        static const Term& Type();
        static Value ToValue( const ptr< sema::OverloadSet >& os );
        static optional< ptr< sema::OverloadSet > > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/overloadset/typecheck.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
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
    {
        // func type param / overloadset arg
        e.typeCheckingRuleSet()->addTypeCheckingRule(

            ParamPat( FuncTypePattern() ),

            ValueToEIR( ValuePattern(
                TSID( constant ),
                GetValueType< ptr< sema::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 = BuildCallPatternFromFuncType( *EIRToValue( type ) );

            auto rhsVal = *EIRToValue( 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 savedRHSSubContext = tcc.RHSSubContext();
            auto localNSId = tcc.newNamespaceIndex();
            tcc.RHSSubContext().namespaceIndex = localNSId;

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

                // Restore the namespace
                tcc.flip();
                tcc.RHSSubContext() = savedRHSSubContext;

                auto overload = ovl;
                auto wrapped = WrapWithPostprocFunc( s,

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


                        if( func.isPoison() )
                            return nullopt;

                        return ValueToEIR( move( func ) );
                    } );

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

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

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

            ValueToEIR( ValuePattern(
                TSID( constant ),
                GetValueType< ptr< sema::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 [tfType,_] = DecomposeTFTSig( type );
            auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *EIRToValue( tfType ) );
            assert( callPat );

            auto rhsVal = *EIRToValue( 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 savedRHSSubContext = tcc.RHSSubContext();
            auto localNSId = tcc.newNamespaceIndex();
            tcc.RHSSubContext().namespaceIndex = localNSId;

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

                tcc.flip();
                tcc.RHSSubContext() = savedRHSSubContext;

                auto overload = ovl;
                auto wrapped = WrapWithPostprocFunc( s,

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


                        if( func.isPoison() )
                            return nullopt;

                        return ValueToEIR( move( func ) );
                    } );

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







<
|
<

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

|
|

|
|

|

|
|
|
|
|

|
|
|
|

|
|
|

|
|
>
|
|
|
|
>

|
|

|
|

|
|
|





<
|
<


<
|
<

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

|
|
|
|

|
|

|

|
|
|
|
|

|
|
|
|

|
|

|
|
>
|
|
|
|
>

|
|

|
|

|
|
|

|
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
    {
        // func type param / overloadset arg
        e.typeCheckingRuleSet()->addTypeCheckingRule(

            ParamPat( FuncTypePattern() ),

            ValueToEIR( ValuePattern(

                TSID( constant ), GetValueType< ptr< sema::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 = BuildCallPatternFromFuncType( *EIRToValue( type ) );

                auto rhsVal = *EIRToValue( 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 savedRHSSubContext = tcc.RHSSubContext();
                auto localNSId = tcc.newNamespaceIndex();
                tcc.RHSSubContext().namespaceIndex = localNSId;

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

                    // Restore the namespace
                    tcc.flip();
                    tcc.RHSSubContext() = savedRHSSubContext;

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

                            if( func.isPoison() )
                                return nullopt;

                            return ValueToEIR( move( func ) );
                        } );

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

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

            ValueToEIR( ValuePattern(

                ANYTERM( _ ), TFuncTypeSigPattern( ANYTERM( _ ), ANYTERM( _ ) ), ANYTERM( _ ) ) ),


            ValueToEIR( ValuePattern(

                TSID( constant ), GetValueType< ptr< sema::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 [tfType, _] = DecomposeTFTSig( type );
                auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *EIRToValue( tfType ) );
                assert( callPat );

                auto rhsVal = *EIRToValue( 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 savedRHSSubContext = tcc.RHSSubContext();
                auto localNSId = tcc.newNamespaceIndex();
                tcc.RHSSubContext().namespaceIndex = localNSId;

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

                    tcc.flip();
                    tcc.RHSSubContext() = savedRHSSubContext;

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

                            if( func.isPoison() )
                                return nullopt;

                            return ValueToEIR( move( func ) );
                        } );

                    co_yield { move( wrapped ), tcc };
                }
            } );
    }
} // namespace goose::builtins
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
28
29
30
31
32
33
34
35
36

37
38
#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 = EIRToValue( 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 = EIRToValue( type ); typeVal && IsTuple( *typeVal ) )
            return ValueToEIR( ValuePattern( TSID( param ), ValueToEIR( TupleOfTypesToTupleType( *typeVal ) ), HOLE( "_"_sid ) ) );


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

    // This special param pattern forces the argument to be passed as is without any typechecking.
    struct ForwarderPattern
    {
        static auto GetPattern()
        {
            return HOLE( "_"_sid, "fwd"_sid );
        }
    };
}


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

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

    // This special param pattern forces the argument to be passed as is without any typechecking.
    struct ForwarderPattern
    {
        static auto GetPattern() { return HOLE( "_"_sid, "fwd"_sid ); }



    };

} // namespace goose::builtins

#endif
Changes to bs/builtins/types/predicates/predicates.cpp.
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
    }

    optional< ptr< Propositions > > GetTypePredicates( const Value& t )
    {
        if( !t.isType() )
            return nullopt;

        auto vec = Decompose( t.val(),
            Val< pvec >()
        );

        if( !vec )
            return nullopt;

        if( vec->get()->terms().size() < 3 )
            return nullopt;

        auto result = Decompose( vec->get()->terms()[1],
            Vec(
                Lit( "predicates"_sid ),
                Val< ptr< void > >()
            )
        );

        if( !result )
            return nullopt;

        auto&& [pt] = *result;
        return static_pointer_cast< Propositions >( pt );
    }

    Term InjectPredicatesIntoStdType( const Term& t, const ptr< Propositions >& predicates )
    {
        Term result( t );

        auto vec = Decompose( t,
            Val< pvec >()
        );

        assert( vec );
        assert( vec->get()->terms().size() >= 3 );

        // We need to copy vec, otherwise we're actually going to change the original type
        auto newVec = make_shared< Vector >( *vec->get() );








|
<
<







|
<
|
<
<
<












|
<
<







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
    }

    optional< ptr< Propositions > > GetTypePredicates( const Value& t )
    {
        if( !t.isType() )
            return nullopt;

        auto vec = Decompose( t.val(), Val< pvec >() );



        if( !vec )
            return nullopt;

        if( vec->get()->terms().size() < 3 )
            return nullopt;

        auto result = Decompose(

            vec->get()->terms()[1], Vec( Lit( "predicates"_sid ), Val< ptr< void > >() ) );




        if( !result )
            return nullopt;

        auto&& [pt] = *result;
        return static_pointer_cast< Propositions >( pt );
    }

    Term InjectPredicatesIntoStdType( const Term& t, const ptr< Propositions >& predicates )
    {
        Term result( t );

        auto vec = Decompose( t, Val< pvec >() );



        assert( vec );
        assert( vec->get()->terms().size() >= 3 );

        // We need to copy vec, otherwise we're actually going to change the original type
        auto newVec = make_shared< Vector >( *vec->get() );

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

        const auto& vec = *get< pvec >( identity );
        auto result = make_shared< Vector >();
        result->reserve( vec.terms().size() );

        for( auto&& t : vec.terms() )
        {
            auto decomp = Decompose( t,
                Vec(
                    Lit( "pred_hash"_sid ),
                    Val< uint64_t >()
                )
            );

            if( decomp )
                continue;

            const auto* pNestedVec = get_if< pvec >( &t );
            if( pNestedVec )
                result->append( StripPredicateHashesFromIdentity( t ) );
            else
                result->append( t );
        }

        return result;
    }
}







|
<
<
<
<
<













|
73
74
75
76
77
78
79
80





81
82
83
84
85
86
87
88
89
90
91
92
93
94

        const auto& vec = *get< pvec >( identity );
        auto result = make_shared< Vector >();
        result->reserve( vec.terms().size() );

        for( auto&& t : vec.terms() )
        {
            auto decomp = Decompose( t, Vec( Lit( "pred_hash"_sid ), Val< uint64_t >() ) );






            if( decomp )
                continue;

            const auto* pNestedVec = get_if< pvec >( &t );
            if( pNestedVec )
                result->append( StripPredicateHashesFromIdentity( t ) );
            else
                result->append( t );
        }

        return result;
    }
} // namespace goose::builtins
Changes to bs/builtins/types/predicates/predicates.h.
10
11
12
13
14
15
16
17
18
19
    extern optional< ptr< Propositions > > GetTypePredicates( const Value& t );
    extern Term InjectPredicatesIntoStdType( const Term& t, const ptr< Propositions >& predicates );

    extern bool ParseTypePredicates( const Context& c, const Value& type );

    extern Term AppendPredicatesHashToIdentity( const Term& identity, uint64_t hash );
    extern Term StripPredicateHashesFromIdentity( const Term& identity );
}

#endif







|


10
11
12
13
14
15
16
17
18
19
    extern optional< ptr< Propositions > > GetTypePredicates( const Value& t );
    extern Term InjectPredicatesIntoStdType( const Term& t, const ptr< Propositions >& predicates );

    extern bool ParseTypePredicates( const Context& c, const Value& type );

    extern Term AppendPredicatesHashToIdentity( const Term& identity, uint64_t hash );
    extern Term StripPredicateHashesFromIdentity( const Term& identity );
} // namespace goose::builtins

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

#include "builtins/builtins.h"

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

namespace goose::builtins
{
    void SetupPredicatesTypeChecking( Env& e )
    {
        e.typeCheckingRuleSet()->addTypeCheckingRule(
            VEC( TSID( predicates ), ANYTERM( _ ) ),
            VEC( TSID( predicates ), ANYTERM( _ ) ),
            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                co_yield { rhs, tcc };
            } );
    }
}











|

<

<
|
<

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

13

14

15

16
#include "builtins/builtins.h"

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

namespace goose::builtins
{
    void SetupPredicatesTypeChecking( Env& e )
    {
        e.typeCheckingRuleSet()->addTypeCheckingRule( VEC( TSID( predicates ), ANYTERM( _ ) ),
            VEC( TSID( predicates ), ANYTERM( _ ) ),

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

            { co_yield { rhs, tcc }; } );

    }

} // namespace goose::builtins
Changes to bs/builtins/types/pretty.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

#include "builtins/builtins.h"

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

namespace goose::builtins
{
    void SetupBasicTypesPrettyPrinting()
    {
        auto& pp = PrettyPrinter::GetInstance();

        pp.addRule( TypeType(), [&]( auto&& out, auto&& t ) { out << "TypeType"; return true; } );






        pp.addRule( ValueToEIR( ValuePattern( TSID( constant ), ANYTERM(_), ANYTERM(_) ) ),
            [&]( auto&& out, auto&& t )
            {
                auto v = *EIRToValue( t );
                auto ty = EIRToValue( v.type() );
                out << "constant[ ";
                pp.print( out, ty ? ty->val() : v.type() );
                out << " ]{ ";
                pp.print( out, v.val() );
                out << " }";
                return true;
            } );

        pp.addRule( ValueToEIR( ValuePattern( TSID( constant ), TypeType(), ANYTERM(_) ) ),
            [&]( auto&& out, auto&& t )
            {
                auto v = *EIRToValue( t );
                out << "type[ ";
                pp.print( out, v.val() );
                out << " ]";
                return true;
            } );

        pp.addRule( ValueToEIR( ValuePattern( TSID( computed ), ANYTERM(_), ANYTERM(_) ) ),
            [&]( auto&& out, auto&& t )
            {
                auto v = *EIRToValue( t );
                auto ty = EIRToValue( v.type() );
                out << "value[ ";
                pp.print( out, ty ? ty->val() : v.type() );
                out << " ]";
                if( v.cir() )
                {
                    out << "{ ";
                    for( const auto& instr : *v.cir() )
                        out << instr << ' ';
                    out << "}";
                }
                return true;
            } );

        pp.addRule( ValueToEIR( ValuePattern( TSID( constant ),
            ValueToEIR( Value( TypeType(), VEC( TSID( decl ), ANYTERM( _ ) ) ) ),
                ANYTERM( _ ) ) ),
            [&]( auto&& out, auto&& t )
            {
                auto decl = *FromValue< Decl >( *EIRToValue( t ) );
                out << "decl(" << decl.type() << ", " << decl.name() << ")";
                return true;
            } );

        pp.addRule( ValueToEIR( ValuePattern( TSID( param ), ANYTERM(_), ANYTERM(_) ) ),
            [&]( auto&& out, auto&& t )
            {
                auto v = *EIRToValuePattern( t );
                auto ty = EIRToValue( v.type() );
                out << "param[ ";
                pp.print( out, ty ? ty->val() : v.type() );
                out << " ]{ ";
                pp.print( out, v.val() );
                out << " }";
                return true;
            } );


        pp.addRule( ValueToEIR( ValuePattern( TSID( constant ), BFuncTypePattern(), ANYTERM(_) ) ),
            [&]( auto&& out, auto&& t )
            {
                out << "builtin_func";

                auto v = *EIRToValue( t );
                auto loc = Location::Get( v.locationId() );
                if( !loc )
                    return true;

                out << '@' << loc->filename() << ':' << loc->line();
                return true;
            } );


        pp.addRule( ValueToEIR( ValuePattern( TSID( constant ), EagerBFuncTypePattern(), ANYTERM(_) ) ),
            [&]( auto&& out, auto&& t )
            {
                out << "eager_builtin_func";

                auto v = *EIRToValue( t );
                auto loc = Location::Get( v.locationId() );
                if( !loc )
                    return true;

                out << '@' << loc->filename() << ':' << loc->line();
                return true;
            } );

        pp.addRule( GetValueType< bool >(), [&]( auto&& out, auto&& t ) { out << "bool"; return true; } );





        pp.addRule( MkStdType( TSID( rt_type ), TSID( bool ) ), [&]( auto&& out, auto&& t ) { out << "bool"; return true; } );






        pp.addRule( GetValueType< string >(), [&]( auto&& out, auto&& t ) { out << "ct_string"; return true; } );





        pp.addRule( MkStdType( TSID( ct_type ), TSID( ct_string ) ), [&]( auto&& out, auto&& t ) { out << "ct_string"; return true; } );






        pp.addRule( GetValueType< BigInt >(), [&]( auto&& out, auto&& t ) { out << "ct_int"; return true; } );





        pp.addRule( MkStdType( TSID( ct_type ), TSID( ct_int ) ), [&]( auto&& out, auto&& t ) { out << "ct_int"; return true; } );




    }
}













|
>
|
>
>
>
>
|












|









|


















|
|







|












>
|













>
|













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

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

namespace goose::builtins
{
    void SetupBasicTypesPrettyPrinting()
    {
        auto& pp = PrettyPrinter::GetInstance();

        pp.addRule( TypeType(),
            [&]( auto&& out, auto&& t )
            {
                out << "TypeType";
                return true;
            } );

        pp.addRule( ValueToEIR( ValuePattern( TSID( constant ), ANYTERM( _ ), ANYTERM( _ ) ) ),
            [&]( auto&& out, auto&& t )
            {
                auto v = *EIRToValue( t );
                auto ty = EIRToValue( v.type() );
                out << "constant[ ";
                pp.print( out, ty ? ty->val() : v.type() );
                out << " ]{ ";
                pp.print( out, v.val() );
                out << " }";
                return true;
            } );

        pp.addRule( ValueToEIR( ValuePattern( TSID( constant ), TypeType(), ANYTERM( _ ) ) ),
            [&]( auto&& out, auto&& t )
            {
                auto v = *EIRToValue( t );
                out << "type[ ";
                pp.print( out, v.val() );
                out << " ]";
                return true;
            } );

        pp.addRule( ValueToEIR( ValuePattern( TSID( computed ), ANYTERM( _ ), ANYTERM( _ ) ) ),
            [&]( auto&& out, auto&& t )
            {
                auto v = *EIRToValue( t );
                auto ty = EIRToValue( v.type() );
                out << "value[ ";
                pp.print( out, ty ? ty->val() : v.type() );
                out << " ]";
                if( v.cir() )
                {
                    out << "{ ";
                    for( const auto& instr : *v.cir() )
                        out << instr << ' ';
                    out << "}";
                }
                return true;
            } );

        pp.addRule( ValueToEIR( ValuePattern( TSID( constant ),
                        ValueToEIR( Value( TypeType(), VEC( TSID( decl ), ANYTERM( _ ) ) ) ),
                        ANYTERM( _ ) ) ),
            [&]( auto&& out, auto&& t )
            {
                auto decl = *FromValue< Decl >( *EIRToValue( t ) );
                out << "decl(" << decl.type() << ", " << decl.name() << ")";
                return true;
            } );

        pp.addRule( ValueToEIR( ValuePattern( TSID( param ), ANYTERM( _ ), ANYTERM( _ ) ) ),
            [&]( auto&& out, auto&& t )
            {
                auto v = *EIRToValuePattern( t );
                auto ty = EIRToValue( v.type() );
                out << "param[ ";
                pp.print( out, ty ? ty->val() : v.type() );
                out << " ]{ ";
                pp.print( out, v.val() );
                out << " }";
                return true;
            } );

        pp.addRule(
            ValueToEIR( ValuePattern( TSID( constant ), BFuncTypePattern(), ANYTERM( _ ) ) ),
            [&]( auto&& out, auto&& t )
            {
                out << "builtin_func";

                auto v = *EIRToValue( t );
                auto loc = Location::Get( v.locationId() );
                if( !loc )
                    return true;

                out << '@' << loc->filename() << ':' << loc->line();
                return true;
            } );

        pp.addRule(
            ValueToEIR( ValuePattern( TSID( constant ), EagerBFuncTypePattern(), ANYTERM( _ ) ) ),
            [&]( auto&& out, auto&& t )
            {
                out << "eager_builtin_func";

                auto v = *EIRToValue( t );
                auto loc = Location::Get( v.locationId() );
                if( !loc )
                    return true;

                out << '@' << loc->filename() << ':' << loc->line();
                return true;
            } );

        pp.addRule( GetValueType< bool >(),
            [&]( auto&& out, auto&& t )
            {
                out << "bool";
                return true;
            } );
        pp.addRule( MkStdType( TSID( rt_type ), TSID( bool ) ),
            [&]( auto&& out, auto&& t )
            {
                out << "bool";
                return true;
            } );

        pp.addRule( GetValueType< string >(),
            [&]( auto&& out, auto&& t )
            {
                out << "ct_string";
                return true;
            } );
        pp.addRule( MkStdType( TSID( ct_type ), TSID( ct_string ) ),
            [&]( auto&& out, auto&& t )
            {
                out << "ct_string";
                return true;
            } );

        pp.addRule( GetValueType< BigInt >(),
            [&]( auto&& out, auto&& t )
            {
                out << "ct_int";
                return true;
            } );
        pp.addRule( MkStdType( TSID( ct_type ), TSID( ct_int ) ),
            [&]( auto&& out, auto&& t )
            {
                out << "ct_int";
                return true;
            } );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/propositions/drop.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
#include "builtins/builtins.h"
#include "parse/parse.h"

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

namespace goose::builtins
{
    void SetupPropositionsDropValue( Env& e )
    {
        // When a boolean is dropped into Propositions, it is appended as a new proposition.
        RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< Propositions > >, bool ) > >( e, e.extDropValue(),

            []( const Context& c, const Value& p, const Value& v )
            {
                auto props = *FromValue< TypeWrapper< ptr< Propositions > > >( p );
                props->append( v );
            } );

        // When anything else is dropped into Propositions, complain if we're only supposed
        // to have bools.
        RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< Propositions > >, Value ) > >( e, e.extDropValue(),

            []( const Context& c, const Value& p, const Value& v )
            {
                auto props = *FromValue< TypeWrapper< ptr< Propositions > > >( p );
                props->poison();

                DiagnosticsManager::GetInstance().emitErrorMessage( v.locationId(),
                    "only boolean expressions are allowed in proposition lists.", 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
#include "builtins/builtins.h"
#include "parse/parse.h"

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

namespace goose::builtins
{
    void SetupPropositionsDropValue( Env& e )
    {
        // When a boolean is dropped into Propositions, it is appended as a new proposition.
        RegisterBuiltinFunc< Intrinsic< void( TypeWrapper< ptr< Propositions > >, bool ) > >( e,
            e.extDropValue(),
            []( const Context& c, const Value& p, const Value& v )
            {
                auto props = *FromValue< TypeWrapper< ptr< Propositions > > >( p );
                props->append( v );
            } );

        // When anything else is dropped into Propositions, complain if we're only supposed
        // to have bools.
        RegisterBuiltinFunc< Intrinsic< void( TypeWrapper< ptr< Propositions > >, Value ) > >( e,
            e.extDropValue(),
            []( const Context& c, const Value& p, const Value& v )
            {
                auto props = *FromValue< TypeWrapper< ptr< Propositions > > >( p );
                props->poison();

                DiagnosticsManager::GetInstance().emitErrorMessage( v.locationId(),
                    "only boolean expressions are allowed in proposition lists.", 0 );
            } );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/propositions/propositions.cpp.
17
18
19
20
21
22
23
24
25

26
27
28
29
30
31
32
33
        if( m_unparsedPropositions.empty() )
            return true;

        Context localContext( c.env(), identity() );

        // We don't have a propositions builder class, because we don't need any specific logic atm.
        // We just want to be able to override DropValue() in the context of parsing propositions
        // to append any dropped boolean expression as a proposition, and politely reject anything else
        // as contextually invalid.

        localContext.setBuilder( ToValue( TypeWrapper< ptr< Propositions > >( shared_from_this() ) ) );

        for( auto&& toks : m_unparsedPropositions )
        {
            auto tokProvider = lex::MakeVectorAdapter( toks );
            auto r = make_shared< parse::Resolver >( tokProvider, localContext );
            parse::Parser p( r );
            p.parseSequence();







|
|
>
|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
        if( m_unparsedPropositions.empty() )
            return true;

        Context localContext( c.env(), identity() );

        // We don't have a propositions builder class, because we don't need any specific logic atm.
        // We just want to be able to override DropValue() in the context of parsing propositions
        // to append any dropped boolean expression as a proposition, and politely reject anything
        // else as contextually invalid.
        localContext.setBuilder(
            ToValue( TypeWrapper< ptr< Propositions > >( shared_from_this() ) ) );

        for( auto&& toks : m_unparsedPropositions )
        {
            auto tokProvider = lex::MakeVectorAdapter( toks );
            auto r = make_shared< parse::Resolver >( tokProvider, localContext );
            parse::Parser p( r );
            p.parseSequence();
49
50
51
52
53
54
55
56
        /*verify::Propositions pv( c );
        if( !pv.addPropositions( m_propositions ) )
            return false;

        return pv.verify();*/
        return true;
    }
}







|
50
51
52
53
54
55
56
57
        /*verify::Propositions pv( c );
        if( !pv.addPropositions( m_propositions ) )
            return false;

        return pv.verify();*/
        return true;
    }
} // namespace goose::builtins
Changes to bs/builtins/types/propositions/propositions.h.
1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
18
19
20


21
22
23
24


25
26
27
28
29
30
31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#ifndef GOOSE_BUILTINS_TYPE_PROPOSITIONS_H
#define GOOSE_BUILTINS_TYPE_PROPOSITIONS_H

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

    using UnparsedPropositions = llvm::SmallVector< vector< TermLoc >, 8 >;

    class Propositions : public LifeCycleManager< Propositions >, public enable_shared_from_this< Propositions >

    {
        public:
            Propositions() = default;

            Propositions( const Propositions& ) = default;

            Propositions( ValueVec&& props ) :
                m_propositions( move( props ) )
            {}



            Propositions( const ValueVec& props ) :
                m_propositions( props )
            {}



            size_t hash() const;

            bool parse( const Context& c );

            template< typename T >
            void setIdentity( T&& identity );

            const auto& identity() const { return m_identity; }

            template< typename T >
            void append( T&& val );

            void append( Propositions&& props )
            {

                m_unparsedPropositions.reserve( m_unparsedPropositions.size() + props.m_unparsedPropositions.size() );
                for( auto&& x : props.m_unparsedPropositions )
                    m_unparsedPropositions.emplace_back( move( x ) );
            }

            // Used to mark the proposition list as poisoned if something goes wrong during
            // its parsing.
            void poison()
            {
                m_poison = true;
            }

            auto& unparsed() { return m_unparsedPropositions; }

            template< typename T >
            void setUnparsedProps( T&& toks );

            const auto& props() const { return m_propositions; }

            bool empty() const { return m_propositions.empty(); }

            template< typename F >
            void forEach( F&& func );

        private:
            ValueVec m_propositions;
            UnparsedPropositions m_unparsedPropositions;
            Term m_identity;

            mutable optional< size_t > m_hash;

            bool m_poison = false;
    };
}

#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
64
65
66
67
68
69
70
71
#ifndef GOOSE_BUILTINS_TYPE_PROPOSITIONS_H
#define GOOSE_BUILTINS_TYPE_PROPOSITIONS_H

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

    using UnparsedPropositions = llvm::SmallVector< vector< TermLoc >, 8 >;

    class Propositions : public LifeCycleManager< Propositions >,
                         public enable_shared_from_this< Propositions >
    {
      public:
        Propositions() = default;

        Propositions( const Propositions& ) = default;

        Propositions( ValueVec&& props ) :
            m_propositions( move( props ) )

        {
        }

        Propositions( const ValueVec& props ) :
            m_propositions( props )

        {
        }

        size_t hash() const;

        bool parse( const Context& c );

        template< typename T > void setIdentity( T&& identity );


        const auto& identity() const { return m_identity; }

        template< typename T > void append( T&& val );


        void append( Propositions&& props )
        {
            m_unparsedPropositions.reserve(
                m_unparsedPropositions.size() + props.m_unparsedPropositions.size() );
            for( auto&& x : props.m_unparsedPropositions )
                m_unparsedPropositions.emplace_back( move( x ) );
        }

        // Used to mark the proposition list as poisoned if something goes wrong during
        // its parsing.
        void poison() { m_poison = true; }




        auto& unparsed() { return m_unparsedPropositions; }

        template< typename T > void setUnparsedProps( T&& toks );


        const auto& props() const { return m_propositions; }

        bool empty() const { return m_propositions.empty(); }

        template< typename F > void forEach( F&& func );


      private:
        ValueVec m_propositions;
        UnparsedPropositions m_unparsedPropositions;
        Term m_identity;

        mutable optional< size_t > m_hash;

        bool m_poison = false;
    };
} // namespace goose::builtins

#endif
Changes to bs/builtins/types/propositions/propositions.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
#ifndef GOOSE_BUILTINS_TYPE_PROPOSITIONS_INL
#define GOOSE_BUILTINS_TYPE_PROPOSITIONS_INL

namespace goose::builtins
{
    template< typename T >
    void Propositions::setIdentity( T&& identity )
    {
        m_identity = forward< T >( identity );
    }

    template< typename T >
    void Propositions::append( T&& val )
    {
        m_propositions.emplace_back( forward< T >( val ) );
    }

    template< typename T >
    void Propositions::setUnparsedProps( T&& toks )
    {
        m_unparsedPropositions = forward< T >( toks );
    }

    template< typename F >
    void Propositions::forEach( F&& func )
    {
        for( auto&& v : m_propositions )
            func( v );
    }
}

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

namespace goose::builtins
{

    template< typename T > void Propositions::setIdentity( T&& identity )
    {
        m_identity = forward< T >( identity );
    }

    template< typename T > void Propositions::append( T&& val )

    {
        m_propositions.emplace_back( forward< T >( val ) );
    }


    template< typename T > void Propositions::setUnparsedProps( T&& toks )
    {
        m_unparsedPropositions = forward< T >( toks );
    }

    template< typename F > void Propositions::forEach( F&& func )

    {
        for( auto&& v : m_propositions )
            func( v );
    }
} // namespace goose::builtins

#endif
Changes to bs/builtins/types/reference/init.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"
#include "parse/parse.h"

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

namespace goose::builtins
{
    void SetupReferenceInitialize( Env& e )
    {
        RegisterBuiltinFunc< Intrinsic< Value ( CustomPattern< Value,

            ReferenceType::PatternMutableOf< ReferenceType::PatternXOfTypeT > >,
            CustomPattern< Value, ReferenceType::PatternXOfTypeT > ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r, const Value& initVal )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(),
                    initVal, r, Store( refType.type(), initVal.locationId(), r.locationId() ) );
            } );
    }
}










|
>
|





|
|


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

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

namespace goose::builtins
{
    void SetupReferenceInitialize( Env& e )
    {
        RegisterBuiltinFunc< Intrinsic< Value(
            CustomPattern< Value,
                ReferenceType::PatternMutableOf< ReferenceType::PatternXOfTypeT > >,
            CustomPattern< Value, ReferenceType::PatternXOfTypeT > ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r, const Value& initVal )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(), initVal, r,
                    Store( refType.type(), initVal.locationId(), r.locationId() ) );
            } );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/reference/lower.cpp.
1
2
3
4
5
6
7
8
9
10

11

12
13
14
15
16
17
18
19
20
21
22
23

#include "builtins/builtins.h"

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

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

        RegisterBuiltinFunc< Intrinsic< Value ( TypePatternParam< Value, ReferenceType::PatternAny > ) > >( e, e.extLowerType(),

            []( const Context& c, const Value& refType )
        {
            auto rt = *FromValue< ReferenceType >( refType );

            auto loweredType = LowerType( c, *EIRToValue( rt.type() ) );
            if( !loweredType || loweredType->isPoison() )
                return PoisonValue();

            return ToValue( PointerType( ValueToEIR( *loweredType ) ) );
        } );
    }
}











>
|
>

|
|

|
|
|

|
|

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

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

namespace goose::builtins
{
    void SetupReferenceLowering( Env& e )
    {
        RegisterBuiltinFunc<
            Intrinsic< Value( TypePatternParam< Value, ReferenceType::PatternAny > ) > >( e,
            e.extLowerType(),
            []( const Context& c, const Value& refType )
            {
                auto rt = *FromValue< ReferenceType >( refType );

                auto loweredType = LowerType( c, *EIRToValue( rt.type() ) );
                if( !loweredType || loweredType->isPoison() )
                    return PoisonValue();

                return ToValue( PointerType( ValueToEIR( *loweredType ) ) );
            } );
    }

} // namespace goose::builtins
Changes to bs/builtins/types/reference/parse.cpp.
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
                    dm.emitSyntaxErrorMessage( lhs.locationId(),
                        "expected an access specifier or a template variable.", 0 );
                    return PoisonValue();
                }

                if( !IsTExpr( rhs ) && !IsType( p.context(), rhs ) )
                {
                    dm.emitSyntaxErrorMessage( rhs.locationId(),
                        "expected a type.", 0 );
                    return PoisonValue();
                }

                // If the accessSpecifier is a TVar, turn it into a TDecl, so we can constrain it to
                // the AccessSpecifier type. Otherwise, it woud be possible to do all kind of
                // funky stuff to create refs with pretty much any old value of any type as
                // the access level.
                if( IsTVar( lhs ) )
                {
                    auto tv = *FromValue< TVar >( lhs );
                    lhs = ToValue( TDecl( GetValueType< AccessSpecifier >(), tv.name() ) )
                        .setLocationId( lhs.locationId() );
                }

                ReferenceType rt( ValueToEIR( rhs ), ValueToEIR( lhs ) );
                return ToDeclValue( rt );
            } );

        RegisterRule( e, "ref"_sid, move( r ) );
    }
}







|
<











|








|
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
                    dm.emitSyntaxErrorMessage( lhs.locationId(),
                        "expected an access specifier or a template variable.", 0 );
                    return PoisonValue();
                }

                if( !IsTExpr( rhs ) && !IsType( p.context(), rhs ) )
                {
                    dm.emitSyntaxErrorMessage( rhs.locationId(), "expected a type.", 0 );

                    return PoisonValue();
                }

                // If the accessSpecifier is a TVar, turn it into a TDecl, so we can constrain it to
                // the AccessSpecifier type. Otherwise, it woud be possible to do all kind of
                // funky stuff to create refs with pretty much any old value of any type as
                // the access level.
                if( IsTVar( lhs ) )
                {
                    auto tv = *FromValue< TVar >( lhs );
                    lhs = ToValue( TDecl( GetValueType< AccessSpecifier >(), tv.name() ) )
                              .setLocationId( lhs.locationId() );
                }

                ReferenceType rt( ValueToEIR( rhs ), ValueToEIR( lhs ) );
                return ToDeclValue( rt );
            } );

        RegisterRule( e, "ref"_sid, move( r ) );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/reference/reference.cpp.
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
            return nullopt;

        return FromValue< Lifetime >( *ltVal );
    }

    bool IsReferenceType( const Term& t )
    {
        auto result = Decompose( EIRToValue( t )->val(),
            Vec(
                Lit( "reference"_sid ),
                SubTerm(),
                SubTerm()
            )
        );

        return !!result;
    }

    bool IsMutReferenceType( const ReferenceType& rt )
    {
        auto asVal = EIRToValue( rt.accessSpec() );
        if( !asVal )
            return false;

        auto as = FromValue< AccessSpecifier >( *asVal );
        if( !as )
            return false;

        return as->mode() == TSID( mut );
    }

    const Term& ReferenceType::PatternAny::GetPattern()
    {

        static auto pattern = ValueToEIR( ToValue( ReferenceType( HOLE( "_"_sid ), HOLE( "_"_sid ) ) ) );
        return pattern;
    }

    const Term& ReferenceType::PatternAnyTRef::GetPattern()
    {
        static auto pattern = ValueToEIR( Value( GetValueType< ReferenceType >(),
            TVecToEIR( Vector::Make( TSID( reference ), HOLE( "_"_sid ), HOLE( "_"_sid ) ) ) ) );
        return pattern;
    }

    const Term& ReferenceType::PatternAnyMutable::GetPattern()
    {

        static auto pattern = ValueToEIR( ToValue( ReferenceType( HOLE( "_"_sid ), MutAccessSpecifier() ) ) );
        return pattern;
    }

    const Term& ReferenceType::PatternAnyMutableOfTypeT::GetPattern()
    {

        static auto pattern = ValueToEIR( ToValue( ReferenceType( HOLE( "T"_sid, "tvar"_sid ), MutAccessSpecifier() ) ) );
        return pattern;
    }

    const Term& ReferenceType::PatternXOfTypeT::GetPattern()
    {

        static auto pattern = ValueToEIR( ToValue( ReferenceType( HOLE( "T"_sid, "fwd"_sid ), HOLE( "X"_sid ) ) ) );
        return pattern;
    }

    // Returns an instruction that computes the address of whatever's contained in the locvar.
    cir::Instruction GetAddrFromLocalVar( const LocalVar& lv )
    {
        // TODO LOC: may need loc in LocalVar







|
<
|
<
<
<
<



















>
|












>
|





>
|





>
|







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
            return nullopt;

        return FromValue< Lifetime >( *ltVal );
    }

    bool IsReferenceType( const Term& t )
    {
        auto result = Decompose(

            EIRToValue( t )->val(), Vec( Lit( "reference"_sid ), SubTerm(), SubTerm() ) );





        return !!result;
    }

    bool IsMutReferenceType( const ReferenceType& rt )
    {
        auto asVal = EIRToValue( rt.accessSpec() );
        if( !asVal )
            return false;

        auto as = FromValue< AccessSpecifier >( *asVal );
        if( !as )
            return false;

        return as->mode() == TSID( mut );
    }

    const Term& ReferenceType::PatternAny::GetPattern()
    {
        static auto pattern =
            ValueToEIR( ToValue( ReferenceType( HOLE( "_"_sid ), HOLE( "_"_sid ) ) ) );
        return pattern;
    }

    const Term& ReferenceType::PatternAnyTRef::GetPattern()
    {
        static auto pattern = ValueToEIR( Value( GetValueType< ReferenceType >(),
            TVecToEIR( Vector::Make( TSID( reference ), HOLE( "_"_sid ), HOLE( "_"_sid ) ) ) ) );
        return pattern;
    }

    const Term& ReferenceType::PatternAnyMutable::GetPattern()
    {
        static auto pattern =
            ValueToEIR( ToValue( ReferenceType( HOLE( "_"_sid ), MutAccessSpecifier() ) ) );
        return pattern;
    }

    const Term& ReferenceType::PatternAnyMutableOfTypeT::GetPattern()
    {
        static auto pattern = ValueToEIR(
            ToValue( ReferenceType( HOLE( "T"_sid, "tvar"_sid ), MutAccessSpecifier() ) ) );
        return pattern;
    }

    const Term& ReferenceType::PatternXOfTypeT::GetPattern()
    {
        static auto pattern =
            ValueToEIR( ToValue( ReferenceType( HOLE( "T"_sid, "fwd"_sid ), HOLE( "X"_sid ) ) ) );
        return pattern;
    }

    // Returns an instruction that computes the address of whatever's contained in the locvar.
    cir::Instruction GetAddrFromLocalVar( const LocalVar& lv )
    {
        // TODO LOC: may need loc in LocalVar
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
    }

    const Term& TempAccessSpecifier()
    {
        static auto al = ValueToEIR( ToValue( AccessSpecifier( "temp"_sid ) ) );
        return al;
    }
}

namespace goose::eir
{
    const Term& Bridge< ReferenceType >::Type()
    {
        return TypeType();
    }

    Value Bridge< ReferenceType >::ToDeclValue( const ReferenceType& t )
    {
        return Value( Type(), TVEC( TSID( reference ), t.accessSpec(), t.type() ) );
    }

    optional< ReferenceType > Bridge< ReferenceType >::FromDeclValue( const Value& v )
    {
        auto tv = TVecFromEIR( v.val() );
        if( !tv )
            return FromValue( v );

        auto result = Decompose( tv->content(),
            Vec(
                Lit( "reference"_sid ),
                SubTerm(),
                SubTerm()
            )
        );

        if( !result )
            return nullopt;

        auto&& [bhv, type] = *result;
        return ReferenceType( move( type ), move( bhv ) );
    }

    Value Bridge< ReferenceType >::ToValue( const ReferenceType& t )
    {
        return Value( Type(), VEC( TSID( reference ), t.accessSpec(), t.type() ) );
    }

    optional< ReferenceType > Bridge< ReferenceType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),
            Vec(
                Lit( "reference"_sid ),
                SubTerm(),
                SubTerm()
            )
        );

        if( !result )
            return nullopt;

        auto&& [bhv, type] = *result;
        return ReferenceType( move( type ), move( bhv ) );
    }

    const Term& Bridge< AccessSpecifier >::Type()
    {

        static auto type = ValueToEIR( Value( TypeType(), TVEC( TSID( ct_type ), TSID( access_specifier ) ) ) );
        return type;
    }

    Value Bridge< AccessSpecifier >::ToValue( const AccessSpecifier& a )
    {
        return Value( Type(), TVEC( a.mode(), a.lifetime() ) );
    }

    optional< AccessSpecifier > Bridge< AccessSpecifier >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),
            Vec(
                SubTerm(),
                SubTerm()
            )
        );

        if( !result )
            return nullopt;

        auto&& [mode, lifetime] = *result;
        return AccessSpecifier( mode, lifetime );
    }
}







|



















|
<
|
<
<
<
<















|
<
<
<
<
<
<










>
|










|
<
<
<
<
<







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

    const Term& TempAccessSpecifier()
    {
        static auto al = ValueToEIR( ToValue( AccessSpecifier( "temp"_sid ) ) );
        return al;
    }
} // namespace goose::builtins

namespace goose::eir
{
    const Term& Bridge< ReferenceType >::Type()
    {
        return TypeType();
    }

    Value Bridge< ReferenceType >::ToDeclValue( const ReferenceType& t )
    {
        return Value( Type(), TVEC( TSID( reference ), t.accessSpec(), t.type() ) );
    }

    optional< ReferenceType > Bridge< ReferenceType >::FromDeclValue( const Value& v )
    {
        auto tv = TVecFromEIR( v.val() );
        if( !tv )
            return FromValue( v );

        auto result =

            Decompose( tv->content(), Vec( Lit( "reference"_sid ), SubTerm(), SubTerm() ) );





        if( !result )
            return nullopt;

        auto&& [bhv, type] = *result;
        return ReferenceType( move( type ), move( bhv ) );
    }

    Value Bridge< ReferenceType >::ToValue( const ReferenceType& t )
    {
        return Value( Type(), VEC( TSID( reference ), t.accessSpec(), t.type() ) );
    }

    optional< ReferenceType > Bridge< ReferenceType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(), Vec( Lit( "reference"_sid ), SubTerm(), SubTerm() ) );







        if( !result )
            return nullopt;

        auto&& [bhv, type] = *result;
        return ReferenceType( move( type ), move( bhv ) );
    }

    const Term& Bridge< AccessSpecifier >::Type()
    {
        static auto type =
            ValueToEIR( Value( TypeType(), TVEC( TSID( ct_type ), TSID( access_specifier ) ) ) );
        return type;
    }

    Value Bridge< AccessSpecifier >::ToValue( const AccessSpecifier& a )
    {
        return Value( Type(), TVEC( a.mode(), a.lifetime() ) );
    }

    optional< AccessSpecifier > Bridge< AccessSpecifier >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(), Vec( SubTerm(), SubTerm() ) );






        if( !result )
            return nullopt;

        auto&& [mode, lifetime] = *result;
        return AccessSpecifier( mode, lifetime );
    }
} // namespace goose::eir
Changes to bs/builtins/types/reference/reference.h.
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

    extern const Term& MutAccessSpecifier();
    extern const Term& ConstAccessSpecifier();
    extern const Term& TempAccessSpecifier();

    class AccessSpecifier
    {
        public:
            AccessSpecifier( const Term& mode, const Term& lifetime = cir::Lifetime::Unspecified() ) :
                m_mode( mode ),
                m_lifetime( lifetime )
            {}



            const auto& mode() const { return m_mode; }

            const auto& lifetime() const { return m_lifetime; }

            auto& lifetime() { return m_lifetime; }

        private:
            // Either of these can be TVars, so we store them as Terms.
            Term m_mode;
            Term m_lifetime;
    };

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



            const auto& type() const { return m_type; }

            const auto& accessSpec() const { return m_accessSpec; }

            auto& type() { return m_type; }

            optional< cir::Lifetime > lifetime() const;

            struct PatternAny
            {
                static const Term& GetPattern();
            };

            struct PatternAnyTRef
            {
                static const Term& GetPattern();
            };

            struct PatternAnyMutable
            {
                static const Term& GetPattern();
            };

            struct PatternAnyMutableOfTypeT
            {
                static const Term& GetPattern();
            };

            struct PatternXOfTypeT
            {
                static const Term& GetPattern();
            };

            template< typename T >
            struct PatternMutableOfType
            {
                static const Term& GetPattern()
                {

                    static auto pattern = ValueToEIR( ToValue( ReferenceType{ GetValueType< T >(), MutAccessSpecifier() } ) );
                    return pattern;
                }
            };

            template< typename T >
            struct PatternConstOfType
            {
                static const Term& GetPattern()
                {

                    static auto pattern = ValueToEIR( ToValue( ReferenceType{ GetValueType< T >(), ConstAccessSpecifier() } ) );
                    return pattern;
                }
            };

            template< typename PP >

            struct PatternAnyOf
            {
                static const Term& GetPattern()
                {
                    static auto pattern = ValueToEIR( ToValue( ReferenceType{ PP::GetPattern(), HOLE( "_"_sid ) } ) );
                    return pattern;
                }
            };

            template< typename PP >
            struct PatternMutableOf
            {
                static const Term& GetPattern()
                {

                    static auto pattern = ValueToEIR( ToValue( ReferenceType{ PP::GetPattern(), MutAccessSpecifier() } ) );
                    return pattern;
                }
            };

            template< typename PP >
            struct PatternConst
            {
                static auto GetPattern()
                {

                    static auto pattern = ValueToEIR( ToValue( ReferenceType{ PP::GetPattern(), ConstAccessSpecifier() } ) );
                    return pattern;
                }
            };

        private:
            Term m_type;

            // Not an enum because we want to be able
            // to have holes there and it's a pain in
            // the ass
            Term m_accessSpec;
    };

    extern bool IsMutReferenceType( const ReferenceType& rt );

    extern cir::Instruction GetAddrFromLocalVar( const LocalVar& lv );
    extern Value BuildLocalVarMutRef( const LocalVar& lv );
}

namespace goose::eir
{
    template<>
    struct Bridge< builtins::ReferenceType >
    {
        static const Term& Type();

        static Value ToDeclValue( const builtins::ReferenceType& t );
        static optional< builtins::ReferenceType > FromDeclValue( const Value& v );

        static Value ToValue( const builtins::ReferenceType& t );
        static optional< builtins::ReferenceType > FromValue( const Value& v );
    };

    template<>
    struct Bridge< builtins::AccessSpecifier >
    {
        static const Term& Type();

        static Value ToValue( const builtins::AccessSpecifier& t );
        static optional< builtins::AccessSpecifier > FromValue( const Value& v );
    };
}

#endif







|
|
|
|
<
|
>
>
|
>
|
>
|

|
|
|
|




|
|
|
|
|
<
|
>
>
|
>
|

|

|

|
|
|
|

|
|
|
|

|
|
|
|

|
|
|
|

|
|
|
|

|
<
|
|
|
>
|
|
|
|

|
<
|
|
|
>
|
|
|
|

|
>
|

|
<
|
|
|
|

|
<
|
|
|
>
|
|
|
|

|
<
|
|
|
>
|
|
|
|

|
|

|
|
|
|






|



<
|










<
|






|


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

    extern const Term& MutAccessSpecifier();
    extern const Term& ConstAccessSpecifier();
    extern const Term& TempAccessSpecifier();

    class AccessSpecifier
    {
      public:
        AccessSpecifier( const Term& mode, const Term& lifetime = cir::Lifetime::Unspecified() ) :
            m_mode( mode ),
            m_lifetime( lifetime )

        {
        }

        const auto& mode() const { return m_mode; }

        const auto& lifetime() const { return m_lifetime; }

        auto& lifetime() { return m_lifetime; }

      private:
        // Either of these can be TVars, so we store them as Terms.
        Term m_mode;
        Term m_lifetime;
    };

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

        {
        }

        const auto& type() const { return m_type; }

        const auto& accessSpec() const { return m_accessSpec; }

        auto& type() { return m_type; }

        optional< cir::Lifetime > lifetime() const;

        struct PatternAny
        {
            static const Term& GetPattern();
        };

        struct PatternAnyTRef
        {
            static const Term& GetPattern();
        };

        struct PatternAnyMutable
        {
            static const Term& GetPattern();
        };

        struct PatternAnyMutableOfTypeT
        {
            static const Term& GetPattern();
        };

        struct PatternXOfTypeT
        {
            static const Term& GetPattern();
        };

        template< typename T > struct PatternMutableOfType

        {
            static const Term& GetPattern()
            {
                static auto pattern = ValueToEIR(
                    ToValue( ReferenceType{ GetValueType< T >(), MutAccessSpecifier() } ) );
                return pattern;
            }
        };

        template< typename T > struct PatternConstOfType

        {
            static const Term& GetPattern()
            {
                static auto pattern = ValueToEIR(
                    ToValue( ReferenceType{ GetValueType< T >(), ConstAccessSpecifier() } ) );
                return pattern;
            }
        };

        template< typename PP > struct PatternAnyOf
        {
            static const Term& GetPattern()
            {
                static auto pattern =

                    ValueToEIR( ToValue( ReferenceType{ PP::GetPattern(), HOLE( "_"_sid ) } ) );
                return pattern;
            }
        };

        template< typename PP > struct PatternMutableOf

        {
            static const Term& GetPattern()
            {
                static auto pattern = ValueToEIR(
                    ToValue( ReferenceType{ PP::GetPattern(), MutAccessSpecifier() } ) );
                return pattern;
            }
        };

        template< typename PP > struct PatternConst

        {
            static auto GetPattern()
            {
                static auto pattern = ValueToEIR(
                    ToValue( ReferenceType{ PP::GetPattern(), ConstAccessSpecifier() } ) );
                return pattern;
            }
        };

      private:
        Term m_type;

        // Not an enum because we want to be able
        // to have holes there and it's a pain in
        // the ass
        Term m_accessSpec;
    };

    extern bool IsMutReferenceType( const ReferenceType& rt );

    extern cir::Instruction GetAddrFromLocalVar( const LocalVar& lv );
    extern Value BuildLocalVarMutRef( const LocalVar& lv );
} // namespace goose::builtins

namespace goose::eir
{

    template<> struct Bridge< builtins::ReferenceType >
    {
        static const Term& Type();

        static Value ToDeclValue( const builtins::ReferenceType& t );
        static optional< builtins::ReferenceType > FromDeclValue( const Value& v );

        static Value ToValue( const builtins::ReferenceType& t );
        static optional< builtins::ReferenceType > FromValue( const Value& v );
    };


    template<> struct Bridge< builtins::AccessSpecifier >
    {
        static const Term& Type();

        static Value ToValue( const builtins::AccessSpecifier& t );
        static optional< builtins::AccessSpecifier > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/reference/typecheck.cpp.
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
282
283
284
285
286
287
288
289

290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310

        auto ltype = EIRToValuePattern( lhs )->type();

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

        auto ref = ValueToEIR( ToValue( BuildLocalVarMutRef( *locvar ) )
            .setLocationId( lvval.locationId() ) );

        co_yield TypeCheck( lhs, ref, tcc );
    }

    TCGen TypeCheckingDereference( const Term& lhs, const Term& rhs, TypeCheckingContext tcc )
    {
        auto refVal = EIRToValue( rhs );
        if( !refVal )
            co_return;

        auto refType = FromValue< ReferenceType >( *EIRToValue( refVal->type() ) );
        if( !refType )
            co_return;

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

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


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

        auto lRefType = *FromValue< ReferenceType >( *EIRToValue( EIRToValuePattern( lhs )->type() ) );
        if( IsMutReferenceType( lRefType ) )
            co_return;

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

        for( auto&& [s,tcc] : TypeCheck( lhsPat, rhs, tcc ) )
        {
            auto wrapped = WrapWithPostprocFunc( VEC( s, rhs ),
            [lhs]( const Term& t, TypeCheckingContext tcc ) -> optional< Term >
            {
                auto result = Decompose( t,
                    Vec(
                        SubTerm(),
                        SubTerm()
                    )
                );

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

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

                auto rhsVal = *EIRToValue( rhs );

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

                // TODO create an ext point for this
                auto loc = rhsVal.locationId();
                auto tempIndex = util::GenerateNewUID();
                return ValueToEIR( BuildComputedValue( ValueToEIR( ToValue( rt ) ), rhsVal,

                    DataPathOf( tempIndex, loc ) ).setLocationId( loc ) );
            } );

            // Override the weight because we don't want
            // this solution to count more than directly using
            // the value without wrapping it into a tempref
            SetWeight( wrapped, GetWeight( rhs ) - 1 );
            co_yield { move( wrapped ), tcc };
        }
    }
}

namespace goose::builtins
{
    void SetupReferenceTypeChecking( Env& e )
    {
        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 ), ConstAccessSpecifier(), ANYTERM( _ ) ) ) );

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

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

        auto localVarPattern = GetValueType< LocalVar >( ANYTERM( _ ) );

        // Reference type checking rule.
        e.typeCheckingRuleSet()->addTypeCheckingRule(

            ParamPat( refTypePattern ),

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

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

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

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

                // Type check the accessSpecs

                for( auto&& [b, tcc] : TypeCheck( lRefType.accessSpec(), rRefType.accessSpec(), tcc ) )
                {
                    // Check the types
                    for( auto&& [t, tcc] : TypeCheck( lRefType.type(), rRefType.type(), tcc ) )
                    {
                        co_yield { ValueToEIR( ValuePattern( rhsVal.sort(), ValueToEIR( ToValue( ReferenceType( t, b ) ) ),


                            rhsVal.val() ).setLocationId( rhsVal.locationId() ) ), tcc };
                    }
                }
            }
        );

        // mut -> const reference type checking rule.
        e.typeCheckingRuleSet()->addTypeCheckingRule(
            ConstAccessSpecifier(),
            MutAccessSpecifier(),

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

        // mut -> const reference unification rule.
        e.typeCheckingRuleSet()->addUnificationRule(
            ConstAccessSpecifier(),
            MutAccessSpecifier(),

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


        );



        // temp -> const reference type checking rule.
        e.typeCheckingRuleSet()->addTypeCheckingRule(
            ConstAccessSpecifier(),
            TempAccessSpecifier(),

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

        // temp -> const reference unification rule.
        e.typeCheckingRuleSet()->addUnificationRule(
            ConstAccessSpecifier(),
            TempAccessSpecifier(),

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

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

            ParamPat( ANYTERM( _ ) ),

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

            TypeCheckingDereference
        );

        // References versus TVars: a tvar should never directly bind to a reference.
        // Therefore, when unifying a TVar against a reference, we strip the reference.
        // And when typechecking a TVar typed parameter against a reference, we dereference.
        e.typeCheckingRuleSet()->addTypeCheckingRule(

            ParamPat( HOLE( ""_sid, "tvar"_sid ) ),

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

            TypeCheckingDereference
        );

        // Unifying a TVar with a reference type isn't allowed.
        // A reference whose type is a TVar, on the other hand,
        // can be unified with another reference type.
        e.typeCheckingRuleSet()->addUnificationRule(

            HOLE( ""_sid, "tvar"_sid ),

            refTypePattern,

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

        // LocalVar type checking against a param (implicit referencing)
        e.typeCheckingRuleSet()->addTypeCheckingRule(

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

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

            LocVarToRef
        );

        // The rule above is ambiguous with the WrappedArg rule when passing a locvar
        // to a wrappedarg param. So we add a disambiguation rule that prioritizes
        // turning the locvar into a ref in that case (since typically we don't want to manipulate
        // 0_locvars directly, so anything that wants a wrapped value probably wants whatever's in the var)

        e.typeCheckingRuleSet()->addTypeCheckingRule(

            ValueToEIR( ValuePattern(
                TSID( param ),
                ValueToEIR( BuildWrappedType( ANYTERM( _ ) ) ),
                ANYTERM( _ ) ) ),

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

            LocVarToRef
        );

        // Typechecking ref of ref against a ref: dereference the first ref and attempt to typecheck it
        // against the other ref
        e.typeCheckingRuleSet()->addTypeCheckingRule(

            ParamPat( refTypePattern ),

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

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

            auto rrefVal = *EIRToValue( rhs );
            auto rrType = FromValue< ReferenceType >( *EIRToValue( rrefVal.type() ) );
            if( !rrType )
                co_return;

            auto loc = rrefVal.locationId();
            auto content = ValueToEIR( BuildComputedValue( rrType->type(),
                rrefVal, cir::Load( rrType->type(), loc ) ).setLocationId( loc ) );


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

        // Implicit referencing of non-variables against a non mutable ref: build a tempref
        e.typeCheckingRuleSet()->addTypeCheckingRule(

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

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

            TypeCheckingBuildTempRef
        );
    }
}








|
|














|
|
|
|





>
|

>
|





|


|
|
|
<
<
<
|
<
<
|
|

|
|

|

|
|
|

|
|
|
|
>
|
|

|
<
|




|





|
|

|
|

|
|

|
|

|
|








|
<
<
<



>
|





>
|




|
>
>
|
|
|
|
|


|
<
<


<
<
<
<
<
<
<
<
<
<
<
<
|
|
>
>
|
>
>


|
<
<


<
|
|
<
<

|
<
<


<
|
<
<







|
<
<
<

|
<








|
<
<
<

|
<











<
|
<
<




|
<
<
<

|
<
<
<

|
<




|
>



<
|
<

|
<
<
<

|
<

|
|




|
<
<
<

|
|
|

|
|
|
|

|
|
|
>

|
|




|
<
<
<

|
<
<
<

|
<

<
>
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
        auto ltype = EIRToValuePattern( lhs )->type();

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

        auto ref = ValueToEIR(
            ToValue( BuildLocalVarMutRef( *locvar ) ).setLocationId( lvval.locationId() ) );

        co_yield TypeCheck( lhs, ref, tcc );
    }

    TCGen TypeCheckingDereference( const Term& lhs, const Term& rhs, TypeCheckingContext tcc )
    {
        auto refVal = EIRToValue( rhs );
        if( !refVal )
            co_return;

        auto refType = FromValue< ReferenceType >( *EIRToValue( refVal->type() ) );
        if( !refType )
            co_return;

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

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

    TCGen TypeCheckingBuildTempRef(
        const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc )
    {
        auto lRefType =
            *FromValue< ReferenceType >( *EIRToValue( EIRToValuePattern( lhs )->type() ) );
        if( IsMutReferenceType( lRefType ) )
            co_return;

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

        for( auto&& [s, tcc] : TypeCheck( lhsPat, rhs, tcc ) )
        {
            auto wrapped = WrapWithPostprocFunc( VEC( s, rhs ),
                [lhs]( const Term& t, TypeCheckingContext tcc ) -> optional< Term >
                {
                    auto result = Decompose( t, Vec( SubTerm(), SubTerm() ) );






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

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

                    auto rhsVal = *EIRToValue( rhs );

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

                    // TODO create an ext point for this
                    auto loc = rhsVal.locationId();
                    auto tempIndex = util::GenerateNewUID();
                    return ValueToEIR( BuildComputedValue(
                        ValueToEIR( ToValue( rt ) ), rhsVal, DataPathOf( tempIndex, loc ) )
                                           .setLocationId( loc ) );
                } );

            // Override the weight because we don't want this solution to count more than directly

            // using the value without wrapping it into a tempref
            SetWeight( wrapped, GetWeight( rhs ) - 1 );
            co_yield { move( wrapped ), tcc };
        }
    }
} // namespace

namespace goose::builtins
{
    void SetupReferenceTypeChecking( Env& e )
    {
        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 ), ConstAccessSpecifier(), ANYTERM( _ ) ) ) );

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

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

        auto localVarPattern = GetValueType< LocalVar >( ANYTERM( _ ) );

        // Reference type checking rule.
        e.typeCheckingRuleSet()->addTypeCheckingRule(

            ParamPat( refTypePattern ),

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




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

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

                // Type check the accessSpecs
                for( auto&& [b, tcc] :
                    TypeCheck( lRefType.accessSpec(), rRefType.accessSpec(), tcc ) )
                {
                    // Check the types
                    for( auto&& [t, tcc] : TypeCheck( lRefType.type(), rRefType.type(), tcc ) )
                    {
                        co_yield { ValueToEIR( ValuePattern( rhsVal.sort(),
                                       ValueToEIR( ToValue( ReferenceType( t, b ) ) ),
                                       rhsVal.val() )
                                                   .setLocationId( rhsVal.locationId() ) ),
                            tcc };
                    }
                }
            } );

        // mut -> const reference type checking rule.
        e.typeCheckingRuleSet()->addTypeCheckingRule( ConstAccessSpecifier(), MutAccessSpecifier(),



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












            { co_yield { lhs, tcc }; } );

        // mut -> const reference unification rule.
        e.typeCheckingRuleSet()->addUnificationRule( ConstAccessSpecifier(), MutAccessSpecifier(),

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

        // temp -> const reference type checking rule.
        e.typeCheckingRuleSet()->addTypeCheckingRule( ConstAccessSpecifier(), TempAccessSpecifier(),



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

            { co_yield { lhs, tcc }; } );



        // temp -> const reference unification rule.
        e.typeCheckingRuleSet()->addUnificationRule( ConstAccessSpecifier(), TempAccessSpecifier(),



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

            { co_yield { lhs, tcc }; } );



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

            ParamPat( ANYTERM( _ ) ),

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




            TypeCheckingDereference );


        // References versus TVars: a tvar should never directly bind to a reference.
        // Therefore, when unifying a TVar against a reference, we strip the reference.
        // And when typechecking a TVar typed parameter against a reference, we dereference.
        e.typeCheckingRuleSet()->addTypeCheckingRule(

            ParamPat( HOLE( ""_sid, "tvar"_sid ) ),

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




            TypeCheckingDereference );


        // Unifying a TVar with a reference type isn't allowed.
        // A reference whose type is a TVar, on the other hand,
        // can be unified with another reference type.
        e.typeCheckingRuleSet()->addUnificationRule(

            HOLE( ""_sid, "tvar"_sid ),

            refTypePattern,

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

            { co_return; } );



        // LocalVar type checking against a param (implicit referencing)
        e.typeCheckingRuleSet()->addTypeCheckingRule(

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




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




            LocVarToRef );


        // The rule above is ambiguous with the WrappedArg rule when passing a locvar
        // to a wrappedarg param. So we add a disambiguation rule that prioritizes
        // turning the locvar into a ref in that case (since typically we don't want to manipulate
        // 0_locvars directly, so anything that wants a wrapped value probably wants whatever's in
        // the var)
        e.typeCheckingRuleSet()->addTypeCheckingRule(

            ValueToEIR( ValuePattern(

                TSID( param ), ValueToEIR( BuildWrappedType( ANYTERM( _ ) ) ), ANYTERM( _ ) ) ),


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




            LocVarToRef );


        // Typechecking ref of ref against a ref: dereference the first ref and attempt to typecheck
        // it against the other ref
        e.typeCheckingRuleSet()->addTypeCheckingRule(

            ParamPat( refTypePattern ),

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




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

                auto rrefVal = *EIRToValue( rhs );
                auto rrType = FromValue< ReferenceType >( *EIRToValue( rrefVal.type() ) );
                if( !rrType )
                    co_return;

                auto loc = rrefVal.locationId();
                auto content = ValueToEIR(
                    BuildComputedValue( rrType->type(), rrefVal, cir::Load( rrType->type(), loc ) )
                        .setLocationId( loc ) );

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

        // Implicit referencing of non-variables against a non mutable ref: build a tempref
        e.typeCheckingRuleSet()->addTypeCheckingRule(

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




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




            TypeCheckingBuildTempRef );

    }

} // namespace goose::builtins
Changes to bs/builtins/types/runtime/array.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
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::builtins;

namespace goose::builtins
{
    void SetupRuntimeArrayType( Env& e )
    {
        RegisterBuiltinFunc< Eager< Value > ( Value, uint32_t ) >( e, "array"_sid,
            []( const Value& containedType, uint32_t count )
            {
                if( !GetCodegenType( containedType ) )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( containedType.locationId(), "runtime arrays can only contain runtime types." );

                    return PoisonValue();
                }

                return ToValue( ArrayType( ValueToEIR( containedType ), count ) );
            } );
    }

    const codegen::Type* GetCodegenType( const ArrayType& a )
    {
        // TODO_SSA reenable
        /*return codegen::Type::Get( llvm::ArrayType::get(
            *GetCodegenType( *EIRToValue( a.m_containedType ) ), a.m_count ) );*/
        return nullptr;
    }
}

namespace goose::eir
{
    Value Bridge< ArrayType >::ToValue( const ArrayType& a )
    {
        return Value( Type(), MkStdRTType( TSID( rt_type ),
            GetCodegenType( a ), TSID( array ),
            TERM( a.m_count ), a.m_containedType ) );
    }

    optional< ArrayType > Bridge< ArrayType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),
            Vec(
                Lit( "rt_type"_sid ),
                SubTerm(),
                Val< void* >(),
                Lit( "array"_sid ),
                Val< uint64_t >(),
                SubTerm()
            )
        );

        if( !result )
            return nullopt;

        auto&& [predicates, cgType, count, containedType] = *result;
        return ArrayType( containedType, count );
    }
}









|




|
>











|


|





|
|
|





<
|
<
<
<
|
<
<
<







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

using namespace goose;
using namespace goose::builtins;

namespace goose::builtins
{
    void SetupRuntimeArrayType( Env& e )
    {
        RegisterBuiltinFunc< Eager< Value >( Value, uint32_t ) >( e, "array"_sid,
            []( const Value& containedType, uint32_t count )
            {
                if( !GetCodegenType( containedType ) )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( containedType.locationId(),
                        "runtime arrays can only contain runtime types." );
                    return PoisonValue();
                }

                return ToValue( ArrayType( ValueToEIR( containedType ), count ) );
            } );
    }

    const codegen::Type* GetCodegenType( const ArrayType& a )
    {
        // TODO_SSA reenable
        /*return codegen::Type::Get( llvm::ArrayType::get(
         *GetCodegenType( *EIRToValue( a.m_containedType ) ), a.m_count ) );*/
        return nullptr;
    }
} // namespace goose::builtins

namespace goose::eir
{
    Value Bridge< ArrayType >::ToValue( const ArrayType& a )
    {
        return Value( Type(),
            MkStdRTType( TSID( rt_type ), GetCodegenType( a ), TSID( array ), TERM( a.m_count ),
                a.m_containedType ) );
    }

    optional< ArrayType > Bridge< ArrayType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),

            Vec( Lit( "rt_type"_sid ), SubTerm(), Val< void* >(), Lit( "array"_sid ),



                Val< uint64_t >(), SubTerm() ) );




        if( !result )
            return nullopt;

        auto&& [predicates, cgType, count, containedType] = *result;
        return ArrayType( containedType, count );
    }
} // namespace goose::eir
Changes to bs/builtins/types/runtime/array.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
#ifndef GOOSE_BUILTINS_TYPES_RUNTIME_ARRAY_H
#define GOOSE_BUILTINS_TYPES_RUNTIME_ARRAY_H

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

    struct ArrayType
    {
        template< typename T >
        ArrayType( T&& containedType, uint64_t count ) :
            m_containedType( forward< T >( containedType ) ),
            m_count( count )
        {}



        Term m_containedType;
        uint64_t m_count = 1;
    };

    extern const codegen::Type* GetCodegenType( const ArrayType& a );
}

namespace goose::eir
{
    template<>
    struct Bridge< builtins::ArrayType >
    {
        static const Term& Type() { return TypeType(); }

        static Value ToValue( const builtins::ArrayType& a );
        static optional< builtins::ArrayType > FromValue( const Value& v );
    };
}

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

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

    struct ArrayType
    {
        template< typename T >
        ArrayType( T&& containedType, uint64_t count ) :
            m_containedType( forward< T >( containedType ) ),
            m_count( count )

        {
        }

        Term m_containedType;
        uint64_t m_count = 1;
    };

    extern const codegen::Type* GetCodegenType( const ArrayType& a );
} // namespace goose::builtins

namespace goose::eir
{

    template<> struct Bridge< builtins::ArrayType >
    {
        static const Term& Type() { return TypeType(); }

        static Value ToValue( const builtins::ArrayType& a );
        static optional< builtins::ArrayType > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/runtime/basic.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
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
{
    void SetupRuntimeBasicTypes( Env& e )
    {
        DefineConstant( e, "half"_sid, ValueToEIR( ToValue( HalfFloatType() ) ) );
        DefineConstant( e, "float"_sid, ValueToEIR( ToValue( FloatType() ) ) );
        DefineConstant( e, "double"_sid, ValueToEIR( ToValue( DoubleFloatType() ) ) );

        RegisterBuiltinFunc< Eager< Value > ( uint32_t ) >( e, "uint"_sid,
            []( uint32_t numBits )
            {
                return ToValue( IntegerType( numBits ) );
            } );

        RegisterBuiltinFunc< Eager< Value > ( uint32_t ) >( e, "sint"_sid,
            []( uint32_t numBits )
            {
                return ToValue( IntegerType( numBits, true ) );
            } );
    }

    const Term& IntegerType::Pattern::GetPattern()
    {
        static auto pattern = ValueToEIR( Value( TypeType(), MkStdRTType( TSID( rt_type ),
            nullptr, TSID( integer ),
            VEC( HOLE( "size"_sid ), HOLE( "signedness"_sid ) ) ) ) );

        return pattern;
    }

    const Term& IntegerType::PatternSigned::GetPattern()
    {
        static auto pattern = ValueToEIR( Value( TypeType(), MkStdRTType( TSID( rt_type ),
            nullptr, TSID( integer ),
            VEC( HOLE( "size"_sid ), TERM( 1U ) ) ) ) );

        return pattern;
    }

    const Term& IntegerType::PatternUnsigned::GetPattern()
    {
        static auto pattern = ValueToEIR( Value( TypeType(), MkStdRTType( TSID( rt_type ),
            nullptr, TSID( integer ),
            VEC( HOLE( "size"_sid ), TERM( 0U ) ) ) ) );

        return pattern;
    }

    const Term& IntegerType::PatternUnsigned32::GetPattern()
    {
        static auto pattern = ValueToEIR( Value( TypeType(), MkStdRTType( TSID( rt_type ),
            nullptr, TSID( integer ),
            VEC( TERM( 32U ), TERM( 0U ) ) ) ) );

        return pattern;
    }

    const codegen::Type* GetCodegenType( const HalfFloatType& t )
    {
        // TODO_SSA reenable
        return nullptr; //codegen::Type::Get( llvm::Type::getHalfTy( GetLLVMContext() ) );
    }

    const codegen::Type* GetCodegenType( const FloatType& t )
    {
        // TODO_SSA reenable
        return nullptr; //codegen::Type::Get( llvm::Type::getFloatTy( GetLLVMContext() ) );
    }

    const codegen::Type* GetCodegenType( const DoubleFloatType& t )
    {
        // TODO_SSA reenable
        return nullptr; //codegen::Type::Get( llvm::Type::getDoubleTy( GetLLVMContext() ) );
    }

    const codegen::Type* GetCodegenType( const IntegerType& t )
    {
        // TODO_SSA reenable
        return nullptr; //codegen::Type::Get( llvm::IntegerType::get( GetLLVMContext(), t.m_numBits ) );

    }
}


namespace goose::eir
{
    //// Half
    Value Bridge< HalfFloatType >::ToValue( const HalfFloatType& t )
    {
        return Value( Type(), MkStdRTType( TSID( rt_type ), GetCodegenType( t ), TSID( half ) ) );







|
<
<
|
<

|
<
<
|
<




|
|
|






|
|
|






|
|
|






|
|
|







|





|





|





|
>

<
>







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
{
    void SetupRuntimeBasicTypes( Env& e )
    {
        DefineConstant( e, "half"_sid, ValueToEIR( ToValue( HalfFloatType() ) ) );
        DefineConstant( e, "float"_sid, ValueToEIR( ToValue( FloatType() ) ) );
        DefineConstant( e, "double"_sid, ValueToEIR( ToValue( DoubleFloatType() ) ) );

        RegisterBuiltinFunc< Eager< Value >( uint32_t ) >(


            e, "uint"_sid, []( uint32_t numBits ) { return ToValue( IntegerType( numBits ) ); } );


        RegisterBuiltinFunc< Eager< Value >( uint32_t ) >( e, "sint"_sid,


            []( uint32_t numBits ) { return ToValue( IntegerType( numBits, true ) ); } );

    }

    const Term& IntegerType::Pattern::GetPattern()
    {
        static auto pattern = ValueToEIR( Value( TypeType(),
            MkStdRTType( TSID( rt_type ), nullptr, TSID( integer ),
                VEC( HOLE( "size"_sid ), HOLE( "signedness"_sid ) ) ) ) );

        return pattern;
    }

    const Term& IntegerType::PatternSigned::GetPattern()
    {
        static auto pattern = ValueToEIR( Value( TypeType(),
            MkStdRTType( TSID( rt_type ), nullptr, TSID( integer ),
                VEC( HOLE( "size"_sid ), TERM( 1U ) ) ) ) );

        return pattern;
    }

    const Term& IntegerType::PatternUnsigned::GetPattern()
    {
        static auto pattern = ValueToEIR( Value( TypeType(),
            MkStdRTType( TSID( rt_type ), nullptr, TSID( integer ),
                VEC( HOLE( "size"_sid ), TERM( 0U ) ) ) ) );

        return pattern;
    }

    const Term& IntegerType::PatternUnsigned32::GetPattern()
    {
        static auto pattern = ValueToEIR( Value( TypeType(),
            MkStdRTType(
                TSID( rt_type ), nullptr, TSID( integer ), VEC( TERM( 32U ), TERM( 0U ) ) ) ) );

        return pattern;
    }

    const codegen::Type* GetCodegenType( const HalfFloatType& t )
    {
        // TODO_SSA reenable
        return nullptr; // codegen::Type::Get( llvm::Type::getHalfTy( GetLLVMContext() ) );
    }

    const codegen::Type* GetCodegenType( const FloatType& t )
    {
        // TODO_SSA reenable
        return nullptr; // codegen::Type::Get( llvm::Type::getFloatTy( GetLLVMContext() ) );
    }

    const codegen::Type* GetCodegenType( const DoubleFloatType& t )
    {
        // TODO_SSA reenable
        return nullptr; // codegen::Type::Get( llvm::Type::getDoubleTy( GetLLVMContext() ) );
    }

    const codegen::Type* GetCodegenType( const IntegerType& t )
    {
        // TODO_SSA reenable
        return nullptr; // codegen::Type::Get( llvm::IntegerType::get( GetLLVMContext(), t.m_numBits
                        // ) );
    }

} // namespace goose::builtins

namespace goose::eir
{
    //// Half
    Value Bridge< HalfFloatType >::ToValue( const HalfFloatType& t )
    {
        return Value( Type(), MkStdRTType( TSID( rt_type ), GetCodegenType( t ), TSID( half ) ) );
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

        return {};
    }

    //// Integer
    Value Bridge< IntegerType >::ToValue( const IntegerType& t )
    {

        return Value( Type(), MkStdRTType( TSID( rt_type ), GetCodegenType( t ), TSID( integer ),
            VEC( TERM( t.m_numBits ), t.m_signed ? TERM( 1U ) : TERM( 0U ) ) ) );
    }

    optional< IntegerType > Bridge< IntegerType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),
            Vec(
                Lit( "rt_type"_sid ),
                SubTerm(),
                Val< void* >(),
                Lit( "integer"_sid ),
                Vec(
                    Val< uint64_t >(),
                    Val< uint64_t >()
                )
            )
        );

        if( !result )
            return nullopt;

        auto&& [predicates, cgType, params] = *result;
        auto&& [numBits, signd] = params;
        return IntegerType( numBits, !!signd );
    }

    Term Bridge< APSInt >::Type( const APSInt& i )
    {
        return ValueToEIR( eir::ToValue(
            IntegerType( i.getBitWidth(), i.isSigned() ) ) );
    }

    Value Bridge< APSInt >::ToValue( const APSInt& i )
    {
        return Value( Type( i ), TERM( i ) );
    }

    optional< APSInt > Bridge< APSInt >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),
            Val< APSInt >()
        );

        if( !result )
            return nullopt;

        return *result;
    }


    const Term& Bridge< uint8_t >::Type()
    {
        static auto type = ValueToEIR( eir::ToValue(
            IntegerType( 8, false ) ) );
        return type;
    }

    Value Bridge< uint8_t >::ToValue( uint8_t x )
    {
        return eir::ToValue( APSInt::getUnsigned( x ).trunc( 8 ) );
    }

    optional< uint8_t > Bridge< uint8_t >::FromValue( const Value& v )
    {
        return FromValue< APSInt >( v )->getLimitedValue();
    }


    const Term& Bridge< uint32_t >::Type()
    {
        static auto type = ValueToEIR( eir::ToValue(
            IntegerType( 32, false ) ) );
        return type;
    }

    Value Bridge< uint32_t >::ToValue( uint32_t x )
    {
        return eir::ToValue( APSInt::getUnsigned( x ).trunc( 32 ) );
    }

    optional< uint32_t > Bridge< uint32_t >::FromValue( const Value& v )
    {
        return FromValue< APSInt >( v )->getLimitedValue();
    }


    const Term& Bridge< int32_t >::Type()
    {
        static auto type = ValueToEIR( eir::ToValue(
            IntegerType( 32, true ) ) );
        return type;
    }

    Value Bridge< int32_t >::ToValue( int32_t x )
    {
        return eir::ToValue( APSInt::get( x ).trunc( 32 ) );
    }

    optional< int32_t > Bridge< int32_t >::FromValue( const Value& v )
    {
        return FromValue< APSInt >( v )->getExtValue();
    }


    const Term& Bridge< uint64_t >::Type()
    {
        static auto type = ValueToEIR( eir::ToValue(
            IntegerType( 64, false ) ) );
        return type;
    }

    Value Bridge< uint64_t >::ToValue( uint64_t x )
    {
        return eir::ToValue( APSInt::getUnsigned( x ) );
    }

    optional< uint64_t > Bridge< uint64_t >::FromValue( const Value& v )
    {
        if( !v.isConstant() )
            return nullopt;
        return FromValue< APSInt >( v )->getLimitedValue();
    }
}







>
|
|





<
|
<
<
<
<
|
<
<
<
<











|
<









|
<
<







<


|
<













<


|
<













<


|
<













<


|
<














|
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

        return {};
    }

    //// Integer
    Value Bridge< IntegerType >::ToValue( const IntegerType& t )
    {
        return Value( Type(),
            MkStdRTType( TSID( rt_type ), GetCodegenType( t ), TSID( integer ),
                VEC( TERM( t.m_numBits ), t.m_signed ? TERM( 1U ) : TERM( 0U ) ) ) );
    }

    optional< IntegerType > Bridge< IntegerType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),

            Vec( Lit( "rt_type"_sid ), SubTerm(), Val< void* >(), Lit( "integer"_sid ),




                Vec( Val< uint64_t >(), Val< uint64_t >() ) ) );





        if( !result )
            return nullopt;

        auto&& [predicates, cgType, params] = *result;
        auto&& [numBits, signd] = params;
        return IntegerType( numBits, !!signd );
    }

    Term Bridge< APSInt >::Type( const APSInt& i )
    {
        return ValueToEIR( eir::ToValue( IntegerType( i.getBitWidth(), i.isSigned() ) ) );

    }

    Value Bridge< APSInt >::ToValue( const APSInt& i )
    {
        return Value( Type( i ), TERM( i ) );
    }

    optional< APSInt > Bridge< APSInt >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(), Val< APSInt >() );



        if( !result )
            return nullopt;

        return *result;
    }


    const Term& Bridge< uint8_t >::Type()
    {
        static auto type = ValueToEIR( eir::ToValue( IntegerType( 8, false ) ) );

        return type;
    }

    Value Bridge< uint8_t >::ToValue( uint8_t x )
    {
        return eir::ToValue( APSInt::getUnsigned( x ).trunc( 8 ) );
    }

    optional< uint8_t > Bridge< uint8_t >::FromValue( const Value& v )
    {
        return FromValue< APSInt >( v )->getLimitedValue();
    }


    const Term& Bridge< uint32_t >::Type()
    {
        static auto type = ValueToEIR( eir::ToValue( IntegerType( 32, false ) ) );

        return type;
    }

    Value Bridge< uint32_t >::ToValue( uint32_t x )
    {
        return eir::ToValue( APSInt::getUnsigned( x ).trunc( 32 ) );
    }

    optional< uint32_t > Bridge< uint32_t >::FromValue( const Value& v )
    {
        return FromValue< APSInt >( v )->getLimitedValue();
    }


    const Term& Bridge< int32_t >::Type()
    {
        static auto type = ValueToEIR( eir::ToValue( IntegerType( 32, true ) ) );

        return type;
    }

    Value Bridge< int32_t >::ToValue( int32_t x )
    {
        return eir::ToValue( APSInt::get( x ).trunc( 32 ) );
    }

    optional< int32_t > Bridge< int32_t >::FromValue( const Value& v )
    {
        return FromValue< APSInt >( v )->getExtValue();
    }


    const Term& Bridge< uint64_t >::Type()
    {
        static auto type = ValueToEIR( eir::ToValue( IntegerType( 64, false ) ) );

        return type;
    }

    Value Bridge< uint64_t >::ToValue( uint64_t x )
    {
        return eir::ToValue( APSInt::getUnsigned( x ) );
    }

    optional< uint64_t > Bridge< uint64_t >::FromValue( const Value& v )
    {
        if( !v.isConstant() )
            return nullopt;
        return FromValue< APSInt >( v )->getLimitedValue();
    }
} // namespace goose::eir
Changes to bs/builtins/types/runtime/basic.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_BUILTINS_TYPES_RUNTIME_BASIC_H
#define GOOSE_BUILTINS_TYPES_RUNTIME_BASIC_H

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

    struct HalfFloatType {};



    struct FloatType {};



    struct DoubleFloatType {};



    struct IntegerType
    {
        IntegerType( uint32_t numBits, bool signd = false ) :
            m_numBits( numBits ),
            m_signed( signd )
        {}



        uint32_t m_numBits = 1;
        bool m_signed = false;

        // Helpers to provide generic param patterns for builtin funcs
        struct Pattern
        {








|
>
>
>
|
>
>
>
|
>
>






<
>
>







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

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

    struct HalfFloatType
    {
    };

    struct FloatType
    {
    };

    struct DoubleFloatType
    {
    };

    struct IntegerType
    {
        IntegerType( uint32_t numBits, bool signd = false ) :
            m_numBits( numBits ),
            m_signed( signd )

        {
        }

        uint32_t m_numBits = 1;
        bool m_signed = false;

        // Helpers to provide generic param patterns for builtin funcs
        struct Pattern
        {
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
        };
    };

    extern const codegen::Type* GetCodegenType( const HalfFloatType& t );
    extern const codegen::Type* GetCodegenType( const FloatType& t );
    extern const codegen::Type* GetCodegenType( const DoubleFloatType& t );
    extern const codegen::Type* GetCodegenType( const IntegerType& t );
}

namespace goose::eir
{
    template<>
    struct Bridge< builtins::HalfFloatType >
    {
        static const Term& Type() { return TypeType(); }

        static Value ToValue( const builtins::HalfFloatType& i );
        static optional< builtins::HalfFloatType > FromValue( const Value& v );
    };

    template<>
    struct Bridge< builtins::FloatType >
    {
        static const Term& Type() { return TypeType(); }

        static Value ToValue( const builtins::FloatType& i );
        static optional< builtins::FloatType > FromValue( const Value& v );
    };

    template<>
    struct Bridge< builtins::DoubleFloatType >
    {
        static const Term& Type() { return TypeType(); }

        static Value ToValue( const builtins::DoubleFloatType& i );
        static optional< builtins::DoubleFloatType > FromValue( const Value& v );
    };

    template<>
    struct Bridge< builtins::IntegerType >
    {
        static const Term& Type() { return TypeType(); }

        static Value ToValue( const builtins::IntegerType& i );
        static optional< builtins::IntegerType > FromValue( const Value& v );
    };

    template<>
    struct Bridge< APSInt >
    {
        static Term Type( const APSInt& i );
        static Value ToValue( const APSInt& i );
        static optional< APSInt > FromValue( const Value& v );
    };

    template<>
    struct Bridge< uint8_t >
    {
        static const Term& Type();
        static Value ToValue( uint8_t x );
        static optional< uint8_t> FromValue( const Value& v );
    };

    template<>
    struct Bridge< uint32_t >
    {
        static const Term& Type();
        static Value ToValue( uint32_t x );
        static optional< uint32_t> FromValue( const Value& v );
    };

    template<>
    struct Bridge< int32_t >
    {
        static const Term& Type();
        static Value ToValue( int32_t x );
        static optional< int32_t> FromValue( const Value& v );
    };

    template<>
    struct Bridge< uint64_t >
    {
        static const Term& Type();
        static Value ToValue( uint64_t x );
        static optional< uint64_t> FromValue( const Value& v );
    };
}

#endif







|



<
|


>




<
|


>




<
|


>




<
|


>




<
|






<
|



|


<
|



|


<
|



|


<
|



|

|


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

    extern const codegen::Type* GetCodegenType( const HalfFloatType& t );
    extern const codegen::Type* GetCodegenType( const FloatType& t );
    extern const codegen::Type* GetCodegenType( const DoubleFloatType& t );
    extern const codegen::Type* GetCodegenType( const IntegerType& t );
} // namespace goose::builtins

namespace goose::eir
{

    template<> struct Bridge< builtins::HalfFloatType >
    {
        static const Term& Type() { return TypeType(); }

        static Value ToValue( const builtins::HalfFloatType& i );
        static optional< builtins::HalfFloatType > FromValue( const Value& v );
    };


    template<> struct Bridge< builtins::FloatType >
    {
        static const Term& Type() { return TypeType(); }

        static Value ToValue( const builtins::FloatType& i );
        static optional< builtins::FloatType > FromValue( const Value& v );
    };


    template<> struct Bridge< builtins::DoubleFloatType >
    {
        static const Term& Type() { return TypeType(); }

        static Value ToValue( const builtins::DoubleFloatType& i );
        static optional< builtins::DoubleFloatType > FromValue( const Value& v );
    };


    template<> struct Bridge< builtins::IntegerType >
    {
        static const Term& Type() { return TypeType(); }

        static Value ToValue( const builtins::IntegerType& i );
        static optional< builtins::IntegerType > FromValue( const Value& v );
    };


    template<> struct Bridge< APSInt >
    {
        static Term Type( const APSInt& i );
        static Value ToValue( const APSInt& i );
        static optional< APSInt > FromValue( const Value& v );
    };


    template<> struct Bridge< uint8_t >
    {
        static const Term& Type();
        static Value ToValue( uint8_t x );
        static optional< uint8_t > FromValue( const Value& v );
    };


    template<> struct Bridge< uint32_t >
    {
        static const Term& Type();
        static Value ToValue( uint32_t x );
        static optional< uint32_t > FromValue( const Value& v );
    };


    template<> struct Bridge< int32_t >
    {
        static const Term& Type();
        static Value ToValue( int32_t x );
        static optional< int32_t > FromValue( const Value& v );
    };


    template<> struct Bridge< uint64_t >
    {
        static const Term& Type();
        static Value ToValue( uint64_t x );
        static optional< uint64_t > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/runtime/init.cpp.
10
11
12
13
14
15
16
17

18
19
20
21
22
23
24
25
26
    {
        using IntegerMutRefType =
            CustomPattern< Value, ReferenceType::PatternMutableOf< IntegerType::Pattern > >;

        using IntegerType = CustomPattern< IntegerType, IntegerType::Pattern >;

        // Initialization for integer vars
        RegisterBuiltinFunc< Intrinsic< Value ( IntegerMutRefType, IntegerType ) > >( e, e.extInitialize(),

            []( auto&& c, const Value& r, const Value& initVal )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(),
                    initVal, r, Store( refType.type(), initVal.locationId(), r.locationId() ) );
            } );
    }
}







|
>




|
|


|
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    {
        using IntegerMutRefType =
            CustomPattern< Value, ReferenceType::PatternMutableOf< IntegerType::Pattern > >;

        using IntegerType = CustomPattern< IntegerType, IntegerType::Pattern >;

        // Initialization for integer vars
        RegisterBuiltinFunc< Intrinsic< Value( IntegerMutRefType, IntegerType ) > >( e,
            e.extInitialize(),
            []( auto&& c, const Value& r, const Value& initVal )
            {
                G_VAL_ASSERT( r, !r.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
                return BuildComputedValue( GetValueType< void >(), initVal, r,
                    Store( refType.type(), initVal.locationId(), r.locationId() ) );
            } );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/runtime/pointer.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
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::builtins;

namespace goose::builtins
{
    void SetupRuntimePointerType( Env& e )
    {
        // null pointer literal
        e.storeValue( AppendToVectorTerm( RootG0Identity(), TSID( nullptr ) ), ANYTERM( _ ), ValueToEIR( ToValue( NullPointer() ) ) );


        RegisterBuiltinFunc< Eager< Value > ( Value ) >( e, "pointer"_sid,
            []( const Value& pointedType )
            {
                if( !GetCodegenType( pointedType ) )
                {
                    // TODO come up with some lightweight builtin option type
                    // for the builtin apis, because this is a very bullshit
                    // way to handle errors
                    return ToValue( "error"s );
                }

                return ToValue( PointerType( ValueToEIR( pointedType ) ) );
            } );
    }

    const codegen::Type* GetCodegenType( const PointerType& p )
    {
        // TODO_SSA reenable
        //return codegen::PointerType::Get( GetCodegenType( *EIRToValue( p.m_pointedType ) ) );
        return nullptr;
    }
}

namespace goose::eir
{
    Value Bridge< PointerType >::ToValue( const PointerType& p )
    {
        return Value( Type(), MkStdRTType( TSID( rt_type ),
            GetCodegenType( p ), TSID( pointer ),
            p.m_pointedType ) );
    }

    optional< PointerType > Bridge< PointerType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),
            Vec(
                Lit( "rt_type"_sid ),
                SubTerm(),
                Val< void* >(),
                Lit( "pointer"_sid ),
                SubTerm()
            )
        );

        if( !result )
            return nullopt;

        auto&& [predicates, cgType, pointedType] = *result;
        return PointerType( pointedType );
    }

    const Value& Bridge< NullPointerType >::ToValue( const NullPointerType& np )
    {
        static auto val = Value( Type(), TSID( nullptr ) );
        return val;
    }

    optional< NullPointerType > Bridge< NullPointerType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),
            Lit( "nullptr"_sid )
        );

        if( !result )
            return nullopt;

        return NullPointerType();
    }











|
>

|

















|


|





|
|
<





<
|
|
<
<
<
<
<
















|
<
<







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

using namespace goose;
using namespace goose::builtins;

namespace goose::builtins
{
    void SetupRuntimePointerType( Env& e )
    {
        // null pointer literal
        e.storeValue( AppendToVectorTerm( RootG0Identity(), TSID( nullptr ) ), ANYTERM( _ ),
            ValueToEIR( ToValue( NullPointer() ) ) );

        RegisterBuiltinFunc< Eager< Value >( Value ) >( e, "pointer"_sid,
            []( const Value& pointedType )
            {
                if( !GetCodegenType( pointedType ) )
                {
                    // TODO come up with some lightweight builtin option type
                    // for the builtin apis, because this is a very bullshit
                    // way to handle errors
                    return ToValue( "error"s );
                }

                return ToValue( PointerType( ValueToEIR( pointedType ) ) );
            } );
    }

    const codegen::Type* GetCodegenType( const PointerType& p )
    {
        // TODO_SSA reenable
        // return codegen::PointerType::Get( GetCodegenType( *EIRToValue( p.m_pointedType ) ) );
        return nullptr;
    }
} // namespace goose::builtins

namespace goose::eir
{
    Value Bridge< PointerType >::ToValue( const PointerType& p )
    {
        return Value( Type(),
            MkStdRTType( TSID( rt_type ), GetCodegenType( p ), TSID( pointer ), p.m_pointedType ) );

    }

    optional< PointerType > Bridge< PointerType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),

            Vec( Lit( "rt_type"_sid ), SubTerm(), Val< void* >(), Lit( "pointer"_sid ),
                SubTerm() ) );






        if( !result )
            return nullopt;

        auto&& [predicates, cgType, pointedType] = *result;
        return PointerType( pointedType );
    }

    const Value& Bridge< NullPointerType >::ToValue( const NullPointerType& np )
    {
        static auto val = Value( Type(), TSID( nullptr ) );
        return val;
    }

    optional< NullPointerType > Bridge< NullPointerType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(), Lit( "nullptr"_sid ) );



        if( !result )
            return nullopt;

        return NullPointerType();
    }

92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
    }

    optional< NullPointer > Bridge< NullPointer >::FromValue( const Value& v )
    {
        if( !FromValue< NullPointerType >( *EIRToValue( v.type() ) ) )
            return nullopt;

        auto result = Decompose( v.val(),
            Lit( "nullptr"_sid )
        );

        if( !result )
            return nullopt;

        return NullPointer();
    }
}







|
<
<






|
84
85
86
87
88
89
90
91


92
93
94
95
96
97
98
    }

    optional< NullPointer > Bridge< NullPointer >::FromValue( const Value& v )
    {
        if( !FromValue< NullPointerType >( *EIRToValue( v.type() ) ) )
            return nullopt;

        auto result = Decompose( v.val(), Lit( "nullptr"_sid ) );



        if( !result )
            return nullopt;

        return NullPointer();
    }
} // namespace goose::eir
Changes to bs/builtins/types/runtime/pointer.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
#ifndef GOOSE_BUILTINS_TYPES_RUNTIME_POINTER_H
#define GOOSE_BUILTINS_TYPES_RUNTIME_POINTER_H

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

    struct PointerType
    {
        template< typename T >
        PointerType( T&& pointedType ) :
            m_pointedType( forward< T >( pointedType ) )
        {}



        Term m_pointedType;
    };

    struct NullPointerType

    {};

    struct NullPointer

    {};

    extern const codegen::Type* GetCodegenType( const PointerType& p );
}


namespace goose::eir
{
    template<>
    struct Bridge< builtins::PointerType >
    {
        static const Term& Type() { return TypeType(); }

        static Value ToValue( const builtins::PointerType& p );
        static optional< builtins::PointerType > FromValue( const Value& v );
    };

    template<>
    struct Bridge< builtins::NullPointerType >
    {
        static const Term& Type() { return TypeType(); }

        static const Value& ToValue( const builtins::NullPointerType& np );
        static optional< builtins::NullPointerType > FromValue( const Value& v );
    };

    template<>
    struct Bridge< builtins::NullPointer >
    {
        static const Term& Type();
        static const Value& ToValue( const builtins::NullPointer& np );
        static optional< builtins::NullPointer > FromValue( const Value& v );
    };
}

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

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

    struct PointerType
    {
        template< typename T >
        PointerType( T&& pointedType ) :
            m_pointedType( forward< T >( pointedType ) )

        {
        }

        Term m_pointedType;
    };

    struct NullPointerType
    {
    };

    struct NullPointer
    {
    };

    extern const codegen::Type* GetCodegenType( const PointerType& p );

} // namespace goose::builtins

namespace goose::eir
{

    template<> struct Bridge< builtins::PointerType >
    {
        static const Term& Type() { return TypeType(); }

        static Value ToValue( const builtins::PointerType& p );
        static optional< builtins::PointerType > FromValue( const Value& v );
    };


    template<> struct Bridge< builtins::NullPointerType >
    {
        static const Term& Type() { return TypeType(); }

        static const Value& ToValue( const builtins::NullPointerType& np );
        static optional< builtins::NullPointerType > FromValue( const Value& v );
    };


    template<> struct Bridge< builtins::NullPointer >
    {
        static const Term& Type();
        static const Value& ToValue( const builtins::NullPointer& np );
        static optional< builtins::NullPointer > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/runtime/runtime.cpp.
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
    const codegen::Type* GetCodegenType( const Value& t )
    {
        if( !t.isType() )
            return nullptr;

        if( t.val() == TSID( void ) )
            // TODO_SSA reenable
            //return codegen::Type::Get( llvm::Type::getVoidTy( GetLLVMContext() ) );
            return nullptr;

        if( IsTupleType( t ) )
            return GetTupleCodegenType( t );

        if( IsFuncType( t ) )
            return GetFuncCodegenType( *FromValue< FuncType >( t ) );

        auto vec = Decompose( t.val(),
            Val< pvec >()
        );

        if( vec )
        {
            if( vec->get()->terms().size() < 3 )
                return nullptr;

            auto lt = Decompose( vec->get()->terms()[2],
                Val< void* >()
            );

            if( !lt )
                return nullptr;

            if( lt->get() )
                return static_cast< codegen::Type* >( lt->get() );

            const auto& id = get< StringId >( vec->get()->terms()[3] );

            if( id == "bool"_sid )
                // TODO_SSA reenable
                //return codegen::Type::Get( llvm::Type::getInt1Ty( GetLLVMContext() ) );
                return nullptr;

            if( id == "integer"_sid )
            {
                if( auto it = FromValue< IntegerType >( t ) )
                    return GetCodegenType( *it );
            }







|








|
<
<






|
<
<











|







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
    const codegen::Type* GetCodegenType( const Value& t )
    {
        if( !t.isType() )
            return nullptr;

        if( t.val() == TSID( void ) )
            // TODO_SSA reenable
            // return codegen::Type::Get( llvm::Type::getVoidTy( GetLLVMContext() ) );
            return nullptr;

        if( IsTupleType( t ) )
            return GetTupleCodegenType( t );

        if( IsFuncType( t ) )
            return GetFuncCodegenType( *FromValue< FuncType >( t ) );

        auto vec = Decompose( t.val(), Val< pvec >() );



        if( vec )
        {
            if( vec->get()->terms().size() < 3 )
                return nullptr;

            auto lt = Decompose( vec->get()->terms()[2], Val< void* >() );



            if( !lt )
                return nullptr;

            if( lt->get() )
                return static_cast< codegen::Type* >( lt->get() );

            const auto& id = get< StringId >( vec->get()->terms()[3] );

            if( id == "bool"_sid )
                // TODO_SSA reenable
                // return codegen::Type::Get( llvm::Type::getInt1Ty( GetLLVMContext() ) );
                return nullptr;

            if( id == "integer"_sid )
            {
                if( auto it = FromValue< IntegerType >( t ) )
                    return GetCodegenType( *it );
            }
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
                if( auto at = FromValue< ArrayType >( t ) )
                    return GetCodegenType( *at );
            }
        }

        // If it is not a run time data type, it may still be a runtime function type.
        auto result = Decompose( t.val(),
            Vec(
                Lit( "func"_sid ),
                Val< void* >(),
                SubTerm(),  // return type
                SubTerm()  // param types
            )
        );

        if( !result )
            return nullptr;

        auto&& [cgType, rtype, paramTypes] = *result;
        return static_cast< codegen::Type* >( cgType );
    }
}







<
|
<
|
|
|
<







|
74
75
76
77
78
79
80

81

82
83
84

85
86
87
88
89
90
91
92
                if( auto at = FromValue< ArrayType >( t ) )
                    return GetCodegenType( *at );
            }
        }

        // If it is not a run time data type, it may still be a runtime function type.
        auto result = Decompose( t.val(),

            Vec( Lit( "func"_sid ), Val< void* >(),

                SubTerm(), // return type
                SubTerm() // param types
                ) );


        if( !result )
            return nullptr;

        auto&& [cgType, rtype, paramTypes] = *result;
        return static_cast< codegen::Type* >( cgType );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/runtime/runtime.h.
16
17
18
19
20
21
22
23
24
25
        SetupRuntimePointerType( e );
        SetupRuntimeArrayType( e );
        SetupRuntimeTypesChecking( e );
    }

    extern bool IsRuntimeType( const Value& t );
    extern const codegen::Type* GetCodegenType( const Value& t );
}

#endif







|


16
17
18
19
20
21
22
23
24
25
        SetupRuntimePointerType( e );
        SetupRuntimeArrayType( e );
        SetupRuntimeTypesChecking( e );
    }

    extern bool IsRuntimeType( const Value& t );
    extern const codegen::Type* GetCodegenType( const Value& t );
} // namespace goose::builtins

#endif
Changes to bs/builtins/types/runtime/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

#include "builtins/builtins.h"

using namespace goose;
using namespace goose::eir;

namespace goose::builtins
{
    // TODO: converting a computed ct_int to a rt int is legit in compile time code.
    // Consider this:
    // var someTuple = (123,456)
    // void someCompileTimeFunc( ( uint(32), uint(32) tup )
    // Can't perform a compile time to someCompileTimeFunc with someTuple as the later contains computed ct_ints


    // So we need to allow this conversion, but only at compile time. Perhaps emit the conversion as a call to a compile time conversion

    // func, which therefore would fail when compiling because the ct_int wouldn't be able to be lowered to a runtime type?
    // (we'd also need to emit asserton checks for the ct_int bounds)

    // Probably something to do in the prelude.

    void SetupRuntimeTypesChecking( Env& e )
    {
        auto rtIntTypePattern = Value( TypeType(), MkStdType( TSID( rt_type ), TSID( integer ),
            VEC( ANYTERM( _ ), ANYTERM( _ ) ) ) );

        // Conversion rule for ct_int to runtime int:
        // we verify that the ct_int fits in the bitsize/signage
        // of the runtime int
        e.typeCheckingRuleSet()->addTypeCheckingRule(

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

            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                GetValueType< BigInt >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, 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 = *EIRToValue( rhs );
            if( !rhsVal.isConstant() )
                co_return;

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

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

            auto wrapped = WrapWithPostprocFunc( *s,
            [rhs]( const Term& t, TypeCheckingContext tcc ) -> optional< Term >
            {
                auto argVal = EIRToValue( rhs );
                assert( argVal );

                auto ct = *FromValue< BigInt >( *argVal );

                auto rttypeVal = EIRToValue( t );
                assert( rttypeVal );

                auto rttype = FromValue< IntegerType >( *rttypeVal );
                assert( rttype );

                APSInt valToLoad;

                if( rttype->m_signed )
                {
                    if( ct.getSignificantBits() > rttype->m_numBits )
                        return nullopt;

                    valToLoad = ct.sext( rttype->m_numBits );
                    valToLoad.setIsSigned( true );
                }
                else
                {
                    if( ct.isNegative() )
                        return nullopt;

                    if( ct.getActiveBits() > rttype->m_numBits )
                        return nullopt;

                    valToLoad = ct.zext( rttype->m_numBits );
                    valToLoad.setIsSigned( false );
                }

                return TERM( valToLoad );
            } );

            co_yield { ValueToEIR( Value( *s, move( wrapped ) )
                .setLocationId( rhsVal.locationId() ) ), tcc };

        } );

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

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

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

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

        // nullptr constant type checking 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(

            ParamPat( ValueToEIR( ptrTypePattern ) ),

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

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












|
>

|
>
|
|





|
|






|
<
|
<

|
<
<
<

|
|
|
|
|
|
|
|
|
|

|
|
|

|
|
|

|
|
|
|
|

|

|
|

|
|

|

|
|
|
|

|
|
|
|
|
|
|

|
|

|
|
|

|
|

|
|
>
|

|
|

|
|



|
<

|
|
|
|
|

|
|








|
<
<
<

|
|
|
|
|

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

using namespace goose;
using namespace goose::eir;

namespace goose::builtins
{
    // TODO: converting a computed ct_int to a rt int is legit in compile time code.
    // Consider this:
    // var someTuple = (123,456)
    // void someCompileTimeFunc( ( uint(32), uint(32) tup )
    // Can't perform a compile time to someCompileTimeFunc with someTuple as the later contains
    // computed ct_ints

    // So we need to allow this conversion, but only at compile time. Perhaps emit the conversion as
    // a call to a compile time conversion func, which therefore would fail when compiling because
    // the ct_int wouldn't be able to be lowered to a runtime type? (we'd also need to emit asserton
    // checks for the ct_int bounds)

    // Probably something to do in the prelude.

    void SetupRuntimeTypesChecking( Env& e )
    {
        auto rtIntTypePattern = Value( TypeType(),
            MkStdType( TSID( rt_type ), TSID( integer ), VEC( ANYTERM( _ ), ANYTERM( _ ) ) ) );

        // Conversion rule for ct_int to runtime int:
        // we verify that the ct_int fits in the bitsize/signage
        // of the runtime int
        e.typeCheckingRuleSet()->addTypeCheckingRule(

            ValueToEIR(

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


            ValueToEIR( ValuePattern( ANYTERM( _ ), GetValueType< BigInt >(), ANYTERM( _ ) ) ),




            []( const Term& lhs, const Term& rhs, 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 = *EIRToValue( rhs );
                if( !rhsVal.isConstant() )
                    co_return;

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

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

                auto wrapped = WrapWithPostprocFunc( *s,
                    [rhs]( const Term& t, TypeCheckingContext tcc ) -> optional< Term >
                    {
                        auto argVal = EIRToValue( rhs );
                        assert( argVal );

                        auto ct = *FromValue< BigInt >( *argVal );

                        auto rttypeVal = EIRToValue( t );
                        assert( rttypeVal );

                        auto rttype = FromValue< IntegerType >( *rttypeVal );
                        assert( rttype );

                        APSInt valToLoad;

                        if( rttype->m_signed )
                        {
                            if( ct.getSignificantBits() > rttype->m_numBits )
                                return nullopt;

                            valToLoad = ct.sext( rttype->m_numBits );
                            valToLoad.setIsSigned( true );
                        }
                        else
                        {
                            if( ct.isNegative() )
                                return nullopt;

                            if( ct.getActiveBits() > rttype->m_numBits )
                                return nullopt;

                            valToLoad = ct.zext( rttype->m_numBits );
                            valToLoad.setIsSigned( false );
                        }

                        return TERM( valToLoad );
                    } );

                co_yield { ValueToEIR(
                               Value( *s, move( wrapped ) ).setLocationId( rhsVal.locationId() ) ),
                    tcc };
            } );

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

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

        // ct_string type against a char*:
        // return the char* type.
        e.typeCheckingRuleSet()->addTypeCheckingRule( ValueToEIR( rtInt8PtrTypePattern ),

            GetValueType< string >(),
            []( const Term& lhs, const Term& rhs, TypeCheckingContext c ) -> TCGen
            {
                if( auto s = HalfUnify( lhs, c ) )
                    co_yield { *s, c };
            } );

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

        // nullptr constant type checking 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(

            ParamPat( ValueToEIR( ptrTypePattern ) ),

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




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

} // namespace goose::builtins
Changes to bs/builtins/types/struct/builder.cpp.
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
        underlyingType = AppendToTupleType( underlyingType, mv.type );

        auto tVal = *EIRToValue( mv.type );

        auto loweredMemberType = LowerType( c, tVal );
        if( !loweredMemberType )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( tVal.locationId(), "can't lower this type." );

            return false;
        }

        loweredType = AppendToTupleType( loweredType, *loweredMemberType );
    }

    c.env()->storeValue( AppendToVectorTerm( identity(), TERM( 0U ), TSID( underlying_type ) ), ANYTERM( _ ),
        ValueToEIR( underlyingType ) );

    c.env()->storeValue( AppendToVectorTerm( identity(), TERM( 0U ), TSID( lowered_type ) ), ANYTERM( _ ),
        ValueToEIR( loweredType ) );

    unordered_map< StringId, LocationId > memberNames;

    // Store the index matching each struct member, to be able to remap member names
    // to tuple indices in operator_dot
    uint32_t index = 0;
    for( const auto& mv : m_memberVars )
    {
        if( auto it = memberNames.find( mv.name ); it != memberNames.end() )
        {
            DiagnosticsContext dc( it->second, "Previous declaration." );
            DiagnosticsManager::GetInstance().emitErrorMessage( mv.loc, "a member with this name was already defined for this struct." );

            return false;
        }

        memberNames.emplace( mv.name, mv.loc );

        c.env()->storeValue( AppendToVectorTerm( identity(), TERM( mv.name ) ), ANYTERM( _ ),
            ValueToEIR( ToValue( index++ ) ) );
    }

    auto pCtorEntryBB = m_defaultCtorCFG->entryBB();

    // Complete the ctor's CFG with a RetVoid
    m_defaultCtorCFG->currentBB()->setTerminator( RetVoid( {} ) );

    ReindexVars( m_defaultCtorCFG );

    // Create the default ctor function
    ReferenceType refType( ValueToEIR( st ), MutAccessSpecifier() );

    auto params = MakeClosedTuple( ToValue( Decl( ValueToEIR( ToValue( refType ) ), "instance"_sid ) ) );
    auto ctorFunc = BuildFunc( c, AppendToVectorTerm( identity(), TERM( 0U ), TSID( ctor ) ),
        *EIRToValue( GetValueType< void >() ), ToValue( params ), m_defaultCtorCFG );

    // BuildFunc can fail if a type can't be lowered.
    if( !ctorFunc )
        return false;

    // Add it as an overload of _Initialize

    c.env()->extInitialize()->add( *c.env(), ToValue( *ctorFunc ).setLocationId( st.locationId() ) );
    return true;
}







|
>






|
|

|
|











|
>


















>
|








>
|


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
        underlyingType = AppendToTupleType( underlyingType, mv.type );

        auto tVal = *EIRToValue( mv.type );

        auto loweredMemberType = LowerType( c, tVal );
        if( !loweredMemberType )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage(
                tVal.locationId(), "can't lower this type." );
            return false;
        }

        loweredType = AppendToTupleType( loweredType, *loweredMemberType );
    }

    c.env()->storeValue( AppendToVectorTerm( identity(), TERM( 0U ), TSID( underlying_type ) ),
        ANYTERM( _ ), ValueToEIR( underlyingType ) );

    c.env()->storeValue( AppendToVectorTerm( identity(), TERM( 0U ), TSID( lowered_type ) ),
        ANYTERM( _ ), ValueToEIR( loweredType ) );

    unordered_map< StringId, LocationId > memberNames;

    // Store the index matching each struct member, to be able to remap member names
    // to tuple indices in operator_dot
    uint32_t index = 0;
    for( const auto& mv : m_memberVars )
    {
        if( auto it = memberNames.find( mv.name ); it != memberNames.end() )
        {
            DiagnosticsContext dc( it->second, "Previous declaration." );
            DiagnosticsManager::GetInstance().emitErrorMessage(
                mv.loc, "a member with this name was already defined for this struct." );
            return false;
        }

        memberNames.emplace( mv.name, mv.loc );

        c.env()->storeValue( AppendToVectorTerm( identity(), TERM( mv.name ) ), ANYTERM( _ ),
            ValueToEIR( ToValue( index++ ) ) );
    }

    auto pCtorEntryBB = m_defaultCtorCFG->entryBB();

    // Complete the ctor's CFG with a RetVoid
    m_defaultCtorCFG->currentBB()->setTerminator( RetVoid( {} ) );

    ReindexVars( m_defaultCtorCFG );

    // Create the default ctor function
    ReferenceType refType( ValueToEIR( st ), MutAccessSpecifier() );
    auto params =
        MakeClosedTuple( ToValue( Decl( ValueToEIR( ToValue( refType ) ), "instance"_sid ) ) );
    auto ctorFunc = BuildFunc( c, AppendToVectorTerm( identity(), TERM( 0U ), TSID( ctor ) ),
        *EIRToValue( GetValueType< void >() ), ToValue( params ), m_defaultCtorCFG );

    // BuildFunc can fail if a type can't be lowered.
    if( !ctorFunc )
        return false;

    // Add it as an overload of _Initialize
    c.env()->extInitialize()->add(
        *c.env(), ToValue( *ctorFunc ).setLocationId( st.locationId() ) );
    return true;
}
Changes to bs/builtins/types/struct/builder.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
#ifndef GOOSE_BUILTINS_TYPES_STRUCT_BUILDER_H
#define GOOSE_BUILTINS_TYPES_STRUCT_BUILDER_H

namespace goose::builtins
{
    class StructBuilder
    {
        public:
            template< typename I >
            StructBuilder( I&& identity ) :
                m_identity( forward< I >( identity ) )
            {
                m_defaultCtorCFG->createBB();
                m_defaultCtorCFG->setCurrentBB( m_defaultCtorCFG->entryBB() );
            }

            const auto& identity() const { return m_identity; }

            const auto& defaultCtorCFG() const { return m_defaultCtorCFG; }

            template< typename T >
            void declareMemberVar( const Context& c, StringId name, T&& type, LocationId loc );

            template< typename T, typename V >

            void declareMemberVar( const Context& c, StringId name, T&& type, LocationId loc, V&& init );

            bool finalize( const Context& c, const Value& st ) const;

        private:
            Term m_identity;
            ptr< cir::CFG > m_defaultCtorCFG = make_shared< cir::CFG >( 0 );

            struct MemberVar
            {
                optional< Value > init;
                Term type;
                StringId name;
                LocationId loc;
            };

            llvm::SmallVector< MemberVar, 8 > m_memberVars;
    };

}

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

namespace goose::builtins
{
    class StructBuilder
    {
      public:
        template< typename I >
        StructBuilder( I&& identity ) :
            m_identity( forward< I >( identity ) )
        {
            m_defaultCtorCFG->createBB();
            m_defaultCtorCFG->setCurrentBB( m_defaultCtorCFG->entryBB() );
        }

        const auto& identity() const { return m_identity; }

        const auto& defaultCtorCFG() const { return m_defaultCtorCFG; }

        template< typename T >
        void declareMemberVar( const Context& c, StringId name, T&& type, LocationId loc );

        template< typename T, typename V >
        void declareMemberVar(
            const Context& c, StringId name, T&& type, LocationId loc, V&& init );

        bool finalize( const Context& c, const Value& st ) const;

      private:
        Term m_identity;
        ptr< cir::CFG > m_defaultCtorCFG = make_shared< cir::CFG >( 0 );

        struct MemberVar
        {
            optional< Value > init;
            Term type;
            StringId name;
            LocationId loc;
        };

        llvm::SmallVector< MemberVar, 8 > m_memberVars;
    };

} // namespace goose::builtins

#endif
Changes to bs/builtins/types/struct/builder.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
#ifndef GOOSE_BUILTINS_TYPES_STRUCT_BUILDER_INL
#define GOOSE_BUILTINS_TYPES_STRUCT_BUILDER_INL

namespace goose::builtins
{
    template< typename T >
    void StructBuilder::declareMemberVar( const Context& c, StringId name, T&& type, LocationId loc )

    {
        auto rt = ValueToEIR( ToValue( ReferenceType{ type, MutAccessSpecifier() } ) );

        auto ref = BuildComputedValue( move( rt ),
            cir::DataPathOf( 0, {} ),
            cir::Select( m_memberVars.size(), {} )
        ).setLocationId( loc );


        c.env()->storeValue( AppendToVectorTerm( identity(), TERM( name ) ), identity(), ValueToEIR( ref ) );

        auto initResult = InvokeOverloadSet( c,
            c.env()->extInitialize(),
            MakeClosedTuple( ref ), loc );

        if( !initResult.isPoison() )
        {
            DiagnosticsContext dc2( initResult.locationId(), "When invoking _DropValue." );
            InvokeOverloadSet( c, c.env()->extDropValue(),
                MakeClosedTuple( c.builder(), move( initResult ) ), initResult.locationId() );
        }

        m_memberVars.emplace_back( MemberVar{ nullopt, forward< T >( type ), name, loc } );
    }

    template< typename T, typename V >

    void StructBuilder::declareMemberVar( const Context& c, StringId name, T&& type, LocationId loc, V&& init )
    {
        auto rt = ValueToEIR( ToValue( ReferenceType{ type, MutAccessSpecifier() } ) );

        auto ref = BuildComputedValue( move( rt ),
            cir::DataPathOf( 0, {} ),
            cir::Select( m_memberVars.size(), {} )
        ).setLocationId( loc );


        c.env()->storeValue( AppendToVectorTerm( identity(), TERM( name ) ), identity(), ValueToEIR( ref ) );

        auto initResult = InvokeOverloadSet( c,
            c.env()->extInitialize(),
            MakeClosedTuple( ref, init ), loc );

        if( !initResult.isPoison() )
        {
            DiagnosticsContext dc2( initResult.locationId(), "When invoking _DropValue." );
            InvokeOverloadSet( c, c.env()->extDropValue(),
                MakeClosedTuple( c.builder(), move( initResult ) ), initResult.locationId() );
        }


        m_memberVars.emplace_back( MemberVar{ forward< V >( init ), forward< T >( type ), name, loc } );
    }
}


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

namespace goose::builtins
{
    template< typename T >
    void StructBuilder::declareMemberVar(
        const Context& c, StringId name, T&& type, LocationId loc )
    {
        auto rt = ValueToEIR( ToValue( ReferenceType{ type, MutAccessSpecifier() } ) );

        auto ref = BuildComputedValue(

            move( rt ), cir::DataPathOf( 0, {} ), cir::Select( m_memberVars.size(), {} ) )
                       .setLocationId( loc );

        c.env()->storeValue(
            AppendToVectorTerm( identity(), TERM( name ) ), identity(), ValueToEIR( ref ) );

        auto initResult =

            InvokeOverloadSet( c, c.env()->extInitialize(), MakeClosedTuple( ref ), loc );

        if( !initResult.isPoison() )
        {
            DiagnosticsContext dc2( initResult.locationId(), "When invoking _DropValue." );
            InvokeOverloadSet( c, c.env()->extDropValue(),
                MakeClosedTuple( c.builder(), move( initResult ) ), initResult.locationId() );
        }

        m_memberVars.emplace_back( MemberVar{ nullopt, forward< T >( type ), name, loc } );
    }

    template< typename T, typename V >
    void StructBuilder::declareMemberVar(
        const Context& c, StringId name, T&& type, LocationId loc, V&& init )
    {
        auto rt = ValueToEIR( ToValue( ReferenceType{ type, MutAccessSpecifier() } ) );

        auto ref = BuildComputedValue(

            move( rt ), cir::DataPathOf( 0, {} ), cir::Select( m_memberVars.size(), {} ) )
                       .setLocationId( loc );

        c.env()->storeValue(
            AppendToVectorTerm( identity(), TERM( name ) ), identity(), ValueToEIR( ref ) );

        auto initResult =

            InvokeOverloadSet( c, c.env()->extInitialize(), MakeClosedTuple( ref, init ), loc );

        if( !initResult.isPoison() )
        {
            DiagnosticsContext dc2( initResult.locationId(), "When invoking _DropValue." );
            InvokeOverloadSet( c, c.env()->extDropValue(),
                MakeClosedTuple( c.builder(), move( initResult ) ), initResult.locationId() );
        }

        m_memberVars.emplace_back(
            MemberVar{ forward< V >( init ), forward< T >( type ), name, loc } );
    }

} // namespace goose::builtins

#endif
Changes to bs/builtins/types/struct/init.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

#include "builtins/builtins.h"
#include "parse/parse.h"

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

namespace goose::builtins
{
    void SetupStructInitialize( Env& e )
    {
        using StructMutRefType =
            CustomPattern< Value, ReferenceType::PatternMutableOf< StructType::Pattern > >;

        RegisterBuiltinFunc< Intrinsic< Value ( StructMutRefType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& stRef )
            {
                auto refType = *FromValue< ReferenceType >( *EIRToValue( stRef.type() ) );
                auto st = *EIRToValue( refType.type() );
                auto structType = *FromValue< StructType >( st );

                if( structType.wasParsed() )
                    G_VAL_ERROR( stRef, "can't resolve _Initialize() for this struct" );

                if( !structType.parse( c.env() ) )
                    return PoisonValue();

                return InvokeOverloadSet( c, c.env()->extInitialize(),
                    MakeClosedTuple( stRef ) );
            } );
    }
}














|












|
<


<
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

28
29

30
#include "builtins/builtins.h"
#include "parse/parse.h"

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

namespace goose::builtins
{
    void SetupStructInitialize( Env& e )
    {
        using StructMutRefType =
            CustomPattern< Value, ReferenceType::PatternMutableOf< StructType::Pattern > >;

        RegisterBuiltinFunc< Intrinsic< Value( StructMutRefType ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& stRef )
            {
                auto refType = *FromValue< ReferenceType >( *EIRToValue( stRef.type() ) );
                auto st = *EIRToValue( refType.type() );
                auto structType = *FromValue< StructType >( st );

                if( structType.wasParsed() )
                    G_VAL_ERROR( stRef, "can't resolve _Initialize() for this struct" );

                if( !structType.parse( c.env() ) )
                    return PoisonValue();

                return InvokeOverloadSet( c, c.env()->extInitialize(), MakeClosedTuple( stRef ) );

            } );
    }

} // namespace goose::builtins
Changes to bs/builtins/types/struct/lower.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

#include "builtins/builtins.h"

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

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

        RegisterBuiltinFunc< Intrinsic< Value ( TypePatternParam< StructType, StructType::Pattern > ) > >( e, e.extGetUnderlyingType(),

            []( const Context& c, const Value& structType )
        {
            auto st = *FromValue< StructType >( structType );

            if( !st.parse( c.env() ) )
                return PoisonValue();


            auto tupType = c.env()->retrieveValue( TSID( underlying_type ), AppendToVectorTerm( st.identity(), TERM( 0U ) ) );
            G_VAL_ASSERT( structType, tupType );

            return *EIRToValue( *tupType );
        } );


        RegisterBuiltinFunc< Intrinsic< Value ( TypePatternParam< StructType, StructType::Pattern > ) > >( e, e.extLowerType(),

            []( const Context& c, const Value& structType )
        {
            auto st = *FromValue< StructType >( structType );

            if( !st.parse( c.env() ) )
                return PoisonValue();


            auto tupType = c.env()->retrieveValue( TSID( lowered_type ), AppendToVectorTerm( st.identity(), TERM( 0U ) ) );
            G_VAL_ASSERT( structType, tupType );

            return *EIRToValue( *tupType );
        } );


        RegisterBuiltinFunc< Intrinsic< Value ( CustomPattern< StructType, StructType::Pattern > ) > >( e, e.extLowerValue(),

            []( const Context& c, const Value& structVal )
        {
            G_VAL_ERROR( structVal, "LowerValue for structs NYI" );
            return PoisonValue();
        } );
    }
}











>
|
>

|
|

|
|

>
|
|

|
|

>
|
>

|
|

|
|

>
|
|

|
|

>
|
>

|
|
|
|

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

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

namespace goose::builtins
{
    void SetupStructLowering( Env& e )
    {
        RegisterBuiltinFunc<
            Intrinsic< Value( TypePatternParam< StructType, StructType::Pattern > ) > >( e,
            e.extGetUnderlyingType(),
            []( const Context& c, const Value& structType )
            {
                auto st = *FromValue< StructType >( structType );

                if( !st.parse( c.env() ) )
                    return PoisonValue();

                auto tupType = c.env()->retrieveValue(
                    TSID( underlying_type ), AppendToVectorTerm( st.identity(), TERM( 0U ) ) );
                G_VAL_ASSERT( structType, tupType );

                return *EIRToValue( *tupType );
            } );

        RegisterBuiltinFunc<
            Intrinsic< Value( TypePatternParam< StructType, StructType::Pattern > ) > >( e,
            e.extLowerType(),
            []( const Context& c, const Value& structType )
            {
                auto st = *FromValue< StructType >( structType );

                if( !st.parse( c.env() ) )
                    return PoisonValue();

                auto tupType = c.env()->retrieveValue(
                    TSID( lowered_type ), AppendToVectorTerm( st.identity(), TERM( 0U ) ) );
                G_VAL_ASSERT( structType, tupType );

                return *EIRToValue( *tupType );
            } );

        RegisterBuiltinFunc<
            Intrinsic< Value( CustomPattern< StructType, StructType::Pattern > ) > >( e,
            e.extLowerValue(),
            []( const Context& c, const Value& structVal )
            {
                G_VAL_ERROR( structVal, "LowerValue for structs NYI" );
                return PoisonValue();
            } );
    }

} // namespace goose::builtins
Changes to bs/builtins/types/struct/parse.cpp.
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
        auto handleStruct = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();

            auto nameTok = p.resolver()->consume();
            if( !nameTok )
            {
                dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(),
                    "expected an identifier.", 0 );
                return false;
            }

            const auto& [name,nameLoc] = *nameTok;
            if( !holds_alternative< StringId >( name ) )
            {
                dm.emitSyntaxErrorMessage( nameLoc,
                    "expected an identifier.", 0 );
                return false;
            }

            auto next = p.resolver()->lookAheadUnresolved();
            if( !next )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(







|
|



|


|
<







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

30
31
32
33
34
35
36
        auto handleStruct = []( Parser& p, LocationId locationId, uint32_t prec )
        {
            auto& dm = DiagnosticsManager::GetInstance();

            auto nameTok = p.resolver()->consume();
            if( !nameTok )
            {
                dm.emitSyntaxErrorMessage(
                    p.resolver()->currentLocation(), "expected an identifier.", 0 );
                return false;
            }

            const auto& [name, nameLoc] = *nameTok;
            if( !holds_alternative< StringId >( name ) )
            {
                dm.emitSyntaxErrorMessage( nameLoc, "expected an identifier.", 0 );

                return false;
            }

            auto next = p.resolver()->lookAheadUnresolved();
            if( !next )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
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
            }

            StructType::Unparsed::value_type toks;

            auto g = p.resolver()->consumeUnit();
            move( g.begin(), g.end(), back_inserter( toks ) );

            // Take the current identity, but remove "0_locvars" from the end: the struct def should live in the "constant compile time"
            // visibility hierarchy, and also only see that.

            auto parentIdentity = TakeVectorTerm( p.context().identity(), VecSize( p.context().identity() ) - 1 );
            auto structIdentity = TakeVectorTerm( parentIdentity, VecSize( parentIdentity ) - 1 );
            structIdentity = AppendToVectorTerm( structIdentity, name );
            p.context().env()->addVisibilityRule( parentIdentity, structIdentity );

            auto structLoc = Location::CreateSpanningLocation( locationId, nameLoc );
            auto st = ValueToEIR( ToValue( StructType( structIdentity, structLoc,
                make_shared< StructType::Unparsed >( move( toks ) ) ) ) );

            // Like functions, structs are parsed lazily to make out of order and circular references
            // possible. However, unlike functions which can only be consumed in two ways (calling and
            // passing as value), structs can be consumed in a number of ways (passing instances,
            // referencing instances, passing struct types, referencing struct type...) and all of them
            // need to perform lazy parsing. Instead of handling it on a per case basis,
            // we setup a custom value provider which will trigger the struct's lazy parsing
            // whenever its name is invoked.
            p.context().env()->storeValue( structIdentity, ANYTERM( _ ), st );
            return true;
        };

        RegisterRule( e, "struct"_sid, Rule( handleStruct ) );
    }
}







|
|
>
|








|
|
|
|
|
|







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

            StructType::Unparsed::value_type toks;

            auto g = p.resolver()->consumeUnit();
            move( g.begin(), g.end(), back_inserter( toks ) );

            // Take the current identity, but remove "0_locvars" from the end: the struct def should
            // live in the "constant compile time" visibility hierarchy, and also only see that.
            auto parentIdentity =
                TakeVectorTerm( p.context().identity(), VecSize( p.context().identity() ) - 1 );
            auto structIdentity = TakeVectorTerm( parentIdentity, VecSize( parentIdentity ) - 1 );
            structIdentity = AppendToVectorTerm( structIdentity, name );
            p.context().env()->addVisibilityRule( parentIdentity, structIdentity );

            auto structLoc = Location::CreateSpanningLocation( locationId, nameLoc );
            auto st = ValueToEIR( ToValue( StructType( structIdentity, structLoc,
                make_shared< StructType::Unparsed >( move( toks ) ) ) ) );

            // Like functions, structs are parsed lazily to make out of order and circular
            // references possible. However, unlike functions which can only be consumed in two ways
            // (calling and passing as value), structs can be consumed in a number of ways (passing
            // instances, referencing instances, passing struct types, referencing struct type...)
            // and all of them need to perform lazy parsing. Instead of handling it on a per case
            // basis, we setup a custom value provider which will trigger the struct's lazy parsing
            // whenever its name is invoked.
            p.context().env()->storeValue( structIdentity, ANYTERM( _ ), st );
            return true;
        };

        RegisterRule( e, "struct"_sid, Rule( handleStruct ) );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/struct/struct.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
#ifndef GOOSE_BUILTINS_TYPES_STRUCT_H
#define GOOSE_BUILTINS_TYPES_STRUCT_H

namespace goose::builtins
{
    extern void SetupStructParsingRule( Env& e );
    extern void SetupStructInitialize( Env& e );
    extern void SetupStructLowering( Env& e );

    class Struct
    {
        public:

        private:
            ptr< StructType > m_type;
    };
}

namespace goose::eir
{
    template<>
    struct Bridge< builtins::Struct >
    {
        static Term Type( const Term& type );
        static Value ToValue( const builtins::Struct& lv );
        static optional< builtins::Struct > FromValue( const Value& v );
    };
}

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

namespace goose::builtins
{
    extern void SetupStructParsingRule( Env& e );
    extern void SetupStructInitialize( Env& e );
    extern void SetupStructLowering( Env& e );

    class Struct
    {
      public:

      private:
        ptr< StructType > m_type;
    };
} // namespace goose::builtins

namespace goose::eir
{

    template<> struct Bridge< builtins::Struct >
    {
        static Term Type( const Term& type );
        static Value ToValue( const builtins::Struct& lv );
        static optional< builtins::Struct > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/struct/structtype.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

#include "builtins/builtins.h"
#include "parse/parse.h"

using namespace goose::builtins;

namespace goose::builtins
{
    bool StructType::parse( const ptr< Env > e )
    {
        if( wasParsed() )
            return true;

        Context ctxt( e, identity() );
        ctxt.setBuilder( ToValue( TypeWrapper< ptr< StructBuilder > >(
            make_shared< StructBuilder >( m_identity ) ) ) );

        auto tokProvider = lex::MakeVectorAdapter( **m_unparsed );
        auto r = make_shared< parse::Resolver >( tokProvider, ctxt );
        parse::Parser p( r );
        bool success = p.parseBraceBlock();
        p.flushValue();

        m_unparsed->reset();

        if( !success )
            return false;

        DiagnosticsContext dc2( m_loc, "When invoking _Finalize." );
        auto result = InvokeOverloadSet( ctxt, e->extFinalize(),
            MakeClosedTuple( ctxt.builder(), ToValue( *this ) ), m_loc );

        if( result.isPoison() )
            return false;

        auto res = FromValue< bool >( result );
        return res && *res;
    }

    const Term& StructType::Pattern::GetPattern()
    {
        static auto pattern = ValueToEIR( Value( TypeType(), MkStdType( TSID( rt_type ), TSID( struct ), VEC( HOLE( "_"_sid ), HOLE( "_"_sid ) ) ) ) );


        return pattern;
    }
}


namespace goose::eir
{
    const Term& Bridge< StructType >::Type()
    {
        return TypeType();
    }

    Value Bridge< StructType >::ToValue( const StructType& t )
    {


        return Value( Type(), MkStdType( TSID( rt_type ), TSID( struct ), VEC( t.identity(), static_pointer_cast< void >( t.unparsed() ) ) ) ).setLocationId( t.location() );

    }

    optional< StructType > Bridge< StructType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),
            Vec(
                Lit( "rt_type"_sid ),
                SubTerm(),
                SubTerm(),
                Lit( "struct"_sid ),
                Vec(
                    SubTerm(),
                    Val< ptr< void > >()
                )
            )
        );

        if( !result )
            return nullopt;

        auto&& [_, __,infos] = *result;
        auto&& [identity,unparsed] = infos;

        return StructType( identity, v.locationId(), static_pointer_cast< StructType::Unparsed >( unparsed ) );
    }
}














|
|













|
|










|
>
>


<
>










>
>
|
>





<
|
<
<
<
<
<
|
<
<
<




|
|
>
|

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

using namespace goose::builtins;

namespace goose::builtins
{
    bool StructType::parse( const ptr< Env > e )
    {
        if( wasParsed() )
            return true;

        Context ctxt( e, identity() );
        ctxt.setBuilder( ToValue(
            TypeWrapper< ptr< StructBuilder > >( make_shared< StructBuilder >( m_identity ) ) ) );

        auto tokProvider = lex::MakeVectorAdapter( **m_unparsed );
        auto r = make_shared< parse::Resolver >( tokProvider, ctxt );
        parse::Parser p( r );
        bool success = p.parseBraceBlock();
        p.flushValue();

        m_unparsed->reset();

        if( !success )
            return false;

        DiagnosticsContext dc2( m_loc, "When invoking _Finalize." );
        auto result = InvokeOverloadSet(
            ctxt, e->extFinalize(), MakeClosedTuple( ctxt.builder(), ToValue( *this ) ), m_loc );

        if( result.isPoison() )
            return false;

        auto res = FromValue< bool >( result );
        return res && *res;
    }

    const Term& StructType::Pattern::GetPattern()
    {
        static auto pattern = ValueToEIR( Value( TypeType(),
            MkStdType(
                TSID( rt_type ), TSID( struct ), VEC( HOLE( "_"_sid ), HOLE( "_"_sid ) ) ) ) );
        return pattern;
    }

} // namespace goose::builtins

namespace goose::eir
{
    const Term& Bridge< StructType >::Type()
    {
        return TypeType();
    }

    Value Bridge< StructType >::ToValue( const StructType& t )
    {
        return Value( Type(),
            MkStdType( TSID( rt_type ), TSID( struct ),
                VEC( t.identity(), static_pointer_cast< void >( t.unparsed() ) ) ) )
            .setLocationId( t.location() );
    }

    optional< StructType > Bridge< StructType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),

            Vec( Lit( "rt_type"_sid ), SubTerm(), SubTerm(), Lit( "struct"_sid ),





                Vec( SubTerm(), Val< ptr< void > >() ) ) );




        if( !result )
            return nullopt;

        auto&& [_, __, infos] = *result;
        auto&& [identity, unparsed] = infos;
        return StructType(
            identity, v.locationId(), static_pointer_cast< StructType::Unparsed >( unparsed ) );
    }

} // namespace goose::eir
Changes to bs/builtins/types/struct/structtype.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
#ifndef GOOSE_BUILTINS_TYPES_STRUCT_TYPE_H
#define GOOSE_BUILTINS_TYPES_STRUCT_TYPE_H

namespace goose::builtins
{
    // struct type encoding:
    // We use MkStdType (because predicates + llvm type are both useful to have), and append a ptr< void > to unparsed token as extra (since MkStdType allows to provide extra data)

    // The funcs identity is the context of the struct's decl + its name (struct are nominative, unlike tuples). The struct's identity will serve
    // as root to store all the details of the struct type (members etc.)


    //  Should contain the struct's identity + pointer to unparsed tokens (or null if already parsed)

    class StructType
    {
        public:
            template< typename T, typename UP >
            StructType( T&& identity, LocationId loc, UP&& unparsed ) :
                m_identity( identity ),
                m_unparsed( unparsed ),
                m_loc( loc )
            {}



            using Unparsed = optional< llvm::SmallVector< TermLoc, 8 > >;

            const auto& identity() const { return m_identity; }

            auto location() const { return m_loc; }

            const auto& unparsed() const { return m_unparsed; }

            bool wasParsed() const { return !( *m_unparsed ); }

            bool parse( const ptr< Env > e );

            struct Pattern
            {
                static const Term& GetPattern();
            };

        private:
            Term m_identity;
            ptr< Unparsed > m_unparsed;
            LocationId m_loc;
    };

}

namespace goose::eir
{
    template<>
    struct Bridge< builtins::StructType >
    {
        static const Term& Type();
        static Value ToValue( const builtins::StructType& t );
        static optional< builtins::StructType > FromValue( const Value& v );
    };
}

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

namespace goose::builtins
{
    // struct type encoding:
    // We use MkStdType (because predicates + llvm type are both useful to have), and append a ptr<
    // void > to unparsed token as extra (since MkStdType allows to provide extra data) The funcs
    // identity is the context of the struct's decl + its name (struct are nominative, unlike
    // tuples). The struct's identity will serve as root to store all the details of the struct type
    // (members etc.)

    //  Should contain the struct's identity + pointer to unparsed tokens (or null if already
    //  parsed)
    class StructType
    {
      public:
        template< typename T, typename UP >
        StructType( T&& identity, LocationId loc, UP&& unparsed ) :
            m_identity( identity ),
            m_unparsed( unparsed ),
            m_loc( loc )

        {
        }

        using Unparsed = optional< llvm::SmallVector< TermLoc, 8 > >;

        const auto& identity() const { return m_identity; }

        auto location() const { return m_loc; }

        const auto& unparsed() const { return m_unparsed; }

        bool wasParsed() const { return !( *m_unparsed ); }

        bool parse( const ptr< Env > e );

        struct Pattern
        {
            static const Term& GetPattern();
        };

      private:
        Term m_identity;
        ptr< Unparsed > m_unparsed;
        LocationId m_loc;
    };
} // namespace goose::builtins


namespace goose::eir
{

    template<> struct Bridge< builtins::StructType >
    {
        static const Term& Type();
        static Value ToValue( const builtins::StructType& t );
        static optional< builtins::StructType > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/template/build.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
#include "builtins/builtins.h"

namespace goose::builtins
{
    TFuncType BuildTFuncType( const Value& returnType, const Value& params )
    {
        auto v = make_shared< Vector >();
        v->reserve( TupleSize( params ) );

        ForEachInTuple( params, [&]( auto&& param )

        {
            v->append( ValueToEIR( param ) );
            return true;
        } );

        return TFuncType( ValueToEIR( returnType ), v );
    }

    optional< Term > BuildTFuncSignature( const Context& c, const TFuncType& tft )
    {
        TemplateContext tc( c );

        auto v = make_shared< Vector >();
        v->reserve( VecSize( tft.params() ) + 1 );

        if( !PrepareTemplate( tc, tft.returnType() ) )
            return nullopt;

        auto rtSig = BuildTemplateSignature( tc, tft.returnType() );
        if( !rtSig )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( EIRToValue( tft.returnType() )->locationId(),

                "Invalid template return type or texpr." );
            return nullopt;
        }
        v->append( move( *rtSig ) );

        bool success = true;
        LocationId packLoc;

        ForEachInVectorTerm( tft.params(), [&]( auto&& param )

        {
            if( !packLoc.invalid() )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage( packLoc,
                    "pack expressions can only appear at the end of parameter lists." );
                success = false;
                return false;
            }

            if( !PrepareTemplate( tc, param ) )
            {
                success = false;
                return false;
            }

            auto teSig = BuildTemplateSignature( tc, param );
            if( !teSig )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage( EIRToValue( param )->locationId(),
                    "invalid template parameter." );
                success = false;
                return false;
            }

            if( IsTemplatePackExpr( c, param ) )
            {
                packLoc = EIRToValue( param )->locationId();
                v->setRepetitionTerm( move( *teSig ) );
            }
            else
                v->append( move( *teSig ) );

            return true;
        } );

        if( !success )
            return nullopt;

        return TERM( v );
    }










|
>
|
|
|
|

















|
>








|
>
|
|
|
|
|
|
|
|

|
|
|
|
|

|
|
|
|
|
|
|
|

|
|
|
|
|
|
|

|
|







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

namespace goose::builtins
{
    TFuncType BuildTFuncType( const Value& returnType, const Value& params )
    {
        auto v = make_shared< Vector >();
        v->reserve( TupleSize( params ) );

        ForEachInTuple( params,
            [&]( auto&& param )
            {
                v->append( ValueToEIR( param ) );
                return true;
            } );

        return TFuncType( ValueToEIR( returnType ), v );
    }

    optional< Term > BuildTFuncSignature( const Context& c, const TFuncType& tft )
    {
        TemplateContext tc( c );

        auto v = make_shared< Vector >();
        v->reserve( VecSize( tft.params() ) + 1 );

        if( !PrepareTemplate( tc, tft.returnType() ) )
            return nullopt;

        auto rtSig = BuildTemplateSignature( tc, tft.returnType() );
        if( !rtSig )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage(
                EIRToValue( tft.returnType() )->locationId(),
                "Invalid template return type or texpr." );
            return nullopt;
        }
        v->append( move( *rtSig ) );

        bool success = true;
        LocationId packLoc;

        ForEachInVectorTerm( tft.params(),
            [&]( auto&& param )
            {
                if( !packLoc.invalid() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( packLoc,
                        "pack expressions can only appear at the end of parameter lists." );
                    success = false;
                    return false;
                }

                if( !PrepareTemplate( tc, param ) )
                {
                    success = false;
                    return false;
                }

                auto teSig = BuildTemplateSignature( tc, param );
                if( !teSig )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        EIRToValue( param )->locationId(), "invalid template parameter." );
                    success = false;
                    return false;
                }

                if( IsTemplatePackExpr( c, param ) )
                {
                    packLoc = EIRToValue( param )->locationId();
                    v->setRepetitionTerm( move( *teSig ) );
                }
                else
                    v->append( move( *teSig ) );

                return true;
            } );

        if( !success )
            return nullopt;

        return TERM( v );
    }

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

        auto apv = make_shared< Vector >();
        apv->reserve( VecSize( ftype->params() ) + 1 );

        auto rtArgPat = BuildTemplateArgPattern( c, ftype->returnType() );
        if( !rtArgPat )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( EIRToValue( ftype->returnType() )->locationId(),

                "Invalid template return type or texpr." );
            return nullopt;
        }
        apv->append( move( *rtArgPat ) );

        bool success = true;
        ForEachInVectorTerm( ftype->params(), [&]( auto&& param )

        {
            auto teArgPat = BuildTemplateArgPattern( c, param );
            if( !teArgPat )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage( EIRToValue( param )->locationId(),
                    "invalid template parameter." );
                success = false;
                return false;
            }

            apv->append( move( *teArgPat ) );
            return true;
        } );

        if( !success )
            return nullopt;

        return TERM( apv );
    }
}







|
>






|
>
|
|
|
|
|
|
|
|
|

|
|
|






|
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

        auto apv = make_shared< Vector >();
        apv->reserve( VecSize( ftype->params() ) + 1 );

        auto rtArgPat = BuildTemplateArgPattern( c, ftype->returnType() );
        if( !rtArgPat )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage(
                EIRToValue( ftype->returnType() )->locationId(),
                "Invalid template return type or texpr." );
            return nullopt;
        }
        apv->append( move( *rtArgPat ) );

        bool success = true;
        ForEachInVectorTerm( ftype->params(),
            [&]( auto&& param )
            {
                auto teArgPat = BuildTemplateArgPattern( c, param );
                if( !teArgPat )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        EIRToValue( param )->locationId(), "invalid template parameter." );
                    success = false;
                    return false;
                }

                apv->append( move( *teArgPat ) );
                return true;
            } );

        if( !success )
            return nullopt;

        return TERM( apv );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/template/build.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14


15
16
#ifndef GOOSE_BUILTINS_TEMPLATE_BUILD_H
#define GOOSE_BUILTINS_TEMPLATE_BUILD_H

namespace goose::builtins
{
    extern TFuncType BuildTFuncType( const Value& returnType, const Value& params );

    extern optional< Term > BuildTFuncSignature( const Context& c, const TFuncType& tft );

    extern optional< TFunc > BuildTFunc( const Context& c, const TFuncType& tft,
        const Term& parentIdentity, const Term& identity, const Value& params, ptr< void > body );

    extern optional< Term > BuildArgPatternFromTFuncType( const Context& c, const Value& tfuncType );
}



#endif












|
<
>
>


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

14
15
16
17
#ifndef GOOSE_BUILTINS_TEMPLATE_BUILD_H
#define GOOSE_BUILTINS_TEMPLATE_BUILD_H

namespace goose::builtins
{
    extern TFuncType BuildTFuncType( const Value& returnType, const Value& params );

    extern optional< Term > BuildTFuncSignature( const Context& c, const TFuncType& tft );

    extern optional< TFunc > BuildTFunc( const Context& c, const TFuncType& tft,
        const Term& parentIdentity, const Term& identity, const Value& params, ptr< void > body );

    extern optional< Term > BuildArgPatternFromTFuncType(

        const Context& c, const Value& tfuncType );
} // namespace goose::builtins

#endif
Changes to bs/builtins/types/template/instantiate.cpp.
1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
17
18
19
20
21
22
23
#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& checkedCallPat, const TypeCheckingContext& tcContext )

    {
        auto tf = FromValue< TFunc >( callee );
        assert( tf );

        auto callDecomp = Decompose( checkedCallPat,
            Val< pvec >()
        );

        const auto& typecheckedRType = callDecomp->get()->terms().front();
        auto typecheckedArgs = DropVectorTerm( checkedCallPat, 1 );

        // Build the instance function param types
        auto instanceParams = EmptyClosedTuple();
        auto argGen = ForEachInVectorTerm( typecheckedArgs );








|
>




|
<
<







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"
#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& checkedCallPat,
        const TypeCheckingContext& tcContext )
    {
        auto tf = FromValue< TFunc >( callee );
        assert( tf );

        auto callDecomp = Decompose( checkedCallPat, Val< pvec >() );



        const auto& typecheckedRType = callDecomp->get()->terms().front();
        auto typecheckedArgs = DropVectorTerm( checkedCallPat, 1 );

        // Build the instance function param types
        auto instanceParams = EmptyClosedTuple();
        auto argGen = ForEachInVectorTerm( typecheckedArgs );
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
        auto instanceSig = GetFuncSigFromType( ToValue( *instanceType ) );

        const auto& vec = get< pvec >( tf->type().params() );
        auto g = ContainerTypePredicatesHashGenerator( c, tcContext, vec->terms() );
        uint64_t predHash = llvm::hash_combine_range( g.begin(), g.end() );

        Term instanceIdentity = AppendPredicatesHashToIdentity(
             AppendToVectorTerm( tf->identity(), instanceSig ), predHash );

        // Look for an already existing instanced function
        Value instanceFunc = PoisonValue();

        Term result;
        switch( c.env()->retrieveValue( instanceIdentity, c.identity(), result ) )
        {







|







40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
        auto instanceSig = GetFuncSigFromType( ToValue( *instanceType ) );

        const auto& vec = get< pvec >( tf->type().params() );
        auto g = ContainerTypePredicatesHashGenerator( c, tcContext, vec->terms() );
        uint64_t predHash = llvm::hash_combine_range( g.begin(), g.end() );

        Term instanceIdentity = AppendPredicatesHashToIdentity(
            AppendToVectorTerm( tf->identity(), instanceSig ), predHash );

        // Look for an already existing instanced function
        Value instanceFunc = PoisonValue();

        Term result;
        switch( c.env()->retrieveValue( instanceIdentity, c.identity(), result ) )
        {
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
                instanceType->verifInfos()->setPostCondToks( tf->type().postConds()->unparsed() );

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

                // Setup the template TVars binding in the verification context.
                auto tcc = tcContext;
                ForEachInVectorTerm( tf->type().params(), [&]( auto&& param )

                {
                    TemplateSetup( verifContext, tcc, param );
                    return true;
                } );

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


                tcc = tcContext;
                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 );

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

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

        return instanceFunc;
    }
}







|
>
|
|
|
|


|
>


|
>
|
|
|
|




>
|














|
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
                instanceType->verifInfos()->setPostCondToks( tf->type().postConds()->unparsed() );

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

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

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

                tcc = tcContext;
                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 );

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

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

        return instanceFunc;
    }
} // namespace goose::builtins
Changes to bs/builtins/types/template/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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

67

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

#include "builtins/builtins.h"
#include "parse/parse.h"

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

namespace goose::builtins
{
    class TemplateFunctionInvocationRule : public InvocationRule
    {
        public:

            Value resolveInvocation( Context& c, LocationId loc, const Value& callee, const Term& args ) const final
            {
                auto tf = FromValue< TFunc >( callee );
                assert( tf );

                optional< TypeCheckingContext > bestTCC;
                optional< Term > bestSol;

                auto callPat = PrependToVectorTerm( args, HOLE( "_"_sid, "fwd"_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( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& checkedCallPat, TypeCheckingContext& tcc ) const final

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

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

                auto pIR = GetInvocationRule( *c.env(), instanceFunc );
                G_VAL_ASSERT( callee, pIR );
                G_VAL_ASSERT( callee, pIR->canBeInvoked( c, instanceFunc ) );

                return pIR->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, LocationId funcValLocation, const Value& callee, const Term& checkedCallPat, TypeCheckingContext& tcc ) const final

            {

                DiagnosticsContext dc( funcValLocation, funcValLocation.invalid() ? "" : "Instantiated here.", false );
                return InstantiateTFunc( c, callee, checkedCallPat, tcc );
            }
    };

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

    void SetupTemplateFunctionInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( TSID( constant ),
                TFuncTypePattern(),
                ANYTERM( _ ) ) ),
            GetTFuncInvocationRule() );
    }
}











|
>
|
|
|
|

|
|

|
|

|
|
|
|
|
|
|

|
|
|
|
|
|
|

|

|
|

|
>
|
|

|
|
|

|
|
|

|
|

|
|
|
|
|
|

|
>
|
>
|
|
|











|
<
<


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

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

namespace goose::builtins
{
    class TemplateFunctionInvocationRule : public InvocationRule
    {
      public:
        Value resolveInvocation(
            Context& c, LocationId loc, const Value& callee, const Term& args ) const final
        {
            auto tf = FromValue< TFunc >( callee );
            assert( tf );

            optional< TypeCheckingContext > bestTCC;
            optional< Term > bestSol;

            auto callPat = PrependToVectorTerm( args, HOLE( "_"_sid, "fwd"_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( Context& c, LocationId loc, const Value& callee, const Term& args,
            const Term& checkedCallPat, TypeCheckingContext& tcc ) const final
        {
            DiagnosticsContext dc( loc, loc.invalid() ? "" : "Called here.", false );

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

            auto pIR = GetInvocationRule( *c.env(), instanceFunc );
            G_VAL_ASSERT( callee, pIR );
            G_VAL_ASSERT( callee, pIR->canBeInvoked( c, instanceFunc ) );

            return pIR->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, LocationId funcValLocation, const Value& callee,
            const Term& checkedCallPat, TypeCheckingContext& tcc ) const final
        {
            DiagnosticsContext dc(
                funcValLocation, funcValLocation.invalid() ? "" : "Instantiated here.", false );
            return InstantiateTFunc( c, callee, checkedCallPat, tcc );
        }
    };

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

    void SetupTemplateFunctionInvocationRule( Env& e )
    {
        e.invocationRuleSet()->addRule(
            ValueToEIR( ValuePattern( TSID( constant ), TFuncTypePattern(), ANYTERM( _ ) ) ),


            GetTFuncInvocationRule() );
    }

} // namespace goose::builtins
Changes to bs/builtins/types/template/pretty.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"

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

namespace goose::builtins
{
    void SetupTemplatePrettyPrinting()
    {
        auto& pp = PrettyPrinter::GetInstance();

        pp.addRule( ValueToEIR( ValuePattern( TSID( constant ),
            ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( tvar ) ) ) ),
                ANYTERM( _ ) ) ),
            [&]( auto&& out, auto&& t )
            {
                auto tvar = *FromValue< TVar >( *EIRToValue( t ) );
                out << "TVar(" << tvar.name() << ')';
                return true;
            } );

        pp.addRule( ValueToEIR( ValuePattern( TSID( constant ),
            ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( ttvar ) ) ) ),
                ANYTERM( _ ) ) ),
            [&]( auto&& out, auto&& t )
            {
                auto tvar = *FromValue< TTVar >( *EIRToValue( t ) );
                out << "TTVar(" << tvar.name() << ')';
                return true;
            } );

        pp.addRule( ValueToEIR( ValuePattern( TSID( constant ),
            ValueToEIR( Value( TypeType(), TSID( tnameddecl ) ) ),
                VEC( ANYTERM( _ ), ANYTERM( _ ) ) ) ),
            [&]( auto&& out, auto&& t )
            {
                auto decl = *FromValue< TNamedDecl >( *EIRToValue( t ) );
                out << "tnameddecl(" << decl.type() << ", " << decl.name() << ")";
                return true;
            } );
    }
}













|
|








|
|








|
|







|
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"

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

namespace goose::builtins
{
    void SetupTemplatePrettyPrinting()
    {
        auto& pp = PrettyPrinter::GetInstance();

        pp.addRule( ValueToEIR( ValuePattern( TSID( constant ),
                        ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( tvar ) ) ) ),
                        ANYTERM( _ ) ) ),
            [&]( auto&& out, auto&& t )
            {
                auto tvar = *FromValue< TVar >( *EIRToValue( t ) );
                out << "TVar(" << tvar.name() << ')';
                return true;
            } );

        pp.addRule( ValueToEIR( ValuePattern( TSID( constant ),
                        ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( ttvar ) ) ) ),
                        ANYTERM( _ ) ) ),
            [&]( auto&& out, auto&& t )
            {
                auto tvar = *FromValue< TTVar >( *EIRToValue( t ) );
                out << "TTVar(" << tvar.name() << ')';
                return true;
            } );

        pp.addRule( ValueToEIR( ValuePattern( TSID( constant ),
                        ValueToEIR( Value( TypeType(), TSID( tnameddecl ) ) ),
                        VEC( ANYTERM( _ ), ANYTERM( _ ) ) ) ),
            [&]( auto&& out, auto&& t )
            {
                auto decl = *FromValue< TNamedDecl >( *EIRToValue( t ) );
                out << "tnameddecl(" << decl.type() << ", " << decl.name() << ")";
                return true;
            } );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/template/rules/decl.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"

namespace goose::builtins
{
    class DeclTemplateRule : public TemplateRule
    {
        optional< Term > buildSignature( const TemplateContext& c, const Term& val ) const final
        {
            auto decl = FromValue< Decl >( *EIRToValue( val ) );
            assert( decl );
            return ParamPat( decl->type() );
        }


        Generator< Value > buildParamDecls( const Context& c, const Term& param, TermGen& args ) const final
        {
            args.consume();
            co_yield *EIRToValue( param );
        }

        optional< Term > buildArgPattern( const Context& c, const Term& val ) const final
        {
            auto decl = FromValue< Decl >( *EIRToValue( val ) );
            assert( decl );

            return ValueToEIR( ValuePattern( TSID( computed ), decl->type(), TERM( ptr< void >() ) ) );
        }

        bool isPackExpr( const Context& c, const Term& val ) const final
        {
            return false;
        }
    };

    void SetupDeclTemplateRule( Env& e )
    {
        auto declTypePat = ValueToEIR( Value( TypeType(), VEC( TSID( decl ), ANYTERM( _ ) ) ) );
        e.templateRuleSet()->addRule(
            ValueToEIR( ValuePattern( TSID( constant ), declTypePat, ANYTERM( _ ) ) ),
            make_shared< DeclTemplateRule >() );
    }
}













>
|









>
|


|
<
<
<









|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29



30
31
32
33
34
35
36
37
38
39
#include "builtins/builtins.h"

namespace goose::builtins
{
    class DeclTemplateRule : public TemplateRule
    {
        optional< Term > buildSignature( const TemplateContext& c, const Term& val ) const final
        {
            auto decl = FromValue< Decl >( *EIRToValue( val ) );
            assert( decl );
            return ParamPat( decl->type() );
        }

        Generator< Value > buildParamDecls(
            const Context& c, const Term& param, TermGen& args ) const final
        {
            args.consume();
            co_yield *EIRToValue( param );
        }

        optional< Term > buildArgPattern( const Context& c, const Term& val ) const final
        {
            auto decl = FromValue< Decl >( *EIRToValue( val ) );
            assert( decl );
            return ValueToEIR(
                ValuePattern( TSID( computed ), decl->type(), TERM( ptr< void >() ) ) );
        }

        bool isPackExpr( const Context& c, const Term& val ) const final { return false; }



    };

    void SetupDeclTemplateRule( Env& e )
    {
        auto declTypePat = ValueToEIR( Value( TypeType(), VEC( TSID( decl ), ANYTERM( _ ) ) ) );
        e.templateRuleSet()->addRule(
            ValueToEIR( ValuePattern( TSID( constant ), declTypePat, ANYTERM( _ ) ) ),
            make_shared< DeclTemplateRule >() );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/template/rules/rules.h.
1
2
3
4
5

6

7
8
9
10

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

namespace goose::builtins
{

    extern void SetupTVar( const Context& c, TypeCheckingContext& tcc, StringId name, bool forwarding );

    extern uint64_t HashTVarTypePredicates( const Context& c, const TypeCheckingContext& tcc, StringId name, bool forwarding );

    template< typename C >
    Generator< uint64_t > ContainerTypePredicatesHashGenerator( const Context& context, const TypeCheckingContext& tcc, const C& c )

    {
        for( const auto& x : c )
            co_yield TemplateHashTypePredicates( context, tcc, x );
    }

    extern void SetupTVarTemplateRule( Env& e );
    extern void SetupDeclTemplateRule( Env& e );





>
|
>
|


|
>







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

namespace goose::builtins
{
    extern void SetupTVar(
        const Context& c, TypeCheckingContext& tcc, StringId name, bool forwarding );
    extern uint64_t HashTVarTypePredicates(
        const Context& c, const TypeCheckingContext& tcc, StringId name, bool forwarding );

    template< typename C >
    Generator< uint64_t > ContainerTypePredicatesHashGenerator(
        const Context& context, const TypeCheckingContext& tcc, const C& c )
    {
        for( const auto& x : c )
            co_yield TemplateHashTypePredicates( context, tcc, x );
    }

    extern void SetupTVarTemplateRule( Env& e );
    extern void SetupDeclTemplateRule( Env& e );
31
32
33
34
35
36
37
38
39
40
        SetupTDeclTemplateRule( e );
        SetupTVecTypeTemplateRule( e );
        SetupValueTemplateRule( e );
        SetupTFuncTypeTemplateRule( e );
        SetupTPackTemplateRule( e );
        SetupTupleTemplateRule( e );
    }
}

#endif







|


34
35
36
37
38
39
40
41
42
43
        SetupTDeclTemplateRule( e );
        SetupTVecTypeTemplateRule( e );
        SetupValueTemplateRule( e );
        SetupTFuncTypeTemplateRule( e );
        SetupTPackTemplateRule( e );
        SetupTupleTemplateRule( e );
    }
} // namespace goose::builtins

#endif
Changes to bs/builtins/types/template/rules/tdecl.cpp.
15
16
17
18
19
20
21

22
23
24
25

26
27
28
29
30
31
32
33
        {
            auto td = *FromValue< TDecl >( *EIRToValue( val ) );

            auto sig = BuildTemplateSignature( c, td.type() );
            if( !sig )
                return nullopt;


            return TDeclSigPattern( TERM( td.name() ), c.getTTVarPackIndex( td.name() ) ? Hole::Behavior::Pack : Hole::Behavior::Standard,
                move( *sig ) );
        }


        Generator< Value > buildParamDecls( const Context& c, const Term& param, TermGen& args ) const final
        {
            auto tdecl = FromValue< TDecl >( *EIRToValue( param ) );
            assert( tdecl );

            if( IsTemplatePackExpr( c, tdecl->type() ) )
            {
                co_yield buildParamDecls( c, tdecl->type(), args );







>
|



>
|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
        {
            auto td = *FromValue< TDecl >( *EIRToValue( val ) );

            auto sig = BuildTemplateSignature( c, td.type() );
            if( !sig )
                return nullopt;

            return TDeclSigPattern( TERM( td.name() ),
                c.getTTVarPackIndex( td.name() ) ? Hole::Behavior::Pack : Hole::Behavior::Standard,
                move( *sig ) );
        }

        Generator< Value > buildParamDecls(
            const Context& c, const Term& param, TermGen& args ) const final
        {
            auto tdecl = FromValue< TDecl >( *EIRToValue( param ) );
            assert( tdecl );

            if( IsTemplatePackExpr( c, tdecl->type() ) )
            {
                co_yield buildParamDecls( c, tdecl->type(), args );
42
43
44
45
46
47
48

49
50
51
52
53
54
55
56
57
58
59
60
61
62
            auto tdecl = FromValue< TDecl >( *EIRToValue( val ) );
            assert( tdecl );

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


        uint64_t hashTypePredicates( const Context& c, const TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto tdecl = FromValue< TDecl >( *EIRToValue( val ) );
            assert( tdecl );

            return llvm::hash_combine(
                TemplateHashTypePredicates( c, tcc, tdecl->type() ),
                HashTVarTypePredicates( c, tcc, tdecl->name(), false ) );
        }

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







>
|




<
|







44
45
46
47
48
49
50
51
52
53
54
55
56

57
58
59
60
61
62
63
64
            auto tdecl = FromValue< TDecl >( *EIRToValue( val ) );
            assert( tdecl );

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

        uint64_t hashTypePredicates(
            const Context& c, const TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto tdecl = FromValue< TDecl >( *EIRToValue( val ) );
            assert( tdecl );


            return llvm::hash_combine( TemplateHashTypePredicates( c, tcc, tdecl->type() ),
                HashTVarTypePredicates( c, tcc, tdecl->name(), false ) );
        }

        optional< Term > buildArgPattern( const Context& c, const Term& val ) const final
        {
            return buildSignature( c, val );
        }
71
72
73
74
75
76
77
78

    void SetupTDeclTemplateRule( Env& e )
    {
        e.templateRuleSet()->addRule(
            ValueToEIR( ValuePattern( TSID( constant ), GetValueType< TDecl >(), ANYTERM( _ ) ) ),
            make_shared< TDeclTemplateRule >() );
    }
}







|
73
74
75
76
77
78
79
80

    void SetupTDeclTemplateRule( Env& e )
    {
        e.templateRuleSet()->addRule(
            ValueToEIR( ValuePattern( TSID( constant ), GetValueType< TDecl >(), ANYTERM( _ ) ) ),
            make_shared< TDeclTemplateRule >() );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/template/rules/tfunctype.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

#include "builtins/builtins.h"

namespace goose::builtins
{
    class TFuncTypeTemplateRule : public TemplateRule
    {
        bool prepare( TemplateContext& c, const Term& val ) const final
        {
            auto tft = FromValue< TFuncType >( *EIRToValue( val ) );
            assert( tft );

            bool success = true;

            ForEachInVectorTerm( tft->params(), [&]( auto&& param )

            {
                if( !PrepareTemplate( c, param ) )
                {
                    success = false;
                    return false;
                }

                return true;
            } );

            if( !success )
                return false;

            return PrepareTemplate( c, tft->returnType() );
        }

        optional< Term > buildSignature( const TemplateContext& c, const Term& val ) const final
        {
            auto tfuncType = FromValue< TFuncType >( *EIRToValue( val ) );

            auto sig = BuildTFuncSignature( c.semaContext(), *tfuncType );
            if( !sig )
                return nullopt;

            return TFuncTypeSigPattern( val, move( *sig ) );
        }


        Generator< Value > buildParamDecls( const Context& c, const Term& param, TermGen& args ) const final
        {
            co_yield *EIRToValue( *args.consume() );
        }

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

            ForEachInVectorTerm( tft->params(), [&]( auto&& param )

            {
                TemplateSetup( c, tcc, param );
                return true;
            } );

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


        uint64_t hashTypePredicates( const Context& c, const TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto tft = FromValue< TFuncType >( *EIRToValue( val ) );
            assert( tft );

            const auto& vec = *get< pvec >( tft->params() );
            auto g = ContainerTypePredicatesHashGenerator( c, tcc, vec.terms() );
            auto paramsHash = llvm::hash_combine_range( g.begin(), g.end() );

            return llvm::hash_combine( paramsHash,
                TemplateHashTypePredicates( c, tcc, tft->returnType() ) );
        }

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

        bool isPackExpr( const Context& c, const Term& val ) const final
        {
            return false;
        }
    };

    void SetupTFuncTypeTemplateRule( Env& e )
    {
        e.templateRuleSet()->addRule( TFuncTypePattern(),
            make_shared< TFuncTypeTemplateRule >() );
    }
}














|
>
|
|
|
|
|
|

|
|


















>
|









|
>
|
|
|
|




>
|








|
|







|
<
<
<




|
<

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

namespace goose::builtins
{
    class TFuncTypeTemplateRule : public TemplateRule
    {
        bool prepare( TemplateContext& c, const Term& val ) const final
        {
            auto tft = FromValue< TFuncType >( *EIRToValue( val ) );
            assert( tft );

            bool success = true;

            ForEachInVectorTerm( tft->params(),
                [&]( auto&& param )
                {
                    if( !PrepareTemplate( c, param ) )
                    {
                        success = false;
                        return false;
                    }

                    return true;
                } );

            if( !success )
                return false;

            return PrepareTemplate( c, tft->returnType() );
        }

        optional< Term > buildSignature( const TemplateContext& c, const Term& val ) const final
        {
            auto tfuncType = FromValue< TFuncType >( *EIRToValue( val ) );

            auto sig = BuildTFuncSignature( c.semaContext(), *tfuncType );
            if( !sig )
                return nullopt;

            return TFuncTypeSigPattern( val, move( *sig ) );
        }

        Generator< Value > buildParamDecls(
            const Context& c, const Term& param, TermGen& args ) const final
        {
            co_yield *EIRToValue( *args.consume() );
        }

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

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

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

        uint64_t hashTypePredicates(
            const Context& c, const TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto tft = FromValue< TFuncType >( *EIRToValue( val ) );
            assert( tft );

            const auto& vec = *get< pvec >( tft->params() );
            auto g = ContainerTypePredicatesHashGenerator( c, tcc, vec.terms() );
            auto paramsHash = llvm::hash_combine_range( g.begin(), g.end() );

            return llvm::hash_combine(
                paramsHash, TemplateHashTypePredicates( c, tcc, tft->returnType() ) );
        }

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

        bool isPackExpr( const Context& c, const Term& val ) const final { return false; }



    };

    void SetupTFuncTypeTemplateRule( Env& e )
    {
        e.templateRuleSet()->addRule( TFuncTypePattern(), make_shared< TFuncTypeTemplateRule >() );

    }

} // namespace goose::builtins
Changes to bs/builtins/types/template/rules/tnameddecl.cpp.
19
20
21
22
23
24
25

26
27
28
29
30
31
32
33
            auto typeSig = BuildTemplateSignature( c, tnd->type() );
            if( !typeSig )
                return nullopt;

            return ParamPat( *typeSig );
        }


        Generator< Value > buildParamDecls( const Context& c, const Term& param, TermGen& args ) const final
        {
            auto tnd = FromValue< TNamedDecl >( *EIRToValue( param ) );
            assert( tnd );

            if( IsTemplatePackExpr( c, tnd->type() ) )
            {
                auto tup = EmptyOpenTuple();







>
|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
            auto typeSig = BuildTemplateSignature( c, tnd->type() );
            if( !typeSig )
                return nullopt;

            return ParamPat( *typeSig );
        }

        Generator< Value > buildParamDecls(
            const Context& c, const Term& param, TermGen& args ) const final
        {
            auto tnd = FromValue< TNamedDecl >( *EIRToValue( param ) );
            assert( tnd );

            if( IsTemplatePackExpr( c, tnd->type() ) )
            {
                auto tup = EmptyOpenTuple();
45
46
47
48
49
50
51

52
53
54
55
56
57
58
59
        void setup( const Context& c, TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto tnd = FromValue< TNamedDecl >( *EIRToValue( val ) );
            assert( tnd );
            TemplateSetup( c, tcc, tnd->type() );
        }


        uint64_t hashTypePredicates( const Context& c, const TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto tnd = FromValue< TNamedDecl >( *EIRToValue( val ) );
            assert( tnd );
            return TemplateHashTypePredicates( c, tcc, tnd->type() );
        }

        optional< Term > buildArgPattern( const Context& c, const Term& val ) const final







>
|







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
        void setup( const Context& c, TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto tnd = FromValue< TNamedDecl >( *EIRToValue( val ) );
            assert( tnd );
            TemplateSetup( c, tcc, tnd->type() );
        }

        uint64_t hashTypePredicates(
            const Context& c, const TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto tnd = FromValue< TNamedDecl >( *EIRToValue( val ) );
            assert( tnd );
            return TemplateHashTypePredicates( c, tcc, tnd->type() );
        }

        optional< Term > buildArgPattern( const Context& c, const Term& val ) const final
74
75
76
77
78
79
80
81
82
83
84
85
            assert( tnd );
            return IsTemplatePackExpr( c, tnd->type() );
        }
    };

    void SetupTNamedDeclTemplateRule( Env& e )
    {
        e.templateRuleSet()->addRule(
            ValueToEIR( ValuePattern( TSID( constant ), GetValueType< TNamedDecl >(), ANYTERM( _ ) ) ),
            make_shared< TNamedDeclTemplateRule >() );
    }
}







|
|


|
76
77
78
79
80
81
82
83
84
85
86
87
            assert( tnd );
            return IsTemplatePackExpr( c, tnd->type() );
        }
    };

    void SetupTNamedDeclTemplateRule( Env& e )
    {
        e.templateRuleSet()->addRule( ValueToEIR( ValuePattern( TSID( constant ),
                                          GetValueType< TNamedDecl >(), ANYTERM( _ ) ) ),
            make_shared< TNamedDeclTemplateRule >() );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/template/rules/tpack.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include "builtins/builtins.h"

namespace goose::builtins
{
    class TPackTemplateRule : public TemplateRule
    {
        bool prepare( TemplateContext& c, const Term& val ) const final
        {
            auto v = *EIRToValue( val );

            if( c.currentPackIndex() != 0 )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage( v.locationId(),
                    "pack expressions can't be nested." );
                return false;
            }

            DiagnosticsContext dc( v.locationId(), "Inside this pack expression." );

            uint32_t packIndex = c.newPackIndex();
            c.setCurrentPackIndex( packIndex );












|
|







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

namespace goose::builtins
{
    class TPackTemplateRule : public TemplateRule
    {
        bool prepare( TemplateContext& c, const Term& val ) const final
        {
            auto v = *EIRToValue( val );

            if( c.currentPackIndex() != 0 )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage(
                    v.locationId(), "pack expressions can't be nested." );
                return false;
            }

            DiagnosticsContext dc( v.locationId(), "Inside this pack expression." );

            uint32_t packIndex = c.newPackIndex();
            c.setCurrentPackIndex( packIndex );
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 tp = FromValue< TPack >( *EIRToValue( val ) );
            assert( tp );

            return BuildTemplateSignature( c, tp->pattern() );
        }


        Generator< Value > buildParamDecls( const Context& c, const Term& param, TermGen& args ) const final
        {
            auto tp = FromValue< TPack >( *EIRToValue( param ) );
            assert( tp );

            while( !args.finished() )
                co_yield BuildTemplateParamDecls( c, tp->pattern(), args );
        }

        void setup( const Context& c, TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto tp = FromValue< TPack >( *EIRToValue( val ) );
            assert( tp );
            TemplateSetup( c, tcc, tp->pattern() );
        }


        uint64_t hashTypePredicates( const Context& c, const TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto tp = FromValue< TPack >( *EIRToValue( val ) );
            assert( tp );
            return TemplateHashTypePredicates( c, tcc, tp->pattern() );
        }

        optional< Term > buildArgPattern( const Context& c, const Term& val ) const final
        {
            auto tp = FromValue< TPack >( *EIRToValue( val ) );
            assert( tp );
            return BuildTemplateArgPattern( c, tp->pattern() );
        }

        bool isPackExpr( const Context& c, const Term& val ) const final
        {
            return true;
        }
    };

    void SetupTPackTemplateRule( Env& e )
    {
        e.templateRuleSet()->addRule(
            ValueToEIR( ValuePattern( TSID( constant ), GetValueType< TPack >(), ANYTERM( _ ) ) ),
            make_shared< TPackTemplateRule >() );
    }
}







>
|















>
|













|
<
<
<








|
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
        {
            auto tp = FromValue< TPack >( *EIRToValue( val ) );
            assert( tp );

            return BuildTemplateSignature( c, tp->pattern() );
        }

        Generator< Value > buildParamDecls(
            const Context& c, const Term& param, TermGen& args ) const final
        {
            auto tp = FromValue< TPack >( *EIRToValue( param ) );
            assert( tp );

            while( !args.finished() )
                co_yield BuildTemplateParamDecls( c, tp->pattern(), args );
        }

        void setup( const Context& c, TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto tp = FromValue< TPack >( *EIRToValue( val ) );
            assert( tp );
            TemplateSetup( c, tcc, tp->pattern() );
        }

        uint64_t hashTypePredicates(
            const Context& c, const TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto tp = FromValue< TPack >( *EIRToValue( val ) );
            assert( tp );
            return TemplateHashTypePredicates( c, tcc, tp->pattern() );
        }

        optional< Term > buildArgPattern( const Context& c, const Term& val ) const final
        {
            auto tp = FromValue< TPack >( *EIRToValue( val ) );
            assert( tp );
            return BuildTemplateArgPattern( c, tp->pattern() );
        }

        bool isPackExpr( const Context& c, const Term& val ) const final { return true; }



    };

    void SetupTPackTemplateRule( Env& e )
    {
        e.templateRuleSet()->addRule(
            ValueToEIR( ValuePattern( TSID( constant ), GetValueType< TPack >(), ANYTERM( _ ) ) ),
            make_shared< TPackTemplateRule >() );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/template/rules/tuple.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

#include "builtins/builtins.h"

namespace goose::builtins
{
    class TupleTemplateRule : public TemplateRule
    {
        bool prepare( TemplateContext& c, const Term& t ) const final
        {
            auto tup = *EIRToValue( t );
            LocationId packLoc;

            ForEachTermInTuple( tup, [&]( auto&& t )

            {
                if( !packLoc.invalid() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( packLoc,
                        "pack expressions can only appear at the end of tuples." );
                    return false;
                }

                if( !PrepareTemplate( c, t ) )
                    return false;

                if( IsTemplatePackExpr( c.semaContext(), t ) )
                    packLoc = EIRToValue( t )->locationId();

                return true;
            } );

            return true;
        }

        optional< Term > buildSignature( const TemplateContext& c, const Term& t ) const final
        {
            auto tup = *EIRToValue( t );

            auto v = make_shared< Vector >();
            v->reserve( TupleSize( tup ) );

            ForEachTermInTuple( tup, [&]( auto&& t )

            {
                if( auto s = BuildTemplateSignature( c, t ) )
                {
                    if( IsTemplatePackExpr( c.semaContext(), t ) )
                        v->setRepetitionTerm( move( *s ) );
                    else
                        v->append( move( *s ) );
                }
                else
                    v->append( t );

                return true;
            } );

            return ValueToEIR( ValuePattern( TSID( constant ), ValueToEIR( MkTupleType( TSID( closed ), HOLE( "_"_sid ) ) ), v ) );

        }


        Generator< Value > buildParamDecls( const Context& c, const Term& param, TermGen& args ) const final
        {
            co_yield *EIRToValue( *args.consume() );
        }

        void setup( const Context& c, TypeCheckingContext& tcc, const Term& t ) const final
        {
            auto tup = *EIRToValue( t );
            ForEachTermInTuple( tup, [&]( auto&& t )

            {
                TemplateSetup( c, tcc, t );
                return true;
            } );
        }


        uint64_t hashTypePredicates( const Context& c, const TypeCheckingContext& tcc, const Term& t ) const final
        {
            auto tup = EIRToValuePattern( t );
            auto vec = get< pvec >( tup->val() );
            assert( vec );

            auto g = ContainerTypePredicatesHashGenerator( c, tcc, vec->terms() );
            return llvm::hash_combine_range( g.begin(), g.end() );
        }

        optional< Term > buildArgPattern( const Context& c, const Term& t ) const final
        {
            auto tup = *EIRToValue( t );
            auto v = make_shared< Vector >();
            v->reserve( TupleSize( tup ) );

            ForEachTermInTuple( tup, [&]( auto&& t )

            {
                if( auto s = BuildTemplateArgPattern( c, t ) )
                {
                    if( IsTemplatePackExpr( c, t ) )
                        v->setRepetitionTerm( move( *s ) );
                    else
                        v->append( move( *s ) );
                }
                else
                    v->append( t );

                return true;
            } );

            return ValueToEIR( ValuePattern( TSID( constant ), ValueToEIR( MkTupleType( TSID( closed ), HOLE( "_"_sid ) ) ), v ) );

        }

        bool isPackExpr( const Context& c, const Term& val ) const final
        {
            return false;
        }
    };

    void SetupTupleTemplateRule( Env& e )
    {
        e.templateRuleSet()->addRule(
            ValueToEIR( ValuePattern( TSID( constant ), ValueToEIR( MkTupleType( TSID( closed ), ANYTERM( _ ) ) ), ANYTERM( _ ) ) ),

            make_shared< TupleTemplateRule >() );
    }
}












|
>
|
|
|
|
|
|
|

|
|

|
|

|
|











|
>
|
|
<
|
|
|
|
<
|
|

|
|

|
>


>
|







|
>
|
|
|
|


>
|















|
>
|
|
<
|
|
|
|
<
|
|

|
|

|
>


|
<
<
<





|
>


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

namespace goose::builtins
{
    class TupleTemplateRule : public TemplateRule
    {
        bool prepare( TemplateContext& c, const Term& t ) const final
        {
            auto tup = *EIRToValue( t );
            LocationId packLoc;

            ForEachTermInTuple( tup,
                [&]( auto&& t )
                {
                    if( !packLoc.invalid() )
                    {
                        DiagnosticsManager::GetInstance().emitErrorMessage(
                            packLoc, "pack expressions can only appear at the end of tuples." );
                        return false;
                    }

                    if( !PrepareTemplate( c, t ) )
                        return false;

                    if( IsTemplatePackExpr( c.semaContext(), t ) )
                        packLoc = EIRToValue( t )->locationId();

                    return true;
                } );

            return true;
        }

        optional< Term > buildSignature( const TemplateContext& c, const Term& t ) const final
        {
            auto tup = *EIRToValue( t );

            auto v = make_shared< Vector >();
            v->reserve( TupleSize( tup ) );

            ForEachTermInTuple( tup,
                [&]( auto&& t )
                {
                    if( auto s = BuildTemplateSignature( c, t ) )

                        if( IsTemplatePackExpr( c.semaContext(), t ) )
                            v->setRepetitionTerm( move( *s ) );
                        else
                            v->append( move( *s ) );

                    else
                        v->append( t );

                    return true;
                } );

            return ValueToEIR( ValuePattern( TSID( constant ),
                ValueToEIR( MkTupleType( TSID( closed ), HOLE( "_"_sid ) ) ), v ) );
        }

        Generator< Value > buildParamDecls(
            const Context& c, const Term& param, TermGen& args ) const final
        {
            co_yield *EIRToValue( *args.consume() );
        }

        void setup( const Context& c, TypeCheckingContext& tcc, const Term& t ) const final
        {
            auto tup = *EIRToValue( t );
            ForEachTermInTuple( tup,
                [&]( auto&& t )
                {
                    TemplateSetup( c, tcc, t );
                    return true;
                } );
        }

        uint64_t hashTypePredicates(
            const Context& c, const TypeCheckingContext& tcc, const Term& t ) const final
        {
            auto tup = EIRToValuePattern( t );
            auto vec = get< pvec >( tup->val() );
            assert( vec );

            auto g = ContainerTypePredicatesHashGenerator( c, tcc, vec->terms() );
            return llvm::hash_combine_range( g.begin(), g.end() );
        }

        optional< Term > buildArgPattern( const Context& c, const Term& t ) const final
        {
            auto tup = *EIRToValue( t );
            auto v = make_shared< Vector >();
            v->reserve( TupleSize( tup ) );

            ForEachTermInTuple( tup,
                [&]( auto&& t )
                {
                    if( auto s = BuildTemplateArgPattern( c, t ) )

                        if( IsTemplatePackExpr( c, t ) )
                            v->setRepetitionTerm( move( *s ) );
                        else
                            v->append( move( *s ) );

                    else
                        v->append( t );

                    return true;
                } );

            return ValueToEIR( ValuePattern( TSID( constant ),
                ValueToEIR( MkTupleType( TSID( closed ), HOLE( "_"_sid ) ) ), v ) );
        }

        bool isPackExpr( const Context& c, const Term& val ) const final { return false; }



    };

    void SetupTupleTemplateRule( Env& e )
    {
        e.templateRuleSet()->addRule(
            ValueToEIR( ValuePattern( TSID( constant ),
                ValueToEIR( MkTupleType( TSID( closed ), ANYTERM( _ ) ) ), ANYTERM( _ ) ) ),
            make_shared< TupleTemplateRule >() );
    }

} // namespace goose::builtins
Changes to bs/builtins/types/template/rules/tvar.cpp.
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
                valueToStore = MakeClosedTuple( *valueToStore, *EIRToValue( t ) );
                pack = true;
            }
            else
                valueToStore = AppendToTuple( *valueToStore, *EIRToValue( t ) );
        }

        // 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.
        if( !valueToStore )
            return;

        auto captureIdentity = AppendToVectorTerm( c.identity(), TERM( decoratedName ) );

        c.env()->storeValue( captureIdentity, ANYTERM( _ ), ValueToEIR( *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 type checking context.
        tcc.eraseLHSName( decoratedName );
    }

    Generator< size_t > PackPredicateHashGenerator( const Context& c, const TypeCheckingContext& tcc, StringId name )

    {
        // TODO_SSA: reenable
        /*for( auto&& t : CapturedPackTermGen( tcc, name ) )
        {
            auto val = EIRToValue( t );
            auto preds = GetTypePredicates( *val );
            if( !preds || !*preds )
                continue;

            ( *preds )->parse( c );
            co_yield ( *preds )->hash();
        }*/
        co_yield 0;
    }


    uint64_t HashTVarTypePredicates( const Context& c, const TypeCheckingContext& tcc, StringId name, bool forwarding )
    {
        // By convention, the callee's pattern will always be on the lhs of the type checking,
        // which means that the captures TVars will always be in the "left hand side" namespace
        // of the type checking context.
        StringId decoratedName = forwarding ? DecorateTTVarName( name ) : DecorateTVarName( name );

        // 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 g = PackPredicateHashGenerator( c, tcc, decoratedName );
        return llvm::hash_combine_range( g.begin(), g.end() );
    }

    class TVarTemplateRule : public TemplateRule
    {
        bool prepare( TemplateContext& c, const Term& val ) const final







|
<
|

|
|
|
|














|
>















>
|






|
<
|

|
|
|
|







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
                valueToStore = MakeClosedTuple( *valueToStore, *EIRToValue( t ) );
                pack = true;
            }
            else
                valueToStore = AppendToTuple( *valueToStore, *EIRToValue( t ) );
        }

        // 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.
        if( !valueToStore )
            return;

        auto captureIdentity = AppendToVectorTerm( c.identity(), TERM( decoratedName ) );

        c.env()->storeValue( captureIdentity, ANYTERM( _ ), ValueToEIR( *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 type checking context.
        tcc.eraseLHSName( decoratedName );
    }

    Generator< size_t > PackPredicateHashGenerator(
        const Context& c, const TypeCheckingContext& tcc, StringId name )
    {
        // TODO_SSA: reenable
        /*for( auto&& t : CapturedPackTermGen( tcc, name ) )
        {
            auto val = EIRToValue( t );
            auto preds = GetTypePredicates( *val );
            if( !preds || !*preds )
                continue;

            ( *preds )->parse( c );
            co_yield ( *preds )->hash();
        }*/
        co_yield 0;
    }

    uint64_t HashTVarTypePredicates(
        const Context& c, const TypeCheckingContext& tcc, StringId name, bool forwarding )
    {
        // By convention, the callee's pattern will always be on the lhs of the type checking,
        // which means that the captures TVars will always be in the "left hand side" namespace
        // of the type checking context.
        StringId decoratedName = forwarding ? DecorateTTVarName( name ) : DecorateTVarName( name );

        // 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 g = PackPredicateHashGenerator( c, tcc, decoratedName );
        return llvm::hash_combine_range( g.begin(), g.end() );
    }

    class TVarTemplateRule : public TemplateRule
    {
        bool prepare( TemplateContext& c, const Term& val ) const final
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
        optional< Term > buildSignature( const TemplateContext& c, const Term& val ) const final
        {
            auto tvar = FromValue< TVar >( *EIRToValue( val ) );
            assert( tvar );

            auto decoratedName = DecorateTVarName( tvar->name() );
            return HOLE( decoratedName, "tvar"_sid,
                c.getTVarPackIndex( decoratedName ) ? Hole::Behavior::Pack : Hole::Behavior::Standard );

        }


        Generator< Value > buildParamDecls( const Context& c, const Term& param, TermGen& args ) const final
        {
            co_yield *EIRToValue( *args.consume() );
        }

        void setup( const Context& c, TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto tvar = FromValue< TVar >( *EIRToValue( val ) );
            assert( tvar );
            SetupTVar( c, tcc, tvar->name(), false );
        }


        uint64_t hashTypePredicates( const Context& c, const TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto tvar = FromValue< TVar >( *EIRToValue( val ) );
            assert( tvar );
            return HashTVarTypePredicates( c, tcc, tvar->name(), false );
        }

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

        bool isPackExpr( const Context& c, const Term& val ) const final
        {
            return false;
        }
    };

    class TTVarTemplateRule : public TemplateRule
    {
        bool prepare( TemplateContext& c, const Term& val ) const final
        {
            auto ttvar = FromValue< TTVar >( *EIRToValue( val ) );
            assert( ttvar );
            c.TTVarOccurred( DecorateTTVarName( ttvar->name() ) );
            return true;
        }

        optional< Term > buildSignature( const TemplateContext& c, const Term& val ) const final
        {
            auto ttvar = FromValue< TTVar >( *EIRToValue( val ) );
            assert( ttvar );

            auto decoratedName = DecorateTTVarName( ttvar->name() );
            return HOLE( decoratedName, "fwd"_sid,
                c.getTTVarPackIndex( decoratedName ) ? Hole::Behavior::Pack : Hole::Behavior::Standard );

        }


        Generator< Value > buildParamDecls( const Context& c, const Term& param, TermGen& args ) const final
        {
            co_yield *EIRToValue( *args.consume() );
        }

        void setup( const Context& c, TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto ttvar = FromValue< TTVar >( *EIRToValue( val ) );
            assert( ttvar );
            SetupTVar( c, tcc, ttvar->name(), true );
        }


        uint64_t hashTypePredicates( const Context& c, const TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto ttvar = FromValue< TTVar >( *EIRToValue( val ) );
            assert( ttvar );
            return HashTVarTypePredicates( c, tcc, ttvar->name(), true );
        }

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

        bool isPackExpr( const Context& c, const Term& val ) const final
        {
            return false;
        }
    };

    void SetupTVarTemplateRule( Env& e )
    {
        // TVar
        e.templateRuleSet()->addRule(
            ValueToEIR( ValuePattern( TSID( constant ), GetValueType< TVar >(), ANYTERM( _ ) ) ),
            make_shared< TVarTemplateRule >() );

        // TTVar
        e.templateRuleSet()->addRule(
            ValueToEIR( ValuePattern( TSID( constant ), GetValueType< TTVar >(), ANYTERM( _ ) ) ),
            make_shared< TTVarTemplateRule >() );
    }
}







|
>


>
|











>
|











|
<
<
<



















|
>


>
|











>
|











|
<
<
<














|
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
        optional< Term > buildSignature( const TemplateContext& c, const Term& val ) const final
        {
            auto tvar = FromValue< TVar >( *EIRToValue( val ) );
            assert( tvar );

            auto decoratedName = DecorateTVarName( tvar->name() );
            return HOLE( decoratedName, "tvar"_sid,
                c.getTVarPackIndex( decoratedName ) ? Hole::Behavior::Pack :
                                                      Hole::Behavior::Standard );
        }

        Generator< Value > buildParamDecls(
            const Context& c, const Term& param, TermGen& args ) const final
        {
            co_yield *EIRToValue( *args.consume() );
        }

        void setup( const Context& c, TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto tvar = FromValue< TVar >( *EIRToValue( val ) );
            assert( tvar );
            SetupTVar( c, tcc, tvar->name(), false );
        }

        uint64_t hashTypePredicates(
            const Context& c, const TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto tvar = FromValue< TVar >( *EIRToValue( val ) );
            assert( tvar );
            return HashTVarTypePredicates( c, tcc, tvar->name(), false );
        }

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

        bool isPackExpr( const Context& c, const Term& val ) const final { return false; }



    };

    class TTVarTemplateRule : public TemplateRule
    {
        bool prepare( TemplateContext& c, const Term& val ) const final
        {
            auto ttvar = FromValue< TTVar >( *EIRToValue( val ) );
            assert( ttvar );
            c.TTVarOccurred( DecorateTTVarName( ttvar->name() ) );
            return true;
        }

        optional< Term > buildSignature( const TemplateContext& c, const Term& val ) const final
        {
            auto ttvar = FromValue< TTVar >( *EIRToValue( val ) );
            assert( ttvar );

            auto decoratedName = DecorateTTVarName( ttvar->name() );
            return HOLE( decoratedName, "fwd"_sid,
                c.getTTVarPackIndex( decoratedName ) ? Hole::Behavior::Pack :
                                                       Hole::Behavior::Standard );
        }

        Generator< Value > buildParamDecls(
            const Context& c, const Term& param, TermGen& args ) const final
        {
            co_yield *EIRToValue( *args.consume() );
        }

        void setup( const Context& c, TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto ttvar = FromValue< TTVar >( *EIRToValue( val ) );
            assert( ttvar );
            SetupTVar( c, tcc, ttvar->name(), true );
        }

        uint64_t hashTypePredicates(
            const Context& c, const TypeCheckingContext& tcc, const Term& val ) const final
        {
            auto ttvar = FromValue< TTVar >( *EIRToValue( val ) );
            assert( ttvar );
            return HashTVarTypePredicates( c, tcc, ttvar->name(), true );
        }

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

        bool isPackExpr( const Context& c, const Term& val ) const final { return false; }



    };

    void SetupTVarTemplateRule( Env& e )
    {
        // TVar
        e.templateRuleSet()->addRule(
            ValueToEIR( ValuePattern( TSID( constant ), GetValueType< TVar >(), ANYTERM( _ ) ) ),
            make_shared< TVarTemplateRule >() );

        // TTVar
        e.templateRuleSet()->addRule(
            ValueToEIR( ValuePattern( TSID( constant ), GetValueType< TTVar >(), ANYTERM( _ ) ) ),
            make_shared< TTVarTemplateRule >() );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/template/rules/tvectype.cpp.
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
        {
            auto tvecType = EIRToValuePattern( t );
            auto tvec = TVecFromEIR( tvecType->val() );
            assert( tvec );

            for( auto&& x : tvec->content()->terms() )
            {
                // TVec is a way to substitute a Vec for a TVec when building a type representation
                // to easily make TExpr versions of complex types without having to handle them specifically.

                // However, it's still a fixed structuring element, we don't want to allow packs in there.
                if( IsTemplatePackExpr( c.semaContext(), x ) )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( EIRToValue( x )->locationId(),
                        "pack expressions aren't allowed here." );
                    return false;
                }

                if( !PrepareTemplate( c, x ) )
                    return false;
            }

            return true;
        }

        optional< Term > buildSignature( const TemplateContext& c, const Term& t ) const final
        {
            auto tvecType = EIRToValuePattern( t );
            auto tvec = TVecFromEIR( tvecType->val() );
            assert( tvec );

            auto v = make_shared< Vector >();
            v->reserve( tvec->content()->terms().size() );

            for( auto&& x : tvec->content()->terms() )
            {
                if( auto s = BuildTemplateSignature( c, x ) )
                    v->append( move( *s ) );
                else
                    v->append( x );
            }

            return ValueToEIR( ValuePattern( TSID( constant ), TypeType(), v ) );
        }


        Generator< Value > buildParamDecls( const Context& c, const Term& param, TermGen& args ) const final
        {
            co_yield *EIRToValue( *args.consume() );
        }

        void setup( const Context& c, TypeCheckingContext& tcc, const Term& t ) const final
        {
            auto tvecType = EIRToValuePattern( t );
            auto tvec = TVecFromEIR( tvecType->val() );
            assert( tvec );

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


        uint64_t hashTypePredicates( const Context& c, const TypeCheckingContext& tcc, const Term& t ) const final
        {
            auto tvecType = EIRToValuePattern( t );
            auto tvec = TVecFromEIR( tvecType->val() );
            assert( tvec );

            auto g = ContainerTypePredicatesHashGenerator( c, tcc, tvec->content()->terms() );
            return llvm::hash_combine_range( g.begin(), g.end() );







|
|
>
|


|
|




















<




<




>
|














>
|







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
        {
            auto tvecType = EIRToValuePattern( t );
            auto tvec = TVecFromEIR( tvecType->val() );
            assert( tvec );

            for( auto&& x : tvec->content()->terms() )
            {
                // TVec is a way to substitute a Vec for a TVec when building a type
                // representation to easily make TExpr versions of complex types without having
                // to handle them specifically. However, it's still a fixed structuring element,
                // we don't want to allow packs in there.
                if( IsTemplatePackExpr( c.semaContext(), x ) )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        EIRToValue( x )->locationId(), "pack expressions aren't allowed here." );
                    return false;
                }

                if( !PrepareTemplate( c, x ) )
                    return false;
            }

            return true;
        }

        optional< Term > buildSignature( const TemplateContext& c, const Term& t ) const final
        {
            auto tvecType = EIRToValuePattern( t );
            auto tvec = TVecFromEIR( tvecType->val() );
            assert( tvec );

            auto v = make_shared< Vector >();
            v->reserve( tvec->content()->terms().size() );

            for( auto&& x : tvec->content()->terms() )

                if( auto s = BuildTemplateSignature( c, x ) )
                    v->append( move( *s ) );
                else
                    v->append( x );


            return ValueToEIR( ValuePattern( TSID( constant ), TypeType(), v ) );
        }

        Generator< Value > buildParamDecls(
            const Context& c, const Term& param, TermGen& args ) const final
        {
            co_yield *EIRToValue( *args.consume() );
        }

        void setup( const Context& c, TypeCheckingContext& tcc, const Term& t ) const final
        {
            auto tvecType = EIRToValuePattern( t );
            auto tvec = TVecFromEIR( tvecType->val() );
            assert( tvec );

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

        uint64_t hashTypePredicates(
            const Context& c, const TypeCheckingContext& tcc, const Term& t ) const final
        {
            auto tvecType = EIRToValuePattern( t );
            auto tvec = TVecFromEIR( tvecType->val() );
            assert( tvec );

            auto g = ContainerTypePredicatesHashGenerator( c, tcc, tvec->content()->terms() );
            return llvm::hash_combine_range( g.begin(), g.end() );
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
                else
                    v->append( x );
            }

            return ValueToEIR( ValuePattern( TSID( constant ), TypeType(), v ) );
        }

        bool isPackExpr( const Context& c, const Term& val ) const final
        {
            return false;
        }
    };

    void SetupTVecTypeTemplateRule( Env& e )
    {
        e.templateRuleSet()->addRule(
            ValueToEIR( ValuePattern( TSID( constant ), TypeType(), VEC( TSID( texpr ), TSID( tvec ), ANYTERM( _ ) ) ) ),
            make_shared< TVecTypeTemplateRule >() );
    }
}







|
<
<
<




|
|


|
92
93
94
95
96
97
98
99



100
101
102
103
104
105
106
107
108
                else
                    v->append( x );
            }

            return ValueToEIR( ValuePattern( TSID( constant ), TypeType(), v ) );
        }

        bool isPackExpr( const Context& c, const Term& val ) const final { return false; }



    };

    void SetupTVecTypeTemplateRule( Env& e )
    {
        e.templateRuleSet()->addRule( ValueToEIR( ValuePattern( TSID( constant ), TypeType(),
                                          VEC( TSID( texpr ), TSID( tvec ), ANYTERM( _ ) ) ) ),
            make_shared< TVecTypeTemplateRule >() );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/template/rules/value.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
#include "builtins/builtins.h"

namespace goose::builtins
{
    class ValueTemplateRule : public TemplateRule
    {
        bool prepare( TemplateContext& c, const Term& valEIR ) const final
        {
            auto val = *EIRToValue( valEIR );
            return PrepareTemplate( c, val.type() )
                && PrepareTemplate( c, val.val() );
        }

        optional< Term > buildSignature( const TemplateContext& c, const Term& valEIR ) const final
        {
            auto val = *EIRToValue( valEIR );
            auto vsig = BuildTemplateSignature( c, val.val() );
            if( IsType( c.semaContext(), val ) )
                return ParamPat( val.type(), vsig ? move( *vsig ) : val.val() );

            auto tsig = BuildTemplateSignature( c, val.type() );
            return ParamPat( tsig ? move( *tsig ) : val.type(), vsig ? move( *vsig ) : val.val() );
        }

        Generator< Value > buildParamDecls( const Context& c, const Term& val, TermGen& args ) const final

        {
            args.consume();
            co_yield *EIRToValue( val );
        }

        void setup( const Context& c, TypeCheckingContext& tcc, const Term& valEIR ) const final
        {
            auto val = *EIRToValue( valEIR );
            TemplateSetup( c, tcc, val.type() );
            TemplateSetup( c, tcc, val.val() );
        }


        uint64_t hashTypePredicates( const Context& c, const TypeCheckingContext& tcc, const Term& valEIR ) const final
        {
            auto val = *EIRToValue( valEIR );
            return llvm::hash_combine(
                TemplateHashTypePredicates( c, tcc, val.type() ),
                TemplateHashTypePredicates( c, tcc, val.val() ) );
        }

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

        bool isPackExpr( const Context& c, const Term& val ) const final
        {
            return false;
        }
    };

    void SetupValueTemplateRule( Env& e )
    {
        e.templateRuleSet()->addRule(
            ValueToEIR( ValuePattern( TSID( constant ), ANYTERM( _ ), ANYTERM( _ ) ) ),
            make_shared< ValueTemplateRule >() );
    }
}









|
<













|
>












>
|


<
|








|
<
<
<








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

namespace goose::builtins
{
    class ValueTemplateRule : public TemplateRule
    {
        bool prepare( TemplateContext& c, const Term& valEIR ) const final
        {
            auto val = *EIRToValue( valEIR );
            return PrepareTemplate( c, val.type() ) && PrepareTemplate( c, val.val() );

        }

        optional< Term > buildSignature( const TemplateContext& c, const Term& valEIR ) const final
        {
            auto val = *EIRToValue( valEIR );
            auto vsig = BuildTemplateSignature( c, val.val() );
            if( IsType( c.semaContext(), val ) )
                return ParamPat( val.type(), vsig ? move( *vsig ) : val.val() );

            auto tsig = BuildTemplateSignature( c, val.type() );
            return ParamPat( tsig ? move( *tsig ) : val.type(), vsig ? move( *vsig ) : val.val() );
        }

        Generator< Value > buildParamDecls(
            const Context& c, const Term& val, TermGen& args ) const final
        {
            args.consume();
            co_yield *EIRToValue( val );
        }

        void setup( const Context& c, TypeCheckingContext& tcc, const Term& valEIR ) const final
        {
            auto val = *EIRToValue( valEIR );
            TemplateSetup( c, tcc, val.type() );
            TemplateSetup( c, tcc, val.val() );
        }

        uint64_t hashTypePredicates(
            const Context& c, const TypeCheckingContext& tcc, const Term& valEIR ) const final
        {
            auto val = *EIRToValue( valEIR );

            return llvm::hash_combine( TemplateHashTypePredicates( c, tcc, val.type() ),
                TemplateHashTypePredicates( c, tcc, val.val() ) );
        }

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

        bool isPackExpr( const Context& c, const Term& val ) const final { return false; }



    };

    void SetupValueTemplateRule( Env& e )
    {
        e.templateRuleSet()->addRule(
            ValueToEIR( ValuePattern( TSID( constant ), ANYTERM( _ ), ANYTERM( _ ) ) ),
            make_shared< ValueTemplateRule >() );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/template/tc-tdecl.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
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

    void SetupTDeclTypeChecking( Env& e )
    {
        auto tDeclPat = TDeclSigPattern( ANYTERM( _ ), Hole::Behavior::Any, ANYTERM( _ ) );

        e.typeCheckingRuleSet()->addHalfUnificationRule( tDeclPat,
            []( const Term& lhs, TypeCheckingContext& c )
            {
                auto [name,bhv,sig] = DecomposeTDSig( lhs );
                return HalfUnify( sig, c );
            } );

        e.typeCheckingRuleSet()->addTypeCheckingRule( tDeclPat, ANYTERM( _ ),
            []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
            {
                auto [name,bhv,typeSig] = DecomposeTDSig( lhs );
                auto tdeclHole = HOLE( DecorateTVarName( name ), ""_sid, bhv );

                auto pat = ValueToEIR( Value( move( typeSig ), HOLE( "_"_sid ) ) );
                for( auto&& [s,tcc] : TypeCheck( pat, rhs, tcc ) )
                {
                    // We need to typecheck 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 savedRHSSubContext = tcc.RHSSubContext();
                    tcc.RHSSubContext() = tcc.LHSSubContext();

                    for( auto&& [s,tcc] : TypeCheck( s, tdeclHole, tcc ) )
                    {
                        tcc.RHSSubContext() = savedRHSSubContext;
                        co_yield { s, tcc };
                    }
                }
            } );

        e.typeCheckingRuleSet()->addUnificationRule( tDeclPat, ANYTERM( _ ),
            []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
            {
                auto [name,bhv,typeSig] = DecomposeTDSig( lhs );
                auto tdeclHole = HOLE( DecorateTVarName( name ), ""_sid, bhv );

                auto pat = ValueToEIR( Value( move( typeSig ), HOLE( "_"_sid ) ) );
                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 savedRHSSubContext = tcc.RHSSubContext();
                    tcc.RHSSubContext() = tcc.LHSSubContext();

                    for( auto&& [s,tcc] : Unify( s, tdeclHole, tcc ) )
                    {
                        tcc.RHSSubContext() = savedRHSSubContext;
                        co_yield { s, tcc };
                    }
                }
            } );

        // tfunc tdecl param / tfunc arg

        auto tDeclTFuncPat = TDeclSigPattern( ANYTERM( _ ), Hole::Behavior::Any, TFuncTypeSigPattern( ANYTERM( _ ), ANYTERM( _ ) ) );

        e.typeCheckingRuleSet()->addTypeCheckingRule(

            tDeclTFuncPat,

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

        []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
        {
            auto [name,bhv,typeSig] = DecomposeTDSig( lhs );
            auto [_,tfSig] = DecomposeTFTSig( typeSig );

            auto callPat = BuildArgPatternFromTDecl( tcc.context(), move( typeSig ) );
            auto tdeclHole = HOLE( DecorateTVarName( name ), ""_sid, bhv );

            auto rhsVal = *EIRToValue( rhs );

            ConstrainedFunc cfunc( tfSig, GetTFuncInvocationRule(), rhsVal );
            auto cFuncTerm = ValueToEIR( ToValue( move( cfunc ) ) );

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

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

            for( auto&& [s, tcc] : TypeCheck( callPat, rhs, tcc ) )
            {
                // Restore the namespace
                tcc.RHSSubContext() = savedRHSSubContext;
                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.RHSSubContext() = tcc.LHSSubContext();

                for( auto&& [s,tcc] : Unify( cFuncTerm, tdeclHole, tcc ) )
                {
                    tcc.RHSSubContext() = savedRHSSubContext;
                    co_yield { s, tcc };
                }
            }
        } );

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

            move( tDeclTFuncPat ),

            ValueToEIR( ValuePattern(
                TSID( constant ),
                GetValueType< ptr< sema::OverloadSet > >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
        {
            auto [name,bhv,typeSig] = DecomposeTDSig( lhs );
            auto [_,tfSig] = DecomposeTFTSig( typeSig );

            auto callPat = BuildArgPatternFromTDecl( tcc.context(), move( typeSig ) );
            auto tdeclHole = HOLE( DecorateTVarName( name ), ""_sid, bhv );

            auto rhsVal = *EIRToValue( rhs );

            ConstrainedFunc cfunc( tfSig, GetOverloadSetInvocationRule(), rhsVal );
            auto cFuncTerm = ValueToEIR( ToValue( move( cfunc ) ) );

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

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

            for( auto&& [s, tcc] : TypeCheck( callPat, rhs, tcc ) )
            {
                // Restore the namespace
                tcc.RHSSubContext() = savedRHSSubContext;
                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.RHSSubContext() = tcc.LHSSubContext();

                for( auto&& [s,tcc] : Unify( cFuncTerm, tdeclHole, tcc ) )
                {
                    tcc.RHSSubContext() = savedRHSSubContext;
                    co_yield { s, tcc };
                }
            }
        } );
    }
}








|






|



|

|
>
|



|










|



|

|
>
|



|








>
|





|
<
<
<

|
|
|
|

|
|

|

|
|

|
|
|
|

|
|

|
|
|
|
|

|
>
|
|

|
|
|
|
|
|
|







<
|
<

|
|
|
|

|
|

|

|
|

|
|
|
|

|
|

|
|
|
|
|

|
>
|
|

|
|
|
|
|
|
|

<
>
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
    void SetupTDeclTypeChecking( Env& e )
    {
        auto tDeclPat = TDeclSigPattern( ANYTERM( _ ), Hole::Behavior::Any, ANYTERM( _ ) );

        e.typeCheckingRuleSet()->addHalfUnificationRule( tDeclPat,
            []( const Term& lhs, TypeCheckingContext& c )
            {
                auto [name, bhv, sig] = DecomposeTDSig( lhs );
                return HalfUnify( sig, c );
            } );

        e.typeCheckingRuleSet()->addTypeCheckingRule( tDeclPat, ANYTERM( _ ),
            []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
            {
                auto [name, bhv, typeSig] = DecomposeTDSig( lhs );
                auto tdeclHole = HOLE( DecorateTVarName( name ), ""_sid, bhv );

                auto pat = ValueToEIR( Value( move( typeSig ), HOLE( "_"_sid ) ) );
                for( auto&& [s, tcc] : TypeCheck( pat, rhs, tcc ) )
                {
                    // We need to typecheck 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 savedRHSSubContext = tcc.RHSSubContext();
                    tcc.RHSSubContext() = tcc.LHSSubContext();

                    for( auto&& [s, tcc] : TypeCheck( s, tdeclHole, tcc ) )
                    {
                        tcc.RHSSubContext() = savedRHSSubContext;
                        co_yield { s, tcc };
                    }
                }
            } );

        e.typeCheckingRuleSet()->addUnificationRule( tDeclPat, ANYTERM( _ ),
            []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
            {
                auto [name, bhv, typeSig] = DecomposeTDSig( lhs );
                auto tdeclHole = HOLE( DecorateTVarName( name ), ""_sid, bhv );

                auto pat = ValueToEIR( Value( move( typeSig ), HOLE( "_"_sid ) ) );
                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 savedRHSSubContext = tcc.RHSSubContext();
                    tcc.RHSSubContext() = tcc.LHSSubContext();

                    for( auto&& [s, tcc] : Unify( s, tdeclHole, tcc ) )
                    {
                        tcc.RHSSubContext() = savedRHSSubContext;
                        co_yield { s, tcc };
                    }
                }
            } );

        // tfunc tdecl param / tfunc arg
        auto tDeclTFuncPat = TDeclSigPattern(
            ANYTERM( _ ), Hole::Behavior::Any, TFuncTypeSigPattern( ANYTERM( _ ), ANYTERM( _ ) ) );

        e.typeCheckingRuleSet()->addTypeCheckingRule(

            tDeclTFuncPat,

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




            []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
            {
                auto [name, bhv, typeSig] = DecomposeTDSig( lhs );
                auto [_, tfSig] = DecomposeTFTSig( typeSig );

                auto callPat = BuildArgPatternFromTDecl( tcc.context(), move( typeSig ) );
                auto tdeclHole = HOLE( DecorateTVarName( name ), ""_sid, bhv );

                auto rhsVal = *EIRToValue( rhs );

                ConstrainedFunc cfunc( tfSig, GetTFuncInvocationRule(), rhsVal );
                auto cFuncTerm = ValueToEIR( ToValue( move( cfunc ) ) );

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

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

                for( auto&& [s, tcc] : TypeCheck( callPat, rhs, tcc ) )
                {
                    // Restore the namespace
                    tcc.RHSSubContext() = savedRHSSubContext;
                    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.RHSSubContext() = tcc.LHSSubContext();

                    for( auto&& [s, tcc] : Unify( cFuncTerm, tdeclHole, tcc ) )
                    {
                        tcc.RHSSubContext() = savedRHSSubContext;
                        co_yield { s, tcc };
                    }
                }
            } );

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

            move( tDeclTFuncPat ),

            ValueToEIR( ValuePattern(

                TSID( constant ), GetValueType< ptr< sema::OverloadSet > >(), ANYTERM( _ ) ) ),


            []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
            {
                auto [name, bhv, typeSig] = DecomposeTDSig( lhs );
                auto [_, tfSig] = DecomposeTFTSig( typeSig );

                auto callPat = BuildArgPatternFromTDecl( tcc.context(), move( typeSig ) );
                auto tdeclHole = HOLE( DecorateTVarName( name ), ""_sid, bhv );

                auto rhsVal = *EIRToValue( rhs );

                ConstrainedFunc cfunc( tfSig, GetOverloadSetInvocationRule(), rhsVal );
                auto cFuncTerm = ValueToEIR( ToValue( move( cfunc ) ) );

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

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

                for( auto&& [s, tcc] : TypeCheck( callPat, rhs, tcc ) )
                {
                    // Restore the namespace
                    tcc.RHSSubContext() = savedRHSSubContext;
                    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.RHSSubContext() = tcc.LHSSubContext();

                    for( auto&& [s, tcc] : Unify( cFuncTerm, tdeclHole, tcc ) )
                    {
                        tcc.RHSSubContext() = savedRHSSubContext;
                        co_yield { s, tcc };
                    }
                }
            } );
    }

} // namespace goose::builtins
Changes to bs/builtins/types/template/tdecl.cpp.
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
            return VEC( TSID( tdecl_sig ), name, ANYTERM( _ ), move( sig ) );
        else
            return VEC( TSID( tdecl_sig ), name, TERM( HoleBhvToSid( bhv ) ), move( sig ) );
    }

    tuple< StringId, Hole::Behavior, Term > DecomposeTDSig( const Term& tdsig )
    {
        auto result = Decompose( tdsig,
            Vec(
                Lit( "tdecl_sig"_sid ),
                Val< StringId >(),
                SubTerm(),
                SubTerm()
            )
        );

        auto&& [name,bhvTerm,sig] = *result;

        if( bhvTerm == ANYTERM( _ ) )
            return { name, Hole::Behavior::Any, sig };

        return { name, SidToHoleBhv( get< StringId >( bhvTerm ) ), sig };
    }
}

namespace goose::eir
{
    const Term& Bridge< TDecl >::Type()
    {
        static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( tdecl ) ) ) );
        return type;
    }

    Value Bridge< TDecl >::ToValue( const TDecl& td )
    {
        return Value( Type(), VEC( td.type(), TERM( td.name() ) ) );
    }

    optional< TDecl > Bridge< TDecl >::FromValue( const Value& v )
    {
        if( !IsTDecl( v ) )
            return nullopt;

        auto result = Decompose( v.val(),
            Vec(
                SubTerm(),
                Val< StringId >()
            )
        );

        if( !result )
            return nullopt;

        auto&& [type,name] = *result;
        return TDecl( type, name );
    }
}







|
<
|
<
<
<
|
<
<
|






|



















|
<
<
<
<
<




|


|
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
            return VEC( TSID( tdecl_sig ), name, ANYTERM( _ ), move( sig ) );
        else
            return VEC( TSID( tdecl_sig ), name, TERM( HoleBhvToSid( bhv ) ), move( sig ) );
    }

    tuple< StringId, Hole::Behavior, Term > DecomposeTDSig( const Term& tdsig )
    {
        auto result = Decompose(

            tdsig, Vec( Lit( "tdecl_sig"_sid ), Val< StringId >(), SubTerm(), SubTerm() ) );






        auto&& [name, bhvTerm, sig] = *result;

        if( bhvTerm == ANYTERM( _ ) )
            return { name, Hole::Behavior::Any, sig };

        return { name, SidToHoleBhv( get< StringId >( bhvTerm ) ), sig };
    }
} // namespace goose::builtins

namespace goose::eir
{
    const Term& Bridge< TDecl >::Type()
    {
        static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( tdecl ) ) ) );
        return type;
    }

    Value Bridge< TDecl >::ToValue( const TDecl& td )
    {
        return Value( Type(), VEC( td.type(), TERM( td.name() ) ) );
    }

    optional< TDecl > Bridge< TDecl >::FromValue( const Value& v )
    {
        if( !IsTDecl( v ) )
            return nullopt;

        auto result = Decompose( v.val(), Vec( SubTerm(), Val< StringId >() ) );






        if( !result )
            return nullopt;

        auto&& [type, name] = *result;
        return TDecl( type, name );
    }
} // namespace goose::eir
Changes to bs/builtins/types/template/tdecl.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
#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, StringId name ) :
                m_type( forward< T >( type ) ),
                m_name( name )
            {}



            const auto& type() const { return m_type; }

            const auto& name() const { return m_name; }

        private:
            Term m_type;
            StringId m_name;
    };

    extern bool IsTDecl( const Value& td );

    extern Term TDeclSigPattern( const Term& name, Hole::Behavior bhv, Term&& sig );
    extern tuple< StringId, Hole::Behavior, Term > DecomposeTDSig( const Term& tdsig );
}

namespace goose::eir
{
    template<>
    struct Bridge< builtins::TDecl >
    {
        static const Term& Type();
        static Value ToValue( const builtins::TDecl& td );
        static optional< builtins::TDecl > FromValue( const Value& v );
    };
}

#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
#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, StringId name ) :
            m_type( forward< T >( type ) ),
            m_name( name )

        {
        }

        const auto& type() const { return m_type; }

        const auto& name() const { return m_name; }

      private:
        Term m_type;
        StringId m_name;
    };

    extern bool IsTDecl( const Value& td );

    extern Term TDeclSigPattern( const Term& name, Hole::Behavior bhv, Term&& sig );
    extern tuple< StringId, Hole::Behavior, Term > DecomposeTDSig( const Term& tdsig );
} // namespace goose::builtins

namespace goose::eir
{

    template<> struct Bridge< builtins::TDecl >
    {
        static const Term& Type();
        static Value ToValue( const builtins::TDecl& td );
        static optional< builtins::TDecl > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/template/texpr.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
    }

    bool IsTExpr( const Value& te )
    {
        if( te.isConstant() )
        {
            const auto* ppVec = get_if< pvec >( &te.val() );
            if( ppVec && !( *ppVec )->empty()
                &&( **ppVec )[0] == TSID( texpr ) )
            {
                return true;
            }

            if( IsTuple( te ) )
            {
                bool isTExpr = false;

                ForEachInTuple( te, [&]( auto&& v )

                {
                    if( !IsTExpr( v ) )
                        return true;

                    isTExpr = true;
                    return false;
                } );

                return isTExpr;
            }
        }

        auto typeVal = EIRToValue( te.type() );
        if( !typeVal )
            return false;

        const auto* ppVec = get_if< pvec >( &typeVal->val() );
        if( !ppVec )
            return false;

        if( ( *ppVec )->empty() )
            return false;

        return ( **ppVec )[0] == TSID( texpr );
    }
}







|
<
<

<





|
>
|
|
|

|
|
|


















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

    bool IsTExpr( const Value& te )
    {
        if( te.isConstant() )
        {
            const auto* ppVec = get_if< pvec >( &te.val() );
            if( ppVec && !( *ppVec )->empty() && ( **ppVec )[0] == TSID( texpr ) )


                return true;


            if( IsTuple( te ) )
            {
                bool isTExpr = false;

                ForEachInTuple( te,
                    [&]( auto&& v )
                    {
                        if( !IsTExpr( v ) )
                            return true;

                        isTExpr = true;
                        return false;
                    } );

                return isTExpr;
            }
        }

        auto typeVal = EIRToValue( te.type() );
        if( !typeVal )
            return false;

        const auto* ppVec = get_if< pvec >( &typeVal->val() );
        if( !ppVec )
            return false;

        if( ( *ppVec )->empty() )
            return false;

        return ( **ppVec )[0] == TSID( texpr );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/template/tfunc.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

    Term Bridge< TFunc >::Type( const builtins::TFunc& tf )
    {
        return ValueToEIR( ::ToValue( tf.type() ) );
    }

    Value Bridge< TFunc >::ToValue( const TFunc& tf )
    {
        return Value( Type( tf ), VEC( Quote( tf.signature() ), tf.parentIdentity(),

             tf.identity(), TERM( tf.tokens() ) ) );
    }

    optional< TFunc > Bridge< TFunc >::FromValue( const Value& v )
    {
        auto typeVal = EIRToValue( v.type() );
        auto type = ::FromValue< TFuncType >( *typeVal );
        if( !type )
            return nullopt;

        auto result = Decompose( v.val(),
            Vec(
                SubTerm(),              // signature
                SubTerm(),              // parentIdentity
                SubTerm(),              // identity
                Val< ptr< void > >()    // tokens
            )
        );

        if( !result )
            return nullopt;

        auto&& [signature, parentIdentity, identity, tokens] = *result;

        return TFunc( move( *type ), *Unquote( signature ),
            parentIdentity, identity, tokens );
    }
}








|
>
|










<
|
|
|
|
|
<






|
<

<
>
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
    Term Bridge< TFunc >::Type( const builtins::TFunc& tf )
    {
        return ValueToEIR( ::ToValue( tf.type() ) );
    }

    Value Bridge< TFunc >::ToValue( const TFunc& tf )
    {
        return Value( Type( tf ),
            VEC( Quote( tf.signature() ), tf.parentIdentity(), tf.identity(),
                TERM( tf.tokens() ) ) );
    }

    optional< TFunc > Bridge< TFunc >::FromValue( const Value& v )
    {
        auto typeVal = EIRToValue( v.type() );
        auto type = ::FromValue< TFuncType >( *typeVal );
        if( !type )
            return nullopt;

        auto result = Decompose( v.val(),

            Vec( SubTerm(), // signature
                SubTerm(), // parentIdentity
                SubTerm(), // identity
                Val< ptr< void > >() // tokens
                ) );


        if( !result )
            return nullopt;

        auto&& [signature, parentIdentity, identity, tokens] = *result;

        return TFunc( move( *type ), *Unquote( signature ), parentIdentity, identity, tokens );

    }

} // namespace goose::eir
Changes to bs/builtins/types/template/tfunc.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
#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 PI, typename I, typename V >
            TFunc( T&& type, S&& signature, PI&& parentIdentity, I&& identity, V&& tokens ) :
                m_type( forward< T >( type ) ),
                m_signature( forward< S >( signature ) ),
                m_parentIdentity( forward< PI >( parentIdentity ) ),
                m_identity( forward< I >( identity ) ),
                m_tokens( forward< V >( tokens ) )
            {}



            const auto& type() const { return m_type; }

            auto& type() { return m_type; }

            const auto& signature() const { return m_signature; }

            const auto& parentIdentity() const { return m_parentIdentity; }

            const auto& identity() const { return m_identity; }

            const auto& tokens() const { return m_tokens; }

            void setTokens( const ptr< void >& pToks )
            {
                m_tokens = pToks;
            }

        private:
            TFuncType   m_type;
            Term        m_signature;
            Term        m_parentIdentity;
            Term        m_identity;
            ptr< void > m_tokens;
    };

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



namespace goose::eir
{
    template<>
    struct Bridge< builtins::TFunc >
    {
        static Term Type( const builtins::TFunc& tf );
        static Value ToValue( const builtins::TFunc& tf );
        static optional< builtins::TFunc > FromValue( const Value& v );
    };
}

#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
#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 PI, typename I, typename V >
        TFunc( T&& type, S&& signature, PI&& parentIdentity, I&& identity, V&& tokens ) :
            m_type( forward< T >( type ) ),
            m_signature( forward< S >( signature ) ),
            m_parentIdentity( forward< PI >( parentIdentity ) ),
            m_identity( forward< I >( identity ) ),
            m_tokens( forward< V >( tokens ) )

        {
        }

        const auto& type() const { return m_type; }

        auto& type() { return m_type; }

        const auto& signature() const { return m_signature; }

        const auto& parentIdentity() const { return m_parentIdentity; }

        const auto& identity() const { return m_identity; }

        const auto& tokens() const { return m_tokens; }

        void setTokens( const ptr< void >& pToks ) { m_tokens = pToks; }




      private:
        TFuncType m_type;
        Term m_signature;
        Term m_parentIdentity;
        Term m_identity;
        ptr< void > m_tokens;
    };

    extern Value InstantiateTFunc( const Context& c, const Value& callee,

        const Term& checkedCallPat, const TypeCheckingContext& tcc );
} // namespace goose::builtins

namespace goose::eir
{

    template<> struct Bridge< builtins::TFunc >
    {
        static Term Type( const builtins::TFunc& tf );
        static Value ToValue( const builtins::TFunc& tf );
        static optional< builtins::TFunc > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/template/tfunctype.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
#include "builtins/builtins.h"
#include "lex/lex.h"
#include "parse/parse.h"

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

namespace goose::builtins
{
    bool IsTFuncType( const Value& t )
    {
        auto result = Decompose( t.val(),
            Vec(
                Lit( "texpr"_sid ),
                Lit( "tfunc"_sid ),
                SubTerm(),  // return type
                SubTerm(),  // param types
                SubTerm(),  // verif stmts toks
                SubTerm(),  // verif stmts toks
                SubTerm()   // intrinsic flag
            )
        );

        return !!result;
    }

    bool IsTFunc( const Value& t )
    {
        return IsTFuncType( *EIRToValue( t.type() ) );
    }

    const Term& TFuncPattern::GetPattern()
    {
        static auto pattern = ValueToEIR(
            Value( TypeType(), VEC( TSID( texpr ), TSID( tfunc ), HOLE( "_"_sid ),
            HOLE( "_"_sid ), HOLE( "_"_sid ),
            HOLE( "_"_sid ), HOLE( "_"_sid ) ) ) );

        return pattern;
    }

    const Term& TFuncTypePattern()
    {
        static auto tFuncTypePat = ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( tfunc ),

            ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );
        return tFuncTypePat;
    }

    tuple< Term, Term > DecomposeTFTSig( const Term& tdsig )
    {
        auto result = Decompose( tdsig,
            Vec(
                Lit( "tfunctype_sig"_sid ),
                SubTerm(),
                SubTerm()
            )
        );

        auto&& [type,sig] = *result;
        return { type, sig };
    }
}


namespace goose::eir
{
    const Term& Bridge< TFuncType >::Type()
    {
        return TypeType();
    }

    Value Bridge< TFuncType >::ToValue( const TFuncType& tft )
    {
        return Value( Type(), VEC( TSID( texpr ), TSID( tfunc ),
            tft.returnType(), tft.params(),
            static_pointer_cast< void >( tft.preConds() ),
            static_pointer_cast< void >( tft.postConds() ),
            TERM( static_cast< uint64_t >( tft.kind() ) ) ) );
    }

    optional< TFuncType > Bridge< TFuncType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),
            Vec(
                Lit( "texpr"_sid ),
                Lit( "tfunc"_sid ),
                SubTerm(),  // return type
                SubTerm(),  // param types
                Val< ptr< void > >(),  // propositions
                Val< ptr< void > >(),  // propositions
                Val< uint64_t >() // kind
            )
        );

        if( !result )
            return nullopt;

        auto&& [rtype, params, preConds, postConds, kind] = *result;
        return TFuncType( rtype, params,
            static_pointer_cast< Propositions >( preConds ),
            static_pointer_cast< Propositions >( postConds ),
            static_cast< FuncType::Kind >( kind ) );
    }
}












<
|
<
|
|
|
|
|
|
<











|
|
<
|






|
>
|





|
<
<
<
<
|
<
<
|


<
>










|
|
|
|
|





<
|
<
|
|
|
|

|
<





|
<



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

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

namespace goose::builtins
{
    bool IsTFuncType( const Value& t )
    {
        auto result = Decompose( t.val(),

            Vec( Lit( "texpr"_sid ), Lit( "tfunc"_sid ),

                SubTerm(), // return type
                SubTerm(), // param types
                SubTerm(), // verif stmts toks
                SubTerm(), // verif stmts toks
                SubTerm() // intrinsic flag
                ) );


        return !!result;
    }

    bool IsTFunc( const Value& t )
    {
        return IsTFuncType( *EIRToValue( t.type() ) );
    }

    const Term& TFuncPattern::GetPattern()
    {
        static auto pattern = ValueToEIR( Value( TypeType(),
            VEC( TSID( texpr ), TSID( tfunc ), HOLE( "_"_sid ), HOLE( "_"_sid ), HOLE( "_"_sid ),

                HOLE( "_"_sid ), HOLE( "_"_sid ) ) ) );

        return pattern;
    }

    const Term& TFuncTypePattern()
    {
        static auto tFuncTypePat = ValueToEIR( Value( TypeType(),
            VEC( TSID( texpr ), TSID( tfunc ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ),
                ANYTERM( _ ), ANYTERM( _ ) ) ) );
        return tFuncTypePat;
    }

    tuple< Term, Term > DecomposeTFTSig( const Term& tdsig )
    {
        auto result = Decompose( tdsig, Vec( Lit( "tfunctype_sig"_sid ), SubTerm(), SubTerm() ) );







        auto&& [type, sig] = *result;
        return { type, sig };
    }

} // namespace goose::builtins

namespace goose::eir
{
    const Term& Bridge< TFuncType >::Type()
    {
        return TypeType();
    }

    Value Bridge< TFuncType >::ToValue( const TFuncType& tft )
    {
        return Value( Type(),
            VEC( TSID( texpr ), TSID( tfunc ), tft.returnType(), tft.params(),
                static_pointer_cast< void >( tft.preConds() ),
                static_pointer_cast< void >( tft.postConds() ),
                TERM( static_cast< uint64_t >( tft.kind() ) ) ) );
    }

    optional< TFuncType > Bridge< TFuncType >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),

            Vec( Lit( "texpr"_sid ), Lit( "tfunc"_sid ),

                SubTerm(), // return type
                SubTerm(), // param types
                Val< ptr< void > >(), // propositions
                Val< ptr< void > >(), // propositions
                Val< uint64_t >() // kind
                ) );


        if( !result )
            return nullopt;

        auto&& [rtype, params, preConds, postConds, kind] = *result;
        return TFuncType( rtype, params, static_pointer_cast< Propositions >( preConds ),

            static_pointer_cast< Propositions >( postConds ),
            static_cast< FuncType::Kind >( kind ) );
    }
} // namespace goose::eir
Changes to bs/builtins/types/template/tfunctype.h.
1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17


18
19
20
21
22
23
24


25

26
27
28

29
30
31

32
33
34

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TFUNCTYPE_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TFUNCTYPE_H

namespace goose::builtins
{
    class TFuncType
    {
        public:
            template< typename R, typename P, typename PRE, typename POST >
            TFuncType( R&& returnType, P&& params, PRE&& preConds, POST&& postConds, FuncType::Kind kind = FuncType::Kind::Regular ) :

                m_returnType( forward< R >( returnType ) ),
                m_params( forward< P >( params ) ),
                m_preConds( forward< PRE >( preConds ) ),
                m_postConds( forward< POST >( postConds ) ),
                m_kind( kind )
            {}



            template< typename R, typename P >
            TFuncType( R&& returnType, P&& params, FuncType::Kind kind = FuncType::Kind::Regular ) :
                m_returnType( forward< R >( returnType ) ),
                m_params( forward< P >( params ) ),
                m_kind( kind )
            {}



            const auto& returnType() const { return m_returnType; }

            const auto& params() const { return m_params; }

            const auto& preConds() const { return m_preConds; }

            const auto& postConds() const { return m_postConds; }

            auto& preConds() { return m_preConds; }

            auto& postConds() { return m_postConds; }

            auto kind() const { return m_kind; }

            void setKind( FuncType::Kind k ) { m_kind = k; }

        private:
            Term m_returnType;
            Term m_params;

            ptr< Propositions > m_preConds = make_shared< Propositions >();
            ptr< Propositions > m_postConds = make_shared< Propositions >();

            FuncType::Kind m_kind = FuncType::Kind::Regular;
    };

    extern bool IsTFuncType( const Value& t );
    extern bool IsTFunc( const Value& t );

    // Helper to provide generic param patterns for template functions.
    struct TFuncPattern
    {
        static const Term& GetPattern();
    };

    extern const Term& TFuncTypePattern();

    static inline Term TFuncTypeSigPattern( const Term& type, Term&& sig )
    {
        return VEC( TSID( tfunctype_sig ), type, move( sig ) );
    }

    extern tuple< Term, Term > DecomposeTFTSig( const Term& tdsig );
}

namespace goose::eir
{
    template<>
    struct Bridge< builtins::TFuncType >
    {
        static const Term& Type();
        static Value ToValue( const builtins::TFuncType& tft );
        static optional< builtins::TFuncType > FromValue( const Value& v );
    };
}

#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
64
65
66
67
68
69
70
71
72
73
74

75
76
77
78
79
80
81
82
83
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TFUNCTYPE_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TFUNCTYPE_H

namespace goose::builtins
{
    class TFuncType
    {
      public:
        template< typename R, typename P, typename PRE, typename POST >
        TFuncType( R&& returnType, P&& params, PRE&& preConds, POST&& postConds,
            FuncType::Kind kind = FuncType::Kind::Regular ) :
            m_returnType( forward< R >( returnType ) ),
            m_params( forward< P >( params ) ),
            m_preConds( forward< PRE >( preConds ) ),
            m_postConds( forward< POST >( postConds ) ),
            m_kind( kind )

        {
        }

        template< typename R, typename P >
        TFuncType( R&& returnType, P&& params, FuncType::Kind kind = FuncType::Kind::Regular ) :
            m_returnType( forward< R >( returnType ) ),
            m_params( forward< P >( params ) ),
            m_kind( kind )

        {
        }

        const auto& returnType() const { return m_returnType; }

        const auto& params() const { return m_params; }

        const auto& preConds() const { return m_preConds; }

        const auto& postConds() const { return m_postConds; }

        auto& preConds() { return m_preConds; }

        auto& postConds() { return m_postConds; }

        auto kind() const { return m_kind; }

        void setKind( FuncType::Kind k ) { m_kind = k; }

      private:
        Term m_returnType;
        Term m_params;

        ptr< Propositions > m_preConds = make_shared< Propositions >();
        ptr< Propositions > m_postConds = make_shared< Propositions >();

        FuncType::Kind m_kind = FuncType::Kind::Regular;
    };

    extern bool IsTFuncType( const Value& t );
    extern bool IsTFunc( const Value& t );

    // Helper to provide generic param patterns for template functions.
    struct TFuncPattern
    {
        static const Term& GetPattern();
    };

    extern const Term& TFuncTypePattern();

    static inline Term TFuncTypeSigPattern( const Term& type, Term&& sig )
    {
        return VEC( TSID( tfunctype_sig ), type, move( sig ) );
    }

    extern tuple< Term, Term > DecomposeTFTSig( const Term& tdsig );
} // namespace goose::builtins

namespace goose::eir
{

    template<> struct Bridge< builtins::TFuncType >
    {
        static const Term& Type();
        static Value ToValue( const builtins::TFuncType& tft );
        static optional< builtins::TFuncType > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/template/tnameddecl.cpp.
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
    }

    const Term& TNamedDeclWithInit::Pattern::GetPattern()
    {
        static auto pattern = GetValueType< TNamedDeclWithInit >();
        return pattern;
    }
}

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

        static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( tnameddecl ) ) ) );
        return type;
    }

    Value Bridge< TNamedDecl >::ToValue( const TNamedDecl& td )
    {
        return Value( Type(), VEC( td.type(), TERM( td.name() ) ) );
    }

    optional< TNamedDecl > Bridge< TNamedDecl >::FromValue( const Value& v )
    {
        if( !IsTNamedDecl( v ) )
            return nullopt;

        auto result = Decompose( v.val(),
            Vec(
                SubTerm(),
                Val< StringId >()
            )
        );

        if( !result )
            return nullopt;

        auto&& [type,name] = *result;

        return TNamedDecl( type, name );
    }

    const Term& Bridge< TNamedDeclWithInit >::Type()
    {
        static auto type = ValueToEIR( Value( TypeType(), TSID( tnameddecl_with_init ) ) );
        return type;
    }

    Value Bridge< TNamedDeclWithInit >::ToValue( const TNamedDeclWithInit& td )
    {
        return Value( Type(), VEC( td.type(), td.name(), ValueToEIR( td.init() ), td.declLoc() ) );
    }

    optional< TNamedDeclWithInit > Bridge< TNamedDeclWithInit >::FromValue( const Value& v )
    {
        if( v.type() != GetValueType< TNamedDeclWithInit >() )
            return nullopt;

        auto result = Decompose( v.val(),
            Vec(
                SubTerm(),
                Val< StringId >(),
                SubTerm(),
                Val< LocationId >()
            )
        );

        if( !result )
            return nullopt;

        auto&& [type,name,init,declLoc] = *result;

        return TNamedDeclWithInit( move( type ), name, *EIRToValue( init ), declLoc );
    }
}







|





>
|













|
<
<
<
<
<




|




















|
<
<
<
<
|
<
<




|



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

    const Term& TNamedDeclWithInit::Pattern::GetPattern()
    {
        static auto pattern = GetValueType< TNamedDeclWithInit >();
        return pattern;
    }
} // namespace goose::builtins

namespace goose::eir
{
    const Term& Bridge< TNamedDecl >::Type()
    {
        static auto type =
            ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( tnameddecl ) ) ) );
        return type;
    }

    Value Bridge< TNamedDecl >::ToValue( const TNamedDecl& td )
    {
        return Value( Type(), VEC( td.type(), TERM( td.name() ) ) );
    }

    optional< TNamedDecl > Bridge< TNamedDecl >::FromValue( const Value& v )
    {
        if( !IsTNamedDecl( v ) )
            return nullopt;

        auto result = Decompose( v.val(), Vec( SubTerm(), Val< StringId >() ) );






        if( !result )
            return nullopt;

        auto&& [type, name] = *result;

        return TNamedDecl( type, name );
    }

    const Term& Bridge< TNamedDeclWithInit >::Type()
    {
        static auto type = ValueToEIR( Value( TypeType(), TSID( tnameddecl_with_init ) ) );
        return type;
    }

    Value Bridge< TNamedDeclWithInit >::ToValue( const TNamedDeclWithInit& td )
    {
        return Value( Type(), VEC( td.type(), td.name(), ValueToEIR( td.init() ), td.declLoc() ) );
    }

    optional< TNamedDeclWithInit > Bridge< TNamedDeclWithInit >::FromValue( const Value& v )
    {
        if( v.type() != GetValueType< TNamedDeclWithInit >() )
            return nullopt;

        auto result = Decompose(




            v.val(), Vec( SubTerm(), Val< StringId >(), SubTerm(), Val< LocationId >() ) );



        if( !result )
            return nullopt;

        auto&& [type, name, init, declLoc] = *result;

        return TNamedDeclWithInit( move( type ), name, *EIRToValue( init ), declLoc );
    }
} // namespace goose::eir
Changes to bs/builtins/types/template/tnameddecl.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14


15

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38


39

40

41

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TNAMEDDECL_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TNAMEDDECL_H

namespace goose::builtins
{
    class TNamedDecl
    {
        public:
            template< typename T >
            TNamedDecl( T&& type, StringId name ) :
                m_type( forward< T >( type ) ),
                m_name( name )
            {}



            const auto& type() const { return m_type; }

            const auto& name() const { return m_name; }

            struct Pattern
            {
                static const Term& GetPattern();
            };

        private:
            Term m_type;
            StringId m_name;
    };

    class TNamedDeclWithInit
    {
        public:
            template< typename T, typename V >
            TNamedDeclWithInit( T&& type, StringId name, V&& init, LocationId declLoc ) :
                m_init( forward< V >( init ) ),
                m_type( forward< T >( type ) ),
                m_name( name ),
                m_declLoc( declLoc )
            {}



            const auto& type() const { return m_type; }

            const auto& name() const { return m_name; }

            const auto& init() const { return m_init; }

            auto declLoc() const { return m_declLoc; }

            struct Pattern
            {
                static const Term& GetPattern();
            };

        private:
            Value m_init;
            Term m_type;
            StringId m_name;
            LocationId m_declLoc;
    };

    extern bool IsTNamedDecl( const Value& td );
}


namespace goose::eir
{
    template<>
    struct Bridge< builtins::TNamedDecl >
    {
        static const Term& Type();
        static Value ToValue( const builtins::TNamedDecl& td );
        static optional< builtins::TNamedDecl > FromValue( const Value& v );
    };

    template<>
    struct Bridge< builtins::TNamedDeclWithInit >
    {
        static const Term& Type();
        static Value ToValue( const builtins::TNamedDeclWithInit& td );
        static optional< builtins::TNamedDeclWithInit > FromValue( const Value& v );
    };
}

#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
64
65
66

67
68
69
70
71
72
73

74
75
76
77
78
79
80
81
82
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TNAMEDDECL_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TNAMEDDECL_H

namespace goose::builtins
{
    class TNamedDecl
    {
      public:
        template< typename T >
        TNamedDecl( T&& type, StringId name ) :
            m_type( forward< T >( type ) ),
            m_name( name )

        {
        }

        const auto& type() const { return m_type; }

        const auto& name() const { return m_name; }

        struct Pattern
        {
            static const Term& GetPattern();
        };

      private:
        Term m_type;
        StringId m_name;
    };

    class TNamedDeclWithInit
    {
      public:
        template< typename T, typename V >
        TNamedDeclWithInit( T&& type, StringId name, V&& init, LocationId declLoc ) :
            m_init( forward< V >( init ) ),
            m_type( forward< T >( type ) ),
            m_name( name ),
            m_declLoc( declLoc )

        {
        }

        const auto& type() const { return m_type; }

        const auto& name() const { return m_name; }

        const auto& init() const { return m_init; }

        auto declLoc() const { return m_declLoc; }

        struct Pattern
        {
            static const Term& GetPattern();
        };

      private:
        Value m_init;
        Term m_type;
        StringId m_name;
        LocationId m_declLoc;
    };

    extern bool IsTNamedDecl( const Value& td );

} // namespace goose::builtins

namespace goose::eir
{

    template<> struct Bridge< builtins::TNamedDecl >
    {
        static const Term& Type();
        static Value ToValue( const builtins::TNamedDecl& td );
        static optional< builtins::TNamedDecl > FromValue( const Value& v );
    };


    template<> struct Bridge< builtins::TNamedDeclWithInit >
    {
        static const Term& Type();
        static Value ToValue( const builtins::TNamedDeclWithInit& td );
        static optional< builtins::TNamedDeclWithInit > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/template/tpack.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "builtins/builtins.h"

using namespace goose::builtins;

namespace goose::builtins
{
    bool IsTPack( const Value& tp )
    {
        return tp.type() == GetValueType< TPack >();
    }
}

namespace goose::eir
{
    const Term& Bridge< TPack >::Type()
    {
        static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( tpack ) ) ) );
        return type;










|







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

using namespace goose::builtins;

namespace goose::builtins
{
    bool IsTPack( const Value& tp )
    {
        return tp.type() == GetValueType< TPack >();
    }
} // namespace goose::builtins

namespace goose::eir
{
    const Term& Bridge< TPack >::Type()
    {
        static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( tpack ) ) ) );
        return type;
26
27
28
29
30
31
32
33
    optional< TPack > Bridge< TPack >::FromValue( const Value& v )
    {
        if( !IsTPack( v ) )
            return nullopt;

        return TPack( v.val() );
    }
}







|
26
27
28
29
30
31
32
33
    optional< TPack > Bridge< TPack >::FromValue( const Value& v )
    {
        if( !IsTPack( v ) )
            return nullopt;

        return TPack( v.val() );
    }
} // namespace goose::eir
Changes to bs/builtins/types/template/tpack.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
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_PACK_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_PACK_H

namespace goose::builtins
{
    class TPack
    {
        public:
            template< typename T >
            TPack( T&& pattern ) :
                m_pattern( forward< T >( pattern ) )
            {}



            const auto& pattern() const { return m_pattern; }

        private:
            Term m_pattern;
    };

    extern bool IsTPack( const Value& tp );
}

namespace goose::eir
{
    template<>
    struct Bridge< builtins::TPack >
    {
        static const Term& Type();
        static Value ToValue( const builtins::TPack& tp );
        static optional< builtins::TPack > FromValue( const Value& v );
    };
}

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

namespace goose::builtins
{
    class TPack
    {
      public:
        template< typename T >
        TPack( T&& pattern ) :
            m_pattern( forward< T >( pattern ) )

        {
        }

        const auto& pattern() const { return m_pattern; }

      private:
        Term m_pattern;
    };

    extern bool IsTPack( const Value& tp );
} // namespace goose::builtins

namespace goose::eir
{

    template<> struct Bridge< builtins::TPack >
    {
        static const Term& Type();
        static Value ToValue( const builtins::TPack& tp );
        static optional< builtins::TPack > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/template/ttvar.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
    StringId DecorateTTVarName( StringId name )
    {
        if( name == "_"_sid )
            return name;

        return StringId{ "$$" + name.str() };
    }
}

namespace goose::eir
{
    const Term& Bridge< TTVar >::Type()
    {
        static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( ttvar ) ) ) );
        return type;
    }

    Value Bridge< TTVar >::ToValue( TTVar&& td )
    {
        return Value( Type(), TERM( td.name() ) );
    }

    optional< TTVar > Bridge< TTVar >::FromValue( const Value& v )
    {
        if( !IsTTVar( v ) )
            return nullopt;

        auto result = Decompose( v.val(),
            Val< StringId >()
        );

        if( !result )
            return nullopt;

        auto&& name = *result;
        return TTVar( name );
    }
}







|



















|
<
<







|
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
    StringId DecorateTTVarName( StringId name )
    {
        if( name == "_"_sid )
            return name;

        return StringId{ "$$" + name.str() };
    }
} // namespace goose::builtins

namespace goose::eir
{
    const Term& Bridge< TTVar >::Type()
    {
        static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( ttvar ) ) ) );
        return type;
    }

    Value Bridge< TTVar >::ToValue( TTVar&& td )
    {
        return Value( Type(), TERM( td.name() ) );
    }

    optional< TTVar > Bridge< TTVar >::FromValue( const Value& v )
    {
        if( !IsTTVar( v ) )
            return nullopt;

        auto result = Decompose( v.val(), Val< StringId >() );



        if( !result )
            return nullopt;

        auto&& name = *result;
        return TTVar( name );
    }
} // namespace goose::eir
Changes to bs/builtins/types/template/ttvar.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
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TTVAR_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TTVAR_H

namespace goose::builtins
{
    class TTVar
    {
        public:
            TTVar( StringId name ) :
                m_name( name )
            {}



            const auto& name() const { return m_name; }

        private:
            StringId m_name;
    };

    extern bool IsTTVar( const Value& tv );
    extern StringId DecorateTTVarName( StringId name );
}

namespace goose::eir
{
    template<>
    struct Bridge< builtins::TTVar >
    {
        static const Term& Type();
        static Value ToValue( builtins::TTVar&& tv );
        static optional< builtins::TTVar > FromValue( const Value& v );
    };
}

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

namespace goose::builtins
{
    class TTVar
    {
      public:
        TTVar( StringId name ) :
            m_name( name )

        {
        }

        const auto& name() const { return m_name; }

      private:
        StringId m_name;
    };

    extern bool IsTTVar( const Value& tv );
    extern StringId DecorateTTVarName( StringId name );
} // namespace goose::builtins

namespace goose::eir
{

    template<> struct Bridge< builtins::TTVar >
    {
        static const Term& Type();
        static Value ToValue( builtins::TTVar&& tv );
        static optional< builtins::TTVar > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/template/tvar.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
    StringId DecorateTVarName( StringId name )
    {
        if( name == "_"_sid )
            return name;

        return StringId{ "$" + name.str() };
    }
}

namespace goose::eir
{
    const Term& Bridge< TVar >::Type()
    {
        static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( tvar ) ) ) );
        return type;
    }

    Value Bridge< TVar >::ToValue( TVar&& td )
    {
        return Value( Type(), TERM( td.name() ) );
    }

    optional< TVar > Bridge< TVar >::FromValue( const Value& v )
    {
        if( !IsTVar( v ) )
            return nullopt;

        auto result = Decompose( v.val(),
            Val< StringId >()
        );

        if( !result )
            return nullopt;

        auto&& name = *result;
        return TVar( name );
    }
}







|



















|
<
<







|
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
    StringId DecorateTVarName( StringId name )
    {
        if( name == "_"_sid )
            return name;

        return StringId{ "$" + name.str() };
    }
} // namespace goose::builtins

namespace goose::eir
{
    const Term& Bridge< TVar >::Type()
    {
        static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( tvar ) ) ) );
        return type;
    }

    Value Bridge< TVar >::ToValue( TVar&& td )
    {
        return Value( Type(), TERM( td.name() ) );
    }

    optional< TVar > Bridge< TVar >::FromValue( const Value& v )
    {
        if( !IsTVar( v ) )
            return nullopt;

        auto result = Decompose( v.val(), Val< StringId >() );



        if( !result )
            return nullopt;

        auto&& name = *result;
        return TVar( name );
    }
} // namespace goose::eir
Changes to bs/builtins/types/template/tvar.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
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TVAR_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TVAR_H

namespace goose::builtins
{
    class TVar
    {
        public:
            TVar( StringId name ) :
                m_name( name )
            {}



            const auto& name() const { return m_name; }

        private:
            StringId m_name;
    };

    extern bool IsTVar( const Value& tv );

    extern StringId DecorateTVarName( StringId name );
}

namespace goose::eir
{
    template<>
    struct Bridge< builtins::TVar >
    {
        static const Term& Type();
        static Value ToValue( builtins::TVar&& tv );
        static optional< builtins::TVar > FromValue( const Value& v );
    };
}

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

namespace goose::builtins
{
    class TVar
    {
      public:
        TVar( StringId name ) :
            m_name( name )

        {
        }

        const auto& name() const { return m_name; }

      private:
        StringId m_name;
    };

    extern bool IsTVar( const Value& tv );

    extern StringId DecorateTVarName( StringId name );
} // namespace goose::builtins

namespace goose::eir
{

    template<> struct Bridge< builtins::TVar >
    {
        static const Term& Type();
        static Value ToValue( builtins::TVar&& tv );
        static optional< builtins::TVar > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/template/tvec.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
#include "builtins/builtins.h"

// Now that TVecs are properly recognized as TExpr,
// we get an infinite loop during localvar local type inference
// because it contains a MutRef[$T]. This somehow causes something to be wrapped
// into a TVec instead of a Vec at some point because it contained that reference type,
// still as a TVec at that point
//
// should probably try to break in BuildVecOrTVec
// wherever it is that one of the terms is itself a TVec to understand
// how this happens and why this is an issue

namespace goose::builtins
{
    Term TVecToEIR( const TVec& tv )
    {
        return VEC( TSID( texpr ), TSID( tvec ), tv.content() );
    }

    optional< TVec > TVecFromEIR( const Term& t )
    {
        auto result = Decompose( t,
            Vec(
                Lit( "texpr"_sid ),
                Lit( "tvec"_sid ),
                Val< pvec >()
            )
        );

        if( !result )
            return nullopt;

        auto&& [v] = *result;
        return TVec( v );
    }
}


|
<
|
|
|

|
<
|










|
<
<
<
<
<
<







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

// Now that TVecs are properly recognized as TExpr, we get an infinite loop during localvar local

// type inference because it contains a MutRef[$T]. This somehow causes something to be wrapped into
// a TVec instead of a Vec at some point because it contained that reference type, still as a TVec
// at that point
//
// should probably try to break in BuildVecOrTVec wherever it is that one of the terms is itself a

// TVec to understand how this happens and why this is an issue

namespace goose::builtins
{
    Term TVecToEIR( const TVec& tv )
    {
        return VEC( TSID( texpr ), TSID( tvec ), tv.content() );
    }

    optional< TVec > TVecFromEIR( const Term& t )
    {
        auto result = Decompose( t, Vec( Lit( "texpr"_sid ), Lit( "tvec"_sid ), Val< pvec >() ) );







        if( !result )
            return nullopt;

        auto&& [v] = *result;
        return TVec( v );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/template/tvec.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
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TVEC_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TVEC_H

namespace goose::builtins
{
    class TVec
    {
        public:
            template< typename T >
            TVec( T&& content ) :
                m_content( forward< T >( content ) )
            {}



            const auto& content() const { return m_content; }

        private:
            pvec m_content;
    };

    extern Term TVecToEIR( const TVec& tv );
    extern optional< TVec > TVecFromEIR( const Term& t );

    // Construct either a normal Vector term or a TVec.
    // If any of the provided term is a TExpr, construct a TVec.
    //
    // This is used to construct eir expressions for parametric types,
    // so that they are automatically turned into a suitable TExpr if
    // TExprs are passed as parameters.
    template< typename... T >
    extern Term BuildVecOrTVec( T&&... terms )
    {
        auto vec = Vector::Make( terms... );

        if( ( IsTExpr( EIRToValue( terms ) ) || ... ) )
            return TVecToEIR( TVec( move( vec ) ) );

        return vec;
    }

    #define TVEC( ... ) BuildVecOrTVec( __VA_ARGS__ )
}

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

namespace goose::builtins
{
    class TVec
    {
      public:
        template< typename T >
        TVec( T&& content ) :
            m_content( forward< T >( content ) )

        {
        }

        const auto& content() const { return m_content; }

      private:
        pvec m_content;
    };

    extern Term TVecToEIR( const TVec& tv );
    extern optional< TVec > TVecFromEIR( const Term& t );

    // Construct either a normal Vector term or a TVec.
    // If any of the provided term is a TExpr, construct a TVec.
    //
    // This is used to construct eir expressions for parametric types, so that they are
    // automatically turned into a suitable TExpr if TExprs are passed as parameters.

    template< typename... T > extern Term BuildVecOrTVec( T&&... terms )

    {
        auto vec = Vector::Make( terms... );

        if( ( IsTExpr( EIRToValue( terms ) ) || ... ) )
            return TVecToEIR( TVec( move( vec ) ) );

        return vec;
    }

#define TVEC( ... ) BuildVecOrTVec( __VA_ARGS__ )
} // namespace goose::builtins

#endif
Changes to bs/builtins/types/template/typecheck.cpp.
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
    void SetupTemplateFunctionTypeChecking( Env& e )
    {
        // func type param / tfunc arg
        e.typeCheckingRuleSet()->addTypeCheckingRule(

            ParamPat( FuncTypePattern() ),

            ValueToEIR( ValuePattern(
                TSID( constant ),
                TFuncTypePattern(),
                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( *EIRToValue( type ) );

            auto rhsVal = *EIRToValue( 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 savedRHSSubContext = tcc.RHSSubContext();
            auto localNSId = tcc.newNamespaceIndex();
            tcc.RHSSubContext().namespaceIndex = localNSId;

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

                auto wrapped = WrapWithPostprocFunc( s, [rhsVal,localNSId]( const Term& t, TypeCheckingContext tcc )

                    -> optional< Term >
                {
                    tcc.RHSSubContext().namespaceIndex = localNSId;

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

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

                    if( ifunc.isPoison() )
                        return nullopt;

                    return ValueToEIR( ifunc );
                } );

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

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

            ParamPat( TFuncTypeSigPattern( ANYTERM( _ ), ANYTERM( _ ) ) ),

            ValueToEIR( ValuePattern(
                TSID( constant ),
                TFuncTypePattern(),
                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 [tfType,_] = DecomposeTFTSig( type );
            auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *EIRToValue( tfType ) );
            assert( callPat );

            auto rhsVal = *EIRToValue( 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 savedRHSSubContext = tcc.RHSSubContext();
            auto localNSId = tcc.newNamespaceIndex();
            tcc.RHSSubContext().namespaceIndex = localNSId;

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

                auto wrapped = WrapWithPostprocFunc( s, [rhsVal,localNSId]( const Term& t, TypeCheckingContext tcc )

                    -> optional< Term >
                {
                    tcc.RHSSubContext().namespaceIndex = localNSId;

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

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

                    if( ifunc.isPoison() )
                        return nullopt;

                    return ValueToEIR( ifunc );
                } );

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







|
<
<
<

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

|
|

|
|

|
|
|
|
|

|
|
|
|

|
>
|
|
|

|
>
|

|
|

|
|

|
|
|






|
<
<
<

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

|
|
|
|

|
|

|
|
|
|
|

|
|
|
|

|
>
|
|
|

|
>
|

|
|

|
|

|
|
|

|
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
    void SetupTemplateFunctionTypeChecking( Env& e )
    {
        // func type param / tfunc arg
        e.typeCheckingRuleSet()->addTypeCheckingRule(

            ParamPat( FuncTypePattern() ),

            ValueToEIR( ValuePattern( TSID( constant ), TFuncTypePattern(), 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( *EIRToValue( type ) );

                auto rhsVal = *EIRToValue( 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 savedRHSSubContext = tcc.RHSSubContext();
                auto localNSId = tcc.newNamespaceIndex();
                tcc.RHSSubContext().namespaceIndex = localNSId;

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

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

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

                            if( ifunc.isPoison() )
                                return nullopt;

                            return ValueToEIR( ifunc );
                        } );

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

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

            ParamPat( TFuncTypeSigPattern( ANYTERM( _ ), ANYTERM( _ ) ) ),

            ValueToEIR( ValuePattern( TSID( constant ), TFuncTypePattern(), 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 [tfType, _] = DecomposeTFTSig( type );
                auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *EIRToValue( tfType ) );
                assert( callPat );

                auto rhsVal = *EIRToValue( 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 savedRHSSubContext = tcc.RHSSubContext();
                auto localNSId = tcc.newNamespaceIndex();
                tcc.RHSSubContext().namespaceIndex = localNSId;

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

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

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

                            if( ifunc.isPoison() )
                                return nullopt;

                            return ValueToEIR( ifunc );
                        } );

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

{
    void SetupTrivialChecks( Env& e )
    {
        using TypeAny = TypePatternParam< Value, ValuePatternAny >;
        using TypeT = TypePatternParam< Value, ValuePatternT >;

        // Default implementations for IsTrivialInitialization.
        // If lhs and rhs are the same type, we consider the assignment trivial by default. If they differ, then it can't be trivial.

        RegisterBuiltinFunc< Eager< bool >( TypeAny, TypeAny ) >( e, e.extIsTrivialInitialization(),
            []( const Value& lhsType, const Value& rhsType )
            {
                return false;
            } );

        RegisterBuiltinFunc< Eager< bool >( TypeT, TypeT ) >( e, e.extIsTrivialInitialization(),
            []( const Value& lhsType, const Value& rhsType )
            {
                return true;
            } );

        // Overload for tuples
        using TupleTypeAny = TypePatternParam< Value, TuplePattern >;

        RegisterBuiltinFunc< Intrinsic< bool ( TupleTypeAny, TupleTypeAny ) > >( e, e.extIsTrivialInitialization(),
            []( const Context& c, const Value& lhsTupType, const Value& rhsTupType )
            {
                return ToValue( IsTrivialTupleInitialization( c, lhsTupType, rhsTupType ) );
            } );

        // Default implementations for IsTrivialAssignment.
        // If lhs and rhs are the same type, we consider the assignment trivial by default. If they differ, then it can't be trivial.

        RegisterBuiltinFunc< Eager< bool >( TypeAny, TypeAny ) >( e, e.extIsTrivialAssignment(),
            []( const Value& lhsType, const Value& rhsType )
            {
                return false;
            } );

        RegisterBuiltinFunc< Eager< bool >( TypeT, TypeT ) >( e, e.extIsTrivialAssignment(),
            []( const Value& lhsType, const Value& rhsType )
            {
                return true;
            } );

        // Overload for tuples
        using TupleTypeAny = TypePatternParam< Value, TuplePattern >;
        RegisterBuiltinFunc< Intrinsic< bool ( TupleTypeAny, TupleTypeAny ) > >( e, e.extIsTrivialAssignment(),

            []( const Context& c, const Value& lhsTupType, const Value& rhsTupType )
            {
                return ToValue( IsTrivialTupleAssignment( c, lhsTupType, rhsTupType ) );
            } );
    }
}








|
>

|
|
<
<
<

|
|
<
<
<


>
|

<
|
<


|
>

|
|
<
<
<

|
|
<
<
<


|
>

<
|
<

<
>
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
{
    void SetupTrivialChecks( Env& e )
    {
        using TypeAny = TypePatternParam< Value, ValuePatternAny >;
        using TypeT = TypePatternParam< Value, ValuePatternT >;

        // Default implementations for IsTrivialInitialization.
        // If lhs and rhs are the same type, we consider the assignment trivial by default. If they
        // differ, then it can't be trivial.
        RegisterBuiltinFunc< Eager< bool >( TypeAny, TypeAny ) >( e, e.extIsTrivialInitialization(),
            []( const Value& lhsType, const Value& rhsType ) { return false; } );




        RegisterBuiltinFunc< Eager< bool >( TypeT, TypeT ) >( e, e.extIsTrivialInitialization(),
            []( const Value& lhsType, const Value& rhsType ) { return true; } );




        // Overload for tuples
        using TupleTypeAny = TypePatternParam< Value, TuplePattern >;
        RegisterBuiltinFunc< Intrinsic< bool( TupleTypeAny, TupleTypeAny ) > >( e,
            e.extIsTrivialInitialization(),
            []( const Context& c, const Value& lhsTupType, const Value& rhsTupType )

            { return ToValue( IsTrivialTupleInitialization( c, lhsTupType, rhsTupType ) ); } );


        // Default implementations for IsTrivialAssignment.
        // If lhs and rhs are the same type, we consider the assignment trivial by default. If they
        // differ, then it can't be trivial.
        RegisterBuiltinFunc< Eager< bool >( TypeAny, TypeAny ) >( e, e.extIsTrivialAssignment(),
            []( const Value& lhsType, const Value& rhsType ) { return false; } );




        RegisterBuiltinFunc< Eager< bool >( TypeT, TypeT ) >( e, e.extIsTrivialAssignment(),
            []( const Value& lhsType, const Value& rhsType ) { return true; } );




        // Overload for tuples
        using TupleTypeAny = TypePatternParam< Value, TuplePattern >;
        RegisterBuiltinFunc< Intrinsic< bool( TupleTypeAny, TupleTypeAny ) > >( e,
            e.extIsTrivialAssignment(),
            []( const Context& c, const Value& lhsTupType, const Value& rhsTupType )

            { return ToValue( IsTrivialTupleAssignment( c, lhsTupType, rhsTupType ) ); } );

    }

} // namespace goose::builtins
Changes to bs/builtins/types/tuple/destroy.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

#include "builtins/builtins.h"
#include "parse/parse.h"

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

namespace goose::builtins
{
    void SetupTupleDestroyValue( Env& e )
    {
        // DestroyValue() for tuples: destroy every member value.
        RegisterBuiltinFunc< Intrinsic< void ( CustomPattern< Value, TuplePattern > ) > >( e, e.extDestroyValue(),

            []( const Context& c, const Value& tup )
            {
                if( !tup.isConstant() )
                    return;

                ForEachInTuple( tup, [&]( auto&& v )

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

                    if( !result.isPoison() )
                    {
                        DiagnosticsContext dc2( v.locationId(), "When invoking _DropValue." );
                        InvokeOverloadSet( c, c.env()->extDropValue(),
                            MakeClosedTuple(  c.builder(), move( result ) ) );
                    }

                    return true;
                } );
            } );
    }
}












|
>





|
>
|
|
|
|

|
|
|
|
|
|

|
|


<
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

37
#include "builtins/builtins.h"
#include "parse/parse.h"

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

namespace goose::builtins
{
    void SetupTupleDestroyValue( Env& e )
    {
        // DestroyValue() for tuples: destroy every member value.
        RegisterBuiltinFunc< Intrinsic< void( CustomPattern< Value, TuplePattern > ) > >( e,
            e.extDestroyValue(),
            []( const Context& c, const Value& tup )
            {
                if( !tup.isConstant() )
                    return;

                ForEachInTuple( tup,
                    [&]( auto&& v )
                    {
                        DiagnosticsContext dc( v.locationId(), "When invoking _DestroyValue." );
                        auto result = InvokeOverloadSet(
                            c, c.env()->extDestroyValue(), MakeClosedTuple( v ) );

                        if( !result.isPoison() )
                        {
                            DiagnosticsContext dc2( v.locationId(), "When invoking _DropValue." );
                            InvokeOverloadSet( c, c.env()->extDropValue(),
                                MakeClosedTuple( c.builder(), move( result ) ) );
                        }

                        return true;
                    } );
            } );
    }

} // namespace goose::builtins
Changes to bs/builtins/types/tuple/drop.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
#include "builtins/builtins.h"
#include "parse/parse.h"

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

namespace goose::builtins
{
    void SetupTupleDropValue( Env& e )
    {
        // DropValue() for tuples: drop every member value.
        RegisterBuiltinFunc< Intrinsic< void ( Value, CustomPattern< Value, TuplePattern > ) > >( e, e.extDropValue(),

            []( const Context& c, const Value& b, const Value& tup )
            {
                if( !tup.isConstant() )
                    return;

                ForEachInTuple( tup, [&]( auto&& v )

                {
                    DiagnosticsContext dc( v.locationId(), "When invoking _DropValue." );
                    InvokeOverloadSet( c, c.env()->extDropValue(),
                        MakeClosedTuple( b, v ) );
                    return true;
                } );
            } );
    }
}











|
>





|
>
|
|
|
<
|
|


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

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

namespace goose::builtins
{
    void SetupTupleDropValue( Env& e )
    {
        // DropValue() for tuples: drop every member value.
        RegisterBuiltinFunc< Intrinsic< void( Value, CustomPattern< Value, TuplePattern > ) > >( e,
            e.extDropValue(),
            []( const Context& c, const Value& b, const Value& tup )
            {
                if( !tup.isConstant() )
                    return;

                ForEachInTuple( tup,
                    [&]( auto&& v )
                    {
                        DiagnosticsContext dc( v.locationId(), "When invoking _DropValue." );
                        InvokeOverloadSet( c, c.env()->extDropValue(), MakeClosedTuple( b, v ) );

                        return true;
                    } );
            } );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/tuple/init.cpp.
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


        auto rhsTupType = *EIRToValue( initTup.type() );
        if( IsTupleConstant( initTup ) && IsTrivialTupleInitialization( c, tupType, rhsTupType ) )
        {
            auto cfg = GetCFG( c );
            if( !cfg )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "initializing declarations are not allowed here." );

                return;
            }

            if( auto bb = cfg->currentBB() )

                bb->append( initTup, tupRef, Store( lrefType.type(), initTup.locationId(), tupRef.locationId() ) );

            return;
        }

        ForEachInTupleType( tupType, [&]( auto&& t )

        {
            auto elemType = *EIRToValue( t );
            auto loc = elemType.locationId();

            // Create a mutable reference to the element to initialize
            ReferenceType rt( t, MutAccessSpecifier() );

            auto elemRef = BuildComputedValue( ValueToEIR( ToValue( rt ) ),
                tupRef, Select( index, loc ) ).setLocationId( loc );

            auto elemInit = *EIRToValue( GetConstantTupleElement( initTup, index++ ) );

            DiagnosticsContext dc( elemType.locationId(), "When invoking _Initialize." );
            auto init = InvokeOverloadSet( c, c.env()->extInitialize(),
                MakeClosedTuple( elemRef, move( elemInit ) ) );

            DiagnosticsContext dc2( elemType.locationId(), "When invoking _DropValue." );
                InvokeOverloadSet( c, c.env()->extDropValue(),
                MakeClosedTuple( c.builder(), init ) );

            return true;
        } );
    }

    void SetupTupleInitialize( Env& e )
    {
        // Tuples default initialization: attempt to default initialize every element.
        RegisterBuiltinFunc< Intrinsic< void (
                CustomPattern< Value, ReferenceType::PatternMutableOf< TuplePattern > >
                ) > >( e, e.extInitialize(),
            []( const Context& c, const Value& tupRef )
            {
                G_VAL_ASSERT( tupRef, !tupRef.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( tupRef.type() ) );

                uint32_t index = 0;
                auto tupType = *EIRToValue( refType.type() );

                ForEachInTupleType( tupType, [&]( auto&& t )

                {
                    auto elemType = *EIRToValue( t );
                    auto loc = elemType.locationId();

                    // Create a mutable reference to the element to initialize
                    ReferenceType rt( t, MutAccessSpecifier() );
                    auto elemRef = BuildComputedValue( ValueToEIR( ToValue( rt ) ),

                        tupRef, Select( index++, loc ) ).setLocationId( loc );


                    DiagnosticsContext dc( elemType.locationId(), "When invoking _Initialize." );
                    auto init = InvokeOverloadSet( c, c.env()->extInitialize(),
                        MakeClosedTuple( elemRef ) );


                    DiagnosticsContext dc2( elemType.locationId(), "When invoking _DropValue." );
                     InvokeOverloadSet( c, c.env()->extDropValue(),
                        MakeClosedTuple( c.builder(), init ) );

                    return true;
                } );
            } );

        // Tuples initialization: intialize each element with the corresponding element
        // from the initializer tuple.
        RegisterBuiltinFunc< Intrinsic< void (
                CustomPattern< Value, ReferenceType::PatternMutableOf< TuplePatternOfTypeT > >,
                CustomConstantPattern< Value, TuplePatternOfTypeT >
                ) > >( e, e.extInitialize(), InitTuple );

        RegisterBuiltinFunc< Intrinsic< void (
                CustomPattern< Value, ReferenceType::PatternMutableOf< TuplePattern > >,
                CustomConstantPattern< Value, TuplePattern >
                ) > >( e, e.extInitialize(), InitTuple );

        RegisterBuiltinFunc< Intrinsic< void (
                CustomPattern< Value, ReferenceType::PatternMutableOf< TuplePattern > >,
                CustomPattern< Value, TuplePattern >
                ) > >( e, e.extInitialize(), []( const Context& c, const Value& tupRef, const Value& initTup )
        {
            G_ERROR( "tuple init from a computed tuple is not yet implemented." );
        } );
    }
}








|
>




>
|




|
>
|
|
|

|
|
>
|
|

|

|
|
|

|
|
|

|
|





|
|
|








|
>
|
|
|

|
|
|
>
|

>
|
|
|

>
|
|
|

|
|




|
|
|
|

|
|
|
<

|
|
|
|
<
|
<

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

        auto rhsTupType = *EIRToValue( initTup.type() );
        if( IsTupleConstant( initTup ) && IsTrivialTupleInitialization( c, tupType, rhsTupType ) )
        {
            auto cfg = GetCFG( c );
            if( !cfg )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                    0, "initializing declarations are not allowed here." );
                return;
            }

            if( auto bb = cfg->currentBB() )
                bb->append( initTup, tupRef,
                    Store( lrefType.type(), initTup.locationId(), tupRef.locationId() ) );

            return;
        }

        ForEachInTupleType( tupType,
            [&]( auto&& t )
            {
                auto elemType = *EIRToValue( t );
                auto loc = elemType.locationId();

                // Create a mutable reference to the element to initialize
                ReferenceType rt( t, MutAccessSpecifier() );
                auto elemRef =
                    BuildComputedValue( ValueToEIR( ToValue( rt ) ), tupRef, Select( index, loc ) )
                        .setLocationId( loc );

                auto elemInit = *EIRToValue( GetConstantTupleElement( initTup, index++ ) );

                DiagnosticsContext dc( elemType.locationId(), "When invoking _Initialize." );
                auto init = InvokeOverloadSet(
                    c, c.env()->extInitialize(), MakeClosedTuple( elemRef, move( elemInit ) ) );

                DiagnosticsContext dc2( elemType.locationId(), "When invoking _DropValue." );
                InvokeOverloadSet(
                    c, c.env()->extDropValue(), MakeClosedTuple( c.builder(), init ) );

                return true;
            } );
    }

    void SetupTupleInitialize( Env& e )
    {
        // Tuples default initialization: attempt to default initialize every element.
        RegisterBuiltinFunc< Intrinsic< void(
            CustomPattern< Value, ReferenceType::PatternMutableOf< TuplePattern > > ) > >( e,
            e.extInitialize(),
            []( const Context& c, const Value& tupRef )
            {
                G_VAL_ASSERT( tupRef, !tupRef.isConstant() );
                auto refType = *FromValue< ReferenceType >( *EIRToValue( tupRef.type() ) );

                uint32_t index = 0;
                auto tupType = *EIRToValue( refType.type() );

                ForEachInTupleType( tupType,
                    [&]( auto&& t )
                    {
                        auto elemType = *EIRToValue( t );
                        auto loc = elemType.locationId();

                        // Create a mutable reference to the element to initialize
                        ReferenceType rt( t, MutAccessSpecifier() );
                        auto elemRef = BuildComputedValue(
                            ValueToEIR( ToValue( rt ) ), tupRef, Select( index++, loc ) )
                                           .setLocationId( loc );

                        DiagnosticsContext dc(
                            elemType.locationId(), "When invoking _Initialize." );
                        auto init = InvokeOverloadSet(
                            c, c.env()->extInitialize(), MakeClosedTuple( elemRef ) );

                        DiagnosticsContext dc2(
                            elemType.locationId(), "When invoking _DropValue." );
                        InvokeOverloadSet(
                            c, c.env()->extDropValue(), MakeClosedTuple( c.builder(), init ) );

                        return true;
                    } );
            } );

        // Tuples initialization: intialize each element with the corresponding element
        // from the initializer tuple.
        RegisterBuiltinFunc< Intrinsic< void(
            CustomPattern< Value, ReferenceType::PatternMutableOf< TuplePatternOfTypeT > >,
            CustomConstantPattern< Value, TuplePatternOfTypeT > ) > >(
            e, e.extInitialize(), InitTuple );

        RegisterBuiltinFunc< Intrinsic< void(
            CustomPattern< Value, ReferenceType::PatternMutableOf< TuplePattern > >,
            CustomConstantPattern< Value, TuplePattern > ) > >( e, e.extInitialize(), InitTuple );


        RegisterBuiltinFunc< Intrinsic< void(
            CustomPattern< Value, ReferenceType::PatternMutableOf< TuplePattern > >,
            CustomPattern< Value, TuplePattern > ) > >( e, e.extInitialize(),
            []( const Context& c, const Value& tupRef, const Value& initTup )

            { G_ERROR( "tuple init from a computed tuple is not yet implemented." ); } );

    }

} // namespace goose::builtins
Changes to bs/builtins/types/tuple/lower.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

#include "builtins/builtins.h"

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

namespace goose::builtins
{
    void SetupTupleLowering( Env& e )
    {
        RegisterBuiltinFunc< Intrinsic< Value ( TypePatternParam< Value, TuplePattern > ) > >( e, e.extLowerType(),

            []( const Context& c, const Value& tupType )
        {
            auto resultTupType = EmptyClosedTupleType();
            bool success = true;

            ForEachInTupleType( tupType, [&]( auto&& t )

            {
                auto loweredType = LowerType( c, *EIRToValue( t ) );
                if( !loweredType || loweredType->isPoison() )
                {
                    success = false;
                    return false;
                }

                resultTupType = AppendToTupleType( resultTupType, *loweredType );
                return true;
            } );

            if( !success )
                return PoisonValue();

            return resultTupType;
        } );

        RegisterBuiltinFunc< Intrinsic< Value ( CustomPattern< Value, TuplePattern > ) > >( e, e.extLowerValue(),

            []( const Context& c, const Value& tup )
        {
            auto resultTup = IsOpenTuple( tup ) ? EmptyOpenTuple() : EmptyClosedTuple();
            auto types = make_shared< Vector >();
            auto vals = make_shared< Vector >();
            bool success = true;

            ForEachInTuple( tup, [&]( auto&& t )

            {
                auto loweredValue = LowerValue( c, t );
                if( !loweredValue || loweredValue->isPoison() )
                {
                    success = false;
                    return false;
                }

                resultTup = AppendToTuple( resultTup, *loweredValue );
                return true;
            } );

            if( !success )
                return PoisonValue();

            return resultTup;
        } );
    }
}











|
>

|
|
|

|
>
|
|
|
|
|
|
|

|
|
|

|
|

|
|

|
>

|
|
|
|
|

|
>
|
|
|
|
|
|
|

|
|
|

|
|

|
|

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

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

namespace goose::builtins
{
    void SetupTupleLowering( Env& e )
    {
        RegisterBuiltinFunc< Intrinsic< Value( TypePatternParam< Value, TuplePattern > ) > >( e,
            e.extLowerType(),
            []( const Context& c, const Value& tupType )
            {
                auto resultTupType = EmptyClosedTupleType();
                bool success = true;

                ForEachInTupleType( tupType,
                    [&]( auto&& t )
                    {
                        auto loweredType = LowerType( c, *EIRToValue( t ) );
                        if( !loweredType || loweredType->isPoison() )
                        {
                            success = false;
                            return false;
                        }

                        resultTupType = AppendToTupleType( resultTupType, *loweredType );
                        return true;
                    } );

                if( !success )
                    return PoisonValue();

                return resultTupType;
            } );

        RegisterBuiltinFunc< Intrinsic< Value( CustomPattern< Value, TuplePattern > ) > >( e,
            e.extLowerValue(),
            []( const Context& c, const Value& tup )
            {
                auto resultTup = IsOpenTuple( tup ) ? EmptyOpenTuple() : EmptyClosedTuple();
                auto types = make_shared< Vector >();
                auto vals = make_shared< Vector >();
                bool success = true;

                ForEachInTuple( tup,
                    [&]( auto&& t )
                    {
                        auto loweredValue = LowerValue( c, t );
                        if( !loweredValue || loweredValue->isPoison() )
                        {
                            success = false;
                            return false;
                        }

                        resultTup = AppendToTuple( resultTup, *loweredValue );
                        return true;
                    } );

                if( !success )
                    return PoisonValue();

                return resultTup;
            } );
    }

} // namespace goose::builtins
Changes to bs/builtins/types/tuple/tuple.cpp.
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
    }

    Value AppendToTuple( const Value& tup, const ValuePattern& valPat )
    {
        auto tupType = EIRToValue( tup.type() );
        assert( tupType );

        auto result = Value(
            ValueToEIR( AppendToTupleType( *tupType, valPat.type() ) ),
            AppendToVectorTerm( tup.val(), ValueToEIR( valPat ) ) );

        if( tup.isPoison() || valPat.isPoison() )
            result.setPoison();

        return result;
    }

    Value AppendToTuple( const Value& tup, const Value& val )
    {
        auto tupType = EIRToValue( tup.type() );
        auto valType = EIRToValue( val.type() );

        G_VAL_ASSERT( tup, tupType );
        G_VAL_ASSERT( val, valType );

        auto result = Value(
            ValueToEIR( AppendToTupleType( *tupType, *valType ) ),
            AppendToVectorTerm( tup.val(), ValueToEIR( val ) ) );

        if( tup.isPoison() || val.isPoison() )
            result.setPoison();

        return result;
    }

    Value PrependToTuple( const Value& val, const Value& tup )
    {
        auto tupType = EIRToValue( tup.type() );
        auto valType = EIRToValue( val.type() );

        assert( tupType );
        assert( valType );

        auto result = Value(
            ValueToEIR( PrependToTupleType( *valType, *tupType ) ),
            PrependToVectorTerm( tup.val(), ValueToEIR( val ) ) );

        if( tup.isPoison() || val.isPoison() )
            result.setPoison();

        return result;
    }

    Value ConcatenateTuples( const Value& ltup, const Value& rtup )
    {
        auto ltupType = EIRToValue( ltup.type() );
        auto rtupType = EIRToValue( rtup.type() );

        assert( ltupType );
        assert( rtupType );

        auto result = Value(
            ValueToEIR( ConcatenateTupleTypes( *ltupType, *rtupType ) ),
            ConcatenateVectorTerms( ltup.val(), rtup.val() ) );

        if( ltup.isPoison() || rtup.isPoison() )
            result.setPoison();

        return result;
    }

    Value VectorToTuple( const Term& kind, const pvec& vec )
    {
        auto types = make_shared< Vector >();
        types->reserve( vec->terms().size() );

        for( auto&& t : vec->terms() )
        {
            auto val = EIRToValue( t );
            assert( val );
            types->append( val->type() );
        }

        return Value(
            ValueToEIR( MkTupleType( kind, TERM( move( types ) ) ) ), vec );
    }

    bool IsTuple( const Value& t )
    {
        auto typeVal = EIRToValue( t.type() );

        if( !typeVal || !typeVal->isConstant() )
            return false;

        auto result = Decompose( typeVal->val(),
            Vec(
                Lit( "tuple"_sid ),
                SubTerm(),
                SubTerm()
            )
        );

        return !!result;
    }

    bool IsOpenTuple( const Value& t )
    {
        if( !t.isConstant() )
            return false;

        auto typeVal = EIRToValue( t.type() );
        auto result = Decompose( typeVal->val(),
            Vec(
                Lit( "tuple"_sid ),
                Lit( "open"_sid ),
                SubTerm()
            )
        );

        return !!result;
    }

    Value CloseTuple( const Value& tup )
    {
        auto tupType = EIRToValue( tup.type() );
        return Value( ValueToEIR( ToClosedTupleType( *tupType ) ),
            tup.val() ).setLocationId( tup.locationId() );
    }

    size_t TupleSize( const Value& tup )
    {
        auto tupType = *EIRToValue( tup.type() );
        return TupleTypeSize( tupType );
    }







<
|
















<
|
















<
|
















<
|




















<
|









|
<
<
<
<
<
<










|
<
|
<
<
<
<







|
|







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
    }

    Value AppendToTuple( const Value& tup, const ValuePattern& valPat )
    {
        auto tupType = EIRToValue( tup.type() );
        assert( tupType );


        auto result = Value( ValueToEIR( AppendToTupleType( *tupType, valPat.type() ) ),
            AppendToVectorTerm( tup.val(), ValueToEIR( valPat ) ) );

        if( tup.isPoison() || valPat.isPoison() )
            result.setPoison();

        return result;
    }

    Value AppendToTuple( const Value& tup, const Value& val )
    {
        auto tupType = EIRToValue( tup.type() );
        auto valType = EIRToValue( val.type() );

        G_VAL_ASSERT( tup, tupType );
        G_VAL_ASSERT( val, valType );


        auto result = Value( ValueToEIR( AppendToTupleType( *tupType, *valType ) ),
            AppendToVectorTerm( tup.val(), ValueToEIR( val ) ) );

        if( tup.isPoison() || val.isPoison() )
            result.setPoison();

        return result;
    }

    Value PrependToTuple( const Value& val, const Value& tup )
    {
        auto tupType = EIRToValue( tup.type() );
        auto valType = EIRToValue( val.type() );

        assert( tupType );
        assert( valType );


        auto result = Value( ValueToEIR( PrependToTupleType( *valType, *tupType ) ),
            PrependToVectorTerm( tup.val(), ValueToEIR( val ) ) );

        if( tup.isPoison() || val.isPoison() )
            result.setPoison();

        return result;
    }

    Value ConcatenateTuples( const Value& ltup, const Value& rtup )
    {
        auto ltupType = EIRToValue( ltup.type() );
        auto rtupType = EIRToValue( rtup.type() );

        assert( ltupType );
        assert( rtupType );


        auto result = Value( ValueToEIR( ConcatenateTupleTypes( *ltupType, *rtupType ) ),
            ConcatenateVectorTerms( ltup.val(), rtup.val() ) );

        if( ltup.isPoison() || rtup.isPoison() )
            result.setPoison();

        return result;
    }

    Value VectorToTuple( const Term& kind, const pvec& vec )
    {
        auto types = make_shared< Vector >();
        types->reserve( vec->terms().size() );

        for( auto&& t : vec->terms() )
        {
            auto val = EIRToValue( t );
            assert( val );
            types->append( val->type() );
        }


        return Value( ValueToEIR( MkTupleType( kind, TERM( move( types ) ) ) ), vec );
    }

    bool IsTuple( const Value& t )
    {
        auto typeVal = EIRToValue( t.type() );

        if( !typeVal || !typeVal->isConstant() )
            return false;

        auto result = Decompose( typeVal->val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );







        return !!result;
    }

    bool IsOpenTuple( const Value& t )
    {
        if( !t.isConstant() )
            return false;

        auto typeVal = EIRToValue( t.type() );
        auto result =

            Decompose( typeVal->val(), Vec( Lit( "tuple"_sid ), Lit( "open"_sid ), SubTerm() ) );





        return !!result;
    }

    Value CloseTuple( const Value& tup )
    {
        auto tupType = EIRToValue( tup.type() );
        return Value( ValueToEIR( ToClosedTupleType( *tupType ) ), tup.val() )
            .setLocationId( tup.locationId() );
    }

    size_t TupleSize( const Value& tup )
    {
        auto tupType = *EIRToValue( tup.type() );
        return TupleTypeSize( tupType );
    }
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
    {
        auto& vec = *get< pvec >( tup.val() );
        return vec.terms()[index];
    }

    Value GetComputedTupleElement( const Value& tup, uint32_t index, const Term& accessSpecifier )
    {

        auto rt = ValueToEIR( ToValue( ReferenceType( GetTupleElementType( tup, index ), accessSpecifier ) ) );
        return BuildComputedValue( rt, tup, cir::Select( index, tup.locationId() ) );
    }

    bool IsTupleConstant( const Value& tup )
    {
        bool result = true;

        ForEachInTuple( tup, [&]( auto&& t )

        {
            if( !t.isConstant() )
            {
                result = false;
                return false;
            }

            if( IsTuple( t ) && !IsTupleConstant( t ) )
            {
                result = false;
                return false;
            }

            return true;
        } );

        return result;
    }

    Generator< Value > GenerateValuesFromConstantTuple( const Value& tup )
    {
        const auto& vec = *get< pvec >( tup.val() );







>
|







|
>
|
|
|
|
|
|

|
|
|
|
|

|
|







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
    {
        auto& vec = *get< pvec >( tup.val() );
        return vec.terms()[index];
    }

    Value GetComputedTupleElement( const Value& tup, uint32_t index, const Term& accessSpecifier )
    {
        auto rt = ValueToEIR(
            ToValue( ReferenceType( GetTupleElementType( tup, index ), accessSpecifier ) ) );
        return BuildComputedValue( rt, tup, cir::Select( index, tup.locationId() ) );
    }

    bool IsTupleConstant( const Value& tup )
    {
        bool result = true;

        ForEachInTuple( tup,
            [&]( auto&& t )
            {
                if( !t.isConstant() )
                {
                    result = false;
                    return false;
                }

                if( IsTuple( t ) && !IsTupleConstant( t ) )
                {
                    result = false;
                    return false;
                }

                return true;
            } );

        return result;
    }

    Generator< Value > GenerateValuesFromConstantTuple( const Value& tup )
    {
        const auto& vec = *get< pvec >( tup.val() );
231
232
233
234
235
236
237
238
    }

    const Term& TuplePatternOfTypeT::GetPattern()
    {
        static auto pattern = ValueToEIR( MkTupleType( HOLE( "_"_sid ), HOLE( "T"_sid ) ) );
        return pattern;
    }
}







|
217
218
219
220
221
222
223
224
    }

    const Term& TuplePatternOfTypeT::GetPattern()
    {
        static auto pattern = ValueToEIR( MkTupleType( HOLE( "_"_sid ), HOLE( "T"_sid ) ) );
        return pattern;
    }
} // namespace goose::builtins
Changes to bs/builtins/types/tuple/tuple.h.
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
    extern Value AppendToTuple( const Value& tup, const ValuePattern& valPat );

    extern Value PrependToTuple( const Value& val, const Value& tup );
    extern Value ConcatenateTuples( const Value& ltup, const Value& rtup );

    extern Value VectorToTuple( const Term& kind, const pvec& vec );

    template< typename... V >
    Value MakeClosedTuple( V&&... values );

    template< typename... V >
    Value MakeOpenTuple( V&&... values );

    extern bool IsTuple( const Value& t );
    extern bool IsOpenTuple( const Value& t );

    extern Value CloseTuple( const Value& tup );

    extern size_t TupleSize( const Value& tup );

    extern const Term& GetTupleElementType( const Value& tup, uint32_t index );
    extern const Term& GetConstantTupleElement( const Value& tup, uint32_t index );
    extern Term& GetConstantTupleElement( Value& tup, uint32_t index );
    extern Value GetComputedTupleElement( const Value& tup, uint32_t index, const Term& accessSpecifier );


    template< typename F >
    void ForEachTermInTuple( const Value& tup, F&& func );

    template< typename F >
    void ForEachInTuple( const Value& tup, F&& func );

    template< typename F >
    bool ForEachInTuples( const Value& tup1, const Value& tup2, F&& func );

    // Returns true if the tuple and all values it contains (including nested tuples)
    // are all constant.
    extern bool IsTupleConstant( const Value& tup );

    extern Generator< Value > GenerateValuesFromConstantTuple( const Value& tup );
    extern Generator< Value > GenerateValuesFromComputedTuple( const Value& tup );







|
<

|
<











|
>

<
|

<
|

<
|







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
    extern Value AppendToTuple( const Value& tup, const ValuePattern& valPat );

    extern Value PrependToTuple( const Value& val, const Value& tup );
    extern Value ConcatenateTuples( const Value& ltup, const Value& rtup );

    extern Value VectorToTuple( const Term& kind, const pvec& vec );

    template< typename... V > Value MakeClosedTuple( V&&... values );


    template< typename... V > Value MakeOpenTuple( V&&... values );


    extern bool IsTuple( const Value& t );
    extern bool IsOpenTuple( const Value& t );

    extern Value CloseTuple( const Value& tup );

    extern size_t TupleSize( const Value& tup );

    extern const Term& GetTupleElementType( const Value& tup, uint32_t index );
    extern const Term& GetConstantTupleElement( const Value& tup, uint32_t index );
    extern Term& GetConstantTupleElement( Value& tup, uint32_t index );
    extern Value GetComputedTupleElement(
        const Value& tup, uint32_t index, const Term& accessSpecifier );


    template< typename F > void ForEachTermInTuple( const Value& tup, F&& func );


    template< typename F > void ForEachInTuple( const Value& tup, F&& func );


    template< typename F > bool ForEachInTuples( const Value& tup1, const Value& tup2, F&& func );

    // Returns true if the tuple and all values it contains (including nested tuples)
    // are all constant.
    extern bool IsTupleConstant( const Value& tup );

    extern Generator< Value > GenerateValuesFromConstantTuple( const Value& tup );
    extern Generator< Value > GenerateValuesFromComputedTuple( const Value& tup );
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
        static const Term& GetPattern();
    };

    struct TuplePatternOfTypeT
    {
        static const Term& GetPattern();
    };
}

namespace goose::eir
{
    template< typename... T >
    struct Bridge< tuple< T... > >
    {
        static const Term& Type();
        static Value ToValue( const tuple< T... >& x );
        static optional< tuple< T... > > FromVectorTerm( const Term& v );
        static optional< tuple< T... > > FromValue( const Value& v );
    };
}

#endif







|



|
<






|


66
67
68
69
70
71
72
73
74
75
76
77

78
79
80
81
82
83
84
85
86
        static const Term& GetPattern();
    };

    struct TuplePatternOfTypeT
    {
        static const Term& GetPattern();
    };
} // namespace goose::builtins

namespace goose::eir
{
    template< typename... T > struct Bridge< tuple< T... > >

    {
        static const Term& Type();
        static Value ToValue( const tuple< T... >& x );
        static optional< tuple< T... > > FromVectorTerm( const Term& v );
        static optional< tuple< T... > > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/builtins/types/tuple/tuple.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
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
#ifndef GOOSE_BUILTINS_TYPES_TUPLE_INL
#define GOOSE_BUILTINS_TYPES_TUPLE_INL

namespace goose::builtins
{
    template< typename... V >
    Value MakeClosedTuple( V&&... values )
    {
        auto loc = Location::CreateSpanningLocation( values.locationId()... );
        return Value(
            ValueToEIR( MkTupleType( TSID( closed ), TERM( Vector::Make( values.type()... ) ) ) ),
            TERM( Vector::Make( ValueToEIR( values )... ) ) ).setLocationId( loc );

    }

    template< typename... V >
    Value MakeOpenTuple( V&&... values )
    {
        auto loc = Location::CreateSpanningLocation( values.locationId()... );
        return Value(
            ValueToEIR( MkTupleType( TSID( open ), TERM( Vector::Make( values.type()... ) ) ) ),
            TERM( Vector::Make( ValueToEIR( values )... ) ) ).setLocationId( loc );

    }

    template< typename F >








    void ForEachTermInTuple( const Value& tup, F&& func )
    {
        // Check that the tuple's payload is a vector. It might be a hole.
        if( !holds_alternative< pvec >( tup.val() ) )
            return;

        ForEachInVectorTerm( tup.val(), [&]( auto&& t )
        {
            return func( t );
        } );
    }

    template< typename F >
    void ForEachInTuple( const Value& tup, F&& func )
    {
        // Check that the tuple's payload is a vector. It might be a hole.
        if( !holds_alternative< pvec >( tup.val() ) )
            return;

        ForEachInVectorTerm( tup.val(), [&]( auto&& t )
        {
            return func( *EIRToValue( t ) );
        } );
    }

    template< typename F >
    bool ForEachInTupleType( const Value& tupType, F&& func )
    {
        auto decomp = Decompose( tupType.val(),
            Vec(
                Lit( "tuple"_sid ),
                SubTerm(),
                SubTerm()
            )
        );

        if( !decomp )
            return false;

        auto&& [tupKind, tupTypesVec] = *decomp;

        ForEachInVectorTerm( tupTypesVec, func );
        return true;
    }

    template< typename F >
    bool ForEachInTupleTypes( const Value& tupType1, const Value& tupType2, F&& func )
    {
        auto decomp1 = Decompose( tupType1.val(),
            Vec(
                Lit( "tuple"_sid ),
                SubTerm(),
                SubTerm()
            )
        );

        if( !decomp1 )
            return false;

        auto&& [tupKind1, tupTypesVec1] = *decomp1;

        auto decomp2 = Decompose( tupType2.val(),
            Vec(
                Lit( "tuple"_sid ),
                SubTerm(),
                SubTerm()
            )
        );

        if( !decomp2 )
            return false;

        auto&& [tupKind2, tupTypesVec2] = *decomp2;

        return ForEachInVectorTerms( tupTypesVec1, tupTypesVec2, func );
    }

    template< typename F >
    bool ForEachInTuples( const Value& tup1, const Value& tup2, F&& func )
    {
        // Check that the tuples' payloads are vector. They might be holes.
        if( !holds_alternative< pvec >( tup1.val() ) )
            return false;

        if( !holds_alternative< pvec >( tup2.val() ) )
            return false;

        return ForEachInVectorTerms( tup1.val(), tup2.val(), [&]( auto&& t1, auto&& t2 )
        {
            return func( *EIRToValue( t1 ), *EIRToValue( t2 ) );
        } );
    }
}


namespace goose::eir
{
    // Type
    static inline const Value& BuildTupleType( const Value& typeSoFar )
    {
        return typeSoFar;
    }

    template< typename H, typename... T >
    static Value BuildTupleType( const Value& typeSoFar )
    {
        if constexpr( sizeof...( T ) > 0 )
        {
            return BuildTupleType< T... >( builtins::AppendToTupleType( typeSoFar,
                *EIRToValue( GetValueType< H >() ) ) );
        }
        else
            return builtins::AppendToTupleType( typeSoFar, *EIRToValue( GetValueType< H >() ) );
    }

    template< typename... T >
    const Term& Bridge< tuple< T... > >::Type()
    {
        if constexpr( sizeof... ( T ) == 0 )
        {
            static auto type = ValueToEIR( builtins::EmptyClosedTupleType() );
            return type;
        }
        else
        {

            static auto type = ValueToEIR( BuildTupleType< T... >( builtins::EmptyClosedTupleType() ) );
            return type;
        }
    }

    // Val
    template< typename T, size_t... I >
    auto BuildTupleValue( const T& tup, index_sequence< I... > )
    {
        return VEC( ValueToEIR( ToValue( get< I >( tup ) ) )... );
    }

    template< typename... T >
    Value Bridge< tuple< T... > >::ToValue( const tuple< T... >& x )
    {
        auto val = BuildTupleValue( x, index_sequence_for< T... >() );
        return Value( Type(), move( val ) );
    }

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


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

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






|
<




|
>


|
<




|
>


|
>
>
>
>
>
>
>
>
|





|
<
<
<


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

|
<
<
<
<
<
<













|
<
<
<
<
<
<






|
<
<
<
<
<
<









<
|








|
<
|
<

<
>









|
<



|
|





|
<

|






>
|





|
<




<
|








|







>
|







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

namespace goose::builtins
{
    template< typename... V > Value MakeClosedTuple( V&&... values )

    {
        auto loc = Location::CreateSpanningLocation( values.locationId()... );
        return Value(
            ValueToEIR( MkTupleType( TSID( closed ), TERM( Vector::Make( values.type()... ) ) ) ),
            TERM( Vector::Make( ValueToEIR( values )... ) ) )
            .setLocationId( loc );
    }

    template< typename... V > Value MakeOpenTuple( V&&... values )

    {
        auto loc = Location::CreateSpanningLocation( values.locationId()... );
        return Value(
            ValueToEIR( MkTupleType( TSID( open ), TERM( Vector::Make( values.type()... ) ) ) ),
            TERM( Vector::Make( ValueToEIR( values )... ) ) )
            .setLocationId( loc );
    }

    template< typename F > void ForEachTermInTuple( const Value& tup, F&& func )
    {
        // Check that the tuple's payload is a vector. It might be a hole.
        if( !holds_alternative< pvec >( tup.val() ) )
            return;

        ForEachInVectorTerm( tup.val(), [&]( auto&& t ) { return func( t ); } );
    }

    template< typename F > void ForEachInTuple( const Value& tup, F&& func )
    {
        // Check that the tuple's payload is a vector. It might be a hole.
        if( !holds_alternative< pvec >( tup.val() ) )
            return;

        ForEachInVectorTerm( tup.val(), [&]( auto&& t ) { return func( *EIRToValue( t ) ); } );



    }















    template< typename F > bool ForEachInTupleType( const Value& tupType, F&& func )
    {
        auto decomp = Decompose( tupType.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );







        if( !decomp )
            return false;

        auto&& [tupKind, tupTypesVec] = *decomp;

        ForEachInVectorTerm( tupTypesVec, func );
        return true;
    }

    template< typename F >
    bool ForEachInTupleTypes( const Value& tupType1, const Value& tupType2, F&& func )
    {
        auto decomp1 = Decompose( tupType1.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );







        if( !decomp1 )
            return false;

        auto&& [tupKind1, tupTypesVec1] = *decomp1;

        auto decomp2 = Decompose( tupType2.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );







        if( !decomp2 )
            return false;

        auto&& [tupKind2, tupTypesVec2] = *decomp2;

        return ForEachInVectorTerms( tupTypesVec1, tupTypesVec2, func );
    }


    template< typename F > bool ForEachInTuples( const Value& tup1, const Value& tup2, F&& func )
    {
        // Check that the tuples' payloads are vector. They might be holes.
        if( !holds_alternative< pvec >( tup1.val() ) )
            return false;

        if( !holds_alternative< pvec >( tup2.val() ) )
            return false;

        return ForEachInVectorTerms( tup1.val(), tup2.val(),

            [&]( auto&& t1, auto&& t2 ) { return func( *EIRToValue( t1 ), *EIRToValue( t2 ) ); } );

    }

} // namespace goose::builtins

namespace goose::eir
{
    // Type
    static inline const Value& BuildTupleType( const Value& typeSoFar )
    {
        return typeSoFar;
    }

    template< typename H, typename... T > static Value BuildTupleType( const Value& typeSoFar )

    {
        if constexpr( sizeof...( T ) > 0 )
        {
            return BuildTupleType< T... >(
                builtins::AppendToTupleType( typeSoFar, *EIRToValue( GetValueType< H >() ) ) );
        }
        else
            return builtins::AppendToTupleType( typeSoFar, *EIRToValue( GetValueType< H >() ) );
    }

    template< typename... T > const Term& Bridge< tuple< T... > >::Type()

    {
        if constexpr( sizeof...( T ) == 0 )
        {
            static auto type = ValueToEIR( builtins::EmptyClosedTupleType() );
            return type;
        }
        else
        {
            static auto type =
                ValueToEIR( BuildTupleType< T... >( builtins::EmptyClosedTupleType() ) );
            return type;
        }
    }

    // Val
    template< typename T, size_t... I > auto BuildTupleValue( const T& tup, index_sequence< I... > )

    {
        return VEC( ValueToEIR( ToValue( get< I >( tup ) ) )... );
    }


    template< typename... T > Value Bridge< tuple< T... > >::ToValue( const tuple< T... >& x )
    {
        auto val = BuildTupleValue( x, index_sequence_for< T... >() );
        return Value( Type(), move( val ) );
    }

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

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

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

195
196
197
198
199
200
201
202
203
204
    optional< tuple< T... > > Bridge< tuple< T... > >::FromValue( const Value& v )
    {
        if( v.type() != Type() )
            return nullopt;

        return FromVectorTerm( v.val() );
    }
}

#endif







|


163
164
165
166
167
168
169
170
171
172
    optional< tuple< T... > > Bridge< tuple< T... > >::FromValue( const Value& v )
    {
        if( v.type() != Type() )
            return nullopt;

        return FromVectorTerm( v.val() );
    }
} // namespace goose::eir

#endif
Changes to bs/builtins/types/tuple/tupletype.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
256
257
258
259
260
261
262
263
264
265
266
267
#include "builtins/builtins.h"
#include "codegen/codegen.h"

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

namespace goose::builtins
{
    void SetupTupleTypeExtPoints( Env& e )
    {
        RegisterBuiltinFunc< Intrinsic< bool ( CustomPattern< Value, TuplePattern > ) > >( e, e.extIsType(),

            []( const Context& c, const Value& v )
            {
                return ToValue( IsTupleOfTypes( c, v ) );
            } );
    }

    Value MkTupleType( const Term& kind, const Term& types )
    {
        return Value( TypeType(), VEC( TSID( tuple ), kind, types ) );
    }

    bool IsTupleOfTypes( const Context& c, const Value& v )
    {
        if( !v.isConstant() )
            return false;

        bool isType = true;

        ForEachInTuple( v, [&]( auto&& v )

        {
            if( IsTExpr( v ) || IsType( c, v ) )
                return true;

            isType = false;
            return false;
        } );

        return isType;
    }

    extern Value TupleOfTypesToTupleType( const Value& tup )
    {
        auto typeVal = EIRToValue( tup.type() );
        auto result = Decompose( typeVal->val(),
            Vec(
                Lit( "tuple"_sid ),
                SubTerm(),
                SubTerm()
            )
        );

        auto&& [kind,typeTypes] = *result;
        return MkTupleType( kind, tup.val() );
    }

    const Value& EmptyOpenTupleType()
    {
        static auto type = MkTupleType( TSID( open ), VEC() );
        return type;
    }

    const Value& EmptyClosedTupleType()
    {
        static auto type = MkTupleType( TSID( closed ), VEC() );
        return type;
    }

    Value ToClosedTupleType( const Value& tuptype )
    {
        auto decomp = Decompose( tuptype.val(),
            Vec(
                Lit( "tuple"_sid ),
                SubTerm(),
                SubTerm()
            )
        );

        auto&& [tupKind, tupTypesVec] = *decomp;
        return MkTupleType( TSID( closed ), tupTypesVec ).setLocationId( tuptype.locationId() );
    }

    Value AppendToTupleType( const Value& tuptype, const Term& type )
    {
        auto decomp = Decompose( tuptype.val(),
            Vec(
                Lit( "tuple"_sid ),
                SubTerm(),
                SubTerm()
            )
        );

        auto&& [tupKind, tupTypesVec] = *decomp;

        auto newTypeVec = AppendToVectorTerm( tupTypesVec, type );
        auto result = MkTupleType( tupKind, newTypeVec );

        if( tuptype.isPoison() )
            result.setPoison();

        return result;
    }

    Value AppendToTupleType( const Value& tuptype, const Value& type )
    {
        auto decomp = Decompose( tuptype.val(),
            Vec(
                Lit( "tuple"_sid ),
                SubTerm(),
                SubTerm()
            )
        );

        auto&& [tupKind, tupTypesVec] = *decomp;

        auto newTypeVec = AppendToVectorTerm( tupTypesVec, ValueToEIR( type ) );
        auto result = MkTupleType( tupKind, newTypeVec );

        if( tuptype.isPoison() || type.isPoison() )
            result.setPoison();

        return result;
    }

    Value PrependToTupleType( const Value& type, const Value& tuptype )
    {
        auto decomp = Decompose( tuptype.val(),
            Vec(
                Lit( "tuple"_sid ),
                SubTerm(),
                SubTerm()
            )
        );

        auto&& [tupKind, tupTypesVec] = *decomp;

        auto newTypeVec = PrependToVectorTerm( tupTypesVec, ValueToEIR( type ) );
        auto result = MkTupleType( tupKind, newTypeVec );

        if( tuptype.isPoison() || type.isPoison() )
            result.setPoison();

        return result;
    }

    Value ConcatenateTupleTypes( const Value& ltuptype, const Value& rtuptype )
    {
        auto ldecomp = Decompose( ltuptype.val(),
            Vec(
                Lit( "tuple"_sid ),
                SubTerm(),
                SubTerm()
            )
        );

        auto&& [ltupKind, ltupTypesVec] = *ldecomp;

        auto rdecomp = Decompose( rtuptype.val(),
            Vec(
                Lit( "tuple"_sid ),
                SubTerm(),
                SubTerm()
            )
        );

        auto&& [rtupKind, rtupTypesVec] = *rdecomp;

        auto newTypeVec = ConcatenateVectorTerms( ltupTypesVec, rtupTypesVec );
        auto result = MkTupleType( ltupKind, newTypeVec );

        if( ltuptype.isPoison() || rtuptype.isPoison() )
            result.setPoison();

        return result;
    }

    bool IsTupleType( const Value& typeVal )
    {
        if( !typeVal.isConstant() )
            return false;

        auto result = Decompose( typeVal.val(),
            Vec(
                Lit( "tuple"_sid ),
                SubTerm(),
                SubTerm()
            )
        );

        return !!result;
    }

    size_t TupleTypeSize( const Value& tupType )
    {
        auto decomp = Decompose( tupType.val(),
            Vec(
                Lit( "tuple"_sid ),
                SubTerm(),
                Val< pvec >()
            )
        );

        auto&& [tupKind, tupTypesVec] = *decomp;
        return tupTypesVec->terms().size();
    }

    const Term& GetTupleTypeElement( const Value& tupType, uint32_t index )
    {
        auto decomp = Decompose( tupType.val(),
            Vec(
                Lit( "tuple"_sid ),
                SubTerm(),
                Val< pvec >()
            )
        );

        auto&& [tupKind, tupTypesVec] = *decomp;

        assert( index < tupTypesVec->terms().size() || tupTypesVec->repetitionTerm() );
        if( index < tupTypesVec->terms().size() )
            return tupTypesVec->terms()[index];
        else
            return *tupTypesVec->repetitionTerm();
    }


    bool IsTrivialTupleInitialization( const Context& c, const Value& lhsTupType, const Value& rhsTupType )
    {
        bool result = true;

        if( !ForEachInTupleTypes( lhsTupType, rhsTupType, [&]( auto&& lhs, auto&& rhs )

        {
            if( !IsTrivialInitialization( c, *EIRToValue( lhs ), *EIRToValue( rhs ) ) )
            {
                result = false;
                return false;
            }

            return true;
        } ) )
            return false;

        return result;
    }


    bool IsTrivialTupleAssignment( const Context& c, const Value& lhsTupType, const Value& rhsTupType )
    {
        bool result = true;

        if( !ForEachInTupleTypes( lhsTupType, rhsTupType, [&]( auto&& lhs, auto&& rhs )

        {
            if( !IsTrivialAssignment( c, *EIRToValue( lhs ), *EIRToValue( rhs ) ) )
            {
                result = false;
                return false;
            }

            return true;
        } ) )
            return false;

        return result;
    }

    const codegen::Type* GetTupleCodegenType( const Value& tupType )
    {












|
>
|
<
<
<














|
>
|
|
|

|
|
|







|
<
<
<
<
|
<
<
|

















|
<
<
<
<
<
<







|
<
<
<
<
<
<














|
<
<
<
<
<
<














|
<
<
<
<
<
<














|
<
<
<
<
<
<



|
<
<
<
<
<
<

















|
<
<
<
<
<
<






|
<
|
<
<
<
<







|
<
|
<
<
<
<










>
|



|
>
|
|
|
|
|
|

|
|





>
|



|
>
|
|
|
|
|
|

|
|







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

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

namespace goose::builtins
{
    void SetupTupleTypeExtPoints( Env& e )
    {
        RegisterBuiltinFunc< Intrinsic< bool( CustomPattern< Value, TuplePattern > ) > >( e,
            e.extIsType(),
            []( const Context& c, const Value& v ) { return ToValue( IsTupleOfTypes( c, v ) ); } );



    }

    Value MkTupleType( const Term& kind, const Term& types )
    {
        return Value( TypeType(), VEC( TSID( tuple ), kind, types ) );
    }

    bool IsTupleOfTypes( const Context& c, const Value& v )
    {
        if( !v.isConstant() )
            return false;

        bool isType = true;

        ForEachInTuple( v,
            [&]( auto&& v )
            {
                if( IsTExpr( v ) || IsType( c, v ) )
                    return true;

                isType = false;
                return false;
            } );

        return isType;
    }

    extern Value TupleOfTypesToTupleType( const Value& tup )
    {
        auto typeVal = EIRToValue( tup.type() );
        auto result = Decompose( typeVal->val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );







        auto&& [kind, typeTypes] = *result;
        return MkTupleType( kind, tup.val() );
    }

    const Value& EmptyOpenTupleType()
    {
        static auto type = MkTupleType( TSID( open ), VEC() );
        return type;
    }

    const Value& EmptyClosedTupleType()
    {
        static auto type = MkTupleType( TSID( closed ), VEC() );
        return type;
    }

    Value ToClosedTupleType( const Value& tuptype )
    {
        auto decomp = Decompose( tuptype.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );







        auto&& [tupKind, tupTypesVec] = *decomp;
        return MkTupleType( TSID( closed ), tupTypesVec ).setLocationId( tuptype.locationId() );
    }

    Value AppendToTupleType( const Value& tuptype, const Term& type )
    {
        auto decomp = Decompose( tuptype.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );







        auto&& [tupKind, tupTypesVec] = *decomp;

        auto newTypeVec = AppendToVectorTerm( tupTypesVec, type );
        auto result = MkTupleType( tupKind, newTypeVec );

        if( tuptype.isPoison() )
            result.setPoison();

        return result;
    }

    Value AppendToTupleType( const Value& tuptype, const Value& type )
    {
        auto decomp = Decompose( tuptype.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );







        auto&& [tupKind, tupTypesVec] = *decomp;

        auto newTypeVec = AppendToVectorTerm( tupTypesVec, ValueToEIR( type ) );
        auto result = MkTupleType( tupKind, newTypeVec );

        if( tuptype.isPoison() || type.isPoison() )
            result.setPoison();

        return result;
    }

    Value PrependToTupleType( const Value& type, const Value& tuptype )
    {
        auto decomp = Decompose( tuptype.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );







        auto&& [tupKind, tupTypesVec] = *decomp;

        auto newTypeVec = PrependToVectorTerm( tupTypesVec, ValueToEIR( type ) );
        auto result = MkTupleType( tupKind, newTypeVec );

        if( tuptype.isPoison() || type.isPoison() )
            result.setPoison();

        return result;
    }

    Value ConcatenateTupleTypes( const Value& ltuptype, const Value& rtuptype )
    {
        auto ldecomp = Decompose( ltuptype.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );







        auto&& [ltupKind, ltupTypesVec] = *ldecomp;

        auto rdecomp = Decompose( rtuptype.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );







        auto&& [rtupKind, rtupTypesVec] = *rdecomp;

        auto newTypeVec = ConcatenateVectorTerms( ltupTypesVec, rtupTypesVec );
        auto result = MkTupleType( ltupKind, newTypeVec );

        if( ltuptype.isPoison() || rtuptype.isPoison() )
            result.setPoison();

        return result;
    }

    bool IsTupleType( const Value& typeVal )
    {
        if( !typeVal.isConstant() )
            return false;

        auto result = Decompose( typeVal.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );







        return !!result;
    }

    size_t TupleTypeSize( const Value& tupType )
    {
        auto decomp =

            Decompose( tupType.val(), Vec( Lit( "tuple"_sid ), SubTerm(), Val< pvec >() ) );





        auto&& [tupKind, tupTypesVec] = *decomp;
        return tupTypesVec->terms().size();
    }

    const Term& GetTupleTypeElement( const Value& tupType, uint32_t index )
    {
        auto decomp =

            Decompose( tupType.val(), Vec( Lit( "tuple"_sid ), SubTerm(), Val< pvec >() ) );





        auto&& [tupKind, tupTypesVec] = *decomp;

        assert( index < tupTypesVec->terms().size() || tupTypesVec->repetitionTerm() );
        if( index < tupTypesVec->terms().size() )
            return tupTypesVec->terms()[index];
        else
            return *tupTypesVec->repetitionTerm();
    }

    bool IsTrivialTupleInitialization(
        const Context& c, const Value& lhsTupType, const Value& rhsTupType )
    {
        bool result = true;

        if( !ForEachInTupleTypes( lhsTupType, rhsTupType,
                [&]( auto&& lhs, auto&& rhs )
                {
                    if( !IsTrivialInitialization( c, *EIRToValue( lhs ), *EIRToValue( rhs ) ) )
                    {
                        result = false;
                        return false;
                    }

                    return true;
                } ) )
            return false;

        return result;
    }

    bool IsTrivialTupleAssignment(
        const Context& c, const Value& lhsTupType, const Value& rhsTupType )
    {
        bool result = true;

        if( !ForEachInTupleTypes( lhsTupType, rhsTupType,
                [&]( auto&& lhs, auto&& rhs )
                {
                    if( !IsTrivialAssignment( c, *EIRToValue( lhs ), *EIRToValue( rhs ) ) )
                    {
                        result = false;
                        return false;
                    }

                    return true;
                } ) )
            return false;

        return result;
    }

    const codegen::Type* GetTupleCodegenType( const Value& tupType )
    {
287
288
289
290
291
292
293
294
        if( !success )
            return nullptr;

        return codegen::Type::Get(
            llvm::StructType::get( GetLLVMContext(), elements, false ) );*/
        return nullptr;
    }
}







|
232
233
234
235
236
237
238
239
        if( !success )
            return nullptr;

        return codegen::Type::Get(
            llvm::StructType::get( GetLLVMContext(), elements, false ) );*/
        return nullptr;
    }
} // namespace goose::builtins
Changes to bs/builtins/types/tuple/tupletype.h.
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34


35
36
37

38
39
    extern Value ConcatenateTupleTypes( const Value& ltup, const Value& rtup );

    extern bool IsTupleType( const Value& typeVal );

    extern size_t TupleTypeSize( const Value& tupType );
    extern const Term& GetTupleTypeElement( const Value& tupType, uint32_t index );

    template< typename F >
    bool ForEachInTupleType( const Value& tupType, F&& func );

    template< typename F >
    bool ForEachInTupleTypes( const Value& tupType1, const Value& tupType2, F&& func );

    extern bool IsTrivialTupleInitialization( const Context& c, const Value& lhsTupType, const Value& rhsTupType );
    extern bool IsTrivialTupleAssignment( const Context& c, const Value& lhsTupType, const Value& rhsTupType );



    extern const codegen::Type* GetTupleCodegenType( const Value& tupType );
}


#endif







<
|




|
|
>
>


<
>


20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
35
36
37

38
39
40
    extern Value ConcatenateTupleTypes( const Value& ltup, const Value& rtup );

    extern bool IsTupleType( const Value& typeVal );

    extern size_t TupleTypeSize( const Value& tupType );
    extern const Term& GetTupleTypeElement( const Value& tupType, uint32_t index );


    template< typename F > bool ForEachInTupleType( const Value& tupType, F&& func );

    template< typename F >
    bool ForEachInTupleTypes( const Value& tupType1, const Value& tupType2, F&& func );

    extern bool IsTrivialTupleInitialization(
        const Context& c, const Value& lhsTupType, const Value& rhsTupType );
    extern bool IsTrivialTupleAssignment(
        const Context& c, const Value& lhsTupType, const Value& rhsTupType );

    extern const codegen::Type* GetTupleCodegenType( const Value& tupType );

} // namespace goose::builtins

#endif
Changes to bs/builtins/types/tuple/typecheck.cpp.
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

        uint32_t elemIndex = 0;
        for( auto&& t : elems->terms() )
        {
            auto val = EIRToValue( t );
            assert( val );
            types->append( val->type() );

            AppendToInstrSeq( is,
                *val,
                DataPathOf( tupTempUID, locationId ),
                Select( elemIndex++, val->locationId() ),
                Store( val->type(), val->locationId(), locationId )
            );
        }

        auto tupType = MkTupleType( closed ? TSID( closed ) : TSID( open ), TERM( move( types ) ) );
        auto tupTypeTerm = ValueToEIR( tupType );

        auto tupIS = make_shared< InstrSeq >();

        AppendToInstrSeq( *tupIS, AllocVar( tupTypeTerm, tupTempUID, locationId ) );
        AppendToInstrSeq( *tupIS, move( is ) );
        AppendToInstrSeq( *tupIS,
            DataPathOf( tupTempUID, locationId ),
            Load( tupTypeTerm, locationId ) );

        return Value( move( tupTypeTerm ), move( tupIS ) );
    }

    TCGen TypeCheckTuple( const TypeCheckingContext& tcc, const Value& tupType, pvec args, uint32_t index, bool isComputed, const pvec& out, bool closed, LocationId loc )

    {
        auto param = ParamPat( GetTupleTypeElement( tupType, index ) );
        auto tupSize = args->terms().size();

        for( auto&& [s,tcc] : TypeCheck( param, args->terms()[index], tcc ) )
        {
            if( !EIRToValue( s )->isConstant() )
                isComputed = true;

            auto newOut = make_shared< Vector >( Vector::MakeAppend( *out, move( s ) ) );

            if( index == ( tupSize - 1 ) )
            {
                if( isComputed )
                    co_yield { ValueToEIR( BuildComputedTuple( newOut, closed, loc ) ), tcc };
                else

                    co_yield { ValueToEIR( VectorToTuple( closed ? TSID( closed ) : TSID( open ), newOut ).setLocationId( loc ) ), tcc };

            }
            else

                co_yield TypeCheckTuple( tcc, tupType, args, index + 1, isComputed, newOut, closed, loc );
        }
    }

    TCGen TypeCheckConstantTuple( const TypeCheckingContext& tcc, const Value& tupType, const Value& tupArg, const pvec& out, bool closed )

    {
        auto argVec = get< pvec >( tupArg.val() );
        co_yield TypeCheckTuple( tcc, tupType, argVec, 0, false, out, closed, tupArg.locationId() );
    }

    TCGen TypeCheckComputedTuple( const TypeCheckingContext& tcc, const Value& tupType, const Value& tupArg, const Value& tupArgRef, const pvec& out, bool closed )

    {
        G_VAL_ASSERT( tupArgRef, !tupArgRef.isConstant() );

        auto argRefs = make_shared< Vector >();
        argRefs->reserve( TupleSize( tupArg ) );

        uint32_t index = 0;

        ForEachInTupleType( *EIRToValue( tupArg.type() ), [&]( auto&& type )

        {
            ReferenceType rt( type, ConstAccessSpecifier() );

            auto argRef = ValueToEIR( BuildComputedValue( ValueToEIR( ToValue( rt ) ),
                tupArgRef, cir::Select( index++, tupArg.locationId() ) ) );

            argRefs->append( move( argRef ) );
            return true;
        } );

        co_yield TypeCheckTuple( tcc, tupType, argRefs, 0, true, out, closed, tupArg.locationId() );
    }

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

            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                ValueToEIR( MkTupleType( ANYTERM( S ), VECOFLENGTH( L ) ) ),
                ANYTERM( _ ) ) ),

            ValueToEIR( ValuePattern(
                TSID( constant ),
                ValueToEIR( MkTupleType( ANYTERM( S ), VECOFLENGTH( L ) ) ),
                VECOFLENGTH( L ) ) ),

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

            if( !ltup || !rtup )
                co_return;

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

            auto out = make_shared< Vector >();
            out->reserve( TupleSize( *rtup ) );
            co_yield TypeCheckConstantTuple( tcc, tupType, *rtup, out, !IsOpenTuple( *rtup ) );
        } );

        e.typeCheckingRuleSet()->addTypeCheckingRule(

            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                ValueToEIR( MkTupleType( ANYTERM( S ), VECOFLENGTH( L ) ) ),
                ANYTERM( _ ) ) ),

            ValueToEIR( ValuePattern(
                TSID( computed ),
                ValueToEIR( MkTupleType( ANYTERM( S ), VECOFLENGTH( L ) ) ),
                ANYTERM( _ ) ) ),

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

            if( !ltup || !rtup )
                co_return;

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

            auto tupType = *EIRToValue( ltup->type() );
            auto tempIndex = util::GenerateNewUID();


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

            auto out = make_shared< Vector >();
            out->reserve( TupleSize( *rtup ) );


            co_yield TypeCheckComputedTuple( tcc, tupType, *rtup, rtupref, out, !IsOpenTuple( *rtup ) );
        } );

        // 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(

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

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

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

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

        e.typeCheckingRuleSet()->addTypeCheckingRule(

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

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

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            auto tup = EIRToValue( 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 type checking 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(

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

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

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

        // When typechecking a tuple of types against a type,
        // convert it to a tuple type.
        e.typeCheckingRuleSet()->addTypeCheckingRule(

            ParamPat( TypeType() ),

            ValueToEIR( Value(
                ValueToEIR( MkTupleType( ANYTERM( _ ), VEC( REPEAT( TypeType() ) ) ) ),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            co_yield { ValueToEIR( TupleOfTypesToTupleType( *EIRToValue( rhs ) ) ), tcc };
        } );
    }
}








|
<
<

|
<









|
|
<




|
>




|







<



>
|
>
|

>
|



|
>





|
>








|
>
|
|

|
|

|
|
|








|
<
|
<

|
<
|
<

|
|
|
|

|
|

|

|
|
|
|



|
<
|
<

|
<
|
<

|
|
|
|

|
|

|
|
|

|
|

>
|
|

|
|

>
|
|





|
<
<
<

|
<
|
<

|
|
|
|
|

|
|
|



|
<
|
<

|
<
<
<

|
|
|
|
|

|

|
|
|
|
|
|
|
|
|
|

|
|








|
<
|
<



|
<
|
<







|
|
|

|
<
|
<

<
>
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
        uint32_t elemIndex = 0;
        for( auto&& t : elems->terms() )
        {
            auto val = EIRToValue( t );
            assert( val );
            types->append( val->type() );

            AppendToInstrSeq( is, *val, DataPathOf( tupTempUID, locationId ),


                Select( elemIndex++, val->locationId() ),
                Store( val->type(), val->locationId(), locationId ) );

        }

        auto tupType = MkTupleType( closed ? TSID( closed ) : TSID( open ), TERM( move( types ) ) );
        auto tupTypeTerm = ValueToEIR( tupType );

        auto tupIS = make_shared< InstrSeq >();

        AppendToInstrSeq( *tupIS, AllocVar( tupTypeTerm, tupTempUID, locationId ) );
        AppendToInstrSeq( *tupIS, move( is ) );
        AppendToInstrSeq(
            *tupIS, DataPathOf( tupTempUID, locationId ), Load( tupTypeTerm, locationId ) );


        return Value( move( tupTypeTerm ), move( tupIS ) );
    }

    TCGen TypeCheckTuple( const TypeCheckingContext& tcc, const Value& tupType, pvec args,
        uint32_t index, bool isComputed, const pvec& out, bool closed, LocationId loc )
    {
        auto param = ParamPat( GetTupleTypeElement( tupType, index ) );
        auto tupSize = args->terms().size();

        for( auto&& [s, tcc] : TypeCheck( param, args->terms()[index], tcc ) )
        {
            if( !EIRToValue( s )->isConstant() )
                isComputed = true;

            auto newOut = make_shared< Vector >( Vector::MakeAppend( *out, move( s ) ) );

            if( index == ( tupSize - 1 ) )

                if( isComputed )
                    co_yield { ValueToEIR( BuildComputedTuple( newOut, closed, loc ) ), tcc };
                else
                    co_yield { ValueToEIR(
                                   VectorToTuple( closed ? TSID( closed ) : TSID( open ), newOut )
                                       .setLocationId( loc ) ),
                        tcc };
            else
                co_yield TypeCheckTuple(
                    tcc, tupType, args, index + 1, isComputed, newOut, closed, loc );
        }
    }

    TCGen TypeCheckConstantTuple( const TypeCheckingContext& tcc, const Value& tupType,
        const Value& tupArg, const pvec& out, bool closed )
    {
        auto argVec = get< pvec >( tupArg.val() );
        co_yield TypeCheckTuple( tcc, tupType, argVec, 0, false, out, closed, tupArg.locationId() );
    }

    TCGen TypeCheckComputedTuple( const TypeCheckingContext& tcc, const Value& tupType,
        const Value& tupArg, const Value& tupArgRef, const pvec& out, bool closed )
    {
        G_VAL_ASSERT( tupArgRef, !tupArgRef.isConstant() );

        auto argRefs = make_shared< Vector >();
        argRefs->reserve( TupleSize( tupArg ) );

        uint32_t index = 0;

        ForEachInTupleType( *EIRToValue( tupArg.type() ),
            [&]( auto&& type )
            {
                ReferenceType rt( type, ConstAccessSpecifier() );

                auto argRef = ValueToEIR( BuildComputedValue( ValueToEIR( ToValue( rt ) ),
                    tupArgRef, cir::Select( index++, tupArg.locationId() ) ) );

                argRefs->append( move( argRef ) );
                return true;
            } );

        co_yield TypeCheckTuple( tcc, tupType, argRefs, 0, true, out, closed, tupArg.locationId() );
    }

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

            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( MkTupleType( ANYTERM( S ), VECOFLENGTH( L ) ) ), ANYTERM( _ ) ) ),


            ValueToEIR( ValuePattern( TSID( constant ),

                ValueToEIR( MkTupleType( ANYTERM( S ), VECOFLENGTH( L ) ) ), VECOFLENGTH( L ) ) ),


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

                if( !ltup || !rtup )
                    co_return;

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

                auto out = make_shared< Vector >();
                out->reserve( TupleSize( *rtup ) );
                co_yield TypeCheckConstantTuple( tcc, tupType, *rtup, out, !IsOpenTuple( *rtup ) );
            } );

        e.typeCheckingRuleSet()->addTypeCheckingRule(

            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( MkTupleType( ANYTERM( S ), VECOFLENGTH( L ) ) ), ANYTERM( _ ) ) ),


            ValueToEIR( ValuePattern( TSID( computed ),

                ValueToEIR( MkTupleType( ANYTERM( S ), VECOFLENGTH( L ) ) ), ANYTERM( _ ) ) ),


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

                if( !ltup || !rtup )
                    co_return;

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

                auto tupType = *EIRToValue( ltup->type() );
                auto tempIndex = util::GenerateNewUID();

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

                auto out = make_shared< Vector >();
                out->reserve( TupleSize( *rtup ) );

                co_yield TypeCheckComputedTuple(
                    tcc, tupType, *rtup, rtupref, out, !IsOpenTuple( *rtup ) );
            } );

        // 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(

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




            ValueToEIR( ValuePattern( TSID( constant ),

                ValueToEIR( MkTupleType( ANYTERM( _ ), VEC( ANYTERM( _ ) ) ) ), ANYTERM( _ ) ) ),


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

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

        e.typeCheckingRuleSet()->addTypeCheckingRule(

            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( MkTupleType( ANYTERM( _ ), VEC( ANYTERM( _ ) ) ) ), ANYTERM( _ ) ) ),


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




            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                auto tup = EIRToValue( 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 type checking 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(

            ValueToEIR( ValuePattern( ANYTERM( _ ),

                ValueToEIR( MkTupleType( ANYTERM( _ ), VEC( ANYTERM( _ ) ) ) ), ANYTERM( _ ) ) ),


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

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

            { co_yield { lhs, tcc }; } );


        // When typechecking a tuple of types against a type,
        // convert it to a tuple type.
        e.typeCheckingRuleSet()->addTypeCheckingRule(

            ParamPat( TypeType() ),

            ValueToEIR(
                Value( ValueToEIR( MkTupleType( ANYTERM( _ ), VEC( REPEAT( TypeType() ) ) ) ),
                    ANYTERM( _ ) ) ),

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

            { co_yield { ValueToEIR( TupleOfTypesToTupleType( *EIRToValue( rhs ) ) ), tcc }; } );

    }

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

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

        // TOSO_SSA reenable
        //SetupPropositionsDropValue( e );
        //SetupGhostCodeDropValue( e );

        //SetupPredicatesTypeChecking( e );

        SetupBasicTypes( e );
        SetupBasicTypesPrettyPrinting();

        SetupTupleTypeChecking( e );

        SetupFuncInvocationRules( e );









|
|

|







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

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

        // TOSO_SSA reenable
        // SetupPropositionsDropValue( e );
        // SetupGhostCodeDropValue( e );

        // SetupPredicatesTypeChecking( e );

        SetupBasicTypes( e );
        SetupBasicTypesPrettyPrinting();

        SetupTupleTypeChecking( e );

        SetupFuncInvocationRules( e );
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
        SetupTupleLowering( e );

        SetupRuntimeBasicTypesInitialize( e );
    }

    void SetupDefaultTypeExtPoints( Env& e )
    {
        RegisterBuiltinFunc< Intrinsic< bool ( Value ) > >( e, e.extIsType(),
            []( const Context& c, const Value& v )
            {
                return ToValue( false );
            } );

        RegisterBuiltinFunc< Intrinsic< bool ( CustomPattern< Value, PatternType > ) > >( e, e.extIsType(),
            []( const Context& c, const Value& v )
            {
                return ToValue( true );
            } );
    }

    bool IsType( const Context& c, const Value& v, source_location sloc )
    {
        if( v.isType() )
            return true;

        auto result = InvokeOverloadSet( c,
            c.env()->extIsType(),
            MakeClosedTuple( v ), Location::Create( sloc ) );

        if( result.isPoison() )
        {
            PoisonBuilder( c );
            return false;
        }

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

            PoisonBuilder( c );
            return false;
        }

        return *boolRes;
    }

    Value ToType( const Context& c, const Value& v )
    {
        if( v.isType() )
            return v;

        auto result = ConvertValueToType( c, v, TypeType() );
        if( !holds_alternative< Value >( result ) )
            return PoisonType();

        return get< Value >( result );
    }
}







|
|
|
<
<
<
|
|
<
<
<







|
<
|










|
|



















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

        SetupRuntimeBasicTypesInitialize( e );
    }

    void SetupDefaultTypeExtPoints( Env& e )
    {
        RegisterBuiltinFunc< Intrinsic< bool( Value ) > >(
            e, e.extIsType(), []( const Context& c, const Value& v ) { return ToValue( false ); } );




        RegisterBuiltinFunc< Intrinsic< bool( CustomPattern< Value, PatternType > ) > >(
            e, e.extIsType(), []( const Context& c, const Value& v ) { return ToValue( true ); } );



    }

    bool IsType( const Context& c, const Value& v, source_location sloc )
    {
        if( v.isType() )
            return true;

        auto result = InvokeOverloadSet(

            c, c.env()->extIsType(), MakeClosedTuple( v ), Location::Create( sloc ) );

        if( result.isPoison() )
        {
            PoisonBuilder( c );
            return false;
        }

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

            PoisonBuilder( c );
            return false;
        }

        return *boolRes;
    }

    Value ToType( const Context& c, const Value& v )
    {
        if( v.isType() )
            return v;

        auto result = ConvertValueToType( c, v, TypeType() );
        if( !holds_alternative< Value >( result ) )
            return PoisonType();

        return get< Value >( result );
    }
} // namespace goose::builtins
Changes to bs/builtins/types/types.h.
85
86
87
88
89
90
91
92
93


94
95
        static const Term& GetPattern()
        {
            static auto pat = TypeType();
            return pat;
        }
    };

    extern Value InferTypeFromTExprAndInitializer( const Context& c, const Term& typeTExpr, const Value& initVal );
}



#endif







|
<
>
>


85
86
87
88
89
90
91
92

93
94
95
96
        static const Term& GetPattern()
        {
            static auto pat = TypeType();
            return pat;
        }
    };

    extern Value InferTypeFromTExprAndInitializer(

        const Context& c, const Term& typeTExpr, const Value& initVal );
} // namespace goose::builtins

#endif
Changes to bs/builtins/types/wrapper.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378

379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398

399
400
401
402
403
404
405
406
407
408
409
#ifndef GOOSE_BUILTINS_TYPES_WRAPPER_H
#define GOOSE_BUILTINS_TYPES_WRAPPER_H

namespace goose::builtins
{
    template< typename T, bool convertible_to_term = IsTypeInVariant< T, Term > >
    struct TypeWrapperHelper
    {
        using type = T;

        template< typename TT >
        static auto ToTerm( TT&& t )
        {
            return TERM( forward< TT >( t ) );
        }

        static optional< T > FromTerm( const Term& t )
        {
            auto result = get_if< T >( &t );

            if( !result )
                return nullopt;

            return *result;
        }

        template< typename TT >
        static LocationId Loc( TT& t )
        {
            return {};
        }
    };

    template< typename T >
    struct TypeWrapperHelper< ptr< T >, false >
    {
        using type = ptr< T >;

        template< typename TT >
        static auto ToTerm( TT&& t )
        {
            return TERM( static_pointer_cast< void >( forward< TT >( t ) ) );
        }

        static optional< ptr< T > > FromTerm( const Term& t )
        {
            auto pt = get_if< ptr< void > >( &t );
            if( !pt )
                return nullopt;
            return static_pointer_cast< T >( *pt );
        }

        template< typename TT >
        static LocationId Loc( TT& t )
        {
            return {};
        }
    };

    template< typename T >
    struct TypeWrapperHelper< wptr< T >, false >
    {
        using type = ptr< T >;

        template< typename TT >
        static auto ToTerm( TT&& t )
        {
            return TERM( static_pointer_cast< void >( forward< TT >( t.lock() ) ) );
        }

        static optional< ptr< T > > FromTerm( const Term& t )
        {
            auto pt = get_if< ptr< void > >( &t );
            if( !pt )
                return nullopt;
            return static_pointer_cast< T >( *pt );
        }

        template< typename TT >
        static LocationId Loc( TT& t )
        {
            return {};
        }
    };

    template<>
    struct TypeWrapperHelper< Term, false >
    {
        using type = Term;

        template< typename TT >
        static auto ToTerm( TT&& t )
        {
            return forward< TT >( t );
        }

        template< typename TT >
        static optional< Term > FromTerm( TT&& t )
        {
            return forward< TT >( t );
        }

        template< typename TT >
        static LocationId Loc( TT& t )
        {
            return {};
        }
    };

    template<>
    struct TypeWrapperHelper< cir::LowerableType, false >
    {
        using type = Term;

        static auto ToTerm( const cir::LowerableType& t )
        {
            return t.get();
        }

        template< typename TT >
        static optional< Term > FromTerm( TT&& t )
        {
            return forward< TT >( t );
        }

        template< typename TT >
        static LocationId Loc( TT& t )
        {
            return {};
        }
    };

    template<>
    struct TypeWrapperHelper< Value, false >
    {
        using type = Value;

        static auto ToTerm( const Value& v )
        {
            return ValueToEIR( v );
        }

        static optional< Value > FromTerm( const Term& t )
        {
            return EIRToValue( t );
        }

        static LocationId Loc( const Value& v )
        {
            return v.locationId();
        }
    };

    template<>
    struct TypeWrapperHelper< cir::LowerableValue, false >
    {
        using type = Term;

        template< typename T >
        static auto ToTerm( const cir::LowerableValue& t )
        {
            return ValueToEIR( t.get() );
        }

        static optional< Value > FromTerm( const Term& t )
        {
            return EIRToValue( t );
        }

        static LocationId Loc( const cir::LowerableValue& v )
        {
            return v.get().locationId();
        }
    };

    template< typename T >
    class TypeWrapper
    {
        public:
            template< typename TT >
            TypeWrapper( TT&& val ) :
                m_val( forward< TT >( val ) )
            {}

            using type = T;

            const auto& get() const
            {
                return m_val;

            }

            auto& get()
            {
                return m_val;
            }

            operator T&()
            {
                return m_val;
            }

            operator const T&() const
            {
                return m_val;
            }

            auto operator->() const
            {
                return m_val;
            }

            auto toTerm() const
            {
                return TypeWrapperHelper< T >::ToTerm( m_val );
            }

            static auto FromTerm( const Term& t )
            {
                return TypeWrapperHelper< T >::FromTerm( t );
            }

            auto locationId() const
            {
                return TypeWrapperHelper< T >::Loc( m_val );
            }

        private:
            typename TypeWrapperHelper< T >::type m_val;
    };

    template< typename T >
    auto Wrap( T&& val )
    {
        return ToValue( TypeWrapper< remove_cvref_t< T > >( forward< T >( val ) ) );
    }

    template< typename T >
    struct IsTypeWrapper : public false_type

    {};

    template< typename T >
    struct IsTypeWrapper< TypeWrapper< T > > : public true_type

    {};

    template< typename T >
    struct TypeWrapperTraits {};


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

    template<>
    struct TypeWrapperTraits< cir::LowerableType > : public TypeWrapperTraits< Term >

    {};

    template<>
    struct TypeWrapperTraits< Value >
    {
        static auto typeId() { return "Value"_sid; }
    };

    template<>
    struct TypeWrapperTraits< cir::LowerableValue > : public TypeWrapperTraits< Value >

    {};

    template<>
    struct TypeWrapperTraits< pvec >
    {
        static auto typeId() { return "Vec"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< sema::Context > >
    {
        static auto typeId() { return "Context"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< cir::CFG > >
    {
        static auto typeId() { return "CFG"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< CodeBuilder > >
    {
        static auto typeId() { return "CodeBuilder"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< StructBuilder > >
    {
        static auto typeId() { return "StructBuilder"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< Propositions > >
    {
        static auto typeId() { return "Propositions"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< GhostCode > >
    {
        static auto typeId() { return "GhostCode"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< vector< TermLoc > > >
    {
        static auto typeId() { return "Tokens"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< Decl > >
    {
        static auto typeId() { return "WrappedDecl"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< TDecl > >
    {
        static auto typeId() { return "WrappedTDecl"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< TNamedDecl > >
    {
        static auto typeId() { return "WrappedTNamedDecl"_sid; }
    };

    template< typename T > struct SWrapType
    {
        using type = TypeWrapper< T >;
    };

    template<> struct SWrapType< bool >
    {
        using type = bool;
    };

    template<> struct SWrapType< uint32_t >
    {
        using type = uint32_t;
    };

    template< typename T >
    using WrapType = typename SWrapType< T >::type;

    template< typename T >
    struct WrappedValueAccessor
    {
        template< typename TT >
        static auto Get( const TT& src )
        {
            return src.get();
        }
    };

    template<>
    struct WrappedValueAccessor< bool >
    {
        static auto Get( bool src )
        {
            return src;
        }
    };

    template<>
    struct WrappedValueAccessor< uint32_t >
    {
        static auto Get( uint32_t src )
        {
            return src;
        }
    };

}

namespace goose::eir
{
    template< typename T >
    struct Bridge< builtins::TypeWrapper< T > >
    {
        static const Term& Type()
        {
            static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( ct_type ),
                TERM( builtins::TypeWrapperTraits< T >::typeId() ) ) ) );
            return type;
        }

        static Value ToValue( const builtins::TypeWrapper< T >& tw )
        {
            return Value( Type(), sema::Quote( tw.toTerm() ) )
                .setLocationId( tw.locationId() );
        }


        static auto FromValue( const Value& v ) -> decltype( builtins::TypeWrapper< T >::FromTerm( v.val() ) )
        {
            if( !v.isConstant() || v.type() != Type() )
                return {};

            return builtins::TypeWrapper< T >::FromTerm( *sema::Unquote( v.val() ) );
        }
    };
}

#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




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
282
283
284
285
286

287
288
289
290
291
292
293
294
295
296
297
298
299
300
#ifndef GOOSE_BUILTINS_TYPES_WRAPPER_H
#define GOOSE_BUILTINS_TYPES_WRAPPER_H

namespace goose::builtins
{
    template< typename T, bool convertible_to_term = IsTypeInVariant< T, Term > >
    struct TypeWrapperHelper
    {
        using type = T;

        template< typename TT > static auto ToTerm( TT&& t ) { return TERM( forward< TT >( t ) ); }





        static optional< T > FromTerm( const Term& t )
        {
            auto result = get_if< T >( &t );

            if( !result )
                return nullopt;

            return *result;
        }

        template< typename TT > static LocationId Loc( TT& t ) { return {}; }




    };


    template< typename T > struct TypeWrapperHelper< ptr< T >, false >
    {
        using type = ptr< T >;

        template< typename TT > static auto ToTerm( TT&& t )

        {
            return TERM( static_pointer_cast< void >( forward< TT >( t ) ) );
        }

        static optional< ptr< T > > FromTerm( const Term& t )
        {
            auto pt = get_if< ptr< void > >( &t );
            if( !pt )
                return nullopt;
            return static_pointer_cast< T >( *pt );
        }

        template< typename TT > static LocationId Loc( TT& t ) { return {}; }




    };


    template< typename T > struct TypeWrapperHelper< wptr< T >, false >
    {
        using type = ptr< T >;

        template< typename TT > static auto ToTerm( TT&& t )

        {
            return TERM( static_pointer_cast< void >( forward< TT >( t.lock() ) ) );
        }

        static optional< ptr< T > > FromTerm( const Term& t )
        {
            auto pt = get_if< ptr< void > >( &t );
            if( !pt )
                return nullopt;
            return static_pointer_cast< T >( *pt );
        }

        template< typename TT > static LocationId Loc( TT& t ) { return {}; }




    };


    template<> struct TypeWrapperHelper< Term, false >
    {
        using type = Term;

        template< typename TT > static auto ToTerm( TT&& t ) { return forward< TT >( t ); }





        template< typename TT > static optional< Term > FromTerm( TT&& t )

        {
            return forward< TT >( t );
        }

        template< typename TT > static LocationId Loc( TT& t ) { return {}; }




    };


    template<> struct TypeWrapperHelper< cir::LowerableType, false >
    {
        using type = Term;

        static auto ToTerm( const cir::LowerableType& t ) { return t.get(); }




        template< typename TT > static optional< Term > FromTerm( TT&& t )

        {
            return forward< TT >( t );
        }

        template< typename TT > static LocationId Loc( TT& t ) { return {}; }




    };


    template<> struct TypeWrapperHelper< Value, false >
    {
        using type = Value;

        static auto ToTerm( const Value& v ) { return ValueToEIR( v ); }




        static optional< Value > FromTerm( const Term& t ) { return EIRToValue( t ); }




        static LocationId Loc( const Value& v ) { return v.locationId(); }



    };


    template<> struct TypeWrapperHelper< cir::LowerableValue, false >
    {
        using type = Term;


        template< typename T > static auto ToTerm( const cir::LowerableValue& t )
        {
            return ValueToEIR( t.get() );
        }

        static optional< Value > FromTerm( const Term& t ) { return EIRToValue( t ); }




        static LocationId Loc( const cir::LowerableValue& v ) { return v.get().locationId(); }



    };

    template< typename T > class TypeWrapper

    {
      public:
        template< typename TT >
        TypeWrapper( TT&& val ) :
            m_val( forward< TT >( val ) )

        {

        }



        using type = T;


        const auto& get() const { return m_val; }

        auto& get() { return m_val; }


        operator T&() { return m_val; }




        operator const T &() const { return m_val; }




        auto operator->() const { return m_val; }






        auto toTerm() const { return TypeWrapperHelper< T >::ToTerm( m_val ); }


        static auto FromTerm( const Term& t ) { return TypeWrapperHelper< T >::FromTerm( t ); }






        auto locationId() const { return TypeWrapperHelper< T >::Loc( m_val ); }


      private:
        typename TypeWrapperHelper< T >::type m_val;
    };

    template< typename T > auto Wrap( T&& val )

    {
        return ToValue( TypeWrapper< remove_cvref_t< T > >( forward< T >( val ) ) );
    }

    template< typename T > struct IsTypeWrapper : public false_type

    {
    };


    template< typename T > struct IsTypeWrapper< TypeWrapper< T > > : public true_type
    {
    };

    template< typename T > struct TypeWrapperTraits

    {
    };

    template<> struct TypeWrapperTraits< Term >
    {
        static auto typeId() { return "Term"_sid; }
    };


    template<> struct TypeWrapperTraits< cir::LowerableType > : public TypeWrapperTraits< Term >
    {
    };


    template<> struct TypeWrapperTraits< Value >
    {
        static auto typeId() { return "Value"_sid; }
    };


    template<> struct TypeWrapperTraits< cir::LowerableValue > : public TypeWrapperTraits< Value >
    {
    };


    template<> struct TypeWrapperTraits< pvec >
    {
        static auto typeId() { return "Vec"_sid; }
    };


    template<> struct TypeWrapperTraits< ptr< sema::Context > >
    {
        static auto typeId() { return "Context"_sid; }
    };


    template<> struct TypeWrapperTraits< ptr< cir::CFG > >
    {
        static auto typeId() { return "CFG"_sid; }
    };


    template<> struct TypeWrapperTraits< ptr< CodeBuilder > >
    {
        static auto typeId() { return "CodeBuilder"_sid; }
    };


    template<> struct TypeWrapperTraits< ptr< StructBuilder > >
    {
        static auto typeId() { return "StructBuilder"_sid; }
    };


    template<> struct TypeWrapperTraits< ptr< Propositions > >
    {
        static auto typeId() { return "Propositions"_sid; }
    };


    template<> struct TypeWrapperTraits< ptr< GhostCode > >
    {
        static auto typeId() { return "GhostCode"_sid; }
    };


    template<> struct TypeWrapperTraits< ptr< vector< TermLoc > > >
    {
        static auto typeId() { return "Tokens"_sid; }
    };


    template<> struct TypeWrapperTraits< ptr< Decl > >
    {
        static auto typeId() { return "WrappedDecl"_sid; }
    };


    template<> struct TypeWrapperTraits< ptr< TDecl > >
    {
        static auto typeId() { return "WrappedTDecl"_sid; }
    };


    template<> struct TypeWrapperTraits< ptr< TNamedDecl > >
    {
        static auto typeId() { return "WrappedTNamedDecl"_sid; }
    };

    template< typename T > struct SWrapType
    {
        using type = TypeWrapper< T >;
    };

    template<> struct SWrapType< bool >
    {
        using type = bool;
    };

    template<> struct SWrapType< uint32_t >
    {
        using type = uint32_t;
    };


    template< typename T > using WrapType = typename SWrapType< T >::type;

    template< typename T > struct WrappedValueAccessor

    {
        template< typename TT > static auto Get( const TT& src ) { return src.get(); }




    };


    template<> struct WrappedValueAccessor< bool >
    {
        static auto Get( bool src ) { return src; }



    };


    template<> struct WrappedValueAccessor< uint32_t >
    {
        static auto Get( uint32_t src ) { return src; }



    };
} // namespace goose::builtins


namespace goose::eir
{

    template< typename T > struct Bridge< builtins::TypeWrapper< T > >
    {
        static const Term& Type()
        {
            static auto type = ValueToEIR( Value( TypeType(),
                VEC( TSID( ct_type ), TERM( builtins::TypeWrapperTraits< T >::typeId() ) ) ) );
            return type;
        }

        static Value ToValue( const builtins::TypeWrapper< T >& tw )
        {
            return Value( Type(), sema::Quote( tw.toTerm() ) ).setLocationId( tw.locationId() );

        }

        static auto FromValue(
            const Value& v ) -> decltype( builtins::TypeWrapper< T >::FromTerm( v.val() ) )
        {
            if( !v.isConstant() || v.type() != Type() )
                return {};

            return builtins::TypeWrapper< T >::FromTerm( *sema::Unquote( v.val() ) );
        }
    };
} // namespace goose::eir

#endif
Deleted bs/cir/arith.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#ifndef GOOSE_CIR_ARITH_H
#define GOOSE_CIR_ARITH_H

namespace goose::cir
{
    class Add : public BinaryOp
    {
        public:
            Add( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Add& ins )
            {
                return out << "ADD";
            }
    };

    class Sub : public BinaryOp
    {
        public:
            Sub( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Sub& ins )
            {
                return out << "SUB";
            }
    };

    class Mul : public BinaryOp
    {
        public:
            Mul( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Mul& ins )
            {
                return out << "MUL";
            }
    };

    class UDiv : public BinaryOp
    {
        public:
            UDiv( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const UDiv& ins )
            {
                return out << "UDIV";
            }
    };

    class SDiv : public BinaryOp
    {
        public:
            SDiv( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const SDiv& ins )
            {
                return out << "SDIV";
            }
    };

    class URem : public BinaryOp
    {
        public:
            URem( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const URem& ins )
            {
                return out << "UREM";
            }
    };

    class SRem : public BinaryOp
    {
        public:
            SRem( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const SRem& ins )
            {
                return out << "SREM";
            }
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




































































































































































































Deleted bs/cir/ass.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
#ifndef GOOSE_CIR_ASS_H
#define GOOSE_CIR_ASS_H

// This file is not called "assert.h" because that fucks the cassert header up
// when it tries to include assert.h.
// "ass" is both a good shorthand and an accurate description of the problem

namespace goose::cir
{
    class Assert : public BaseInstr< 1, false >
    {
        public:
            Assert( LocationId loc ) :
                BaseInstr( loc )
            {}

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return true; }

            bool operator<( const Assert& rhs ) const
            {
                return false;
            }

            friend ostream& operator<<( ostream& out, const Assert& ins )
            {
                return out << "ASSERT";
            }
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


































































Changes to bs/cir/basicblock.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18


19
20
21

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

45

46

47
48
49
50
51

52
53
54

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

102
103
104
105
106
107
108
109
110
111
112
113
114
115
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
#ifndef GOOSE_CIR_BASICBLOCK_H
#define GOOSE_CIR_BASICBLOCK_H

namespace llvm
{
    class BasicBlock;
}

namespace goose::cir
{
    class BasicBlock
    {
        public:
            BasicBlock( const ptr< CFG >& owner, uint32_t index ) :
                m_owner( owner ),
                m_index( index )
            {}



            using const_iterator = vector< Instruction >::const_iterator;

            auto locationId() const { return m_locId; }

            void setLocationId( LocationId locId ) { m_locId = locId; }

            auto index() const { return m_index; }

            // /!\ Warning: as of now, back edges exclude continuation edges
            // from GhostBranch instructions. It's ok for loop identification because
            // using GhostBranch as a loop intruction makes no sense, and other
            // than that back edges are only used by the verifier. If they are needed
            // elsewhere, they are basically broken.
            void addBackEdge( uint32_t srcBBIndex )
            {
                m_backEdges.emplace_back( srcBBIndex );
            }
            const auto& backEdges() const { return m_backEdges; }
            auto& backEdges() { return m_backEdges; }

            void addLoopEdge( uint32_t srcBBIndex )
            {
                m_loopEdges.emplace_back( srcBBIndex );
            }
            const auto& loopEdges() const { return m_loopEdges; }

            uint32_t loopId() const { return m_loopId; }

            bool isLoopHeader() const { return m_isLoopHeader; }

            void setLoopId( uint32_t id ) { m_loopId = id; }

            void setLoopHeader() { m_isLoopHeader = true; }

            auto empty() const { return m_instructions.empty(); }

            const auto& instructions() const { return m_instructions; }

            auto& instructions() { return m_instructions; }

            using RunnableInstructions = llvm::SmallVector< Instruction, 16 >;

            const RunnableInstructions* runnableInstructions() const
            {
                if( m_dirty )
                {
                    m_dirty = false;

                    m_runnableInstructions.clear();
                    if( !FilterVerificationInstructions( m_instructions, m_runnableInstructions ) )
                        return nullptr;
                }

                return &m_runnableInstructions;
            }

            void clear()
            {
                m_instructions.clear();
                m_dirty = true;
            }

            template< typename... I >
            void append( I&&... instrs )
            {
                AppendToInstrSeq( m_instructions, forward< I >( instrs )... );
                m_dirty = true;
            }

            template< typename T >
            void setTerminator( T&& terminator );

            const auto& terminator() const { return m_terminator; }

            void dirty() { m_dirty = true; }

            bool canBeExecuted() const
            {
                return m_canBeExecuted
                    && ( !m_terminator || m_terminator->canBeExecuted() );
            }

            bool canBeEagerlyEvaluated() const
            {
                return m_canBeEagerlyEvaluated
                    && ( !m_terminator || m_terminator->canBeEagerlyEvaluated() );
            }

            bool codeGenStarted() const { return m_codeGenStarted; }

            void setCodeGenStarted( bool b = true ) { m_codeGenStarted = b; }

            template< typename F >
            void forEachSuccessor( F&& func ) const;

            friend ostream& operator<<( ostream& out, const BasicBlock& bb )
            {
                out << bb.index() << ":\n";
                for( auto&& instr : bb.instructions() )
                    out << "    " << instr << endl;

                if( bb.terminator() )
                    out << "  " << *bb.terminator() << endl;

                return out;
            }

        private:
            InstrSeq                m_instructions;
            mutable RunnableInstructions m_runnableInstructions;
            optional< Terminator >  m_terminator;

            weak_ptr< CFG >         m_owner;

            llvm::SmallVector< uint32_t, 8 >    m_backEdges;
            llvm::SmallVector< uint32_t, 8 >    m_loopEdges;

            uint32_t                m_index = 0;

            uint32_t                m_loopId = 0;           // Id of the header of the loop that this block belongs to, or 0.

            LocationId              m_locId = 0;            // An optional location id. Used by the verifier on loop headers
                                                            // to indicate the location of a loop that it failed to verify.

            bool                    m_isLoopHeader = false;

            bool                    m_canBeExecuted = true;
            bool                    m_canBeEagerlyEvaluated = true;

            mutable bool            m_dirty = false;
            mutable bool            m_codeGenStarted = false;
    };
}

#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
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
#ifndef GOOSE_CIR_BASICBLOCK_H
#define GOOSE_CIR_BASICBLOCK_H

namespace llvm
{
    class BasicBlock;
}

namespace goose::cir
{
    class BasicBlock
    {
      public:
        BasicBlock( const ptr< CFG >& owner, uint32_t index ) :
            m_owner( owner ),
            m_index( index )

        {
        }

        using const_iterator = vector< Instruction >::const_iterator;

        auto locationId() const { return m_locId; }

        void setLocationId( LocationId locId ) { m_locId = locId; }

        auto index() const { return m_index; }

        // /!\ Warning: as of now, back edges exclude continuation edges from GhostBranch
        // instructions. It's ok for loop identification because using GhostBranch as a loop
        // intruction makes no sense, and other than that back edges are only used by the

        // verifier. If they are needed elsewhere, they are basically broken.
        void addBackEdge( uint32_t srcBBIndex ) { m_backEdges.emplace_back( srcBBIndex ); }

        const auto& backEdges() const { return m_backEdges; }


        auto& backEdges() { return m_backEdges; }

        void addLoopEdge( uint32_t srcBBIndex ) { m_loopEdges.emplace_back( srcBBIndex ); }



        const auto& loopEdges() const { return m_loopEdges; }

        uint32_t loopId() const { return m_loopId; }

        bool isLoopHeader() const { return m_isLoopHeader; }

        void setLoopId( uint32_t id ) { m_loopId = id; }

        void setLoopHeader() { m_isLoopHeader = true; }

        auto empty() const { return m_instructions.empty(); }

        const auto& instructions() const { return m_instructions; }

        auto& instructions() { return m_instructions; }

        using RunnableInstructions = llvm::SmallVector< Instruction, 16 >;

        const RunnableInstructions* runnableInstructions() const
        {
            if( m_dirty )
            {
                m_dirty = false;

                m_runnableInstructions.clear();
                if( !FilterVerificationInstructions( m_instructions, m_runnableInstructions ) )
                    return nullptr;
            }

            return &m_runnableInstructions;
        }

        void clear()
        {
            m_instructions.clear();
            m_dirty = true;
        }

        template< typename... I > void append( I&&... instrs )

        {
            AppendToInstrSeq( m_instructions, forward< I >( instrs )... );
            m_dirty = true;
        }

        template< typename T > void setTerminator( T&& terminator );


        const auto& terminator() const { return m_terminator; }

        void dirty() { m_dirty = true; }

        bool canBeExecuted() const
        {

            return m_canBeExecuted && ( !m_terminator || m_terminator->canBeExecuted() );
        }

        bool canBeEagerlyEvaluated() const
        {
            return m_canBeEagerlyEvaluated
                && ( !m_terminator || m_terminator->canBeEagerlyEvaluated() );
        }

        bool codeGenStarted() const { return m_codeGenStarted; }

        void setCodeGenStarted( bool b = true ) { m_codeGenStarted = b; }

        template< typename F > void forEachSuccessor( F&& func ) const;


        friend ostream& operator<<( ostream& out, const BasicBlock& bb )
        {
            out << bb.index() << ":\n";
            for( auto&& instr : bb.instructions() )
                out << "    " << instr << endl;

            if( bb.terminator() )
                out << "  " << *bb.terminator() << endl;

            return out;
        }

      private:
        InstrSeq m_instructions;
        mutable RunnableInstructions m_runnableInstructions;
        optional< Terminator > m_terminator;

        weak_ptr< CFG > m_owner;

        llvm::SmallVector< uint32_t, 8 > m_backEdges;
        llvm::SmallVector< uint32_t, 8 > m_loopEdges;

        uint32_t m_index = 0;

        uint32_t m_loopId = 0; // Id of the header of the loop that this block belongs to, or 0.

        LocationId m_locId = 0; // An optional location id. Used by the verifier on loop headers
                                // to indicate the location of a loop that it failed to verify.

        bool m_isLoopHeader = false;

        bool m_canBeExecuted = true;
        bool m_canBeEagerlyEvaluated = true;

        mutable bool m_dirty = false;
        mutable bool m_codeGenStarted = false;
    };
} // namespace goose::cir

#endif
Changes to bs/cir/basicblock.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
#ifndef GOOSE_CIR_BASICBLOCK_INL
#define GOOSE_CIR_BASICBLOCK_INL

namespace goose::cir
{
    template< typename T >
    void BasicBlock::setTerminator( T&& terminator )
    {
        m_terminator = forward< T >( terminator );
        m_terminator->addCFGEdges( m_owner.lock(), index() );
    }

    template< typename T, typename F >
    void ForEachSuccessor( T&& tr, F&& func )
    {}

    template< typename F >
    void ForEachSuccessor( const Branch& tr, F&& func )
    {
        func( tr.dest().lock() );
    }

    template< typename F >
    void ForEachSuccessor( const CondBranch& tr, F&& func )
    {
        func( tr.trueDest().lock() );
        func( tr.falseDest().lock() );
    }

    template< typename F >
    void ForEachSuccessor( const GhostBranch& tr, F&& func )
    {
        func( tr.ghostCode().lock() );
        func( tr.continuation().lock() );
    }

    template< typename F >
    void ForEachSuccessor( const Terminator& tr, F&& func )
    {
        visit( [&]( auto&& tr )
        {
            ForEachSuccessor( tr, forward< F >( func ) );
        }, tr.content() );
    }

    template< typename F >
    void BasicBlock::forEachSuccessor( F&& func ) const
    {
        if( m_terminator )
            ForEachSuccessor( *m_terminator, forward< F >( func ) );
    }
}

#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
#ifndef GOOSE_CIR_BASICBLOCK_INL
#define GOOSE_CIR_BASICBLOCK_INL

namespace goose::cir
{

    template< typename T > void BasicBlock::setTerminator( T&& terminator )
    {
        m_terminator = forward< T >( terminator );
        m_terminator->addCFGEdges( m_owner.lock(), index() );
    }

    template< typename T, typename F > void ForEachSuccessor( T&& tr, F&& func ) {}




    template< typename F > void ForEachSuccessor( const Branch& tr, F&& func )
    {
        func( tr.dest().lock() );
    }


    template< typename F > void ForEachSuccessor( const CondBranch& tr, F&& func )
    {
        func( tr.trueDest().lock() );
        func( tr.falseDest().lock() );
    }


    template< typename F > void ForEachSuccessor( const GhostBranch& tr, F&& func )
    {
        func( tr.ghostCode().lock() );
        func( tr.continuation().lock() );
    }


    template< typename F > void ForEachSuccessor( const Terminator& tr, F&& func )
    {


        visit( [&]( auto&& tr ) { ForEachSuccessor( tr, forward< F >( func ) ); }, tr.content() );

    }


    template< typename F > void BasicBlock::forEachSuccessor( F&& func ) const
    {
        if( m_terminator )
            ForEachSuccessor( *m_terminator, forward< F >( func ) );
    }
} // namespace goose::cir

#endif
Deleted bs/cir/binaryop.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
#ifndef GOOSE_CIR_BINARYINSTR_H
#define GOOSE_CIR_BINARYINSTR_H

namespace goose::cir
{
    class BinaryOp : public BaseInstr< 2, true >
    {
        public:
            BinaryOp( LocationId loc ) :
                BaseInstr( loc )
            {}

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }
            bool haveSideEffects() const { return false; }

            bool operator<( const BinaryOp& rhs ) const
            {
                return false;
            }
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
















































Deleted bs/cir/bitwise.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
#ifndef GOOSE_CIR_BITWISE_H
#define GOOSE_CIR_BITWISE_H

namespace goose::cir
{
    class Shl : public BinaryOp
    {
        public:
            Shl( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Shl& ins )
            {
                return out << "SHL";
            }
    };

    class LShr : public BinaryOp
    {
        public:
            LShr( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const LShr& ins )
            {
                return out << "LSHR";
            }
    };

    class AShr : public BinaryOp
    {
        public:
            AShr( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const AShr& ins )
            {
                return out << "ASHR";
            }
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































































Changes to bs/cir/branch.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15


16
17
18

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37


38

39
40
41

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64


65

66
67
68

69
70
71
72
73
74
75
76
77
78
79

80
81
#ifndef GOOSE_CIR_BRANCH_H
#define GOOSE_CIR_BRANCH_H

namespace goose::cir
{
    class BasicBlock;

    class Branch
    {
        public:
            template< typename B >
            Branch( B&& dest ) :
                m_dest( forward< B >( dest ) )
            {}



            const auto& dest() const { return m_dest; }

            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return true; }

            void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex );

            friend ostream& operator<<( ostream& out, const Branch& ter );

        private:
            wptr< BasicBlock > m_dest;
    };

    class CondBranch
    {
        public:
            template< typename BT, typename BF >
            CondBranch( BT&& trueDest, BF&& falseDest ) :
                m_trueDest( forward< BT >( trueDest ) ),
                m_falseDest( forward< BF >( falseDest ) )
            {}



            const auto& trueDest() const { return m_trueDest; }

            const auto& falseDest() const { return m_falseDest; }

            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return true; }

            void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex );

            friend ostream& operator<<( ostream& out, const CondBranch& ter );

        private:
            wptr< BasicBlock > m_trueDest;
            wptr< BasicBlock > m_falseDest;
    };

    // A special terminator that points to a basic block to use only
    // during verification (containing "ghost code") and to a continuation
    // basic block
    class GhostBranch
    {
        public:
            template< typename GCB, typename CB >
            GhostBranch( GCB&& ghostCode, CB&& continuation ) :
                m_ghostCode( forward< GCB >( ghostCode ) ),
                m_continuation( forward< CB >( continuation ) )
            {}



            const auto& ghostCode() const { return m_ghostCode; }

            const auto& continuation() const { return m_continuation; }

            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return true; }

            void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex );

            friend ostream& operator<<( ostream& out, const GhostBranch& ter );

        private:
            wptr< BasicBlock > m_ghostCode;
            wptr< BasicBlock > m_continuation;
    };
}


#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
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
#ifndef GOOSE_CIR_BRANCH_H
#define GOOSE_CIR_BRANCH_H

namespace goose::cir
{
    class BasicBlock;

    class Branch
    {
      public:
        template< typename B >
        Branch( B&& dest ) :
            m_dest( forward< B >( dest ) )

        {
        }

        const auto& dest() const { return m_dest; }

        bool canBeExecuted() const { return true; }

        bool canBeEagerlyEvaluated() const { return true; }

        void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex );

        friend ostream& operator<<( ostream& out, const Branch& ter );

      private:
        wptr< BasicBlock > m_dest;
    };

    class CondBranch
    {
      public:
        template< typename BT, typename BF >
        CondBranch( BT&& trueDest, BF&& falseDest ) :
            m_trueDest( forward< BT >( trueDest ) ),
            m_falseDest( forward< BF >( falseDest ) )

        {
        }

        const auto& trueDest() const { return m_trueDest; }

        const auto& falseDest() const { return m_falseDest; }

        bool canBeExecuted() const { return true; }

        bool canBeEagerlyEvaluated() const { return true; }

        void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex );

        friend ostream& operator<<( ostream& out, const CondBranch& ter );

      private:
        wptr< BasicBlock > m_trueDest;
        wptr< BasicBlock > m_falseDest;
    };

    // A special terminator that points to a basic block to use only
    // during verification (containing "ghost code") and to a continuation
    // basic block
    class GhostBranch
    {
      public:
        template< typename GCB, typename CB >
        GhostBranch( GCB&& ghostCode, CB&& continuation ) :
            m_ghostCode( forward< GCB >( ghostCode ) ),
            m_continuation( forward< CB >( continuation ) )

        {
        }

        const auto& ghostCode() const { return m_ghostCode; }

        const auto& continuation() const { return m_continuation; }

        bool canBeExecuted() const { return true; }

        bool canBeEagerlyEvaluated() const { return true; }

        void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex );

        friend ostream& operator<<( ostream& out, const GhostBranch& ter );

      private:
        wptr< BasicBlock > m_ghostCode;
        wptr< BasicBlock > m_continuation;
    };

} // namespace goose::cir

#endif
Changes to bs/cir/break.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
#ifndef GOOSE_CIR_BREAK_H
#define GOOSE_CIR_BREAK_H

namespace goose::cir
{
    class Break
    {
        public:
            Break() {}

            Break( uint32_t level ) :
                m_level( level )
            {}



            const auto& level() const { return m_level; }

            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return true; }

            void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex )  {}

            friend ostream& operator<<( ostream& out, const Break& ter );

        private:
            uint32_t m_level = 0;
    };
}


#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
#ifndef GOOSE_CIR_BREAK_H
#define GOOSE_CIR_BREAK_H

namespace goose::cir
{
    class Break
    {
      public:
        Break() {}

        Break( uint32_t level ) :
            m_level( level )

        {
        }

        const auto& level() const { return m_level; }

        bool canBeExecuted() const { return true; }

        bool canBeEagerlyEvaluated() const { return true; }

        void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex ) {}

        friend ostream& operator<<( ostream& out, const Break& ter );

      private:
        uint32_t m_level = 0;
    };

} // namespace goose::cir

#endif
Deleted bs/cir/call.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#ifndef GOOSE_CIR_CALL_H
#define GOOSE_CIR_CALL_H

namespace goose::cir
{
    class Func;

    class Call : public BaseInstr< 1, true >   // Can pop more depending on arg count
    {
        public:
            Call( uint32_t numArgs, LocationId loc ) :
                BaseInstr( loc ),
                m_numArgs( numArgs )
            {}

            const auto& numArgs() const { return m_numArgs; }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }
            bool haveSideEffects() const { return true; }

            bool operator<( const Call& rhs ) const
            {
                return m_numArgs < rhs.m_numArgs;
            }

            friend ostream& operator<<( ostream& out, const Call& ins )
            {
                return out << "CALL " << ins.m_numArgs;
            }

        private:
            uint32_t m_numArgs = 0;
    };

    class InlineCall : public BaseInstr< 0, true >
    {
        public:
            using ArgUidArray = llvm::SmallVector< uint32_t, 8 >;

            InlineCall( ArgUidArray&& argUids, const cir::Func* pFuncCIR, const eir::Term& retType,
                LocationId loc ) :
                BaseInstr( loc ),
                m_pFuncCIR( pFuncCIR ),
                m_argUids( move( argUids ) ),
                m_retType( retType )
            {}

            const auto* funcCIR() const { return m_pFuncCIR; }
            const auto& argUids() const { return m_argUids; }
            const auto& retType() const { return m_retType; }

            auto& argUids() { return m_argUids; }
            auto& retType() { return m_retType; }

            bool canBeExecuted() const { return false; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return true; }

            bool operator<( const InlineCall& rhs ) const
            {
                if( m_pFuncCIR != rhs.m_pFuncCIR )
                    return m_pFuncCIR < rhs.m_pFuncCIR;

                if( m_retType != rhs.m_retType )
                    return m_retType < rhs.m_retType;

                return m_argUids < rhs.m_argUids;
            }

            friend ostream& operator<<( ostream& out, const InlineCall& ins )
            {
                return out << "INLINECALL " << ins.m_pFuncCIR;
            }

        private:
            const cir::Func* m_pFuncCIR = nullptr;
            ArgUidArray m_argUids;
            eir::Term m_retType;
    };

    class CallCheck : public BaseInstr< 1, true >   // Can pop more depending on arg count
    {
        public:
            CallCheck( uint32_t numArgs, LocationId loc ) :
                BaseInstr( loc ),
                m_numArgs( numArgs )
            {}

            const auto& numArgs() const { return m_numArgs; }

            bool canBeExecuted() const { return false; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return false; }

            bool operator<( const CallCheck& rhs ) const
            {
                return m_numArgs < rhs.m_numArgs;
            }

            friend ostream& operator<<( ostream& out, const CallCheck& ins )
            {
                return out << "CALLCHECK " << ins.m_numArgs;
            }

        private:
            uint32_t m_numArgs = 0;
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






























































































































































































































Changes to bs/cir/cfg.cpp.
1
2
3
4
5
6
7

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

70
71
72
73
74

75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include "cir.h"

using namespace goose;
using namespace goose::cir;

const ptr< cir::BasicBlock >& CFG::createBB()
{

    m_basicBlocks.emplace_back( make_shared< BasicBlock >( shared_from_this(), m_basicBlocks.size() + 1 ) );
    return m_basicBlocks.back();
}

void CFG::addEdge( uint32_t srcIndex, uint32_t destIndex )
{
    m_edges.emplace( srcIndex, destIndex );
}

bool CFG::canBeExecuted() const
{
    for( auto&& bb : m_basicBlocks )
    {
        if( !bb->canBeExecuted() )
            return false;
    }

    return true;
}

bool CFG::canBeEagerlyEvaluated() const
{
    for( auto&& bb : m_basicBlocks )
    {
        if( !bb->canBeEagerlyEvaluated() )
            return false;
    }

    return true;
}

optional< uint32_t > CFG::getDominatorDistance( uint32_t bbIndex1, uint32_t bbIndex2 ) const
{
    assert( !m_idoms.empty() );

    if( bbIndex1 == bbIndex2 )
        return 0;

    uint32_t dist = 1;
    uint32_t currentBB = bbIndex2;

    for(;;)
    {
        assert( currentBB < m_idoms.size() );
        uint32_t nextBB = m_idoms[currentBB];

        if( nextBB == 0 )
            return nullopt;

        if( nextBB == bbIndex1 )
            return dist;

        currentBB = nextBB;
        ++dist;
    }
}

CFG::EdgeLoopCategory CFG::GetEdgeLoopCategory( const BasicBlock& fromBB, const BasicBlock& toBB )
{
    if( fromBB.isLoopHeader() )
    {
        return toBB.loopId() == fromBB.index() ? EdgeLoopCategory::None : EdgeLoopCategory::LoopExitEdge;

    }

    if( toBB.isLoopHeader() )
    {
        return fromBB.loopId() == toBB.index() ? EdgeLoopCategory::LoopEdge : EdgeLoopCategory::None;

    }

    return EdgeLoopCategory::None;
}

ostream& goose::cir::operator<<( ostream& out, const CFG& cfg )
{
    cfg.forEachBB( [&]( auto&& bb )
    {
        out << *bb << endl;
    } );

    return out;
}







>
|











<


<







<


<














|



















|
>




|
>







|
<
<
<



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
#include "cir.h"

using namespace goose;
using namespace goose::cir;

const ptr< cir::BasicBlock >& CFG::createBB()
{
    m_basicBlocks.emplace_back(
        make_shared< BasicBlock >( shared_from_this(), m_basicBlocks.size() + 1 ) );
    return m_basicBlocks.back();
}

void CFG::addEdge( uint32_t srcIndex, uint32_t destIndex )
{
    m_edges.emplace( srcIndex, destIndex );
}

bool CFG::canBeExecuted() const
{
    for( auto&& bb : m_basicBlocks )

        if( !bb->canBeExecuted() )
            return false;


    return true;
}

bool CFG::canBeEagerlyEvaluated() const
{
    for( auto&& bb : m_basicBlocks )

        if( !bb->canBeEagerlyEvaluated() )
            return false;


    return true;
}

optional< uint32_t > CFG::getDominatorDistance( uint32_t bbIndex1, uint32_t bbIndex2 ) const
{
    assert( !m_idoms.empty() );

    if( bbIndex1 == bbIndex2 )
        return 0;

    uint32_t dist = 1;
    uint32_t currentBB = bbIndex2;

    for( ;; )
    {
        assert( currentBB < m_idoms.size() );
        uint32_t nextBB = m_idoms[currentBB];

        if( nextBB == 0 )
            return nullopt;

        if( nextBB == bbIndex1 )
            return dist;

        currentBB = nextBB;
        ++dist;
    }
}

CFG::EdgeLoopCategory CFG::GetEdgeLoopCategory( const BasicBlock& fromBB, const BasicBlock& toBB )
{
    if( fromBB.isLoopHeader() )
    {
        return toBB.loopId() == fromBB.index() ? EdgeLoopCategory::None :
                                                 EdgeLoopCategory::LoopExitEdge;
    }

    if( toBB.isLoopHeader() )
    {
        return fromBB.loopId() == toBB.index() ? EdgeLoopCategory::LoopEdge :
                                                 EdgeLoopCategory::None;
    }

    return EdgeLoopCategory::None;
}

ostream& goose::cir::operator<<( ostream& out, const CFG& cfg )
{
    cfg.forEachBB( [&]( auto&& bb ) { out << *bb << endl; } );




    return out;
}
Changes to bs/cir/cfg.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14


15
16
17
18
19
20
21

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

45
46
47
48
49
50
51
52
53
54

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

72
73
74
75
76
77
78
79
80

81
82
83

84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102

103
104
105
106
107
108
109
110
111


112
113
114
115
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
#ifndef GOOSE_CIR_CFG_H
#define GOOSE_CIR_CFG_H

namespace goose::cir
{
    class BasicBlock;

    class CFG : public enable_shared_from_this< CFG >
    {
        public:
            CFG( uint32_t numParams ) :
                m_temporariesCount( numParams )
            {}



            bool isPoisoned() const { return m_poisoned; }
            void poison()
            {
                m_poisoned = true;
            }

            const auto& entryBB() const { return m_basicBlocks.front(); }

            const auto& lastBB() const { return m_basicBlocks.back(); }

            const auto& currentBB() const { return m_currentBB; }
            template< typename T >
            void setCurrentBB( T&& pBB )
            {
                m_currentBB = forward<  T >( pBB );
            }

            template< typename T >
            void emitTerminator( LocationId locId, T&& terminator )
            {
                if( !currentBB() || currentBB()->terminator() )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                        locId, "unreachable code.", 0 );
                    return;
                }

                currentBB()->setTerminator( forward< T >( terminator ) );
            }

            void addEdge( uint32_t srcIndex, uint32_t destIndex );

            const auto& edges() const { return m_edges; }

            template< typename V >
            void setIdoms( V&& idoms )
            {
                m_idoms = forward< V >( idoms );
            }
            const auto& idoms() const { return m_idoms; }

            uint32_t loopCount() const { return m_loopCount; }

            void incLoopCount() { ++m_loopCount; }

            // If the BB of index 1 dominates the BB of index 2, returns the distance:
            // 0 if BB1 and BB2 are the same, 1 if BB1 is the immediate dominator, 2 if it is
            // the dominator's dominator, and so on.
            // Note: dominators needs to have been computed for the CFG before hand by
            // calling ComputeDominators().
            optional< uint32_t > getDominatorDistance( uint32_t bbIndex1, uint32_t bbIndex2 ) const;

            enum class EdgeLoopCategory
            {
                None,
                LoopEdge,
                LoopExitEdge
            };

            static EdgeLoopCategory GetEdgeLoopCategory( const BasicBlock& fromBB, const BasicBlock& toBB );


            const auto& getBB( uint32_t index ) const { return m_basicBlocks[index - 1]; }

            auto count() const { return m_basicBlocks.size(); }

            const ptr< BasicBlock >& createBB();

            // TODO: to remove: replaced by "valuecount" below
            auto temporariesCount() const { return m_temporariesCount; }

            void setTemporariesCount( uint32_t count ) { m_temporariesCount = count; }

            uint32_t valueCount() const { return m_valueCount; }

            uint32_t nextValueIndex() { return m_valueCount++; }

            bool canBeExecuted() const;
            bool canBeEagerlyEvaluated() const;

            template< typename F >
            void forEachBB( F&& func )
            {
                for( auto&& bb : m_basicBlocks )
                    func( bb );
            }

            template< typename F >
            void forEachBB( F&& func ) const
            {
                for( auto&& bb : m_basicBlocks )
                    func( bb );
            }


            void setDataLocationModifiedByLoop( uint32_t loopId, const eir::Term& type, const DataLocation& dloc )
            {
                m_loopModifiedDataLocations.emplace( make_pair( loopId, dloc ), type );
            }

            template< typename F >
            void forEachDataLocationModifiedByLoop( uint32_t loopId, F&& func ) const
            {
                auto begin = m_loopModifiedDataLocations.lower_bound( { loopId, Address{ {}, 0 } } );


                auto end = m_loopModifiedDataLocations.upper_bound( { loopId, monostate() } );

                for( auto it = begin; it != end; ++it )
                    func( it->second, it->first.second );
            }

            template< typename F >
            void forEachReachableBlock( const ptr< BasicBlock >& bb, F&& func ) const
            {
                unordered_set< uint32_t > m_visitedBlocks;
                stack< uint32_t > m_stack;

                m_stack.push( bb->index() );

                while( !m_stack.empty() )
                {
                    auto bbId = m_stack.top();
                    m_stack.pop();

                    const auto& bb = getBB( bbId );

                    bb->forEachSuccessor( [&]( auto&& succBB )

                    {
                        assert( succBB->index() <= m_basicBlocks.size() );

                        if( m_visitedBlocks.find( succBB->index() ) != m_visitedBlocks.end() )
                            return;

                        m_visitedBlocks.emplace( succBB->index() );
                        m_stack.push( succBB->index() );
                    } );

                    func( bb );
                }
            }

            friend ostream& operator<<( ostream& out, const CFG& cfg );

        private:
            vector< ptr< BasicBlock > > m_basicBlocks;
            ptr< BasicBlock > m_currentBB;

            // All the edges of the CFG in SrcBBIndex, DestBBIndex form
            unordered_multimap< uint32_t, uint32_t > m_edges;

            // For each BB, store the index of its immediate dominator.
            // May be be empty if it has not (yet) been computed.
            vector< uint32_t > m_idoms;

            // For each loop, store all of the storage locations modified
            // during that loop.
            // May be be empty if it has not (yet) been computed.
            multimap< pair< uint32_t, DataLocation >, eir::Term > m_loopModifiedDataLocations;

            // The number of temporary indices used by this CFG.
            uint32_t m_temporariesCount = 0;    // TODO: remove

            uint32_t m_valueCount = 0;

            uint32_t m_loopCount = 0;

            bool m_poisoned = false;
    };

    // Compute the immediate dominators for each BB in the provided CFG,
    // using the Lengauer-Tarjan algorithm.
    extern void ComputeDominators( const ptr< CFG >& cfg );

    // Detect loops and store loop informations into the CFG.
    extern void IdentifyLoops( const ptr< CFG >& cfg );

    // Find which loop modifies which variable.
    extern void IdentifyLoopModifiedAddrs( const ptr< CFG >& cfg );

    // While we generate CIR instructions, we use a simple global unique id for each different temporary.
    // This pass renumbers them and counts them, so that CIR consumers can simply store them in arrays.

    extern void ReindexVars( const ptr< CFG >& cfg );
    extern void ReindexVars( InstrSeq& is );
}


#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
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
#ifndef GOOSE_CIR_CFG_H
#define GOOSE_CIR_CFG_H

namespace goose::cir
{
    class BasicBlock;

    class CFG : public enable_shared_from_this< CFG >
    {
      public:
        CFG( uint32_t numParams ) :
            m_temporariesCount( numParams )

        {
        }

        bool isPoisoned() const { return m_poisoned; }


        void poison() { m_poisoned = true; }


        const auto& entryBB() const { return m_basicBlocks.front(); }

        const auto& lastBB() const { return m_basicBlocks.back(); }

        const auto& currentBB() const { return m_currentBB; }



        template< typename T > void setCurrentBB( T&& pBB ) { m_currentBB = forward< T >( pBB ); }



        template< typename T > void emitTerminator( LocationId locId, T&& terminator )
        {
            if( !currentBB() || currentBB()->terminator() )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                    locId, "unreachable code.", 0 );
                return;
            }

            currentBB()->setTerminator( forward< T >( terminator ) );
        }

        void addEdge( uint32_t srcIndex, uint32_t destIndex );

        const auto& edges() const { return m_edges; }

        template< typename V > void setIdoms( V&& idoms ) { m_idoms = forward< V >( idoms ); }




        const auto& idoms() const { return m_idoms; }

        uint32_t loopCount() const { return m_loopCount; }

        void incLoopCount() { ++m_loopCount; }

        // If the BB of index 1 dominates the BB of index 2, returns the distance:
        // 0 if BB1 and BB2 are the same, 1 if BB1 is the immediate dominator, 2 if it is
        // the dominator's dominator, and so on.
        // Note: dominators needs to have been computed for the CFG before hand by
        // calling ComputeDominators().
        optional< uint32_t > getDominatorDistance( uint32_t bbIndex1, uint32_t bbIndex2 ) const;

        enum class EdgeLoopCategory
        {
            None,
            LoopEdge,
            LoopExitEdge
        };

        static EdgeLoopCategory GetEdgeLoopCategory(
            const BasicBlock& fromBB, const BasicBlock& toBB );

        const auto& getBB( uint32_t index ) const { return m_basicBlocks[index - 1]; }

        auto count() const { return m_basicBlocks.size(); }

        const ptr< BasicBlock >& createBB();

        // TODO: to remove: replaced by "valuecount" below
        auto temporariesCount() const { return m_temporariesCount; }

        void setTemporariesCount( uint32_t count ) { m_temporariesCount = count; }

        uint32_t valueCount() const { return m_valueCount; }

        uint32_t nextValueIndex() { return m_valueCount++; }

        bool canBeExecuted() const;
        bool canBeEagerlyEvaluated() const;

        template< typename F > void forEachBB( F&& func )

        {
            for( auto&& bb : m_basicBlocks )
                func( bb );
        }

        template< typename F > void forEachBB( F&& func ) const

        {
            for( auto&& bb : m_basicBlocks )
                func( bb );
        }

        void setDataLocationModifiedByLoop(
            uint32_t loopId, const eir::Term& type, const DataLocation& dloc )
        {
            m_loopModifiedDataLocations.emplace( make_pair( loopId, dloc ), type );
        }

        template< typename F >
        void forEachDataLocationModifiedByLoop( uint32_t loopId, F&& func ) const
        {
            auto begin = m_loopModifiedDataLocations.lower_bound( {
                loopId, Address{ {}, 0 }
            } );
            auto end = m_loopModifiedDataLocations.upper_bound( { loopId, monostate() } );

            for( auto it = begin; it != end; ++it )
                func( it->second, it->first.second );
        }

        template< typename F >
        void forEachReachableBlock( const ptr< BasicBlock >& bb, F&& func ) const
        {
            unordered_set< uint32_t > m_visitedBlocks;
            stack< uint32_t > m_stack;

            m_stack.push( bb->index() );

            while( !m_stack.empty() )
            {
                auto bbId = m_stack.top();
                m_stack.pop();

                const auto& bb = getBB( bbId );

                bb->forEachSuccessor(
                    [&]( auto&& succBB )
                    {
                        assert( succBB->index() <= m_basicBlocks.size() );

                        if( m_visitedBlocks.find( succBB->index() ) != m_visitedBlocks.end() )
                            return;

                        m_visitedBlocks.emplace( succBB->index() );
                        m_stack.push( succBB->index() );
                    } );

                func( bb );
            }
        }

        friend ostream& operator<<( ostream& out, const CFG& cfg );

      private:
        vector< ptr< BasicBlock > > m_basicBlocks;
        ptr< BasicBlock > m_currentBB;

        // All the edges of the CFG in SrcBBIndex, DestBBIndex form
        unordered_multimap< uint32_t, uint32_t > m_edges;

        // For each BB, store the index of its immediate dominator.
        // May be be empty if it has not (yet) been computed.
        vector< uint32_t > m_idoms;

        // For each loop, store all of the storage locations modified
        // during that loop.
        // May be be empty if it has not (yet) been computed.
        multimap< pair< uint32_t, DataLocation >, eir::Term > m_loopModifiedDataLocations;

        // The number of temporary indices used by this CFG.
        uint32_t m_temporariesCount = 0; // TODO: remove

        uint32_t m_valueCount = 0;

        uint32_t m_loopCount = 0;

        bool m_poisoned = false;
    };

    // Compute the immediate dominators for each BB in the provided CFG,
    // using the Lengauer-Tarjan algorithm.
    extern void ComputeDominators( const ptr< CFG >& cfg );

    // Detect loops and store loop informations into the CFG.
    extern void IdentifyLoops( const ptr< CFG >& cfg );

    // Find which loop modifies which variable.
    extern void IdentifyLoopModifiedAddrs( const ptr< CFG >& cfg );

    // While we generate CIR instructions, we use a simple global unique id for each different
    // temporary. This pass renumbers them and counts them, so that CIR consumers can simply store
    // them in arrays.
    extern void ReindexVars( const ptr< CFG >& cfg );
    extern void ReindexVars( InstrSeq& is );

} // namespace goose::cir

#endif
Changes to bs/cir/cir.h.
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
    using namespace util;
    using namespace diagnostics;

    static constexpr uint32_t InvalidVarId = numeric_limits< uint32_t >::max();

    class CFG;
}

#include "helpers.h"
#include "operation.h"

#include "datalocation.h"

#include "op-constant.h"







|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
    using namespace util;
    using namespace diagnostics;

    static constexpr uint32_t InvalidVarId = numeric_limits< uint32_t >::max();

    class CFG;
} // namespace goose::cir

#include "helpers.h"
#include "operation.h"

#include "datalocation.h"

#include "op-constant.h"
Deleted bs/cir/comparison.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#ifndef GOOSE_CIR_COMPARISON_H
#define GOOSE_CIR_COMPARISON_H

namespace goose::cir
{
    class Eq : public BinaryOp
    {
        public:
            Eq( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Eq& ins )
            {
                return out << "EQ";
            }
    };

    class Neq : public BinaryOp
    {
        public:
            Neq( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Neq& ins )
            {
                return out << "NEQ";
            }
    };

    class UGT : public BinaryOp
    {
        public:
            UGT( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const UGT& ins )
            {
                return out << "UGT";
            }
    };

    class UGE : public BinaryOp
    {
        public:
            UGE( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const UGE& ins )
            {
                return out << "UGE";
            }
    };

    class ULT : public BinaryOp
    {
        public:
            ULT( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const ULT& ins )
            {
                return out << "ULT";
            }
    };

    class ULE : public BinaryOp
    {
        public:
            ULE( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const ULE& ins )
            {
                return out << "ULE";
            }
    };

    class SGT : public BinaryOp
    {
        public:
            SGT( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const SGT& ins )
            {
                return out << "SGT";
            }
    };

    class SGE : public BinaryOp
    {
        public:
            SGE( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const SGE& ins )
            {
                return out << "SGE";
            }
    };

    class SLT : public BinaryOp
    {
        public:
            SLT( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const SLT& ins )
            {
                return out << "SLT";
            }
    };

    class SLE : public BinaryOp
    {
        public:
            SLE( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const SLE& ins )
            {
                return out << "SLE";
            }
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































































































































































Deleted bs/cir/constant.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
#ifndef GOOSE_CIR_CONSTANT_H
#define GOOSE_CIR_CONSTANT_H

namespace goose::cir
{
    class PushConstant : public BaseInstr< 0, true >
    {
        public:
            template< typename V >
            PushConstant( V&& val ) :
                BaseInstr( val.locationId() ),
                m_value( forward< V >( val ) )
            {}

            const auto& value() const { return m_value; }
            auto& value() { return m_value; }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }
            bool haveSideEffects() const { return false; }

            bool operator<( const PushConstant& rhs ) const
            {
                return m_value < rhs.m_value;
            }

            friend ostream& operator<<( ostream& out, const PushConstant& ins )
            {
                return out << "CONSTANT(" << ins.m_value.get() << ')';
            }

        private:
            LowerableValue m_value;
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<










































































Changes to bs/cir/continue.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
#ifndef GOOSE_CIR_CONTINUE_H
#define GOOSE_CIR_CONTINUE_H

namespace goose::cir
{
    class Continue
    {
        public:
            Continue() {}

            Continue( uint32_t level ) :
                m_level( level )
            {}



            const auto& level() const { return m_level; }

            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return true; }

            void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex ) {}

            friend ostream& operator<<( ostream& out, const Continue& ter );

        private:
            uint32_t m_level = 0;
    };
}


#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
#ifndef GOOSE_CIR_CONTINUE_H
#define GOOSE_CIR_CONTINUE_H

namespace goose::cir
{
    class Continue
    {
      public:
        Continue() {}

        Continue( uint32_t level ) :
            m_level( level )

        {
        }

        const auto& level() const { return m_level; }

        bool canBeExecuted() const { return true; }

        bool canBeEagerlyEvaluated() const { return true; }

        void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex ) {}

        friend ostream& operator<<( ostream& out, const Continue& ter );

      private:
        uint32_t m_level = 0;
    };

} // namespace goose::cir

#endif
Changes to bs/cir/datalocation.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
#include "cir/cir.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::builtins;
using namespace goose::cir;

namespace goose::eir
{
    // Lifetime
    const Term& Bridge< Lifetime >::Type()
    {

        static auto type = ValueToEIR( Value( TypeType(), TVEC( TSID( ct_type ), TSID( lifetime ) ) ) );
        return type;
    }

    Value Bridge< Lifetime >::ToValue( const Lifetime& lt )
    {
        return Value( Type(), TERM( lt ) );
    }

    optional< Lifetime > Bridge< Lifetime >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),
            Val< StringId >()
        );

        if( !result )
            return nullopt;

        auto&& name = *result;
        return Lifetime( name );
    }

    // DataPath
    const Term& Bridge< DataPath >::Type()
    {

        static auto type = ValueToEIR( Value( TypeType(), TVEC( TSID( ct_type ), TSID( datapath ) ) ) );
        return type;
    }

    Value Bridge< DataPath >::ToValue( const cir::DataPath& dp )
    {
        return Value( Type(), VEC(
            TERM( dp.lifetime() ),
            TERM( static_cast< uint64_t >( dp.origin() ) ),
            ValueToEIR( ::ToValue( dp.path() ) ) ) )
                .setLocationId( dp.locationId() );
    }

    optional< cir::DataPath > Bridge< DataPath >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),
            Vec(
                Val< StringId >(),
                Val< uint64_t >(),
                SubTerm()
            )
        );

        if( !result )
            return nullopt;

        auto&& [lt,origin,path] = *result;
        auto pathVal = *EIRToValue( path );

        return cir::DataPath{
            static_cast< uint32_t >( origin ),
            *FromValue< SelectPath >( pathVal ), lt,
            v.locationId()
        };
    }

    // Address
    const Term& Bridge< Address >::Type()
    {

        static auto type = ValueToEIR( Value( TypeType(), TVEC( TSID( ct_type ), TSID( address ) ) ) );
        return type;
    }

    Value Bridge< Address >::ToValue( const cir::Address& a )
    {
        return Value( Type(), TERM( a.lifetime() ) );
    }

    optional< cir::Address > Bridge< Address >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(),
            Val< StringId >()
        );

        if( !result )
            return nullopt;

        return cir::Address{ *result, v.locationId() };
    }
}












>
|










|
<
<











>
|





|
<
|
|
|




|
<
<
<
<
<
<




|


|
<
|
<
<





>
|










|
<
<






|
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
#include "cir/cir.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::builtins;
using namespace goose::cir;

namespace goose::eir
{
    // Lifetime
    const Term& Bridge< Lifetime >::Type()
    {
        static auto type =
            ValueToEIR( Value( TypeType(), TVEC( TSID( ct_type ), TSID( lifetime ) ) ) );
        return type;
    }

    Value Bridge< Lifetime >::ToValue( const Lifetime& lt )
    {
        return Value( Type(), TERM( lt ) );
    }

    optional< Lifetime > Bridge< Lifetime >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(), Val< StringId >() );



        if( !result )
            return nullopt;

        auto&& name = *result;
        return Lifetime( name );
    }

    // DataPath
    const Term& Bridge< DataPath >::Type()
    {
        static auto type =
            ValueToEIR( Value( TypeType(), TVEC( TSID( ct_type ), TSID( datapath ) ) ) );
        return type;
    }

    Value Bridge< DataPath >::ToValue( const cir::DataPath& dp )
    {
        return Value( Type(),

            VEC( TERM( dp.lifetime() ), TERM( static_cast< uint64_t >( dp.origin() ) ),
                ValueToEIR( ::ToValue( dp.path() ) ) ) )
            .setLocationId( dp.locationId() );
    }

    optional< cir::DataPath > Bridge< DataPath >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(), Vec( Val< StringId >(), Val< uint64_t >(), SubTerm() ) );







        if( !result )
            return nullopt;

        auto&& [lt, origin, path] = *result;
        auto pathVal = *EIRToValue( path );

        return cir::DataPath{ static_cast< uint32_t >( origin ),

            *FromValue< SelectPath >( pathVal ), lt, v.locationId() };


    }

    // Address
    const Term& Bridge< Address >::Type()
    {
        static auto type =
            ValueToEIR( Value( TypeType(), TVEC( TSID( ct_type ), TSID( address ) ) ) );
        return type;
    }

    Value Bridge< Address >::ToValue( const cir::Address& a )
    {
        return Value( Type(), TERM( a.lifetime() ) );
    }

    optional< cir::Address > Bridge< Address >::FromValue( const Value& v )
    {
        auto result = Decompose( v.val(), Val< StringId >() );



        if( !result )
            return nullopt;

        return cir::Address{ *result, v.locationId() };
    }
} // namespace goose::eir
Changes to bs/cir/datalocation.h.
1
2
3
4
5
6

7
8


9

10
11
12

13
14
15
16
17

18
19
20
21
22
23
24
25
26
27
28
29

30


31
32
33
34
35
36
37
38
39
40
41
42
43
44
45


46

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69


70

71

72

73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100


101

102

103
104
105
106
107
108
109
110
111
112
113
114
115
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
#ifndef GOOSE_CIR_DATALOCATION_H
#define GOOSE_CIR_DATALOCATION_H

// A data location in the CIR can be one of the following:
//
//  - A DataPath: a value index (the origin) + an array of indices indicating which sub members to select inside nested structs.

//  - An address: a numerical address whose value is unknown at compilation time. Unlike datapaths they can be passed
//      as function params and stored in memory. load/store work on both datapaths and addresses.


//      A datapath can be converted into an address. (in codegen it will result in generating a llvm GEP instruction)


// DataPaths and Addresses both have an associated lifetime, either identified by a unique name or unspecified.
// The origin of a datapath may be either a SSA value index or a memory value index (details TBD)


// - A ghost function application: a ghost function call is treated during verification like a value whose name is constructed
//    from a function + parameters, into which it is possible to load/store something. It is basically a declaration that
//      "from this point on, consider that this function called with these parameters would return this".
//      This can only be used by the verification backend.

namespace goose::cir
{
    using SelectPath = Sequence< uint32_t >;

    class Lifetime : public StringId
    {
        public:
            Lifetime() {}

            template< typename S >
            Lifetime( S&& name ) : StringId( forward< S >( name ) )
            {}




            static const Term& Unspecified()
            {
                static auto us = ValueToEIR( ToValue( Lifetime{} ) );
                return us;
            }
    };

    class Address
    {
        public:
            Address( Lifetime lt, LocationId loc ) :
                m_lifetime( lt ),
                m_loc( loc )
            {}



            const auto& lifetime() const { return m_lifetime; }

            auto locationId() const { return m_loc; }

            bool operator<( const Address& rhs ) const
            {
                return m_lifetime < rhs.m_lifetime;
            }

        private:
            Lifetime m_lifetime;
            LocationId m_loc;
    };

    class DataPath
    {
        public:
            template< typename P >
            DataPath( uint32_t origin, P&& path, Lifetime lt, LocationId loc ) :
                m_origin( origin ),
                m_path( forward< P >( path ) ),
                m_lifetime( lt ),
                m_loc( loc )
            {}



            auto origin() const { return m_origin; }

            const auto& path() const { return m_path; }

            auto lifetime() const { return m_lifetime; }

            auto locationId() const { return m_loc; }

            bool operator<( const DataPath& rhs ) const
            {
                if( m_origin != rhs.m_origin )
                    return m_origin < rhs.m_origin;

                if( m_lifetime != rhs.m_lifetime )
                    return m_lifetime < rhs.m_lifetime;

                return m_path < rhs.m_path;
            }

        private:
            uint32_t m_origin = 0;
            SelectPath m_path;
            Lifetime m_lifetime;
            LocationId m_loc;
    };

    class GhostFuncApplication
    {
        public:
            GhostFuncApplication( cir::Value&& func, LocationId loc ) :
                m_func( move( func ) ),
                m_loc( loc )
            {}



            const auto& func() const { return m_func; }

            const auto& args() const { return m_args; }

            auto& args() { return m_args; }

            auto locationId() const { return m_loc; }

            bool operator<( const GhostFuncApplication& rhs ) const
            {
                if( m_func != rhs.m_func )
                    return  m_func < rhs.m_func;

                return m_args < rhs.m_args;
            }

        private:
            eir::Value m_func;
            llvm::SmallVector< variant< eir::Value, Address >, 4 > m_args;
            LocationId m_loc;
    };

    using DataLocation = variant<
        Address,
        DataPath,
        GhostFuncApplication,
        monostate
    >;
}

namespace goose::eir
{
    template<>
    struct Bridge< cir::Lifetime >
    {
        static const Term& Type();

        static Value ToValue( const cir::Lifetime& lt );
        static optional< cir::Lifetime > FromValue( const Value& v );
    };

    template<>
    struct Bridge< cir::DataPath >
    {
        static const Term& Type();

        static Value ToValue( const cir::DataPath& dp );
        static optional< cir::DataPath > FromValue( const Value& v );
    };

    template<>
    struct Bridge< cir::Address >
    {
        static const Term& Type();

        static Value ToValue( const cir::Address& a );
        static optional< cir::Address > FromValue( const Value& v );
    };
}

#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
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
#ifndef GOOSE_CIR_DATALOCATION_H
#define GOOSE_CIR_DATALOCATION_H

// A data location in the CIR can be one of the following:
//
//  - A DataPath: a value index (the origin) + an array of indices indicating which sub members to
//  select inside nested structs.
//  - An address: a numerical address whose value is unknown at compilation time. Unlike datapaths
//  they can be passed as function params and stored in memory. load/store work on both datapaths
//  and addresses.
//
//  A datapath can be converted into an address. (in codegen it will result in generating a llvm GEP
//  instruction)

// DataPaths and Addresses both have an associated lifetime, either identified by a unique name or
// unspecified. The origin of a datapath may be either a SSA value index or a memory value index
// (details TBD)

// - A ghost function application: a ghost function call is treated during verification like a value
// whose name is constructed from a function + parameters, into which it is possible to load/store
// something. It is basically a declaration that "from this point on, consider that this function
// called with these parameters would return this". This can only be used by the verification
// backend.
namespace goose::cir
{
    using SelectPath = Sequence< uint32_t >;

    class Lifetime : public StringId
    {
      public:
        Lifetime() {}

        template< typename S >
        Lifetime( S&& name ) :

            StringId( forward< S >( name ) )
        {
        }

        static const Term& Unspecified()
        {
            static auto us = ValueToEIR( ToValue( Lifetime{} ) );
            return us;
        }
    };

    class Address
    {
      public:
        Address( Lifetime lt, LocationId loc ) :
            m_lifetime( lt ),
            m_loc( loc )

        {
        }

        const auto& lifetime() const { return m_lifetime; }

        auto locationId() const { return m_loc; }

        bool operator<( const Address& rhs ) const { return m_lifetime < rhs.m_lifetime; }




      private:
        Lifetime m_lifetime;
        LocationId m_loc;
    };

    class DataPath
    {
      public:
        template< typename P >
        DataPath( uint32_t origin, P&& path, Lifetime lt, LocationId loc ) :
            m_origin( origin ),
            m_path( forward< P >( path ) ),
            m_lifetime( lt ),
            m_loc( loc )

        {
        }

        auto origin() const { return m_origin; }

        const auto& path() const { return m_path; }

        auto lifetime() const { return m_lifetime; }

        auto locationId() const { return m_loc; }

        bool operator<( const DataPath& rhs ) const
        {
            if( m_origin != rhs.m_origin )
                return m_origin < rhs.m_origin;

            if( m_lifetime != rhs.m_lifetime )
                return m_lifetime < rhs.m_lifetime;

            return m_path < rhs.m_path;
        }

      private:
        uint32_t m_origin = 0;
        SelectPath m_path;
        Lifetime m_lifetime;
        LocationId m_loc;
    };

    class GhostFuncApplication
    {
      public:
        GhostFuncApplication( cir::Value&& func, LocationId loc ) :
            m_func( move( func ) ),
            m_loc( loc )

        {
        }

        const auto& func() const { return m_func; }

        const auto& args() const { return m_args; }

        auto& args() { return m_args; }

        auto locationId() const { return m_loc; }

        bool operator<( const GhostFuncApplication& rhs ) const
        {
            if( m_func != rhs.m_func )
                return m_func < rhs.m_func;

            return m_args < rhs.m_args;
        }

      private:
        eir::Value m_func;
        llvm::SmallVector< variant< eir::Value, Address >, 4 > m_args;
        LocationId m_loc;
    };

    using DataLocation = variant< Address, DataPath, GhostFuncApplication, monostate >;
} // namespace goose::cir






namespace goose::eir
{

    template<> struct Bridge< cir::Lifetime >
    {
        static const Term& Type();

        static Value ToValue( const cir::Lifetime& lt );
        static optional< cir::Lifetime > FromValue( const Value& v );
    };


    template<> struct Bridge< cir::DataPath >
    {
        static const Term& Type();

        static Value ToValue( const cir::DataPath& dp );
        static optional< cir::DataPath > FromValue( const Value& v );
    };


    template<> struct Bridge< cir::Address >
    {
        static const Term& Type();

        static Value ToValue( const cir::Address& a );
        static optional< cir::Address > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/cir/dominators.cpp.
14
15
16
17
18
19
20
21


22
23
24
25
26
27
28
            ancestor( n + 1, 0 ),
            child( n + 1, 0 ),
            vertex( n + 1, 0 ),
            label( n + 1, 0 ),
            semi( n + 1, 0 ),
            size( n + 1, 0 ),
            succ( s )
        {}



        vector< uint32_t > dom;
        vector< uint32_t > parent;
        vector< uint32_t > ancestor;
        vector< uint32_t > child;
        vector< uint32_t > vertex;
        vector< uint32_t > label;







<
>
>







14
15
16
17
18
19
20

21
22
23
24
25
26
27
28
29
            ancestor( n + 1, 0 ),
            child( n + 1, 0 ),
            vertex( n + 1, 0 ),
            label( n + 1, 0 ),
            semi( n + 1, 0 ),
            size( n + 1, 0 ),
            succ( s )

        {
        }

        vector< uint32_t > dom;
        vector< uint32_t > parent;
        vector< uint32_t > ancestor;
        vector< uint32_t > child;
        vector< uint32_t > vertex;
        vector< uint32_t > label;
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

    void Link( DomComputeState& st, uint32_t v, uint32_t w )
    {
        uint32_t s = w;

        while( st.semi[st.label[w]] < st.semi[st.label[st.child[s]]] )
        {
            if( ( st.size[s] + st.size[st.child[st.child[s]] ] ) >= 2*st.size[st.child[s]] )
            {
                st.ancestor[st.child[s]] = s;
                st.child[s] = st.child[st.child[s]];
            }
            else
            {
                st.size[st.child[s]] = st.size[s];
                s = st.ancestor[s] = st.child[s];
            }
        }

        st.label[s] = st.label[w];
        st.size[v] += st.size[w];

        if( st.size[v] < 2*st.size[w] )
            swap( s, st.child[v] );

        while( s != 0 )
        {
            st.ancestor[s] = v;
            s = st.child[s];
        }
    }
}

namespace goose::cir
{
    void ComputeDominators( const ptr< CFG >& cfg )
    {
        // Check if this has already been computed.
        if( !cfg->idoms().empty() )







|














|








|







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

    void Link( DomComputeState& st, uint32_t v, uint32_t w )
    {
        uint32_t s = w;

        while( st.semi[st.label[w]] < st.semi[st.label[st.child[s]]] )
        {
            if( ( st.size[s] + st.size[st.child[st.child[s]]] ) >= 2 * st.size[st.child[s]] )
            {
                st.ancestor[st.child[s]] = s;
                st.child[s] = st.child[st.child[s]];
            }
            else
            {
                st.size[st.child[s]] = st.size[s];
                s = st.ancestor[s] = st.child[s];
            }
        }

        st.label[s] = st.label[w];
        st.size[v] += st.size[w];

        if( st.size[v] < 2 * st.size[w] )
            swap( s, st.child[v] );

        while( s != 0 )
        {
            st.ancestor[s] = v;
            s = st.child[s];
        }
    }
} // namespace

namespace goose::cir
{
    void ComputeDominators( const ptr< CFG >& cfg )
    {
        // Check if this has already been computed.
        if( !cfg->idoms().empty() )
165
166
167
168
169
170
171
172
                s.dom[w] = s.dom[s.dom[w]];
        }

        s.dom[0] = 0;

        cfg->setIdoms( move( s.dom ) );
    }
}







|
166
167
168
169
170
171
172
173
                s.dom[w] = s.dom[s.dom[w]];
        }

        s.dom[0] = 0;

        cfg->setIdoms( move( s.dom ) );
    }
} // namespace goose::cir
Deleted bs/cir/forall.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
#ifndef GOOSE_CIR_FORALL_H
#define GOOSE_CIR_FORALL_H

namespace goose::cir
{
    class ForAllSetup : public BaseInstr< 0, false >  // Can pop more depending on arg count
    {
        public:
            ForAllSetup( uint32_t numArgs, LocationId loc ) :
                BaseInstr( loc ),
                m_numArgs( numArgs )
            {}

            const auto& numArgs() const { return m_numArgs; }

            // This can't be executed, it's only meant to appear
            // in expressions handled by the verifier.
            bool canBeExecuted() const { return false; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return false; }

            bool operator<( const ForAllSetup& rhs ) const
            {
                return m_numArgs < rhs.m_numArgs;
            }

            friend ostream& operator<<( ostream& out, const ForAllSetup& ins )
            {
                return out << "FORALLSETUP(" << ins.m_numArgs << ')';
            }

        private:
            uint32_t m_numArgs = 0;
    };

    class ForAll : public BaseInstr< 1, true >
    {
        public:
            ForAll( LocationId loc ) :
                BaseInstr( loc )
            {}

            // This can't be executed, it's only meant to appear
            // in expressions handled by the verifier.
            bool canBeExecuted() const { return false; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return false; }

            bool operator<( const ForAll& rhs ) const
            {
                return false;
            }

            friend ostream& operator<<( ostream& out, const ForAll& ins )
            {
                return out << "FORALL";
            }
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


























































































































Changes to bs/cir/func.cpp.
14
15
16
17
18
19
20
21
        if( f.body() )
            out << *f.body();
        else
            out << "<invalid>\n";

        return out;
    }
}







|
14
15
16
17
18
19
20
21
        if( f.body() )
            out << *f.body();
        else
            out << "<invalid>\n";

        return out;
    }
} // namespace goose::cir
Changes to bs/cir/func.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
#ifndef GOOSE_CIR_FUNC_H
#define GOOSE_CIR_FUNC_H

namespace goose::cir
{
    class Func
    {
        public:
            enum class State
            {
                Unavailable,
                Invalid,
                Complete
            };

            template< typename I >
            Func( I&& identity ) :
                m_identity( forward< I >( identity ) )
            {}



            template< typename I, typename B >
            Func( I&& identity, B&& body ) :
                m_identity( forward< I >( identity ) ),
                m_body( forward< B >( body ) )
            {
                m_state = State::Complete;
            }

            const auto& identity() const { return m_identity; }

            const auto& body() const { return m_body; }

            auto& body() { return m_body; }

            bool canBeExecuted() const;

            auto state() const { return m_state; }

            void setState( State s ) { m_state = s; }

            bool isComplete() const { return m_state == State::Complete; }

            friend ostream& operator<<( ostream& out, const Func& f );

        private:
            eir::Term m_identity;
            ptr< CFG > m_body;
            State m_state = State::Unavailable;
    };
}


#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
#ifndef GOOSE_CIR_FUNC_H
#define GOOSE_CIR_FUNC_H

namespace goose::cir
{
    class Func
    {
      public:
        enum class State
        {
            Unavailable,
            Invalid,
            Complete
        };

        template< typename I >
        Func( I&& identity ) :
            m_identity( forward< I >( identity ) )

        {
        }

        template< typename I, typename B >
        Func( I&& identity, B&& body ) :
            m_identity( forward< I >( identity ) ),
            m_body( forward< B >( body ) )
        {
            m_state = State::Complete;
        }

        const auto& identity() const { return m_identity; }

        const auto& body() const { return m_body; }

        auto& body() { return m_body; }

        bool canBeExecuted() const;

        auto state() const { return m_state; }

        void setState( State s ) { m_state = s; }

        bool isComplete() const { return m_state == State::Complete; }

        friend ostream& operator<<( ostream& out, const Func& f );

      private:
        eir::Term m_identity;
        ptr< CFG > m_body;
        State m_state = State::Unavailable;
    };

} // namespace goose::cir

#endif
Deleted bs/cir/ghostcall.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
#ifndef GOOSE_CIR_GHOSTCALL_H
#define GOOSE_CIR_GHOSTCALL_H

namespace goose::cir
{
    class GhostCall : public BaseInstr< 1, true >   // Can pop more depending on arg count
    {
        public:
            GhostCall( uint32_t numArgs, LocationId loc ) :
                BaseInstr( loc ),
                m_numArgs( numArgs )
            {}

            const auto& numArgs() const { return m_numArgs; }

            bool canBeExecuted() const { return false; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return true; }

            bool operator<( const GhostCall& rhs ) const
            {
                return m_numArgs < rhs.m_numArgs;
            }

            friend ostream& operator<<( ostream& out, const GhostCall& ins )
            {
                return out << "GHOSTCALL " << ins.m_numArgs;
            }

        private:
            uint32_t m_numArgs = 0;
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






































































Changes to bs/cir/hash.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 "cir/cir.h"

namespace std
{
    size_t hash< goose::cir::LowerableType >::operator()( const goose::cir::LowerableType& lt ) const

    {
        return goose::util::ComputeHash( lt.type() );
    }

    size_t hash< goose::cir::LowerableValue >::operator()( const goose::cir::LowerableValue& lv ) const

    {
        return goose::util::ComputeHash( lv.val() );
    }

    size_t hash< goose::cir::InstrSeq >::operator()( const goose::cir::InstrSeq& is ) const
    {
        auto g = goose::util::ContainerHashGenerator( is );
        return llvm::hash_combine_range( g.begin(), g.end() );
    }

    size_t hash< goose::cir::Instruction >::operator()( const goose::cir::Instruction& x ) const
    {
        return goose::util::ComputeHash( x );
    }

    size_t hash< goose::cir::InlineCall >::operator()( const goose::cir::InlineCall& x ) const
    {
        auto g = goose::util::ContainerHashGenerator( x.argUids() );
        return llvm::hash_combine(
            goose::util::ComputeHash( x.funcCIR() ),
            llvm::hash_combine_range( g.begin(), g.end() ),
            goose::util::ComputeHash( x.retType() ) );
    }

    size_t hash< goose::cir::Call >::operator()( const goose::cir::Call& x ) const
    {
        return goose::util::ComputeHash( x.numArgs() );




|
>




|
>


















|
<







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 "cir/cir.h"

namespace std
{
    size_t hash< goose::cir::LowerableType >::operator()(
        const goose::cir::LowerableType& lt ) const
    {
        return goose::util::ComputeHash( lt.type() );
    }

    size_t hash< goose::cir::LowerableValue >::operator()(
        const goose::cir::LowerableValue& lv ) const
    {
        return goose::util::ComputeHash( lv.val() );
    }

    size_t hash< goose::cir::InstrSeq >::operator()( const goose::cir::InstrSeq& is ) const
    {
        auto g = goose::util::ContainerHashGenerator( is );
        return llvm::hash_combine_range( g.begin(), g.end() );
    }

    size_t hash< goose::cir::Instruction >::operator()( const goose::cir::Instruction& x ) const
    {
        return goose::util::ComputeHash( x );
    }

    size_t hash< goose::cir::InlineCall >::operator()( const goose::cir::InlineCall& x ) const
    {
        auto g = goose::util::ContainerHashGenerator( x.argUids() );
        return llvm::hash_combine( goose::util::ComputeHash( x.funcCIR() ),

            llvm::hash_combine_range( g.begin(), g.end() ),
            goose::util::ComputeHash( x.retType() ) );
    }

    size_t hash< goose::cir::Call >::operator()( const goose::cir::Call& x ) const
    {
        return goose::util::ComputeHash( x.numArgs() );
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
    size_t hash< goose::cir::Select >::operator()( const goose::cir::Select& x ) const
    {
        return goose::util::ComputeHash( x.memberIndex() );
    }

    size_t hash< goose::cir::AllocVar >::operator()( const goose::cir::AllocVar& x ) const
    {

        return llvm::hash_combine( goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.index() ) );
    }

    size_t hash< goose::cir::Load >::operator()( const goose::cir::Load& x ) const
    {
        return goose::util::ComputeHash( x.type() );
    }

    size_t hash< goose::cir::Store >::operator()( const goose::cir::Store& x ) const
    {
        return goose::util::ComputeHash( x.type() );
    }

    size_t hash< goose::cir::Phi >::operator()( const goose::cir::Phi& x ) const
    {
        // Note: we don't bother hashing the incomings here, and we don't really care because we only really need hashing for the
        // predicates and phi doesn't make sense there
        return llvm::hash_combine( goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.numIncomings() ), goose::util::ComputeHash( x.destIndex() ) );


    }

    size_t hash< goose::cir::Not >::operator()( const goose::cir::Not& x ) const
    {
        return 0;
    }








>
|














|
|
|
>
>







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
    size_t hash< goose::cir::Select >::operator()( const goose::cir::Select& x ) const
    {
        return goose::util::ComputeHash( x.memberIndex() );
    }

    size_t hash< goose::cir::AllocVar >::operator()( const goose::cir::AllocVar& x ) const
    {
        return llvm::hash_combine(
            goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.index() ) );
    }

    size_t hash< goose::cir::Load >::operator()( const goose::cir::Load& x ) const
    {
        return goose::util::ComputeHash( x.type() );
    }

    size_t hash< goose::cir::Store >::operator()( const goose::cir::Store& x ) const
    {
        return goose::util::ComputeHash( x.type() );
    }

    size_t hash< goose::cir::Phi >::operator()( const goose::cir::Phi& x ) const
    {
        // Note: we don't bother hashing the incomings here, and we don't really care because we
        // only really need hashing for the predicates and phi doesn't make sense there
        return llvm::hash_combine( goose::util::ComputeHash( x.type() ),
            goose::util::ComputeHash( x.numIncomings() ),
            goose::util::ComputeHash( x.destIndex() ) );
    }

    size_t hash< goose::cir::Not >::operator()( const goose::cir::Not& x ) const
    {
        return 0;
    }

207
208
209
210
211
212
213

214
215
216
217
218
219
220
221
222

223
224
225
226
227
228
229
    size_t hash< goose::cir::Assert >::operator()( const goose::cir::Assert& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::Placeholder >::operator()( const goose::cir::Placeholder& x ) const
    {

        return llvm::hash_combine( goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.name() ) );
    }

    size_t hash< goose::cir::PHOverrideSet >::operator()( const goose::cir::PHOverrideSet& x ) const
    {
        return goose::util::ComputeHash( x.name() );
    }

    size_t hash< goose::cir::PHOverrideClear >::operator()( const goose::cir::PHOverrideClear& x ) const

    {
        return goose::util::ComputeHash( x.name() );
    }

    size_t hash< goose::cir::GhostCall >::operator()( const goose::cir::GhostCall& x ) const
    {
        return goose::util::ComputeHash( x.numArgs() );







>
|







|
>







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
    size_t hash< goose::cir::Assert >::operator()( const goose::cir::Assert& x ) const
    {
        return 0;
    }

    size_t hash< goose::cir::Placeholder >::operator()( const goose::cir::Placeholder& x ) const
    {
        return llvm::hash_combine(
            goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.name() ) );
    }

    size_t hash< goose::cir::PHOverrideSet >::operator()( const goose::cir::PHOverrideSet& x ) const
    {
        return goose::util::ComputeHash( x.name() );
    }

    size_t hash< goose::cir::PHOverrideClear >::operator()(
        const goose::cir::PHOverrideClear& x ) const
    {
        return goose::util::ComputeHash( x.name() );
    }

    size_t hash< goose::cir::GhostCall >::operator()( const goose::cir::GhostCall& x ) const
    {
        return goose::util::ComputeHash( x.numArgs() );
244
245
246
247
248
249
250
251
        return goose::util::ComputeHash( x.type() );
    }

    size_t hash< goose::cir::PushStringId >::operator()( const goose::cir::PushStringId& x ) const
    {
        return goose::util::ComputeHash( x.stringId() );
    }
}







|
250
251
252
253
254
255
256
257
        return goose::util::ComputeHash( x.type() );
    }

    size_t hash< goose::cir::PushStringId >::operator()( const goose::cir::PushStringId& x ) const
    {
        return goose::util::ComputeHash( x.stringId() );
    }
} // namespace std
Changes to bs/cir/hash.h.
238
239
240
241
242
243
244
245
246
247
        size_t operator()( const goose::cir::PushType& x ) const;
    };

    template<> struct hash< goose::cir::PushStringId >
    {
        size_t operator()( const goose::cir::PushStringId& x ) const;
    };
}

#endif







|


238
239
240
241
242
243
244
245
246
247
        size_t operator()( const goose::cir::PushType& x ) const;
    };

    template<> struct hash< goose::cir::PushStringId >
    {
        size_t operator()( const goose::cir::PushStringId& x ) const;
    };
} // namespace std

#endif
Changes to bs/cir/helpers.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include "cir/cir.h"
#include "sema/sema.h"

namespace goose::cir
{
    bool IsValueConstantOrExecutable( const eir::Value& val )
    {
        if( val.isConstant() )
            return true;

        for( const auto& instr : *val.cir() )
        {
            if( !CanBeExecuted( instr ) )
                return false;
        }

        return true;
    }

    bool CanValueBeEagerlyEvaluated( const eir::Value& val )
    {
        if( val.isConstant() )
            return true;

        // Can't think from the top of my head whether
        // we can legitimately come accross such a value
        // that could be eager eval'd. The old scheme with the
        // stack language didn't seem to "think" so, we'll see
        // if we get a surprising rejection of compile time eval.
        if( val.isIndex() )
            return false;

        for( const auto& instr : *val.cir() )
        {
            if( !CanBeEagerlyEvaluated( instr ) )
                return false;
        }

        return true;
    }

    bool DoesInstrSeqHaveSideEffects( const InstrSeq& is )
    {
        for( auto&& instr : is )
        {
            if( HaveSideEffects( instr ) )
                return true;
        }

        return false;
    }

    const eir::Term& LowerableType::get() const
    {
        if( !m_loweredType )











<


<









|
<
|
<
|




<


<







<


<







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 "cir/cir.h"
#include "sema/sema.h"

namespace goose::cir
{
    bool IsValueConstantOrExecutable( const eir::Value& val )
    {
        if( val.isConstant() )
            return true;

        for( const auto& instr : *val.cir() )

            if( !CanBeExecuted( instr ) )
                return false;


        return true;
    }

    bool CanValueBeEagerlyEvaluated( const eir::Value& val )
    {
        if( val.isConstant() )
            return true;

        // Can't think from the top of my head whether we can legitimately come accross such a value

        // that could be eager eval'd. The old scheme with the stack language didn't seem to "think"

        // so, we'll see if we get a surprising rejection of compile time eval.
        if( val.isIndex() )
            return false;

        for( const auto& instr : *val.cir() )

            if( !CanBeEagerlyEvaluated( instr ) )
                return false;


        return true;
    }

    bool DoesInstrSeqHaveSideEffects( const InstrSeq& is )
    {
        for( auto&& instr : is )

            if( HaveSideEffects( instr ) )
                return true;


        return false;
    }

    const eir::Term& LowerableType::get() const
    {
        if( !m_loweredType )
93
94
95
96
97
98
99
100

            auto lv = LowerValue( *c, m_val );
            m_loweredVal = lv ? *lv : PoisonValue();
        }

        return *m_loweredVal;
    }
}







|
85
86
87
88
89
90
91
92

            auto lv = LowerValue( *c, m_val );
            m_loweredVal = lv ? *lv : PoisonValue();
        }

        return *m_loweredVal;
    }
} // namespace goose::cir
Changes to bs/cir/helpers.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108


109

110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127

128
129
#ifndef GOOSE_CIR_HELPERS_H
#define GOOSE_CIR_HELPERS_H

namespace goose::cir
{
    extern bool IsValueConstantOrExecutable( const eir::Value& val );
    extern bool CanValueBeEagerlyEvaluated( const eir::Value& val );

    extern bool DoesInstrSeqHaveSideEffects( const InstrSeq& is );

    template< typename T >
    class TempStorage
    {
        public:
            TempStorage( size_t size = 0 ) :
                m_storage( size )
            {}


            template< typename TT >
            T& set( uint32_t index, TT&& x )
            {
                if( IsUid( index ) )
                {
                    auto [it,_] = m_uidStorage.emplace( index, forward< TT >( x ) );
                    return it->second;
                }

                if( index >= m_storage.size() )
                    m_storage.resize( index + 1 );
                m_storage[index] = forward< TT >( x );
                return m_storage[index];
            }

            const T* get( uint32_t index ) const
            {
                if( IsUid( index ) )
                {
                    auto it = m_uidStorage.find( index );
                    if( it == m_uidStorage.end() )
                        return nullptr;
                    return &it->second;
                }

                if( index < m_storage.size() )
                    return &m_storage[index];

                return nullptr;
            }

            T* get( uint32_t index )
            {
                if( IsUid( index ) )
                    return nullptr;

                if( index < m_storage.size() )
                    return &m_storage[index];

                return nullptr;
            }

        private:
            llvm::SmallVector< T, 16 > m_storage;
            unordered_map< uint32_t, T > m_uidStorage;
    };

    // We need to lower types before consuming them from the CIR. Ideally we'd do this while building the CIR
    // but as things are now it would involve enormous refactoring to store lowered types in a bunch of high level
    // objects, and all the code to round trip them to EIR would have to deal with them, while also making sure
    // they aren't actually participating in pattern matching.
    // A significant redesign will probably allow for an elegant solution, but that's for the rewrite.
    // In the mean time we use this ugly helper that lowers the type when reading it from the CIR, if not previously done.
    // Since it needs the context we have to store the current context as a static member. This will have to do for
    // the BS compiler...
    class LowerableType
    {
        public:
            template< typename T >
            LowerableType( T&& type ) :
                m_type( forward< T >( type ) )
            {}

            const auto& type() const { return m_type; }
            const eir::Term& get() const;

            auto operator<( const LowerableType& other ) const
            {
                return m_type < other.m_type;
            }


            auto operator==( const LowerableType& other ) const
            {
                return m_type == other.m_type;
            }

        private:
            eir::Term m_type;
            mutable optional< eir::Term > m_loweredType;
    };

    // Same as above for values
    class LowerableValue
    {
        public:
            template< typename T >
            LowerableValue( T&& val ) :
                m_val( forward< T >( val ) )
            {}



            const auto& val() const { return m_val; }

            const eir::Value& get() const;
            eir::Value& modify();

            auto operator<( const LowerableValue& other ) const
            {
                return m_val < other.m_val;
            }

            auto operator==( const LowerableValue& other ) const
            {
                return m_val == other.m_val;
            }

        private:
            eir::Value m_val;
            mutable optional< eir::Value > m_loweredVal;
    };
}


#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
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
#ifndef GOOSE_CIR_HELPERS_H
#define GOOSE_CIR_HELPERS_H

namespace goose::cir
{
    extern bool IsValueConstantOrExecutable( const eir::Value& val );
    extern bool CanValueBeEagerlyEvaluated( const eir::Value& val );

    extern bool DoesInstrSeqHaveSideEffects( const InstrSeq& is );

    template< typename T > class TempStorage

    {
      public:
        TempStorage( size_t size = 0 ) :
            m_storage( size )

        {
        }

        template< typename TT > T& set( uint32_t index, TT&& x )
        {
            if( IsUid( index ) )
            {
                auto [it, _] = m_uidStorage.emplace( index, forward< TT >( x ) );
                return it->second;
            }

            if( index >= m_storage.size() )
                m_storage.resize( index + 1 );
            m_storage[index] = forward< TT >( x );
            return m_storage[index];
        }

        const T* get( uint32_t index ) const
        {
            if( IsUid( index ) )
            {
                auto it = m_uidStorage.find( index );
                if( it == m_uidStorage.end() )
                    return nullptr;
                return &it->second;
            }

            if( index < m_storage.size() )
                return &m_storage[index];

            return nullptr;
        }

        T* get( uint32_t index )
        {
            if( IsUid( index ) )
                return nullptr;

            if( index < m_storage.size() )
                return &m_storage[index];

            return nullptr;
        }

      private:
        llvm::SmallVector< T, 16 > m_storage;
        unordered_map< uint32_t, T > m_uidStorage;
    };

    // We need to lower types before consuming them from the CIR. Ideally we'd do this while
    // building the CIR but as things are now it would involve enormous refactoring to store lowered
    // types in a bunch of high level objects, and all the code to round trip them to EIR would have
    // to deal with them, while also making sure they aren't actually participating in pattern
    // matching. A significant redesign will probably allow for an elegant solution, but that's for
    // the rewrite. In the mean time we use this ugly helper that lowers the type when reading it
    // from the CIR, if not previously done. Since it needs the context we have to store the current
    // context as a static member. This will have to do for the BS compiler...
    class LowerableType
    {
      public:
        template< typename T >
        LowerableType( T&& type ) :
            m_type( forward< T >( type ) )

        {


        }


        const auto& type() const { return m_type; }

        const eir::Term& get() const;

        auto operator<( const LowerableType& other ) const { return m_type < other.m_type; }

        auto operator==( const LowerableType& other ) const { return m_type == other.m_type; }


      private:
        eir::Term m_type;
        mutable optional< eir::Term > m_loweredType;
    };

    // Same as above for values
    class LowerableValue
    {
      public:
        template< typename T >
        LowerableValue( T&& val ) :
            m_val( forward< T >( val ) )

        {
        }

        const auto& val() const { return m_val; }

        const eir::Value& get() const;
        eir::Value& modify();

        auto operator<( const LowerableValue& other ) const { return m_val < other.m_val; }




        auto operator==( const LowerableValue& other ) const { return m_val == other.m_val; }




      private:
        eir::Value m_val;
        mutable optional< eir::Value > m_loweredVal;
    };

} // namespace goose::cir

#endif
Changes to bs/cir/inliner.cpp.
29
30
31
32
33
34
35

36
37
38
39
40
41
42
43
                bb->dirty();

                // Extract the remainder of the current BB, to be re-appended to whatever
                // the current BB is after inlining the call
                InstrSeq remainder;
                auto itNext = it;
                ++itNext;

                remainder.splice( remainder.end(), bb->instructions(), itNext, bb->instructions().end() );
                auto termi = move( bb->terminator() );

                auto funcToInline = icInstr->funcCIR();
                auto argUids = move( icInstr->argUids() );
                auto retType = move( icInstr->retType() );
                bb->instructions().erase( it );








>
|







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
                bb->dirty();

                // Extract the remainder of the current BB, to be re-appended to whatever
                // the current BB is after inlining the call
                InstrSeq remainder;
                auto itNext = it;
                ++itNext;
                remainder.splice(
                    remainder.end(), bb->instructions(), itNext, bb->instructions().end() );
                auto termi = move( bb->terminator() );

                auto funcToInline = icInstr->funcCIR();
                auto argUids = move( icInstr->argUids() );
                auto retType = move( icInstr->retType() );
                bb->instructions().erase( it );

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
                // we need to restart from its beginning.
                if( it == bb->instructions().end() )
                    it = bb->instructions().begin();
            }
        }
    }

    ptr< InstrSeq > AppendCFG( CFG& dest, const CFG& src, const eir::Term& retType, const InlineCall::ArgUidArray& argUids )

    {
        auto result = make_shared< InstrSeq >();

        auto bbIndexOffset = dest.count() - 1;

        auto fixUpBBPtr = [&]( const wptr< BasicBlock >& pBB )
        {
            auto index = pBB.lock()->index();
            return dest.getBB( index + bbIndexOffset );
        };

        uint32_t retValUid = 0;

        if( src.count() > 1 && retType != GetValueType< void >() )
        {
            retValUid = util::GenerateNewUID();
            auto rtVal = *EIRToValue( retType );
            auto rtLoc = rtVal.locationId();
            dest.currentBB()->append( AllocVar( retType, retValUid, rtLoc ) );
            AppendToInstrSeq( *result,
                DataPathOf{ retValUid, rtLoc },
                Load{ retType, rtLoc } );
        }

        ptr< BasicBlock > contBB = dest.currentBB();

        for( uint32_t i = 0; i < src.count() - 1; ++i )
            dest.createBB();

        if( src.count() > 1 )
            contBB = dest.createBB();

        for( uint32_t i = 0; i < src.count(); ++i )
        {
            const auto& srcBB = src.getBB( i + 1 );
            const auto& destBB = dest.getBB( i + 1 + bbIndexOffset );

            for( auto instr : srcBB->instructions() )
            {
                if( auto* phiInstr = get_if< Phi >( &instr ) )
                {
                    phiInstr->forAllIncomings( [&]( auto& pBB, auto& val )

                    {
                        pBB = fixUpBBPtr( pBB );
                        return true;
                    } );
                }
                /*else if( auto* gtInstr = get_if< GetTemporary >( &instr ) )
                {
                    if( gtInstr->index() < argUids.size() )
                        gtInstr->setIndex( argUids[gtInstr->index()] );
                }*/

                destBB->instructions().emplace_back( instr );
            }

            assert( srcBB->terminator() );


            visit( [&]< typename T >( const T& termi )
            {
                if constexpr( is_same_v< T, RetVoid > )
                {
                    if( src.count() > 1 )
                        destBB->setTerminator( Branch( contBB ) );
                }
                else if constexpr( is_same_v< T, Ret > )
                {
                    if( src.count() > 1 )
                    {
                        destBB->append(
                            DataPathOf( retValUid, termi.locationId() ),
                            Store( move( retType ), termi.locationId(), termi.locationId() )
                        );

                        destBB->setTerminator( Branch( contBB ) );
                    }
                }
                else if constexpr( is_same_v< T, Branch > )
                {
                    destBB->setTerminator( Branch( fixUpBBPtr( termi.dest() ) ) );
                }
                else if constexpr( is_same_v< T, CondBranch > )
                {
                    destBB->setTerminator( CondBranch(
                        fixUpBBPtr( termi.trueDest() ),
                        fixUpBBPtr( termi.falseDest() ) ) );
                }
                else if constexpr( is_same_v< T, GhostBranch > )
                {
                    destBB->setTerminator( CondBranch(
                        fixUpBBPtr( termi.ghostCode() ),
                        fixUpBBPtr( termi.continuation() ) ) );
                }
                else
                {
                    destBB->setTerminator( T( termi ) );
                }

            }, srcBB->terminator()->content() );
        }

        if( contBB )
            dest.setCurrentBB( contBB );

        return result;
    }
}







|
>



















|
<
<



















|
>
|
|
|
|












>
|
|
|
|
|
|
|
|
|
|
|
<
|
|
<

|
|
|
|
|
|
|
|
|
|
|
<
|
|
|
|
|
<
|
|
|
|
|
|
|







|
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
                // we need to restart from its beginning.
                if( it == bb->instructions().end() )
                    it = bb->instructions().begin();
            }
        }
    }

    ptr< InstrSeq > AppendCFG( CFG& dest, const CFG& src, const eir::Term& retType,
        const InlineCall::ArgUidArray& argUids )
    {
        auto result = make_shared< InstrSeq >();

        auto bbIndexOffset = dest.count() - 1;

        auto fixUpBBPtr = [&]( const wptr< BasicBlock >& pBB )
        {
            auto index = pBB.lock()->index();
            return dest.getBB( index + bbIndexOffset );
        };

        uint32_t retValUid = 0;

        if( src.count() > 1 && retType != GetValueType< void >() )
        {
            retValUid = util::GenerateNewUID();
            auto rtVal = *EIRToValue( retType );
            auto rtLoc = rtVal.locationId();
            dest.currentBB()->append( AllocVar( retType, retValUid, rtLoc ) );
            AppendToInstrSeq( *result, DataPathOf{ retValUid, rtLoc }, Load{ retType, rtLoc } );


        }

        ptr< BasicBlock > contBB = dest.currentBB();

        for( uint32_t i = 0; i < src.count() - 1; ++i )
            dest.createBB();

        if( src.count() > 1 )
            contBB = dest.createBB();

        for( uint32_t i = 0; i < src.count(); ++i )
        {
            const auto& srcBB = src.getBB( i + 1 );
            const auto& destBB = dest.getBB( i + 1 + bbIndexOffset );

            for( auto instr : srcBB->instructions() )
            {
                if( auto* phiInstr = get_if< Phi >( &instr ) )
                {
                    phiInstr->forAllIncomings(
                        [&]( auto& pBB, auto& val )
                        {
                            pBB = fixUpBBPtr( pBB );
                            return true;
                        } );
                }
                /*else if( auto* gtInstr = get_if< GetTemporary >( &instr ) )
                {
                    if( gtInstr->index() < argUids.size() )
                        gtInstr->setIndex( argUids[gtInstr->index()] );
                }*/

                destBB->instructions().emplace_back( instr );
            }

            assert( srcBB->terminator() );

            visit(
                [&]< typename T >( const T& termi )
                {
                    if constexpr( is_same_v< T, RetVoid > )
                    {
                        if( src.count() > 1 )
                            destBB->setTerminator( Branch( contBB ) );
                    }
                    else if constexpr( is_same_v< T, Ret > )
                    {
                        if( src.count() > 1 )
                        {

                            destBB->append( DataPathOf( retValUid, termi.locationId() ),
                                Store( move( retType ), termi.locationId(), termi.locationId() ) );


                            destBB->setTerminator( Branch( contBB ) );
                        }
                    }
                    else if constexpr( is_same_v< T, Branch > )
                    {
                        destBB->setTerminator( Branch( fixUpBBPtr( termi.dest() ) ) );
                    }
                    else if constexpr( is_same_v< T, CondBranch > )
                    {
                        destBB->setTerminator( CondBranch(
                            fixUpBBPtr( termi.trueDest() ), fixUpBBPtr( termi.falseDest() ) ) );

                    }
                    else if constexpr( is_same_v< T, GhostBranch > )
                    {
                        destBB->setTerminator( CondBranch(
                            fixUpBBPtr( termi.ghostCode() ), fixUpBBPtr( termi.continuation() ) ) );

                    }
                    else
                    {
                        destBB->setTerminator( T( termi ) );
                    }
                },
                srcBB->terminator()->content() );
        }

        if( contBB )
            dest.setCurrentBB( contBB );

        return result;
    }
} // namespace goose::cir
Changes to bs/cir/inliner.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17


18
19
#ifndef GOOSE_CIR_INLINER_H
#define GOOSE_CIR_INLINER_H

namespace goose::cir
{
    // Go through a CFG and expand all InlineCall instructions encountered.
    extern void ExpandInlineCalls( CFG& cfg );

    // Go through a basic block and expand all InlineCall instructions encountered.
    extern void ExpandInlineCalls( CFG& cfg, BasicBlock* bb );

    // Append the src cfg to the dest cfg, replacing all return terminators from src
    // to branches to the provided continuation block (which should belong to the dest cfg).
    // The provided callbacks are used to transform input instructions, and to retrieve
    // and returned value generated by the src cfg.
    extern ptr< InstrSeq > AppendCFG( CFG& dest, const CFG& src, const eir::Term& retType, const InlineCall::ArgUidArray& argUids );
}



#endif











|
|
|
|
|
<
>
>


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

17
18
19
20
#ifndef GOOSE_CIR_INLINER_H
#define GOOSE_CIR_INLINER_H

namespace goose::cir
{
    // Go through a CFG and expand all InlineCall instructions encountered.
    extern void ExpandInlineCalls( CFG& cfg );

    // Go through a basic block and expand all InlineCall instructions encountered.
    extern void ExpandInlineCalls( CFG& cfg, BasicBlock* bb );

    // Append the src cfg to the dest cfg, replacing all return terminators from src to branches to
    // the provided continuation block (which should belong to the dest cfg). The provided callbacks
    // are used to transform input instructions, and to retrieve and returned value generated by the
    // src cfg.
    extern ptr< InstrSeq > AppendCFG( CFG& dest, const CFG& src, const eir::Term& retType,

        const InlineCall::ArgUidArray& argUids );
} // namespace goose::cir

#endif
Changes to bs/cir/instruction.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
#include "cir/cir.h"

namespace goose::cir
{
    bool CanBeExecuted( const Instruction& instr )
    {

        return visit( []< typename ET >( const ET& e )
        {
            if constexpr( is_same_v< ET, monostate > )
                return false;
            else
                return e.canBeExecuted();

        }, instr );
    }

    bool CanBeEagerlyEvaluated( const Instruction& instr )
    {

        return visit( []< typename ET >( const ET& e )
        {
            if constexpr( is_same_v< ET, monostate > )
                return false;
            else
                return e.canBeEagerlyEvaluated();

        }, instr );
    }

    bool HaveSideEffects( const Instruction& instr )
    {

        return visit( []< typename ET >( const ET& e )
        {
            if constexpr( is_same_v< ET, monostate > )
                return false;
            else
                return e.haveSideEffects();

        }, instr );
    }

    ostream& operator<<( ostream& out, const Instruction& instr )
    {

        return visit( [&]< typename ET >( const ET& e ) -> ostream&
        {
            if constexpr( is_same_v< ET, monostate > )
                return out << "NIL";
            else
                return out << e;

        }, instr );
    }

    ostream& operator<<( ostream& out, const Select& ins )
    {
        out << "SELECT(" << ins.m_memberIndex;
        return out << ')';
    }






>
|
|
|
|
|
|
>
|




>
|
|
|
|
|
|
>
|




>
|
|
|
|
|
|
>
|




>
|
|
|
|
|
|
>
|







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
#include "cir/cir.h"

namespace goose::cir
{
    bool CanBeExecuted( const Instruction& instr )
    {
        return visit(
            []< typename ET >( const ET& e )
            {
                if constexpr( is_same_v< ET, monostate > )
                    return false;
                else
                    return e.canBeExecuted();
            },
            instr );
    }

    bool CanBeEagerlyEvaluated( const Instruction& instr )
    {
        return visit(
            []< typename ET >( const ET& e )
            {
                if constexpr( is_same_v< ET, monostate > )
                    return false;
                else
                    return e.canBeEagerlyEvaluated();
            },
            instr );
    }

    bool HaveSideEffects( const Instruction& instr )
    {
        return visit(
            []< typename ET >( const ET& e )
            {
                if constexpr( is_same_v< ET, monostate > )
                    return false;
                else
                    return e.haveSideEffects();
            },
            instr );
    }

    ostream& operator<<( ostream& out, const Instruction& instr )
    {
        return visit(
            [&]< typename ET >( const ET& e ) -> ostream&
            {
                if constexpr( is_same_v< ET, monostate > )
                    return out << "NIL";
                else
                    return out << e;
            },
            instr );
    }

    ostream& operator<<( ostream& out, const Select& ins )
    {
        out << "SELECT(" << ins.m_memberIndex;
        return out << ')';
    }
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
        return out << "PHOVERRIDECLEAR(" << ins.m_name << ')';
    }

    ostream& operator<<( ostream& out, const Phi& ins )
    {
        out << "PHI(" << ins.m_destIndex << ", " << ins.m_type.get();

        for( const auto& [bb,val] : ins.m_incomings )
            out << ", " << bb.lock()->index() << ':' << val;

        return out << " )";
    }

    bool Select::operator<( const Select& rhs ) const
    {







|







81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
        return out << "PHOVERRIDECLEAR(" << ins.m_name << ')';
    }

    ostream& operator<<( ostream& out, const Phi& ins )
    {
        out << "PHI(" << ins.m_destIndex << ", " << ins.m_type.get();

        for( const auto& [bb, val] : ins.m_incomings )
            out << ", " << bb.lock()->index() << ':' << val;

        return out << " )";
    }

    bool Select::operator<( const Select& rhs ) const
    {
103
104
105
106
107
108
109
110
        return m_name < rhs.m_name;
    }

    bool PHOverrideClear::operator<( const PHOverrideClear& rhs ) const
    {
        return m_name < rhs.m_name;
    }
}







|
111
112
113
114
115
116
117
118
        return m_name < rhs.m_name;
    }

    bool PHOverrideClear::operator<( const PHOverrideClear& rhs ) const
    {
        return m_name < rhs.m_name;
    }
} // namespace goose::cir
Changes to bs/cir/instruction.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71


72
73
74
75
76
77
78
79
80
#ifndef GOOSE_CIR_INSTRUCTION_H
#define GOOSE_CIR_INSTRUCTION_H

namespace goose::cir
{
    class CFG;

    using Instruction = variant
    <
//        monostate,

        Call,
        InlineCall,
        CallCheck,

        DataPathOf,
        DataPathToAddr,

        Select,

        Constant,
        AllocVar,
        Load,
        Store,
        Phi,

        Not,
        And,
        Or,
        Xor,

        Shl,
        LShr,
        AShr,

        Add,
        Sub,
        Mul,
        UDiv,
        SDiv,
        URem,
        SRem,

        Eq,
        Neq,
        UGT,
        UGE,
        ULT,
        ULE,
        SGT,
        SGE,
        SLT,
        SLE,

        Assert,
        GhostCall,
        PHOverrideSet,
        PHOverrideClear,
        Placeholder,

        ForAllSetup,
        ForAll,

        Implies,
        IsPrefixOf,

        PushType,
        PushStringId
    >;

    class InstrSeq : public list< Instruction > {};



    extern bool CanBeExecuted( const Instruction& instr );
    extern bool CanBeEagerlyEvaluated( const Instruction& instr );
    extern bool HaveSideEffects( const Instruction& instr );

    extern ostream& operator<<( ostream& out, const Instruction& instr );
}

#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
#ifndef GOOSE_CIR_INSTRUCTION_H
#define GOOSE_CIR_INSTRUCTION_H

namespace goose::cir
{
    class CFG;

    using Instruction = variant< Call, InlineCall, CallCheck,







        DataPathOf, DataPathToAddr,


        Select,

        Constant, AllocVar, Load, Store, Phi,





        Not, And, Or, Xor,





        Shl, LShr, AShr,





        Add, Sub, Mul, UDiv, SDiv, URem, SRem,





        Eq, Neq, UGT, UGE, ULT, ULE, SGT, SGE, SLT, SLE,












        Assert, GhostCall, PHOverrideSet, PHOverrideClear, Placeholder,


        ForAllSetup, ForAll,


        Implies, IsPrefixOf,


        PushType, PushStringId >;



    class InstrSeq : public list< Instruction >
    {
    };

    extern bool CanBeExecuted( const Instruction& instr );
    extern bool CanBeEagerlyEvaluated( const Instruction& instr );
    extern bool HaveSideEffects( const Instruction& instr );

    extern ostream& operator<<( ostream& out, const Instruction& instr );
} // namespace goose::cir

#endif
Deleted bs/cir/load.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
#ifndef GOOSE_CIR_LOAD_H
#define GOOSE_CIR_LOAD_H

namespace goose::cir
{
    class Load : public BaseInstr< 1, true >
    {
        public:
            template< typename T >
            Load( T&& type, LocationId loc ) :
                BaseInstr( loc ),
                m_type( forward< T >( type ) )
            {}

            const auto& type() const { return m_type; }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }
            bool haveSideEffects() const { return false; }

            bool operator<( const Load& rhs ) const;
            friend ostream& operator<<( ostream& out, const Load& ins );

        private:
            LowerableType m_type;
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


























































Deleted bs/cir/logic.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
#ifndef GOOSE_CIR_LOGIC_H
#define GOOSE_CIR_LOGIC_H

namespace goose::cir
{
    class And : public BinaryOp
    {
        public:
            And( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const And& ins )
            {
                return out << "AND";
            }
    };

    class Or : public BinaryOp
    {
        public:
            Or( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Or& ins )
            {
                return out << "OR";
            }
    };

    class Xor : public BinaryOp
    {
        public:
            Xor( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Xor& ins )
            {
                return out << "XOR";
            }
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































































Changes to bs/cir/loops.cpp.
1
2
3
4

5
6
7
8
9
10
11
#include "cir.h"

// Note: we only handle reducible CFGs for now. The results are undefined (ie probably a clusterfuck) for
// irreducible CFGs. We'll have to handle irreducible CFGs when (if...) we implement goto.

namespace goose::cir
{
    void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb );

    void MarkLoop( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, uint32_t loopHeaderId )
    {
        if( bb->index() == loopHeaderId )


|
|
>







1
2
3
4
5
6
7
8
9
10
11
12
#include "cir.h"

// Note: we only handle reducible CFGs for now. The results are undefined (ie probably a
// clusterfuck) for irreducible CFGs. We'll have to handle irreducible CFGs when (if...) we
// implement goto.
namespace goose::cir
{
    void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb );

    void MarkLoop( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, uint32_t loopHeaderId )
    {
        if( bb->index() == loopHeaderId )
34
35
36
37
38
39
40

41
42
43
44
45
46
47
48
        for( auto&& x : bb->backEdges() )
            MarkLoop( cfg, cfg->getBB( x ), loopHeaderId );
    }

    // Handle an outgoing edge while computing loops:
    // If the target BB dominates the source BB, create a loop and invoke
    // MarkLoop. Otherwise, continue the recursion through that edge.

    void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, const ptr< BasicBlock >& destBB )
    {
        if( cfg->getDominatorDistance( destBB->index(), bb->index() ) )
        {
            // This is a natural loop whose header is destBB.
            destBB->setLoopHeader();

            // Remove the edge from the header's back edges, and add it to the header's







>
|







35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
        for( auto&& x : bb->backEdges() )
            MarkLoop( cfg, cfg->getBB( x ), loopHeaderId );
    }

    // Handle an outgoing edge while computing loops:
    // If the target BB dominates the source BB, create a loop and invoke
    // MarkLoop. Otherwise, continue the recursion through that edge.
    void IdentifyLoops(
        const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, const ptr< BasicBlock >& destBB )
    {
        if( cfg->getDominatorDistance( destBB->index(), bb->index() ) )
        {
            // This is a natural loop whose header is destBB.
            destBB->setLoopHeader();

            // Remove the edge from the header's back edges, and add it to the header's
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
        }
        else
            IdentifyLoops( cfg, destBB );
    }

    template< typename T >
    void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, const T& tr )
    {}



    void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, const Branch& tr )
    {
        IdentifyLoops( cfg, bb, tr.dest().lock() );
    }

    void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, const CondBranch& tr )
    {
        IdentifyLoops( cfg, bb, tr.trueDest().lock() );
        IdentifyLoops( cfg, bb, tr.falseDest().lock() );
    }

    void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, const GhostBranch& tr )
    {
        IdentifyLoops( cfg, bb, tr.ghostCode().lock() );
        IdentifyLoops( cfg, bb, tr.continuation().lock() );
    }

    void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, const Terminator& tr )
    {
        visit( [&]( auto&& tr )
        {
            IdentifyLoops( cfg, bb, tr );
        }, tr.content() );
    }

    void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb )
    {
        if( bb->terminator() )
            IdentifyLoops( cfg, bb, *bb->terminator() );
    }

    void IdentifyLoops( const ptr< CFG >& cfg )
    {
        ComputeDominators( cfg );
        IdentifyLoops( cfg, cfg->entryBB() );
    }
}







<
>
>




















|
<
<
<













|
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
        }
        else
            IdentifyLoops( cfg, destBB );
    }

    template< typename T >
    void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, const T& tr )

    {
    }

    void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, const Branch& tr )
    {
        IdentifyLoops( cfg, bb, tr.dest().lock() );
    }

    void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, const CondBranch& tr )
    {
        IdentifyLoops( cfg, bb, tr.trueDest().lock() );
        IdentifyLoops( cfg, bb, tr.falseDest().lock() );
    }

    void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, const GhostBranch& tr )
    {
        IdentifyLoops( cfg, bb, tr.ghostCode().lock() );
        IdentifyLoops( cfg, bb, tr.continuation().lock() );
    }

    void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, const Terminator& tr )
    {
        visit( [&]( auto&& tr ) { IdentifyLoops( cfg, bb, tr ); }, tr.content() );



    }

    void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb )
    {
        if( bb->terminator() )
            IdentifyLoops( cfg, bb, *bb->terminator() );
    }

    void IdentifyLoops( const ptr< CFG >& cfg )
    {
        ComputeDominators( cfg );
        IdentifyLoops( cfg, cfg->entryBB() );
    }
} // namespace goose::cir
Deleted bs/cir/not.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
#ifndef GOOSE_CIR_NOT_H
#define GOOSE_CIR_NOT_H

namespace goose::cir
{
    class Not : public BaseInstr< 1, true >
    {
        public:
            Not( LocationId loc ) :
                BaseInstr( loc )
            {}

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }
            bool haveSideEffects() const { return false; }

            bool operator<( const Not& rhs ) const
            {
                return false;
            }

            friend ostream& operator<<( ostream& out, const Not& ins )
            {
                return out << "NOT";
            }
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


























































Changes to bs/cir/op-allocvar.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
#ifndef GOOSE_CIR_ALLOCVAR_H
#define GOOSE_CIR_ALLOCVAR_H

namespace goose::cir
{
    class AllocVar : public Operation< 0, false >
    {
        public:
            template< typename T >
            AllocVar( T&& type, uint32_t index, LocationId loc ) :
                Operation( loc ),
                m_type( forward< T >( type ) ),
                m_index( index )
            {}



            const auto& type() const { return m_type; }

            const auto& index() const { return m_index; }

            void setIndex( uint32_t index ) { m_index = index; }

            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return false; }

            bool haveSideEffects() const { return true; }

            bool operator<( const AllocVar& rhs ) const
            {
                if( m_index != rhs.m_index )
                    return m_index < rhs.m_index;
                return m_type < rhs.m_type;
            }

            friend ostream& operator<<( ostream& out, const AllocVar& ins )
            {
                return out << "ALLOCVAR(" << ins.m_index << ", " << ins.m_type.get() << ')';
            }

        private:
            LowerableType m_type;
            uint32_t m_index = 0;
    };
}

#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
#ifndef GOOSE_CIR_ALLOCVAR_H
#define GOOSE_CIR_ALLOCVAR_H

namespace goose::cir
{
    class AllocVar : public Operation< 0, false >
    {
      public:
        template< typename T >
        AllocVar( T&& type, uint32_t index, LocationId loc ) :
            Operation( loc ),
            m_type( forward< T >( type ) ),
            m_index( index )

        {
        }

        const auto& type() const { return m_type; }

        const auto& index() const { return m_index; }

        void setIndex( uint32_t index ) { m_index = index; }

        bool canBeExecuted() const { return true; }

        bool canBeEagerlyEvaluated() const { return false; }

        bool haveSideEffects() const { return true; }

        bool operator<( const AllocVar& rhs ) const
        {
            if( m_index != rhs.m_index )
                return m_index < rhs.m_index;
            return m_type < rhs.m_type;
        }

        friend ostream& operator<<( ostream& out, const AllocVar& ins )
        {
            return out << "ALLOCVAR(" << ins.m_index << ", " << ins.m_type.get() << ')';
        }

      private:
        LowerableType m_type;
        uint32_t m_index = 0;
    };
} // namespace goose::cir

#endif
Changes to bs/cir/op-arith.h.
1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
35
36
37
38
39

40
41
42
43
44
45
46
47
48
49
50
51
52

53
54
55
56
57
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73
74
75
76
77
78

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

96

97
98
#ifndef GOOSE_CIR_OP_ARITH_H
#define GOOSE_CIR_OP_ARITH_H

namespace goose::cir
{
    class Add : public BinaryOp
    {
        public:
            Add( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Add& ins )

            {
                return out << "ADD";
            }
    };

    class Sub : public BinaryOp
    {
        public:
            Sub( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Sub& ins )

            {
                return out << "SUB";
            }
    };

    class Mul : public BinaryOp
    {
        public:
            Mul( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Mul& ins )

            {
                return out << "MUL";
            }
    };

    class UDiv : public BinaryOp
    {
        public:
            UDiv( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const UDiv& ins )

            {
                return out << "UDIV";
            }
    };

    class SDiv : public BinaryOp
    {
        public:
            SDiv( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const SDiv& ins )

            {
                return out << "SDIV";
            }
    };

    class URem : public BinaryOp
    {
        public:
            URem( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const URem& ins )

            {
                return out << "UREM";
            }
    };

    class SRem : public BinaryOp
    {
        public:
            SRem( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const SRem& ins )
            {
                return out << "SREM";
            }
    };

}


#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
64
65

66

67
68
69

70
71
72
73
74
75
76

77



78
79
80
81
82
83
84
#ifndef GOOSE_CIR_OP_ARITH_H
#define GOOSE_CIR_OP_ARITH_H

namespace goose::cir
{
    class Add : public BinaryOp
    {
      public:
        Add( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const Add& ins ) { return out << "ADD"; }

    };

    class Sub : public BinaryOp
    {
      public:
        Sub( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const Sub& ins ) { return out << "SUB"; }

    };

    class Mul : public BinaryOp
    {
      public:
        Mul( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const Mul& ins ) { return out << "MUL"; }

    };

    class UDiv : public BinaryOp
    {
      public:
        UDiv( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const UDiv& ins ) { return out << "UDIV"; }

    };

    class SDiv : public BinaryOp
    {
      public:
        SDiv( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const SDiv& ins ) { return out << "SDIV"; }

    };

    class URem : public BinaryOp
    {
      public:
        URem( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const URem& ins ) { return out << "UREM"; }

    };

    class SRem : public BinaryOp
    {
      public:
        SRem( LocationId loc ) :
            BinaryOp( loc )

        {



        }

        friend ostream& operator<<( ostream& out, const SRem& ins ) { return out << "SREM"; }
    };
} // namespace goose::cir

#endif
Changes to bs/cir/op-assert.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
#ifndef GOOSE_CIR_OP_ASSERT_H
#define GOOSE_CIR_OP_ASSERT_H

namespace goose::cir
{
    class Assert : public Operation< 1, false >
    {
        public:
            Assert( LocationId loc ) :
                Operation( loc )
            {}



            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return false; }

            bool haveSideEffects() const { return true; }

            bool operator<( const Assert& rhs ) const
            {
                return false;
            }

            friend ostream& operator<<( ostream& out, const Assert& ins )
            {
                return out << "ASSERT";
            }
    };
}


#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
#ifndef GOOSE_CIR_OP_ASSERT_H
#define GOOSE_CIR_OP_ASSERT_H

namespace goose::cir
{
    class Assert : public Operation< 1, false >
    {
      public:
        Assert( LocationId loc ) :
            Operation( loc )

        {
        }

        bool canBeExecuted() const { return true; }

        bool canBeEagerlyEvaluated() const { return false; }

        bool haveSideEffects() const { return true; }

        bool operator<( const Assert& rhs ) const { return false; }




        friend ostream& operator<<( ostream& out, const Assert& ins ) { return out << "ASSERT"; }



    };

} // namespace goose::cir

#endif
Changes to bs/cir/op-binary.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
#ifndef GOOSE_CIR_BINARYINSTR_H
#define GOOSE_CIR_BINARYINSTR_H

namespace goose::cir
{
    class BinaryOp : public Operation< 2, true >
    {
        public:
            BinaryOp( LocationId loc ) :
                Operation( loc )
            {}



            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return true; }

            bool haveSideEffects() const { return false; }

            bool operator<( const BinaryOp& rhs ) const
            {
                return false;
            }
    };
}

#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
#ifndef GOOSE_CIR_BINARYINSTR_H
#define GOOSE_CIR_BINARYINSTR_H

namespace goose::cir
{
    class BinaryOp : public Operation< 2, true >
    {
      public:
        BinaryOp( LocationId loc ) :
            Operation( loc )

        {
        }

        bool canBeExecuted() const { return true; }

        bool canBeEagerlyEvaluated() const { return true; }

        bool haveSideEffects() const { return false; }

        bool operator<( const BinaryOp& rhs ) const { return false; }



    };
} // namespace goose::cir

#endif
Changes to bs/cir/op-bitwise.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
#ifndef GOOSE_CIR_OP_BITWISE_H
#define GOOSE_CIR_OP_BITWISE_H

namespace goose::cir
{
    class Shl : public BinaryOp
    {
        public:
            Shl( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Shl& ins )

            {
                return out << "SHL";
            }
    };

    class LShr : public BinaryOp
    {
        public:
            LShr( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const LShr& ins )

            {
                return out << "LSHR";
            }
    };

    class AShr : public BinaryOp
    {
        public:
            AShr( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const AShr& ins )
            {
                return out << "ASHR";
            }
    };

}


#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
#ifndef GOOSE_CIR_OP_BITWISE_H
#define GOOSE_CIR_OP_BITWISE_H

namespace goose::cir
{
    class Shl : public BinaryOp
    {
      public:
        Shl( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const Shl& ins ) { return out << "SHL"; }

    };

    class LShr : public BinaryOp
    {
      public:
        LShr( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const LShr& ins ) { return out << "LSHR"; }

    };

    class AShr : public BinaryOp
    {
      public:
        AShr( LocationId loc ) :
            BinaryOp( loc )

        {



        }

        friend ostream& operator<<( ostream& out, const AShr& ins ) { return out << "ASHR"; }
    };
} // namespace goose::cir

#endif
Changes to bs/cir/op-call.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15


16
17
18

19

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49


50

51

52
53
54

55
56
57

58

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90


91
92
93

94

95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#ifndef GOOSE_CIR_OP_CALL_H
#define GOOSE_CIR_OP_CALL_H

namespace goose::cir
{
    class Func;

    class Call : public VariadicOperation< 1, true >   // Can pop more depending on arg count
    {
        public:
            Call( uint32_t numArgs, LocationId loc ) :
                VariadicOperation( numArgs, loc ),
                m_numArgs( numArgs )
            {}



            const auto& numArgs() const { return m_numArgs; }

            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return true; }

            bool haveSideEffects() const { return true; }

            bool operator<( const Call& rhs ) const
            {
                return m_numArgs < rhs.m_numArgs;
            }

            friend ostream& operator<<( ostream& out, const Call& ins )
            {
                return out << "CALL " << ins.m_numArgs;
            }

        private:
            // TODO remove, its in the base class now
            uint32_t m_numArgs = 0;
    };

    class InlineCall : public VariadicOperation< 0, true >
    {
        public:
            using ArgUidArray = llvm::SmallVector< uint32_t, 8 >;

            InlineCall( ArgUidArray&& argUids, const cir::Func* pFuncCIR, const eir::Term& retType,
                LocationId loc ) :
                VariadicOperation( argUids.size(), loc ),
                m_pFuncCIR( pFuncCIR ),
                m_argUids( move( argUids ) ),
                m_retType( retType )
            {}



            const auto* funcCIR() const { return m_pFuncCIR; }

            const auto& argUids() const { return m_argUids; }

            const auto& retType() const { return m_retType; }

            auto& argUids() { return m_argUids; }

            auto& retType() { return m_retType; }

            bool canBeExecuted() const { return false; }

            bool canBeEagerlyEvaluated() const { return false; }

            bool haveSideEffects() const { return true; }

            bool operator<( const InlineCall& rhs ) const
            {
                if( m_pFuncCIR != rhs.m_pFuncCIR )
                    return m_pFuncCIR < rhs.m_pFuncCIR;

                if( m_retType != rhs.m_retType )
                    return m_retType < rhs.m_retType;

                return m_argUids < rhs.m_argUids;
            }

            friend ostream& operator<<( ostream& out, const InlineCall& ins )
            {
                return out << "INLINECALL " << ins.m_pFuncCIR;
            }

        private:
            const cir::Func* m_pFuncCIR = nullptr;
            ArgUidArray m_argUids;
            eir::Term m_retType;
    };

    class CallCheck : public VariadicOperation< 1, true >   // Can pop more depending on arg count
    {
        public:
            CallCheck( uint32_t numArgs, LocationId loc ) :
                VariadicOperation( numArgs, loc ),
                m_numArgs( numArgs )
            {}



            const auto& numArgs() const { return m_numArgs; }

            bool canBeExecuted() const { return false; }

            bool canBeEagerlyEvaluated() const { return false; }

            bool haveSideEffects() const { return false; }

            bool operator<( const CallCheck& rhs ) const
            {
                return m_numArgs < rhs.m_numArgs;
            }

            friend ostream& operator<<( ostream& out, const CallCheck& ins )
            {
                return out << "CALLCHECK " << ins.m_numArgs;
            }

        private:
            uint32_t m_numArgs = 0;
    };
}

#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
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
#ifndef GOOSE_CIR_OP_CALL_H
#define GOOSE_CIR_OP_CALL_H

namespace goose::cir
{
    class Func;

    class Call : public VariadicOperation< 1, true > // Can pop more depending on arg count
    {
      public:
        Call( uint32_t numArgs, LocationId loc ) :
            VariadicOperation( numArgs, loc ),
            m_numArgs( numArgs )

        {
        }

        const auto& numArgs() const { return m_numArgs; }

        bool canBeExecuted() const { return true; }

        bool canBeEagerlyEvaluated() const { return true; }

        bool haveSideEffects() const { return true; }

        bool operator<( const Call& rhs ) const { return m_numArgs < rhs.m_numArgs; }




        friend ostream& operator<<( ostream& out, const Call& ins )
        {
            return out << "CALL " << ins.m_numArgs;
        }

      private:
        // TODO remove, its in the base class now
        uint32_t m_numArgs = 0;
    };

    class InlineCall : public VariadicOperation< 0, true >
    {
      public:
        using ArgUidArray = llvm::SmallVector< uint32_t, 8 >;

        InlineCall( ArgUidArray&& argUids, const cir::Func* pFuncCIR, const eir::Term& retType,
            LocationId loc ) :
            VariadicOperation( argUids.size(), loc ),
            m_pFuncCIR( pFuncCIR ),
            m_argUids( move( argUids ) ),
            m_retType( retType )

        {
        }

        const auto* funcCIR() const { return m_pFuncCIR; }

        const auto& argUids() const { return m_argUids; }

        const auto& retType() const { return m_retType; }

        auto& argUids() { return m_argUids; }

        auto& retType() { return m_retType; }

        bool canBeExecuted() const { return false; }

        bool canBeEagerlyEvaluated() const { return false; }

        bool haveSideEffects() const { return true; }

        bool operator<( const InlineCall& rhs ) const
        {
            if( m_pFuncCIR != rhs.m_pFuncCIR )
                return m_pFuncCIR < rhs.m_pFuncCIR;

            if( m_retType != rhs.m_retType )
                return m_retType < rhs.m_retType;

            return m_argUids < rhs.m_argUids;
        }

        friend ostream& operator<<( ostream& out, const InlineCall& ins )
        {
            return out << "INLINECALL " << ins.m_pFuncCIR;
        }

      private:
        const cir::Func* m_pFuncCIR = nullptr;
        ArgUidArray m_argUids;
        eir::Term m_retType;
    };

    class CallCheck : public VariadicOperation< 1, true > // Can pop more depending on arg count
    {
      public:
        CallCheck( uint32_t numArgs, LocationId loc ) :
            VariadicOperation( numArgs, loc ),
            m_numArgs( numArgs )

        {
        }

        const auto& numArgs() const { return m_numArgs; }

        bool canBeExecuted() const { return false; }

        bool canBeEagerlyEvaluated() const { return false; }

        bool haveSideEffects() const { return false; }

        bool operator<( const CallCheck& rhs ) const { return m_numArgs < rhs.m_numArgs; }




        friend ostream& operator<<( ostream& out, const CallCheck& ins )
        {
            return out << "CALLCHECK " << ins.m_numArgs;
        }

      private:
        uint32_t m_numArgs = 0;
    };
} // namespace goose::cir

#endif
Changes to bs/cir/op-comparisons.h.
1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
35
36
37
38
39

40
41
42
43
44
45
46
47
48
49
50
51
52

53
54
55
56
57
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73
74
75
76
77
78

79
80
81
82
83
84
85
86
87
88
89
90
91

92
93
94
95
96
97
98
99
100
101
102
103
104

105
106
107
108
109
110
111
112
113
114
115
116
117

118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134

135

136
137
#ifndef GOOSE_CIR_OP_COMPARISON_H
#define GOOSE_CIR_OP_COMPARISON_H

namespace goose::cir
{
    class Eq : public BinaryOp
    {
        public:
            Eq( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Eq& ins )

            {
                return out << "EQ";
            }
    };

    class Neq : public BinaryOp
    {
        public:
            Neq( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Neq& ins )

            {
                return out << "NEQ";
            }
    };

    class UGT : public BinaryOp
    {
        public:
            UGT( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const UGT& ins )

            {
                return out << "UGT";
            }
    };

    class UGE : public BinaryOp
    {
        public:
            UGE( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const UGE& ins )

            {
                return out << "UGE";
            }
    };

    class ULT : public BinaryOp
    {
        public:
            ULT( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const ULT& ins )

            {
                return out << "ULT";
            }
    };

    class ULE : public BinaryOp
    {
        public:
            ULE( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const ULE& ins )

            {
                return out << "ULE";
            }
    };

    class SGT : public BinaryOp
    {
        public:
            SGT( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const SGT& ins )

            {
                return out << "SGT";
            }
    };

    class SGE : public BinaryOp
    {
        public:
            SGE( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const SGE& ins )

            {
                return out << "SGE";
            }
    };

    class SLT : public BinaryOp
    {
        public:
            SLT( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const SLT& ins )

            {
                return out << "SLT";
            }
    };

    class SLE : public BinaryOp
    {
        public:
            SLE( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const SLE& ins )
            {
                return out << "SLE";
            }
    };

}


#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
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
#ifndef GOOSE_CIR_OP_COMPARISON_H
#define GOOSE_CIR_OP_COMPARISON_H

namespace goose::cir
{
    class Eq : public BinaryOp
    {
      public:
        Eq( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const Eq& ins ) { return out << "EQ"; }

    };

    class Neq : public BinaryOp
    {
      public:
        Neq( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const Neq& ins ) { return out << "NEQ"; }

    };

    class UGT : public BinaryOp
    {
      public:
        UGT( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const UGT& ins ) { return out << "UGT"; }

    };

    class UGE : public BinaryOp
    {
      public:
        UGE( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const UGE& ins ) { return out << "UGE"; }

    };

    class ULT : public BinaryOp
    {
      public:
        ULT( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const ULT& ins ) { return out << "ULT"; }

    };

    class ULE : public BinaryOp
    {
      public:
        ULE( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const ULE& ins ) { return out << "ULE"; }

    };

    class SGT : public BinaryOp
    {
      public:
        SGT( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const SGT& ins ) { return out << "SGT"; }

    };

    class SGE : public BinaryOp
    {
      public:
        SGE( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const SGE& ins ) { return out << "SGE"; }

    };

    class SLT : public BinaryOp
    {
      public:
        SLT( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const SLT& ins ) { return out << "SLT"; }

    };

    class SLE : public BinaryOp
    {
      public:
        SLE( LocationId loc ) :
            BinaryOp( loc )

        {



        }

        friend ostream& operator<<( ostream& out, const SLE& ins ) { return out << "SLE"; }
    };
} // namespace goose::cir

#endif
Changes to bs/cir/op-constant.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
#ifndef GOOSE_CIR_OP_CONSTANT_H
#define GOOSE_CIR_OP_CONSTANT_H

namespace goose::cir
{
    class Constant : public Operation< 0, true >
    {
        public:
            template< typename V >
            Constant( V&& val ) :
                Operation( val.locationId() ),
                m_value( forward< V >( val ) )
            {}



            const auto& value() const { return m_value; }

            auto& value() { return m_value; }

            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return true; }

            bool haveSideEffects() const { return false; }

            bool operator<( const Constant& rhs ) const
            {
                return m_value < rhs.m_value;
            }

            friend ostream& operator<<( ostream& out, const Constant& ins )
            {
                return out << "CONSTANT(" << ins.m_value.get() << ')';
            }

        private:
            LowerableValue m_value;
    };
}


#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
#ifndef GOOSE_CIR_OP_CONSTANT_H
#define GOOSE_CIR_OP_CONSTANT_H

namespace goose::cir
{
    class Constant : public Operation< 0, true >
    {
      public:
        template< typename V >
        Constant( V&& val ) :
            Operation( val.locationId() ),
            m_value( forward< V >( val ) )

        {
        }

        const auto& value() const { return m_value; }

        auto& value() { return m_value; }

        bool canBeExecuted() const { return true; }

        bool canBeEagerlyEvaluated() const { return true; }

        bool haveSideEffects() const { return false; }

        bool operator<( const Constant& rhs ) const { return m_value < rhs.m_value; }




        friend ostream& operator<<( ostream& out, const Constant& ins )
        {
            return out << "CONSTANT(" << ins.m_value.get() << ')';
        }

      private:
        LowerableValue m_value;
    };

} // namespace goose::cir

#endif
Changes to bs/cir/op-datapath.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
#ifndef GOOSE_CIR_DATAPATH_H
#define GOOSE_CIR_DATAPATH_H

namespace goose::cir
{
    class DataPathOf : public Operation< 1, true >
    {
        public:
            DataPathOf( uint32_t origin, LocationId loc ) :
                Operation( loc ),
                m_origin( origin )
            {}



            uint32_t origin() const { return m_origin; }

            void setOrigin( uint32_t origin ) { m_origin = origin; }

            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return true; }

            bool haveSideEffects() const { return false; }

            bool operator<( const DataPathOf& rhs ) const
            {
                return m_origin < rhs.m_origin;
            }

            friend ostream& operator<<( ostream& out, const DataPathOf& ins )
            {
                return out << "DATAPATHOF(" << ins.m_origin << ')';
            }

        private:
            uint32_t m_origin = 0;
    };

    class DataPathToAddr : public Operation< 1, true >
    {
        public:
            DataPathToAddr( LocationId loc ) :
                Operation( loc )
            {}



            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return true; }

            bool haveSideEffects() const { return false; }

            bool operator<( const DataPathToAddr& rhs ) const
            {
                return false;
            }

            friend ostream& operator<<( ostream& out, const DataPathToAddr& ins )
            {
                return out << "DATAPATHTOADDR";
            }
    };
}

#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
#ifndef GOOSE_CIR_DATAPATH_H
#define GOOSE_CIR_DATAPATH_H

namespace goose::cir
{
    class DataPathOf : public Operation< 1, true >
    {
      public:
        DataPathOf( uint32_t origin, LocationId loc ) :
            Operation( loc ),
            m_origin( origin )

        {
        }

        uint32_t origin() const { return m_origin; }

        void setOrigin( uint32_t origin ) { m_origin = origin; }

        bool canBeExecuted() const { return true; }

        bool canBeEagerlyEvaluated() const { return true; }

        bool haveSideEffects() const { return false; }

        bool operator<( const DataPathOf& rhs ) const { return m_origin < rhs.m_origin; }




        friend ostream& operator<<( ostream& out, const DataPathOf& ins )
        {
            return out << "DATAPATHOF(" << ins.m_origin << ')';
        }

      private:
        uint32_t m_origin = 0;
    };

    class DataPathToAddr : public Operation< 1, true >
    {
      public:
        DataPathToAddr( LocationId loc ) :
            Operation( loc )

        {
        }

        bool canBeExecuted() const { return true; }

        bool canBeEagerlyEvaluated() const { return true; }

        bool haveSideEffects() const { return false; }

        bool operator<( const DataPathToAddr& rhs ) const { return false; }




        friend ostream& operator<<( ostream& out, const DataPathToAddr& ins )
        {
            return out << "DATAPATHTOADDR";
        }
    };
} // namespace goose::cir

#endif
Changes to bs/cir/op-forall.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
#ifndef GOOSE_CIR_OP_FORALL_H
#define GOOSE_CIR_OP_FORALL_H

namespace goose::cir
{
    class ForAllSetup : public Operation< 0, false >  // Can pop more depending on arg count
    {
        public:
            ForAllSetup( uint32_t numArgs, LocationId loc ) :
                Operation( loc ),
                m_numArgs( numArgs )
            {}



            const auto& numArgs() const { return m_numArgs; }

            // This can't be executed, it's only meant to appear
            // in expressions handled by the verifier.
            bool canBeExecuted() const { return false; }

            bool canBeEagerlyEvaluated() const { return false; }

            bool haveSideEffects() const { return false; }

            bool operator<( const ForAllSetup& rhs ) const
            {
                return m_numArgs < rhs.m_numArgs;
            }

            friend ostream& operator<<( ostream& out, const ForAllSetup& ins )
            {
                return out << "FORALLSETUP(" << ins.m_numArgs << ')';
            }

        private:
            uint32_t m_numArgs = 0;
    };

    class ForAll : public Operation< 1, true >
    {
        public:
            ForAll( LocationId loc ) :
                Operation( loc )
            {}



            // This can't be executed, it's only meant to appear
            // in expressions handled by the verifier.
            bool canBeExecuted() const { return false; }

            bool canBeEagerlyEvaluated() const { return false; }

            bool haveSideEffects() const { return false; }

            bool operator<( const ForAll& rhs ) const
            {
                return false;
            }

            friend ostream& operator<<( ostream& out, const ForAll& ins )
            {
                return out << "FORALL";
            }
    };
}


#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
#ifndef GOOSE_CIR_OP_FORALL_H
#define GOOSE_CIR_OP_FORALL_H

namespace goose::cir
{
    class ForAllSetup : public Operation< 0, false > // Can pop more depending on arg count
    {
      public:
        ForAllSetup( uint32_t numArgs, LocationId loc ) :
            Operation( loc ),
            m_numArgs( numArgs )

        {
        }

        const auto& numArgs() const { return m_numArgs; }

        // This can't be executed, it's only meant to appear
        // in expressions handled by the verifier.
        bool canBeExecuted() const { return false; }

        bool canBeEagerlyEvaluated() const { return false; }

        bool haveSideEffects() const { return false; }

        bool operator<( const ForAllSetup& rhs ) const { return m_numArgs < rhs.m_numArgs; }




        friend ostream& operator<<( ostream& out, const ForAllSetup& ins )
        {
            return out << "FORALLSETUP(" << ins.m_numArgs << ')';
        }

      private:
        uint32_t m_numArgs = 0;
    };

    class ForAll : public Operation< 1, true >
    {
      public:
        ForAll( LocationId loc ) :
            Operation( loc )

        {
        }

        // This can't be executed, it's only meant to appear
        // in expressions handled by the verifier.
        bool canBeExecuted() const { return false; }

        bool canBeEagerlyEvaluated() const { return false; }

        bool haveSideEffects() const { return false; }

        bool operator<( const ForAll& rhs ) const { return false; }




        friend ostream& operator<<( ostream& out, const ForAll& ins ) { return out << "FORALL"; }



    };

} // namespace goose::cir

#endif
Changes to bs/cir/op-ghostcall.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
#ifndef GOOSE_CIR_OP_GHOSTCALL_H
#define GOOSE_CIR_OP_GHOSTCALL_H

namespace goose::cir
{
    class GhostCall : public Operation< 1, true >   // Can pop more depending on arg count
    {
        public:
            GhostCall( uint32_t numArgs, LocationId loc ) :
                Operation( loc ),
                m_numArgs( numArgs )
            {}



            const auto& numArgs() const { return m_numArgs; }

            bool canBeExecuted() const { return false; }

            bool canBeEagerlyEvaluated() const { return false; }

            bool haveSideEffects() const { return true; }

            bool operator<( const GhostCall& rhs ) const
            {
                return m_numArgs < rhs.m_numArgs;
            }

            friend ostream& operator<<( ostream& out, const GhostCall& ins )
            {
                return out << "GHOSTCALL " << ins.m_numArgs;
            }

        private:
            uint32_t m_numArgs = 0;
    };
}

#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
#ifndef GOOSE_CIR_OP_GHOSTCALL_H
#define GOOSE_CIR_OP_GHOSTCALL_H

namespace goose::cir
{
    class GhostCall : public Operation< 1, true > // Can pop more depending on arg count
    {
      public:
        GhostCall( uint32_t numArgs, LocationId loc ) :
            Operation( loc ),
            m_numArgs( numArgs )

        {
        }

        const auto& numArgs() const { return m_numArgs; }

        bool canBeExecuted() const { return false; }

        bool canBeEagerlyEvaluated() const { return false; }

        bool haveSideEffects() const { return true; }

        bool operator<( const GhostCall& rhs ) const { return m_numArgs < rhs.m_numArgs; }




        friend ostream& operator<<( ostream& out, const GhostCall& ins )
        {
            return out << "GHOSTCALL " << ins.m_numArgs;
        }

      private:
        uint32_t m_numArgs = 0;
    };
} // namespace goose::cir

#endif
Changes to bs/cir/op-load.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
#ifndef GOOSE_CIR_OP_LOAD_H
#define GOOSE_CIR_OP_LOAD_H

namespace goose::cir
{
    class Load : public Operation< 1, true >
    {
        public:
            template< typename T >
            Load( T&& type, LocationId loc ) :
                Operation( loc ),
                m_type( forward< T >( type ) )
            {}



            const auto& type() const { return m_type; }

            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return true; }

            bool haveSideEffects() const { return false; }

            bool operator<( const Load& rhs ) const;
            friend ostream& operator<<( ostream& out, const Load& ins );

        private:
            LowerableType m_type;
    };
}


#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
#ifndef GOOSE_CIR_OP_LOAD_H
#define GOOSE_CIR_OP_LOAD_H

namespace goose::cir
{
    class Load : public Operation< 1, true >
    {
      public:
        template< typename T >
        Load( T&& type, LocationId loc ) :
            Operation( loc ),
            m_type( forward< T >( type ) )

        {
        }

        const auto& type() const { return m_type; }

        bool canBeExecuted() const { return true; }

        bool canBeEagerlyEvaluated() const { return true; }

        bool haveSideEffects() const { return false; }

        bool operator<( const Load& rhs ) const;
        friend ostream& operator<<( ostream& out, const Load& ins );

      private:
        LowerableType m_type;
    };

} // namespace goose::cir

#endif
Changes to bs/cir/op-logic.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
#ifndef GOOSE_CIR_OP_LOGIC_H
#define GOOSE_CIR_OP_LOGIC_H

namespace goose::cir
{
    class And : public BinaryOp
    {
        public:
            And( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const And& ins )

            {
                return out << "AND";
            }
    };

    class Or : public BinaryOp
    {
        public:
            Or( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Or& ins )

            {
                return out << "OR";
            }
    };

    class Xor : public BinaryOp
    {
        public:
            Xor( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Xor& ins )
            {
                return out << "XOR";
            }
    };

}


#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
#ifndef GOOSE_CIR_OP_LOGIC_H
#define GOOSE_CIR_OP_LOGIC_H

namespace goose::cir
{
    class And : public BinaryOp
    {
      public:
        And( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const And& ins ) { return out << "AND"; }

    };

    class Or : public BinaryOp
    {
      public:
        Or( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const Or& ins ) { return out << "OR"; }

    };

    class Xor : public BinaryOp
    {
      public:
        Xor( LocationId loc ) :
            BinaryOp( loc )

        {



        }

        friend ostream& operator<<( ostream& out, const Xor& ins ) { return out << "XOR"; }
    };
} // namespace goose::cir

#endif
Changes to bs/cir/op-not.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
#ifndef GOOSE_CIR_OP_NOT_H
#define GOOSE_CIR_OP_NOT_H

namespace goose::cir
{
    class Not : public Operation< 1, true >
    {
        public:
            Not( LocationId loc ) :
                Operation( loc )
            {}



            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return true; }

            bool haveSideEffects() const { return false; }

            bool operator<( const Not& rhs ) const
            {
                return false;
            }

            friend ostream& operator<<( ostream& out, const Not& ins )
            {
                return out << "NOT";
            }
    };
}


#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
#ifndef GOOSE_CIR_OP_NOT_H
#define GOOSE_CIR_OP_NOT_H

namespace goose::cir
{
    class Not : public Operation< 1, true >
    {
      public:
        Not( LocationId loc ) :
            Operation( loc )

        {
        }

        bool canBeExecuted() const { return true; }

        bool canBeEagerlyEvaluated() const { return true; }

        bool haveSideEffects() const { return false; }

        bool operator<( const Not& rhs ) const { return false; }




        friend ostream& operator<<( ostream& out, const Not& ins ) { return out << "NOT"; }



    };

} // namespace goose::cir

#endif
Changes to bs/cir/op-phi.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

24

25

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

54

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#ifndef GOOSE_CIR_OP_PHI_H
#define GOOSE_CIR_OP_PHI_H

// TODO: turn this into a bunch of arrays stored directly in basic block,
// It's just unwieldy to have this as an instruction that needs to always come first

namespace goose::cir
{
    class BasicBlock;

    class Phi : public Operation< 0, false >
    {
        public:
            template< typename T >
            Phi( T&& type, uint32_t numIncomings, uint32_t destIndex, LocationId loc ) :
                Operation( loc ),
                m_type( forward< T >( type ) ),
                m_destIndex( destIndex )
            {
                m_incomings.reserve( numIncomings );
            }

            const auto& type() const { return m_type; }

            uint32_t numIncomings() const { return m_incomings.size(); }

            const auto& destIndex() const { return m_destIndex; }

            void setDestIndex( uint32_t index ) { m_destIndex = index; }

            void setIncoming( const ptr< BasicBlock >& bb, const eir::Value& val )
            {
                m_incomings.emplace_back( bb, val );
            }

            template< typename F >
            void forAllIncomings( F&& func ) const
            {
                for( auto&& [bb,val] : m_incomings )
                {
                    if( !func( bb.lock(), val ) )
                        return;
                }
            }

            template< typename F >
            void forAllIncomings( F&& func )
            {
                for( auto& [bb,val] : m_incomings )
                {
                    if( !func( bb, val ) )
                        return;
                }
            }

            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return true; }

            bool haveSideEffects() const { return true; }

            bool operator<( const Phi& rhs ) const
            {
                if( m_type != rhs.m_type )
                    return m_type < rhs.m_type;

                return m_destIndex < rhs.m_destIndex;
            }

            friend ostream& operator<<( ostream& out, const Phi& ins );

        private:
            LowerableType m_type;
            uint32_t m_destIndex = 0;

            using incInfo = pair< wptr< BasicBlock >, eir::Value >;
            llvm::SmallVector< incInfo, 2 > m_incomings;
    };
}

#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
64
65
66
67
68
69
70
71
72
73
74
75
#ifndef GOOSE_CIR_OP_PHI_H
#define GOOSE_CIR_OP_PHI_H

// TODO: turn this into a bunch of arrays stored directly in basic block, it's just unwieldy to have
// this as an instruction that needs to always come first

namespace goose::cir
{
    class BasicBlock;

    class Phi : public Operation< 0, false >
    {
      public:
        template< typename T >
        Phi( T&& type, uint32_t numIncomings, uint32_t destIndex, LocationId loc ) :
            Operation( loc ),
            m_type( forward< T >( type ) ),
            m_destIndex( destIndex )
        {
            m_incomings.reserve( numIncomings );
        }

        const auto& type() const { return m_type; }

        uint32_t numIncomings() const { return m_incomings.size(); }

        const auto& destIndex() const { return m_destIndex; }

        void setDestIndex( uint32_t index ) { m_destIndex = index; }

        void setIncoming( const ptr< BasicBlock >& bb, const eir::Value& val )
        {
            m_incomings.emplace_back( bb, val );
        }

        template< typename F > void forAllIncomings( F&& func ) const

        {
            for( auto&& [bb, val] : m_incomings )

                if( !func( bb.lock(), val ) )
                    return;
        }


        template< typename F > void forAllIncomings( F&& func )

        {
            for( auto& [bb, val] : m_incomings )

                if( !func( bb, val ) )
                    return;
        }


        bool canBeExecuted() const { return true; }

        bool canBeEagerlyEvaluated() const { return true; }

        bool haveSideEffects() const { return true; }

        bool operator<( const Phi& rhs ) const
        {
            if( m_type != rhs.m_type )
                return m_type < rhs.m_type;

            return m_destIndex < rhs.m_destIndex;
        }

        friend ostream& operator<<( ostream& out, const Phi& ins );

      private:
        LowerableType m_type;
        uint32_t m_destIndex = 0;

        using incInfo = pair< wptr< BasicBlock >, eir::Value >;
        llvm::SmallVector< incInfo, 2 > m_incomings;
    };
} // namespace goose::cir

#endif
Changes to bs/cir/op-phoverride.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
#ifndef GOOSE_CIR_OP_PHOVERRIDE_H
#define GOOSE_CIR_OP_PHOVERRIDE_H

namespace goose::cir
{
    // A pseudo instruction that defines an override value for a placeholder
    class PHOverrideSet : public Operation< 1, false >
    {
        public:
            template< typename S >
            PHOverrideSet( S&& name, LocationId loc ) :
                Operation( loc ),
                m_name( forward< S >( name ) )
            {}



            const auto& name() const { return m_name; }

            // This can't be executed, it's only meant to appear
            // in expressions handled by the verifier.
            bool canBeExecuted() const { return false; }

            bool canBeEagerlyEvaluated() const { return false; }

            bool haveSideEffects() const { return false; }

            bool operator<( const PHOverrideSet& rhs ) const;
            friend ostream& operator<<( ostream& out, const PHOverrideSet& ins );

        private:
            StringId    m_name;
    };

    // A pseudo instruction that clears an override value for a placeholder
    class PHOverrideClear : public Operation< 0, 0 >
    {
        public:
            template< typename S >
            PHOverrideClear( S&& name, LocationId loc ) :
                Operation( loc ),
                m_name( forward< S >( name ) )
            {}



            const auto& name() const { return m_name; }

            // This can't be executed, it's only meant to appear
            // in expressions handled by the verifier.
            bool canBeExecuted() const { return false; }

            bool canBeEagerlyEvaluated() const { return false; }

            bool haveSideEffects() const { return false; }

            bool operator<( const PHOverrideClear& rhs ) const;
            friend ostream& operator<<( ostream& out, const PHOverrideClear& ins );

        private:
            StringId    m_name;
    };
}


#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_CIR_OP_PHOVERRIDE_H
#define GOOSE_CIR_OP_PHOVERRIDE_H

namespace goose::cir
{
    // A pseudo instruction that defines an override value for a placeholder
    class PHOverrideSet : public Operation< 1, false >
    {
      public:
        template< typename S >
        PHOverrideSet( S&& name, LocationId loc ) :
            Operation( loc ),
            m_name( forward< S >( name ) )

        {
        }

        const auto& name() const { return m_name; }

        // This can't be executed, it's only meant to appear
        // in expressions handled by the verifier.
        bool canBeExecuted() const { return false; }

        bool canBeEagerlyEvaluated() const { return false; }

        bool haveSideEffects() const { return false; }

        bool operator<( const PHOverrideSet& rhs ) const;
        friend ostream& operator<<( ostream& out, const PHOverrideSet& ins );

      private:
        StringId m_name;
    };

    // A pseudo instruction that clears an override value for a placeholder
    class PHOverrideClear : public Operation< 0, 0 >
    {
      public:
        template< typename S >
        PHOverrideClear( S&& name, LocationId loc ) :
            Operation( loc ),
            m_name( forward< S >( name ) )

        {
        }

        const auto& name() const { return m_name; }

        // This can't be executed, it's only meant to appear
        // in expressions handled by the verifier.
        bool canBeExecuted() const { return false; }

        bool canBeEagerlyEvaluated() const { return false; }

        bool haveSideEffects() const { return false; }

        bool operator<( const PHOverrideClear& rhs ) const;
        friend ostream& operator<<( ostream& out, const PHOverrideClear& ins );

      private:
        StringId m_name;
    };

} // namespace goose::cir

#endif
Changes to bs/cir/op-placeholder.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
#ifndef GOOSE_CIR_OP_PLACEHOLDER_H
#define GOOSE_CIR_OP_PLACEHOLDER_H

namespace goose::cir
{
    // A placeholder value with a name, to be replaced with something else.
    // Intended to represent the @ return value placeholder in functions'
    // post conditions.
    class Placeholder : public Operation< 0, true >
    {
        public:
            template< typename T, typename S >
            Placeholder( T&& type, S&& name, LocationId loc ) :
                Operation( loc ),
                m_type( forward< T >( type ) ),
                m_name( forward< S >( name ) )
            {}



            const auto& type() const { return m_type; }

            const auto& name() const { return m_name; }

            // This can't be executed, it's only meant to appear
            // in expressions handled by the verifier.
            bool canBeExecuted() const { return false; }

            bool canBeEagerlyEvaluated() const { return false; }

            bool haveSideEffects() const { return false; }

            bool operator<( const Placeholder& rhs ) const
            {
                if( m_name != rhs.m_name )
                    return m_name < rhs.m_name;
                return m_type < rhs.m_type;
            }

            friend ostream& operator<<( ostream& out, const Placeholder& ins )
            {
                return out << "PLACEHOLDER(" << ins.m_name << ", " << ins.m_type.get() << ')';
            }

        private:
            LowerableType   m_type;
            StringId    m_name;
    };
}

#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
#ifndef GOOSE_CIR_OP_PLACEHOLDER_H
#define GOOSE_CIR_OP_PLACEHOLDER_H

namespace goose::cir
{
    // A placeholder value with a name, to be replaced with something else.
    // Intended to represent the @ return value placeholder in functions'
    // post conditions.
    class Placeholder : public Operation< 0, true >
    {
      public:
        template< typename T, typename S >
        Placeholder( T&& type, S&& name, LocationId loc ) :
            Operation( loc ),
            m_type( forward< T >( type ) ),
            m_name( forward< S >( name ) )

        {
        }

        const auto& type() const { return m_type; }

        const auto& name() const { return m_name; }

        // This can't be executed, it's only meant to appear in expressions handled by the
        // verifier.
        bool canBeExecuted() const { return false; }

        bool canBeEagerlyEvaluated() const { return false; }

        bool haveSideEffects() const { return false; }

        bool operator<( const Placeholder& rhs ) const
        {
            if( m_name != rhs.m_name )
                return m_name < rhs.m_name;
            return m_type < rhs.m_type;
        }

        friend ostream& operator<<( ostream& out, const Placeholder& ins )
        {
            return out << "PLACEHOLDER(" << ins.m_name << ", " << ins.m_type.get() << ')';
        }

      private:
        LowerableType m_type;
        StringId m_name;
    };
} // namespace goose::cir

#endif
Changes to bs/cir/op-select.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
#ifndef GOOSE_CIR_OP_SELECT_H
#define GOOSE_CIR_OP_SELECT_H

namespace goose::cir
{
    class Select : public Operation< 1, true >
    {
        public:
            Select( uint32_t memberIndex, LocationId loc ) :
                Operation( loc ),
                m_memberIndex( memberIndex )
            {}

            uint32_t memberIndex() const

            {
                return m_memberIndex;
            }

            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return false; }

            bool haveSideEffects() const { return false; }

            bool operator<( const Select& rhs ) const;
            friend ostream& operator<<( ostream& out, const Select& ins );

        private:
            uint32_t m_memberIndex = 0;
    };
}

#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
#ifndef GOOSE_CIR_OP_SELECT_H
#define GOOSE_CIR_OP_SELECT_H

namespace goose::cir
{
    class Select : public Operation< 1, true >
    {
      public:
        Select( uint32_t memberIndex, LocationId loc ) :
            Operation( loc ),
            m_memberIndex( memberIndex )

        {

        }

        uint32_t memberIndex() const { return m_memberIndex; }


        bool canBeExecuted() const { return true; }

        bool canBeEagerlyEvaluated() const { return false; }

        bool haveSideEffects() const { return false; }

        bool operator<( const Select& rhs ) const;
        friend ostream& operator<<( ostream& out, const Select& ins );

      private:
        uint32_t m_memberIndex = 0;
    };
} // namespace goose::cir

#endif
Changes to bs/cir/op-store.h.
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
        public:
            template< typename T >
            Store( T&& type, LocationId srcLocId, LocationId destLocId ) :
                Operation( destLocId ),
                m_type( forward< T >( type ) ),
                m_srcLocId( srcLocId ),
                m_destLocId( destLocId )
            {}



            const auto& type() const { return m_type; }

            const auto& srcLocId() const { return m_srcLocId; }

            const auto& destLocId() const { return m_destLocId; }

            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return true; }
            bool haveSideEffects() const { return true; }

         //   static constexpr size_t OperandCount = 2;
          //  static constexpr bool HaveResult = false;

            bool operator<( const Store& rhs ) const;
            friend ostream& operator<<( ostream& out, const Store& ins );

        private:
            LowerableType m_type;
            LocationId m_srcLocId = 0;
            LocationId m_destLocId = 0;
    };
}

#endif







<
|
>
>

>

>



>

<

<
|









|


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
        public:
            template< typename T >
            Store( T&& type, LocationId srcLocId, LocationId destLocId ) :
                Operation( destLocId ),
                m_type( forward< T >( type ) ),
                m_srcLocId( srcLocId ),
                m_destLocId( destLocId )

            {
            }

            const auto& type() const { return m_type; }

            const auto& srcLocId() const { return m_srcLocId; }

            const auto& destLocId() const { return m_destLocId; }

            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return true; }



            bool haveSideEffects() const { return true; }

            bool operator<( const Store& rhs ) const;
            friend ostream& operator<<( ostream& out, const Store& ins );

        private:
            LowerableType m_type;
            LocationId m_srcLocId = 0;
            LocationId m_destLocId = 0;
    };
} // namespace goose::cir

#endif
Changes to bs/cir/op-stringid.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
#ifndef GOOSE_CIR_OP_STRINGID_H
#define GOOSE_CIR_OP_STRINGID_H

namespace goose::cir
{
    class PushStringId : public Operation< 0, true >
    {
        public:
            PushStringId( util::StringId sid, LocationId loc ) :
                Operation( loc ),
                m_stringId( sid )
            {}



            const auto& stringId() const { return m_stringId; }

            bool canBeExecuted() const { return false; }

            bool canBeEagerlyEvaluated() const { return false; }

            bool haveSideEffects() const { return false; }

            bool operator<( const PushStringId& rhs ) const
            {
                return m_stringId < rhs.m_stringId;
            }

            friend ostream& operator<<( ostream& out, const PushStringId& ins )
            {
                return out << "PUSHSTRINGID(" << ins.m_stringId << ')';
            }

        private:
            util::StringId m_stringId;
    };
}

#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
#ifndef GOOSE_CIR_OP_STRINGID_H
#define GOOSE_CIR_OP_STRINGID_H

namespace goose::cir
{
    class PushStringId : public Operation< 0, true >
    {
      public:
        PushStringId( util::StringId sid, LocationId loc ) :
            Operation( loc ),
            m_stringId( sid )

        {
        }

        const auto& stringId() const { return m_stringId; }

        bool canBeExecuted() const { return false; }

        bool canBeEagerlyEvaluated() const { return false; }

        bool haveSideEffects() const { return false; }

        bool operator<( const PushStringId& rhs ) const { return m_stringId < rhs.m_stringId; }




        friend ostream& operator<<( ostream& out, const PushStringId& ins )
        {
            return out << "PUSHSTRINGID(" << ins.m_stringId << ')';
        }

      private:
        util::StringId m_stringId;
    };
} // namespace goose::cir

#endif
Changes to bs/cir/op-type.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
#ifndef GOOSE_CIR_OP_TYPE_H
#define GOOSE_CIR_OP_TYPE_H

namespace goose::cir
{
    class PushType : public Operation< 0, true >
    {
        public:
            template< typename T >
            PushType( T&& type, LocationId loc ) :
                Operation( loc ),
                m_type( forward< T >( type ) )
            {}



            const auto& type() const { return m_type; }

            bool canBeExecuted() const { return false; }

            bool canBeEagerlyEvaluated() const { return false; }

            bool haveSideEffects() const { return false; }

            bool operator<( const PushType& rhs ) const
            {
                return m_type < rhs.m_type;
            }

            friend ostream& operator<<( ostream& out, const PushType& ins )
            {
                return out << "PUSHTYPE(" << ins.m_type << ')';
            }

        private:
            eir::Term m_type;
    };
}

#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
#ifndef GOOSE_CIR_OP_TYPE_H
#define GOOSE_CIR_OP_TYPE_H

namespace goose::cir
{
    class PushType : public Operation< 0, true >
    {
      public:
        template< typename T >
        PushType( T&& type, LocationId loc ) :
            Operation( loc ),
            m_type( forward< T >( type ) )

        {
        }

        const auto& type() const { return m_type; }

        bool canBeExecuted() const { return false; }

        bool canBeEagerlyEvaluated() const { return false; }

        bool haveSideEffects() const { return false; }

        bool operator<( const PushType& rhs ) const { return m_type < rhs.m_type; }




        friend ostream& operator<<( ostream& out, const PushType& ins )
        {
            return out << "PUSHTYPE(" << ins.m_type << ')';
        }

      private:
        eir::Term m_type;
    };
} // namespace goose::cir

#endif
Changes to bs/cir/op-verification.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
#ifndef GOOSE_CIR_OP_VERIFICATION_H
#define GOOSE_CIR_OP_VERIFICATION_H

// Misc verification-only instructions

namespace goose::cir
{
    // Logical implication
    class Implies : public BinaryOp
    {
        public:
            Implies( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Implies& ins )

            {
                return out << "IMPLIES";
            }
    };

    // Tests if a sequence is a prefix of another
    class IsPrefixOf : public BinaryOp
    {
        public:
            IsPrefixOf( LocationId loc ) :
                BinaryOp( loc )
            {}



            friend ostream& operator<<( ostream& out, const IsPrefixOf& ins )
            {
                return out << "ISPREFIXOF";
            }
    };
}


#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
#ifndef GOOSE_CIR_OP_VERIFICATION_H
#define GOOSE_CIR_OP_VERIFICATION_H

// Misc verification-only instructions

namespace goose::cir
{
    // Logical implication
    class Implies : public BinaryOp
    {
      public:
        Implies( LocationId loc ) :
            BinaryOp( loc )

        {

        }

        friend ostream& operator<<( ostream& out, const Implies& ins ) { return out << "IMPLIES"; }

    };

    // Tests if a sequence is a prefix of another
    class IsPrefixOf : public BinaryOp
    {
      public:
        IsPrefixOf( LocationId loc ) :
            BinaryOp( loc )

        {
        }

        friend ostream& operator<<( ostream& out, const IsPrefixOf& ins )
        {
            return out << "ISPREFIXOF";
        }
    };

} // namespace goose::cir

#endif
Changes to bs/cir/operation.h.
1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101


102

103
104
105
106
107
108
109
110
111
112
113
114
115
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
#ifndef GOOSE_CIR_OPERATION_H
#define GOOSE_CIR_OPERATION_H

namespace goose::cir
{
    template< bool enable >
    struct OpResult
    {
        uint32_t index = ~0;
    };

    template<>
    struct OpResult< false >

    {};

    template< size_t operandCount, bool haveResult >
    class Operation
    {
        public:
            Operation( LocationId loc ) :
                m_loc( loc )
            {
                fill( m_operandIndices.begin(), m_operandIndices.end(), 0 );
            }

            static constexpr size_t OperandCount = operandCount;
            static constexpr bool HaveResult = haveResult;

            uint32_t operandIndex( uint32_t operand ) const
            {
                assert( operand < operandCount );
                return m_operandIndices[operand];
            }

            void setOperandIndex( uint32_t operand, uint32_t index )
            {
                assert( operand < operandCount );
                m_operandIndices[operand] = index;
            }

            const auto& operandIndices() const { return m_operandIndices; }

            auto& operandIndices() { return m_operandIndices; }

            int32_t resultIndex() const
            {
                if constexpr( haveResult )
                {
                    return m_result.index;
                }
                else
                {
                    assert( false );
                    return 0;
                }
            }

            void setResultIndex( uint32_t index )
            {
                if constexpr( haveResult )
                {
                    m_result.index = index;
                }
                else
                {
                    assert( false );
                }
            }

            void setLocation( LocationId loc ) { m_loc = loc; }

            auto locationId() const { return m_loc; }

            void printOperands( ostream& out ) const
            {
                if constexpr( operandCount > 0 )
                {
                    out << format( " ({:n})", m_operandIndices );
                }

                if constexpr( haveResult )
                {
                    out << " -> " << m_result.index;
                }
            }

        private:
            LocationId m_loc;
            array< uint32_t, operandCount > m_operandIndices;
            OpResult< haveResult > m_result;
    };

    template< size_t minOperandCount, bool haveResult >
    class VariadicOperation : public Operation< minOperandCount, haveResult >
    {
        using super = Operation< minOperandCount, haveResult >;

        public:
            VariadicOperation( uint32_t extraOperandsCount, LocationId loc ) :
                super( loc ),
                m_extraOperands( extraOperandsCount, 0 )
            {}



            const auto& extraOperandIndices() const { return m_extraOperands; }

            auto& extraOperandIndices() { return m_extraOperands; }

            uint32_t operandIndex( uint32_t operand ) const
            {
                if( operand < minOperandCount )
                    return super::operandIndex( operand );
                operand -= minOperandCount;
                assert( operand < m_extraOperands.size() );
                return m_extraOperands[operand];
            }

            void setOperandIndex( uint32_t operand, uint32_t index )
            {
                if( operand < minOperandCount )
                    super::setOperandIndex( operand, index );
                operand -= minOperandCount;
                assert( operand < m_extraOperands.size() );
                m_extraOperands[operand] = index;
            }

            void printOperands( ostream& out ) const
            {
                if constexpr( minOperandCount > 0 )
                {

                    out << format( "({:n} {:n})", super::operandIndices(), views::all( m_extraOperands ) );
                }
                else
                {
                    out << format( "({:n})", views::all( m_extraOperands ) );
                }

                if constexpr( haveResult )
                {
                    out << " -> " << super::resultIndex();
                }
            }

        private:
            // TODO replace with llvm smallvector once we pull llvm in
            vector< uint32_t > m_extraOperands;
    };
}


#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

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_CIR_OPERATION_H
#define GOOSE_CIR_OPERATION_H

namespace goose::cir
{
    template< bool enable > struct OpResult

    {
        uint32_t index = ~0;
    };


    template<> struct OpResult< false >
    {
    };

    template< size_t operandCount, bool haveResult > class Operation

    {
      public:
        Operation( LocationId loc ) :
            m_loc( loc )
        {
            fill( m_operandIndices.begin(), m_operandIndices.end(), 0 );
        }

        static constexpr size_t OperandCount = operandCount;
        static constexpr bool HaveResult = haveResult;

        uint32_t operandIndex( uint32_t operand ) const
        {
            assert( operand < operandCount );
            return m_operandIndices[operand];
        }

        void setOperandIndex( uint32_t operand, uint32_t index )
        {
            assert( operand < operandCount );
            m_operandIndices[operand] = index;
        }

        const auto& operandIndices() const { return m_operandIndices; }

        auto& operandIndices() { return m_operandIndices; }

        int32_t resultIndex() const
        {
            if constexpr( haveResult )
            {
                return m_result.index;
            }
            else
            {
                assert( false );
                return 0;
            }
        }

        void setResultIndex( uint32_t index )
        {
            if constexpr( haveResult )

                m_result.index = index;

            else

                assert( false );
        }


        void setLocation( LocationId loc ) { m_loc = loc; }

        auto locationId() const { return m_loc; }

        void printOperands( ostream& out ) const
        {
            if constexpr( operandCount > 0 )

                out << format( " ({:n})", m_operandIndices );


            if constexpr( haveResult )

                out << " -> " << m_result.index;
        }


      private:
        LocationId m_loc;
        array< uint32_t, operandCount > m_operandIndices;
        OpResult< haveResult > m_result;
    };

    template< size_t minOperandCount, bool haveResult >
    class VariadicOperation : public Operation< minOperandCount, haveResult >
    {
        using super = Operation< minOperandCount, haveResult >;

      public:
        VariadicOperation( uint32_t extraOperandsCount, LocationId loc ) :
            super( loc ),
            m_extraOperands( extraOperandsCount, 0 )

        {
        }

        const auto& extraOperandIndices() const { return m_extraOperands; }

        auto& extraOperandIndices() { return m_extraOperands; }

        uint32_t operandIndex( uint32_t operand ) const
        {
            if( operand < minOperandCount )
                return super::operandIndex( operand );
            operand -= minOperandCount;
            assert( operand < m_extraOperands.size() );
            return m_extraOperands[operand];
        }

        void setOperandIndex( uint32_t operand, uint32_t index )
        {
            if( operand < minOperandCount )
                super::setOperandIndex( operand, index );
            operand -= minOperandCount;
            assert( operand < m_extraOperands.size() );
            m_extraOperands[operand] = index;
        }

        void printOperands( ostream& out ) const
        {
            if constexpr( minOperandCount > 0 )
            {
                out << format(
                    "({:n} {:n})", super::operandIndices(), views::all( m_extraOperands ) );
            }
            else
            {
                out << format( "({:n})", views::all( m_extraOperands ) );
            }

            if constexpr( haveResult )

                out << " -> " << super::resultIndex();
        }


      private:
        // TODO replace with llvm smallvector once we pull llvm in
        vector< uint32_t > m_extraOperands;
    };

} // namespace goose::cir

#endif
Deleted bs/cir/phi.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#ifndef GOOSE_CIR_PHI_H
#define GOOSE_CIR_PHI_H

namespace goose::cir
{
    class BasicBlock;

    class Phi : public BaseInstr< 0, false >
    {
        public:
            template< typename T >
            Phi( T&& type, uint32_t numIncomings, uint32_t destIndex, LocationId loc ) :
                BaseInstr( loc ),
                m_type( forward< T >( type ) ),
                m_destIndex( destIndex )
            {
                m_incomings.reserve( numIncomings );
            }

            const auto& type() const { return m_type; }
            uint32_t numIncomings() const { return m_incomings.size(); }
            const auto& destIndex() const { return m_destIndex; }
            void setDestIndex( uint32_t index ) { m_destIndex = index; }

            void setIncoming( const ptr< BasicBlock >& bb, const eir::Value& val )
            {
                m_incomings.emplace_back( bb, val );
            }

            template< typename F >
            void forAllIncomings( F&& func ) const
            {
                for( auto&& [bb,val] : m_incomings )
                {
                    if( !func( bb.lock(), val ) )
                        return;
                }
            }

            template< typename F >
            void forAllIncomings( F&& func )
            {
                for( auto& [bb,val] : m_incomings )
                {
                    if( !func( bb, val ) )
                        return;
                }
            }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }
            bool haveSideEffects() const { return true; }

            bool operator<( const Phi& rhs ) const
            {
                if( m_type != rhs.m_type )
                    return m_type < rhs.m_type;

                return m_destIndex < rhs.m_destIndex;
            }

            friend ostream& operator<<( ostream& out, const Phi& ins );

        private:
            LowerableType m_type;
            uint32_t m_destIndex = 0;

            using incInfo = pair< wptr< BasicBlock >, eir::Value >;
            llvm::SmallVector< incInfo, 2 > m_incomings;
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































































Deleted bs/cir/phoverride.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
#ifndef GOOSE_CIR_PHOVERRIDE_H
#define GOOSE_CIR_PHOVERRIDE_H

namespace goose::cir
{
    // A pseudo instruction that defines an override value for a placeholder
    class PHOverrideSet : public BaseInstr< 1, false >
    {
        public:
            template< typename S >
            PHOverrideSet( S&& name, LocationId loc ) :
                BaseInstr( loc ),
                m_name( forward< S >( name ) )
            {}

            const auto& name() const { return m_name; }

            // This can't be executed, it's only meant to appear
            // in expressions handled by the verifier.
            bool canBeExecuted() const { return false; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return false; }

            bool operator<( const PHOverrideSet& rhs ) const;
            friend ostream& operator<<( ostream& out, const PHOverrideSet& ins );

        private:
            StringId    m_name;
    };

    // A pseudo instruction that clears an override value for a placeholder
    class PHOverrideClear : public BaseInstr< 0, 0 >
    {
        public:
            template< typename S >
            PHOverrideClear( S&& name, LocationId loc ) :
                BaseInstr( loc ),
                m_name( forward< S >( name ) )
            {}

            const auto& name() const { return m_name; }

            // This can't be executed, it's only meant to appear
            // in expressions handled by the verifier.
            bool canBeExecuted() const { return false; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return false; }

            bool operator<( const PHOverrideClear& rhs ) const;
            friend ostream& operator<<( ostream& out, const PHOverrideClear& ins );

        private:
            StringId    m_name;
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















































































































Deleted bs/cir/placeholder.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
#ifndef GOOSE_CIR_PLACEHOLDER_H
#define GOOSE_CIR_PLACEHOLDER_H

namespace goose::cir
{
    // A placeholder value with a name, to be replaced with something else.
    // Intended to represent the @ return value placeholder in functions'
    // post conditions.
    class Placeholder : public BaseInstr< 0, true >
    {
        public:
            template< typename T, typename S >
            Placeholder( T&& type, S&& name, LocationId loc ) :
                BaseInstr( loc ),
                m_type( forward< T >( type ) ),
                m_name( forward< S >( name ) )
            {}

            const auto& type() const { return m_type; }
            const auto& name() const { return m_name; }

            // This can't be executed, it's only meant to appear
            // in expressions handled by the verifier.
            bool canBeExecuted() const { return false; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return false; }

            bool operator<( const Placeholder& rhs ) const
            {
                if( m_name != rhs.m_name )
                    return m_name < rhs.m_name;
                return m_type < rhs.m_type;
            }

            friend ostream& operator<<( ostream& out, const Placeholder& ins )
            {
                return out << "PLACEHOLDER(" << ins.m_name << ", " << ins.m_type.get() << ')';
            }

        private:
            LowerableType   m_type;
            StringId    m_name;
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




























































































Changes to bs/cir/reindexvars.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
#include "cir.h"
#include "builtins/builtins.h"

// TODO: we probably won't need this anymore

namespace goose::cir
{
    // While we generate CIR instructions, we use a simple global unique id for each different var and temporary.
    // This pass renumbers them and counts them, so that CIR consumers can simply store them in arrays.
    template< typename F >
    void ReindexVarsInValue( Value& v, F& getOrCreateIndex )
    {
        if( !v.isConstant() )
        {
            ReindexVars( *v.cir(), getOrCreateIndex );
            return;
        }

        if( builtins::IsTuple( v ) )
        {
            builtins::ForEachInTuple( v, [&]( auto&& v )

            {
                ReindexVarsInValue( v, getOrCreateIndex );
                return true;
            } );
        }
    }

    template< typename F >
    void ReindexVars( InstrSeq& is, F& getOrCreateIndex )
    {
        for( auto& x : is )
        {

            visit( [&]< typename T >( T& instr )
            {
                if constexpr( is_same_v< T, Constant > )
                {
                    // Constants can be tuples, and constant tuples can contain computed values,
                    // so we need to recurse into these.
                    ReindexVarsInValue( instr.value().modify(), getOrCreateIndex );
                }
                else if constexpr( is_same_v< T, AllocVar > )
                {
                    instr.setIndex( getOrCreateIndex( instr.index() ) );
                }
                else if constexpr( is_same_v< T, Phi > )
                {
                    instr.setDestIndex( getOrCreateIndex( instr.destIndex() ) );

                    instr.forAllIncomings( [&]( auto&&, auto&& val )

                    {
                        if( val.isConstant() )
                            return true;

                        ReindexVars( *val.cir(), getOrCreateIndex );
                        return true;
                    } );
                }

            }, x );
        }
    }

    void ReindexVars( const ptr< CFG >& cfg )
    {
        unordered_map< uint32_t, uint32_t > uidToIndex;
        uint32_t count = cfg->temporariesCount();

        auto getOrCreateIndex = [&]( uint32_t uid )
        {
            // The indices of function parameters are directly set as indices,
            // not UIDs, and we don't want to modify them as they are assumed to
            // start at 0 in the param order.
            if( !IsUid( uid ) )
                return uid;

            auto it = uidToIndex.find( uid );
            if( it == uidToIndex.end() )
            {
                auto&& [newIt,_] = uidToIndex.emplace( uid, count++ );
                it = newIt;
            }

            return it->second;
        };

        cfg->forEachBB( [&]( auto&& bb )

        {
            ReindexVars( bb->instructions(), getOrCreateIndex );
            bb->dirty();
        } );

        cfg->setTemporariesCount( count );
    }

    void ReindexVars( InstrSeq& is )
    {
        unordered_map< uint32_t, uint32_t > uidToIndex;
        uint32_t count = 0;

        auto getOrCreateIndex = [&]( uint32_t uid )
        {
            // The indices of function parameters are directly set as indices,
            // not UIDs, and we don't want to modify them as they are assumed to
            // start at 0 in the param order.
            if( !IsUid( uid ) )
                return uid;

            auto it = uidToIndex.find( uid );
            if( it == uidToIndex.end() )
            {
                auto&& [newIt,_] = uidToIndex.emplace( uid, count++ );
                it = newIt;
            }

            return it->second;
        };

        ReindexVars( is, getOrCreateIndex );
    }
}







|
|
|
|









|
>
|
|
|
|



<
|



>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
>
|
|
|

|
|
|
|
>
|



















|






|
>
|
|
|
|




















|








|
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 "cir.h"
#include "builtins/builtins.h"

// TODO: we probably won't need this anymore

namespace goose::cir
{
    // While we generate CIR instructions, we use a simple global unique id for each different var
    // and temporary. This pass renumbers them and counts them, so that CIR consumers can simply
    // store them in arrays.
    template< typename F > void ReindexVarsInValue( Value& v, F& getOrCreateIndex )
    {
        if( !v.isConstant() )
        {
            ReindexVars( *v.cir(), getOrCreateIndex );
            return;
        }

        if( builtins::IsTuple( v ) )
        {
            builtins::ForEachInTuple( v,
                [&]( auto&& v )
                {
                    ReindexVarsInValue( v, getOrCreateIndex );
                    return true;
                } );
        }
    }


    template< typename F > void ReindexVars( InstrSeq& is, F& getOrCreateIndex )
    {
        for( auto& x : is )
        {
            visit(
                [&]< typename T >( T& instr )
                {
                    if constexpr( is_same_v< T, Constant > )
                    {
                        // Constants can be tuples, and constant tuples can contain computed values,
                        // so we need to recurse into these.
                        ReindexVarsInValue( instr.value().modify(), getOrCreateIndex );
                    }
                    else if constexpr( is_same_v< T, AllocVar > )
                    {
                        instr.setIndex( getOrCreateIndex( instr.index() ) );
                    }
                    else if constexpr( is_same_v< T, Phi > )
                    {
                        instr.setDestIndex( getOrCreateIndex( instr.destIndex() ) );

                        instr.forAllIncomings(
                            [&]( auto&&, auto&& val )
                            {
                                if( val.isConstant() )
                                    return true;

                                ReindexVars( *val.cir(), getOrCreateIndex );
                                return true;
                            } );
                    }
                },
                x );
        }
    }

    void ReindexVars( const ptr< CFG >& cfg )
    {
        unordered_map< uint32_t, uint32_t > uidToIndex;
        uint32_t count = cfg->temporariesCount();

        auto getOrCreateIndex = [&]( uint32_t uid )
        {
            // The indices of function parameters are directly set as indices,
            // not UIDs, and we don't want to modify them as they are assumed to
            // start at 0 in the param order.
            if( !IsUid( uid ) )
                return uid;

            auto it = uidToIndex.find( uid );
            if( it == uidToIndex.end() )
            {
                auto&& [newIt, _] = uidToIndex.emplace( uid, count++ );
                it = newIt;
            }

            return it->second;
        };

        cfg->forEachBB(
            [&]( auto&& bb )
            {
                ReindexVars( bb->instructions(), getOrCreateIndex );
                bb->dirty();
            } );

        cfg->setTemporariesCount( count );
    }

    void ReindexVars( InstrSeq& is )
    {
        unordered_map< uint32_t, uint32_t > uidToIndex;
        uint32_t count = 0;

        auto getOrCreateIndex = [&]( uint32_t uid )
        {
            // The indices of function parameters are directly set as indices,
            // not UIDs, and we don't want to modify them as they are assumed to
            // start at 0 in the param order.
            if( !IsUid( uid ) )
                return uid;

            auto it = uidToIndex.find( uid );
            if( it == uidToIndex.end() )
            {
                auto&& [newIt, _] = uidToIndex.emplace( uid, count++ );
                it = newIt;
            }

            return it->second;
        };

        ReindexVars( is, getOrCreateIndex );
    }
} // namespace goose::cir
Changes to bs/cir/ret.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_CIR_RET_H
#define GOOSE_CIR_RET_H

namespace goose::cir
{
    class RetVoid
    {
        public:
            RetVoid( LocationId loc ) :
                m_loc( loc )
            {}



            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return true; }

            void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex )  {}

            const auto& locationId() const { return m_loc; }

            friend ostream& operator<<( ostream& out, const RetVoid& ter );

        private:
            LocationId m_loc;
    };

    class Ret
    {
        public:
            Ret( LocationId loc ) :
                m_loc( loc )
            {}



            bool canBeExecuted() const { return true; }

            bool canBeEagerlyEvaluated() const { return true; }

            void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex )  {}

            const auto& locationId() const { return m_loc; }

            friend ostream& operator<<( ostream& out, const Ret& ter );

        private:
            LocationId m_loc;
    };
}


#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
#ifndef GOOSE_CIR_RET_H
#define GOOSE_CIR_RET_H

namespace goose::cir
{
    class RetVoid
    {
      public:
        RetVoid( LocationId loc ) :
            m_loc( loc )

        {
        }

        bool canBeExecuted() const { return true; }

        bool canBeEagerlyEvaluated() const { return true; }

        void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex ) {}

        const auto& locationId() const { return m_loc; }

        friend ostream& operator<<( ostream& out, const RetVoid& ter );

      private:
        LocationId m_loc;
    };

    class Ret
    {
      public:
        Ret( LocationId loc ) :
            m_loc( loc )

        {
        }

        bool canBeExecuted() const { return true; }

        bool canBeEagerlyEvaluated() const { return true; }

        void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex ) {}

        const auto& locationId() const { return m_loc; }

        friend ostream& operator<<( ostream& out, const Ret& ter );

      private:
        LocationId m_loc;
    };

} // namespace goose::cir

#endif
Deleted bs/cir/select.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
#ifndef GOOSE_CIR_SELECT_H
#define GOOSE_CIR_SELECT_H

namespace goose::cir
{
    class Select : public BaseInstr< 1, true >
    {
        public:
            Select( uint32_t memberIndex, LocationId loc ) :
                BaseInstr( loc ),
                m_memberIndex( memberIndex )
            {}

            uint32_t memberIndex() const
            {
                return m_memberIndex;
            }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return false; }

            bool operator<( const Select& rhs ) const;
            friend ostream& operator<<( ostream& out, const Select& ins );

        private:
            uint32_t m_memberIndex = 0;
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






























































Changes to bs/cir/seqbuilder.cpp.
71
72
73
74
75
76
77
78
79
80
81
82
83

    }

    void SeqBuilder::pushResult()
    {
        if( m_instrSeq.empty() )
            return;

        visit( [&]( const auto& op )
        {
            pushResult( op );
        }, m_instrSeq.back() );
    }
}








|
<
<
<

<
>
71
72
73
74
75
76
77
78



79

80
    }

    void SeqBuilder::pushResult()
    {
        if( m_instrSeq.empty() )
            return;

        visit( [&]( const auto& op ) { pushResult( op ); }, m_instrSeq.back() );



    }

} // namespace goose::cir
Changes to bs/cir/seqbuilder.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

64
65
66
67
68
69
70
71
72
73
74
75
76

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

102
103
#ifndef GOOSE_CIR_SEQBUILDER_H
#define GOOSE_CIR_SEQBUILDER_H

namespace goose::cir
{
    extern void AppendInstrSeq( InstrSeq& is, InstrSeq&& isToAppend );
    extern void AppendInstrSeq( InstrSeq& is, const InstrSeq& isToAppend );

    extern void AppendValue( InstrSeq& is, Value&& v );
    extern void AppendValue( InstrSeq& is, const Value& v );

    template< typename I >
    void AppendToInstrSeq( InstrSeq& is, I&& instr )
    {
        using II = remove_cvref_t< I >;

        if constexpr( is_same_v< II, InstrSeq > )
        {
            AppendInstrSeq( is, forward< I >( instr ) );
        }
        else if constexpr( is_same_v< II, Value > )
        {
            AppendValue( is, forward< I >( instr ) );
        }
        else
        {
            is.emplace_back( Instruction( forward< I >( instr ) ) );
        }
    }

    static inline void AppendToInstrSeq( InstrSeq& is, const ptr< InstrSeq >& instr )
    {
        AppendToInstrSeq( is, *instr );
    }

    static inline void AppendToInstrSeq( InstrSeq& is, ptr< InstrSeq >&& instr )
    {
        AppendToInstrSeq( is, move( *instr ) );
    }

    template< typename HI, typename... TI >
    void AppendToInstrSeq( InstrSeq& is, HI&& headInstr, TI&&... tailInstrs )
    {
        AppendToInstrSeq( is, forward< HI >( headInstr ) );
        AppendToInstrSeq( is, forward< TI >( tailInstrs )... );
    }

    template< typename T, typename... I >
    auto BuildComputedValue( T&& type, I&&... instrs )
    {
        auto is = make_shared< InstrSeq >();
        AppendToInstrSeq( *is, forward< I >( instrs )... );

        return eir::Value( forward< T >( type ), move( is ) );
    }

    class SeqBuilder
    {
        public:
            SeqBuilder( const ptr< CFG >& cfg ) :
                m_cfg( cfg )
            {}


            template< typename O >
            void append( O&& op );

            template< typename HI, typename... TI >
            void append( HI&& headInstr, TI&&... tailInstrs );

            void append( uint32_t index );
            void append( const InstrSeq& is );
            void append( InstrSeq&& is );
            void append( Value&& v );
            void append( const Value& v );

            bool isComplete() const { return m_stack.size() <= 1; }

            bool haveResult() const { return m_stack.size() == 1; }

            uint32_t getResultIndex() const
            {
                return m_stack.top();
            }

            auto&& grabInstrSeq() &&
            {
                stack< uint32_t > gg;
                swap( gg, m_stack );
                return move( m_instrSeq );
            }

        private:
            void pushResult();

            template< typename O >
            void pushResult( const O& op );

            ptr< CFG > m_cfg;
            InstrSeq m_instrSeq;
            stack< uint32_t > m_stack;
    };
}


#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
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
#ifndef GOOSE_CIR_SEQBUILDER_H
#define GOOSE_CIR_SEQBUILDER_H

namespace goose::cir
{
    extern void AppendInstrSeq( InstrSeq& is, InstrSeq&& isToAppend );
    extern void AppendInstrSeq( InstrSeq& is, const InstrSeq& isToAppend );

    extern void AppendValue( InstrSeq& is, Value&& v );
    extern void AppendValue( InstrSeq& is, const Value& v );


    template< typename I > void AppendToInstrSeq( InstrSeq& is, I&& instr )
    {
        using II = remove_cvref_t< I >;

        if constexpr( is_same_v< II, InstrSeq > )

            AppendInstrSeq( is, forward< I >( instr ) );

        else if constexpr( is_same_v< II, Value > )

            AppendValue( is, forward< I >( instr ) );

        else

            is.emplace_back( Instruction( forward< I >( instr ) ) );

    }

    static inline void AppendToInstrSeq( InstrSeq& is, const ptr< InstrSeq >& instr )
    {
        AppendToInstrSeq( is, *instr );
    }

    static inline void AppendToInstrSeq( InstrSeq& is, ptr< InstrSeq >&& instr )
    {
        AppendToInstrSeq( is, move( *instr ) );
    }

    template< typename HI, typename... TI >
    void AppendToInstrSeq( InstrSeq& is, HI&& headInstr, TI&&... tailInstrs )
    {
        AppendToInstrSeq( is, forward< HI >( headInstr ) );
        AppendToInstrSeq( is, forward< TI >( tailInstrs )... );
    }

    template< typename T, typename... I > auto BuildComputedValue( T&& type, I&&... instrs )

    {
        auto is = make_shared< InstrSeq >();
        AppendToInstrSeq( *is, forward< I >( instrs )... );

        return eir::Value( forward< T >( type ), move( is ) );
    }

    class SeqBuilder
    {
      public:
        SeqBuilder( const ptr< CFG >& cfg ) :
            m_cfg( cfg )

        {
        }

        template< typename O > void append( O&& op );

        template< typename HI, typename... TI > void append( HI&& headInstr, TI&&... tailInstrs );


        void append( uint32_t index );
        void append( const InstrSeq& is );
        void append( InstrSeq&& is );
        void append( Value&& v );
        void append( const Value& v );

        bool isComplete() const { return m_stack.size() <= 1; }

        bool haveResult() const { return m_stack.size() == 1; }

        uint32_t getResultIndex() const { return m_stack.top(); }




        auto&& grabInstrSeq() &&
        {
            stack< uint32_t > gg;
            swap( gg, m_stack );
            return move( m_instrSeq );
        }

      private:
        void pushResult();

        template< typename O > void pushResult( const O& op );


        ptr< CFG > m_cfg;
        InstrSeq m_instrSeq;
        stack< uint32_t > m_stack;
    };

} // namespace goose::cir

#endif
Changes to bs/cir/seqbuilder.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
#ifndef GOOSE_CIR_SEQBUILDER_INL
#define GOOSE_CIR_SEQBUILDER_INL

namespace goose::cir
{
    template< typename O >
    void SeqBuilder::append( O&& op )
    {
        using T = remove_cvref_t< O >;
        if constexpr( is_same_v< Value, T > )
        {
            visit( [&]< typename PT >( PT&& payload )
            {
                append( forward< PT >( payload ) );
            }, op.payload() );
        }
        else
        {
            for( auto& index : op.operandIndices() | views::reverse )
            {
                assert( !m_stack.empty() );
                index = m_stack.top();
                m_stack.pop();
            }

            if constexpr( requires{ op.extraOperandIndices(); } )
            {
                for( auto& index : op.extraOperandIndices() | views::reverse )
                {
                    assert( !m_stack.empty() );
                    index = m_stack.top();
                    m_stack.pop();
                }





|
<




|
<
<
|










|







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
#ifndef GOOSE_CIR_SEQBUILDER_INL
#define GOOSE_CIR_SEQBUILDER_INL

namespace goose::cir
{
    template< typename O > void SeqBuilder::append( O&& op )

    {
        using T = remove_cvref_t< O >;
        if constexpr( is_same_v< Value, T > )
        {
            visit( [&]< typename PT >( PT&& payload ) { append( forward< PT >( payload ) ); },


                op.payload() );
        }
        else
        {
            for( auto& index : op.operandIndices() | views::reverse )
            {
                assert( !m_stack.empty() );
                index = m_stack.top();
                m_stack.pop();
            }

            if constexpr( requires { op.extraOperandIndices(); } )
            {
                for( auto& index : op.extraOperandIndices() | views::reverse )
                {
                    assert( !m_stack.empty() );
                    index = m_stack.top();
                    m_stack.pop();
                }
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
    template< typename HI, typename... TI >
    void SeqBuilder::append( HI&& headInstr, TI&&... tailInstrs )
    {
        append( forward< HI >( headInstr ) );
        append( forward< TI >( tailInstrs )... );
    }

    template< typename O >
    void SeqBuilder::pushResult( const O& op )
    {
        if constexpr( O::HaveResult )
        {
            auto rindex = op.resultIndex();
            if( rindex != ~0 )
                m_stack.push( op.resultIndex() );
        }
    }
}

#endif







<
|








|


44
45
46
47
48
49
50

51
52
53
54
55
56
57
58
59
60
61
62
    template< typename HI, typename... TI >
    void SeqBuilder::append( HI&& headInstr, TI&&... tailInstrs )
    {
        append( forward< HI >( headInstr ) );
        append( forward< TI >( tailInstrs )... );
    }


    template< typename O > void SeqBuilder::pushResult( const O& op )
    {
        if constexpr( O::HaveResult )
        {
            auto rindex = op.resultIndex();
            if( rindex != ~0 )
                m_stack.push( op.resultIndex() );
        }
    }
} // namespace goose::cir

#endif
Deleted bs/cir/store.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
#ifndef GOOSE_CIR_STORE_H
#define GOOSE_CIR_STORE_H

namespace goose::cir
{
    class Store
    {
        public:
            template< typename T >
            Store( T&& type, LocationId srcLocId, LocationId destLocId ) :
                m_type( forward< T >( type ) ),
                m_srcLocId( srcLocId ),
                m_destLocId( destLocId )
            {}

            const auto& type() const { return m_type; }
            const auto& srcLocId() const { return m_srcLocId; }
            const auto& destLocId() const { return m_destLocId; }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }
            bool haveSideEffects() const { return true; }

            static constexpr size_t PopCount = 2;
            static constexpr bool PushesResult = false;

            bool operator<( const Store& rhs ) const;
            friend ostream& operator<<( ostream& out, const Store& ins );

        private:
            LowerableType m_type;
            LocationId m_srcLocId = 0;
            LocationId m_destLocId = 0;
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<










































































Deleted bs/cir/stringid.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
#ifndef GOOSE_CIR_STRINGID_H
#define GOOSE_CIR_STRINGID_H

namespace goose::cir
{
    class PushStringId : public BaseInstr< 0, true >
    {
        public:
            PushStringId( util::StringId sid, LocationId loc ) :
                BaseInstr( loc ),
                m_stringId( sid )
            {}

            const auto& stringId() const { return m_stringId; }

            bool canBeExecuted() const { return false; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return false; }

            bool operator<( const PushStringId& rhs ) const
            {
                return m_stringId < rhs.m_stringId;
            }

            friend ostream& operator<<( ostream& out, const PushStringId& ins )
            {
                return out << "PUSHSTRINGID(" << ins.m_stringId << ')';
            }

        private:
            util::StringId m_stringId;
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






































































Changes to bs/cir/terminator.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
#include "cir/cir.h"

using namespace goose;
using namespace goose::cir;

bool Terminator::canBeExecuted() const
{
    return visit( []( auto&& e )
    {
        return e.canBeExecuted();
    }, m_content );
}

bool Terminator::canBeEagerlyEvaluated() const
{
    return visit( []( auto&& e )
    {
        return e.canBeEagerlyEvaluated();
    }, m_content );
}

void Terminator::addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex )
{
    visit( [&]( auto&& e )
    {
        e.addCFGEdges( cfg, srcBBIndex );
    }, m_content );
}

void Branch::addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex )
{
    assert( m_dest.lock() );
    cfg->addEdge( srcBBIndex, m_dest.lock()->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
#include "cir/cir.h"

using namespace goose;
using namespace goose::cir;

bool Terminator::canBeExecuted() const
{
    return visit( []( auto&& e ) { return e.canBeExecuted(); }, m_content );



}

bool Terminator::canBeEagerlyEvaluated() const
{
    return visit( []( auto&& e ) { return e.canBeEagerlyEvaluated(); }, m_content );



}

void Terminator::addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex )
{
    visit( [&]( auto&& e ) { e.addCFGEdges( cfg, srcBBIndex ); }, m_content );



}

void Branch::addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex )
{
    assert( m_dest.lock() );
    cfg->addEdge( srcBBIndex, m_dest.lock()->index() );

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
void GhostBranch::addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex )
{
    assert( m_ghostCode.lock() );
    assert( m_continuation.lock() );
    cfg->addEdge( srcBBIndex, m_ghostCode.lock()->index() );
    cfg->addEdge( srcBBIndex, m_continuation.lock()->index() );

    // HACK: we don't include the edge going directly to the continuation
    // block because the verifier needs to ignore that edge, and no one else
    // save the loop identification code uses those back edges. And when
    // it comes to loops, using a GhostBranch as a looping instruction makes
    // no sense anyway. I'll do something better when it inevitably bites me in
    // the ass some day because I need the back edges for something else
    m_ghostCode.lock()->addBackEdge( srcBBIndex );
}

namespace goose::cir
{
    ostream& operator<<( ostream& out, const Terminator& ter )
    {
        return visit( [&]< typename ET >( const ET& e ) -> ostream&
        {
            return out << e;
        }, ter.content() );
    }

    ostream& operator<<( ostream& out, const RetVoid& ter )
    {
        return out << "RETVOID";
    }








|
<
|
|
|
|







<
<
|
|







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
void GhostBranch::addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex )
{
    assert( m_ghostCode.lock() );
    assert( m_continuation.lock() );
    cfg->addEdge( srcBBIndex, m_ghostCode.lock()->index() );
    cfg->addEdge( srcBBIndex, m_continuation.lock()->index() );

    // HACK: we don't include the edge going directly to the continuation block because the verifier

    // needs to ignore that edge, and no one else save the loop identification code uses those back
    // edges. And when it comes to loops, using a GhostBranch as a looping instruction makes no
    // sense anyway. I'll do something better when it inevitably bites me in the ass some day
    // because I need the back edges for something else
    m_ghostCode.lock()->addBackEdge( srcBBIndex );
}

namespace goose::cir
{
    ostream& operator<<( ostream& out, const Terminator& ter )
    {


        return visit(
            [&]< typename ET >( const ET& e ) -> ostream& { return out << e; }, ter.content() );
    }

    ostream& operator<<( ostream& out, const RetVoid& ter )
    {
        return out << "RETVOID";
    }

107
108
109
110
111
112
113
114
        return out << "BREAK " << ter.level();
    }

    ostream& operator<<( ostream& out, const Continue& ter )
    {
        return out << "CONTINUE " << ter.level();
    }
}







|
95
96
97
98
99
100
101
102
        return out << "BREAK " << ter.level();
    }

    ostream& operator<<( ostream& out, const Continue& ter )
    {
        return out << "CONTINUE " << ter.level();
    }
} // namespace goose::cir
Changes to bs/cir/terminator.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
#ifndef GOOSE_CIR_TERMINATOR_H
#define GOOSE_CIR_TERMINATOR_H

namespace goose::cir
{
    class BasicBlock;

    class Terminator
    {
        public:
            Terminator( RetVoid&& r ) :
                m_content( move( r ) )
            {}



            Terminator( Ret&& r ) :
                m_content( move( r ) )
            {}



            Terminator( Branch&& b ) :
                m_content( move( b ) )
            {}



            Terminator( CondBranch&& cb ) :
                m_content( move( cb ) )
            {}

            Terminator( GhostBranch&& gcb ) :
                m_content( move( gcb ) )

            {}





            Terminator( Break&& b ) :
                m_content( move( b ) )
            {}



            Terminator( Continue&& c ) :
                m_content( move( c ) )
            {}

            using Content = variant

            <
                RetVoid,
                Ret,
                Branch,
                CondBranch,
                GhostBranch,
                Break,
                Continue
            >;

            const auto& content() const { return m_content; }

            bool canBeExecuted() const;
            bool canBeEagerlyEvaluated() const;

            void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex );

            friend ostream& operator<<( ostream& out, const Terminator& inst );

        private:
            Content m_content;
    };
}

#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
#ifndef GOOSE_CIR_TERMINATOR_H
#define GOOSE_CIR_TERMINATOR_H

namespace goose::cir
{
    class BasicBlock;

    class Terminator
    {
      public:
        Terminator( RetVoid&& r ) :
            m_content( move( r ) )

        {
        }

        Terminator( Ret&& r ) :
            m_content( move( r ) )

        {
        }

        Terminator( Branch&& b ) :
            m_content( move( b ) )

        {
        }

        Terminator( CondBranch&& cb ) :
            m_content( move( cb ) )

        {


        }

        Terminator( GhostBranch&& gcb ) :
            m_content( move( gcb ) )
        {
        }

        Terminator( Break&& b ) :
            m_content( move( b ) )

        {
        }

        Terminator( Continue&& c ) :
            m_content( move( c ) )

        {

        }





        using Content = variant< RetVoid, Ret, Branch, CondBranch, GhostBranch, Break, Continue >;




        const auto& content() const { return m_content; }

        bool canBeExecuted() const;
        bool canBeEagerlyEvaluated() const;

        void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex );

        friend ostream& operator<<( ostream& out, const Terminator& inst );

      private:
        Content m_content;
    };
} // namespace goose::cir

#endif
Deleted bs/cir/type.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
#ifndef GOOSE_CIR_TYPE_H
#define GOOSE_CIR_TYPE_H

namespace goose::cir
{
    class PushType : public BaseInstr< 0, true >
    {
        public:
            template< typename T >
            PushType( T&& type, LocationId loc ) :
                BaseInstr( loc ),
                m_type( forward< T >( type ) )
            {}

            const auto& type() const { return m_type; }

            bool canBeExecuted() const { return false; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return false; }

            bool operator<( const PushType& rhs ) const
            {
                return m_type < rhs.m_type;
            }

            friend ostream& operator<<( ostream& out, const PushType& ins )
            {
                return out << "PUSHTYPE(" << ins.m_type << ')';
            }

        private:
            eir::Term m_type;
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








































































Deleted bs/cir/verification.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
#ifndef GOOSE_CIR_VERIFICATION_H
#define GOOSE_CIR_VERIFICATION_H

// Misc verification-only instructions

namespace goose::cir
{
    // Logical implication
    class Implies : public BinaryOp
    {
        public:
            Implies( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const Implies& ins )
            {
                return out << "IMPLIES";
            }
    };

    // Tests if a sequence is a prefix of another
    class IsPrefixOf : public BinaryOp
    {
        public:
            IsPrefixOf( LocationId loc ) :
                BinaryOp( loc )
            {}

            friend ostream& operator<<( ostream& out, const IsPrefixOf& ins )
            {
                return out << "ISPREFIXOF";
            }
    };
}

#endif
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<










































































Changes to bs/cir/verifinstrfilter.cpp.
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
            return false;

        count += abs( c );
        push( -count );
        return true;
    }

    template< typename T >
    bool VerifInstrFilter::processInstr( const T& instr )
    {
        size_t count = 1;
        bool isVerifCode = false;

        for( uint32_t i = 0; i < T::OperandCount; ++i )
        {
            auto c = pop();







<
|







198
199
200
201
202
203
204

205
206
207
208
209
210
211
212
            return false;

        count += abs( c );
        push( -count );
        return true;
    }


    template< typename T > bool VerifInstrFilter::processInstr( const T& instr )
    {
        size_t count = 1;
        bool isVerifCode = false;

        for( uint32_t i = 0; i < T::OperandCount; ++i )
        {
            auto c = pop();
226
227
228
229
230
231
232

233
234
235
236
237
238
239
240

241

        return true;
    }

    bool VerifInstrFilter::processInstr( const Instruction& instr )
    {
        m_pendingInstrs.emplace_back( m_instrCount++ );


        return visit( [&]< typename T >( const T& instr )
        {
            if constexpr( is_same_v< T, monostate > )
                return false;
            else
                return VerifInstrFilter::processInstr( instr );
        }, instr );
    }

}








>
|
|
|
|
|
|
<
|
>
|
>
225
226
227
228
229
230
231
232
233
234
235
236
237
238

239
240
241
242
        return true;
    }

    bool VerifInstrFilter::processInstr( const Instruction& instr )
    {
        m_pendingInstrs.emplace_back( m_instrCount++ );

        return visit(
            [&]< typename T >( const T& instr )
            {
                if constexpr( is_same_v< T, monostate > )
                    return false;
                else
                    return VerifInstrFilter::processInstr( instr );

            },
            instr );
    }
} // namespace goose::cir
Changes to bs/cir/verifinstrfilter.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#ifndef GOOSE_CIR_VERIFINSTRFILTER_H
#define GOOSE_CIR_VERIFINSTRFILTER_H

namespace goose::cir
{
    struct VerifInstrFilter
    {
        public:
            VerifInstrFilter( size_t inputInstrCount ) :
                m_instrDiscardFlags( inputInstrCount )
            {
                m_pendingInstrs.reserve( inputInstrCount );
            }

            bool processInstr( const Instruction& instr );

            template< typename T >
            void filter( const InstrSeq& input, T& output )
            {
                assert( input.size() == m_instrDiscardFlags.size() );

                output.reserve( input.size() - m_discardCount );
                auto fit = m_instrDiscardFlags.begin();

                for( const auto& instr : input )
                {
                    if( !*fit )
                        output.emplace_back( instr );
                    ++fit;
                }
            }

        private:
            void push( int32_t instrCount );
            int32_t pop();

            void retire();

            bool processInstr( const Call& instr );
            bool processInstr( const CallCheck& instr );
            bool processInstr( const GhostCall& instr );
            bool processInstr( const Store& instr );
            bool processInstr( const Assert& instr );
            bool processInstr( const Placeholder& instr );
            bool processInstr( const PHOverrideSet& instr );
            bool processInstr( const PHOverrideClear& instr );
            bool processInstr( const ForAllSetup& instr );
            bool processInstr( const ForAll& instr );

            template< typename T >
            bool processInstr( const T& instr );

            llvm::SmallVector< size_t, 16 > m_pendingInstrs;
            vector< bool > m_instrDiscardFlags;
            stack< int32_t > m_stack;

            size_t m_instrCount = 0;
            size_t m_discardCount = 0;
    };

    template< typename T >
    bool FilterVerificationInstructions( const InstrSeq& is, T& destination )
    {
        VerifInstrFilter f( is.size() );

        for( const auto& instr : is )
        {
            if( !f.processInstr( instr ) )
                return false;
        }

        f.filter( is, destination );
        return true;
    }
}

#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

64
65

66
67
68
69
70
71
72
#ifndef GOOSE_CIR_VERIFINSTRFILTER_H
#define GOOSE_CIR_VERIFINSTRFILTER_H

namespace goose::cir
{
    struct VerifInstrFilter
    {
      public:
        VerifInstrFilter( size_t inputInstrCount ) :
            m_instrDiscardFlags( inputInstrCount )
        {
            m_pendingInstrs.reserve( inputInstrCount );
        }

        bool processInstr( const Instruction& instr );


        template< typename T > void filter( const InstrSeq& input, T& output )
        {
            assert( input.size() == m_instrDiscardFlags.size() );

            output.reserve( input.size() - m_discardCount );
            auto fit = m_instrDiscardFlags.begin();

            for( const auto& instr : input )
            {
                if( !*fit )
                    output.emplace_back( instr );
                ++fit;
            }
        }

      private:
        void push( int32_t instrCount );
        int32_t pop();

        void retire();

        bool processInstr( const Call& instr );
        bool processInstr( const CallCheck& instr );
        bool processInstr( const GhostCall& instr );
        bool processInstr( const Store& instr );
        bool processInstr( const Assert& instr );
        bool processInstr( const Placeholder& instr );
        bool processInstr( const PHOverrideSet& instr );
        bool processInstr( const PHOverrideClear& instr );
        bool processInstr( const ForAllSetup& instr );
        bool processInstr( const ForAll& instr );

        template< typename T > bool processInstr( const T& instr );


        llvm::SmallVector< size_t, 16 > m_pendingInstrs;
        vector< bool > m_instrDiscardFlags;
        stack< int32_t > m_stack;

        size_t m_instrCount = 0;
        size_t m_discardCount = 0;
    };


    template< typename T > bool FilterVerificationInstructions( const InstrSeq& is, T& destination )
    {
        VerifInstrFilter f( is.size() );

        for( const auto& instr : is )

            if( !f.processInstr( instr ) )
                return false;


        f.filter( is, destination );
        return true;
    }
} // namespace goose::cir

#endif
Changes to bs/codegen/address.cpp.
1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
16

17
18
19
20
21

22
23
24
25
26

27
28
29
30
31


32
33
34
35
36
37
38
39
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

ptr< codegen::Value > codegen::AddressToGEP( llvm::IRBuilder<>& builder, const codegen::Address& addr )

{
    if( addr.path().empty() )
        return addr.originAddr();

    llvm::SmallVector< llvm::Value*, 8 > idxs;
    idxs.push_back( llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), 0 ) );

    for( auto&& index : addr.path() )

        idxs.push_back( llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), index ) );

    llvm::Type* pointedType = nullptr;
    if( addr.originAddr()->cgType()->isPointer() )
        pointedType = *static_cast< const codegen::PointerType* >( addr.originAddr()->cgType() )->pointedType();

    else
        pointedType = *addr.originAddr()->cgType();

    // This value represents an address, and so it has no type because the only things that it's good for is
    // to be loaded form or stored to, and load/store know what type they're accessing anyway


    // It does mean that invalid CIR (which can be created by compile time user code) will crash the compiler.
    // In the self hosted rewrite, if we mark the type as optional (which we'll have to if we want to omit it)
    // the compiler should never allow us to write code that access it without having tested it first, so it'll
    // be ok because we'll be forced to handle those errors gracefully.


    return codegen::Value::Get( nullptr, builder.CreateGEP( pointedType, *addr.originAddr(), idxs ) );
}

bool Module::buildInstruction( State& st, const VarAddr& va )
{
    auto* ppVal = st.temporaries->get( va.varIndex() );

    assert( ppVal );







|
>








>
|



|
>



|
|
>

|
|
|
|
>
>
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

ptr< codegen::Value > codegen::AddressToGEP(
    llvm::IRBuilder<>& builder, const codegen::Address& addr )
{
    if( addr.path().empty() )
        return addr.originAddr();

    llvm::SmallVector< llvm::Value*, 8 > idxs;
    idxs.push_back( llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), 0 ) );

    for( auto&& index : addr.path() )
        idxs.push_back(
            llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), index ) );

    llvm::Type* pointedType = nullptr;
    if( addr.originAddr()->cgType()->isPointer() )
        pointedType = *static_cast< const codegen::PointerType* >( addr.originAddr()->cgType() )
                           ->pointedType();
    else
        pointedType = *addr.originAddr()->cgType();

    // This value represents an address, and so it has no type because the only things that it's
    // good for is to be loaded form or stored to, and load/store know what type they're accessing
    // anyway

    // It does mean that invalid CIR (which can be created by compile time user code) will crash the
    // compiler. In the self hosted rewrite, if we mark the type as optional (which we'll have to if
    // we want to omit it) the compiler should never allow us to write code that access it without
    // having tested it first, so it'll be ok because we'll be forced to handle those errors
    // gracefully.
    return codegen::Value::Get(
        nullptr, builder.CreateGEP( pointedType, *addr.originAddr(), idxs ) );
}

bool Module::buildInstruction( State& st, const VarAddr& va )
{
    auto* ppVal = st.temporaries->get( va.varIndex() );

    assert( ppVal );
77
78
79
80
81
82
83

84
85
86
87
88
89
90
91
92
93
    auto baseAddr = st.stack.pop();
    if( !baseAddr )
        return false;

    if( holds_alternative< Address >( *baseAddr ) )
        st.stack.push( Address::Select( move( get< Address >( *baseAddr ) ), s.memberIndex() ) );
    else

        st.stack.push( Address::Select( Address( get< ptr< codegen::Value > >( *baseAddr ) ), s.memberIndex() ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::GhostCall& gc )
{
    // We should never encounter this during codegen, since it's a construct only
    // intended to be used in verification expressions.
    return false;
}







>
|









83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
    auto baseAddr = st.stack.pop();
    if( !baseAddr )
        return false;

    if( holds_alternative< Address >( *baseAddr ) )
        st.stack.push( Address::Select( move( get< Address >( *baseAddr ) ), s.memberIndex() ) );
    else
        st.stack.push( Address::Select(
            Address( get< ptr< codegen::Value > >( *baseAddr ) ), s.memberIndex() ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::GhostCall& gc )
{
    // We should never encounter this during codegen, since it's a construct only
    // intended to be used in verification expressions.
    return false;
}
Changes to bs/codegen/address.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
#ifndef GOOSE_CODEGEN_ADDRESS_H
#define GOOSE_CODEGEN_ADDRESS_H

namespace goose::codegen
{
    class Address
    {
        public:
            Address( const ptr< codegen::Value >& originAddr ) :
                m_originAddr( originAddr )
            {}



            const auto& originAddr() const { return m_originAddr; }

            const auto& path() const { return m_path; }

            static Address Select( Address&& baseAddr, uint32_t index )
            {
                baseAddr.m_path.emplace_back( index );
                return baseAddr;
            }

        private:
            SelectPath m_path;
            ptr< codegen::Value > m_originAddr;
    };

    extern ptr< codegen::Value > AddressToGEP( llvm::IRBuilder<>& builder, const codegen::Address& addr );
}



#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
#ifndef GOOSE_CODEGEN_ADDRESS_H
#define GOOSE_CODEGEN_ADDRESS_H

namespace goose::codegen
{
    class Address
    {
      public:
        Address( const ptr< codegen::Value >& originAddr ) :
            m_originAddr( originAddr )

        {
        }

        const auto& originAddr() const { return m_originAddr; }

        const auto& path() const { return m_path; }

        static Address Select( Address&& baseAddr, uint32_t index )
        {
            baseAddr.m_path.emplace_back( index );
            return baseAddr;
        }

      private:
        SelectPath m_path;
        ptr< codegen::Value > m_originAddr;
    };

    extern ptr< codegen::Value > AddressToGEP(

        llvm::IRBuilder<>& builder, const codegen::Address& addr );
} // namespace goose::codegen

#endif
Changes to bs/codegen/arithops.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
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

bool Module::buildInstruction( State& st, const cir::Add& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateAdd( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::Sub& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateSub( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::Mul& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateMul( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::UDiv& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateUDiv( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::SDiv& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateSDiv( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::URem& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateURem( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::SRem& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateSRem( *lhs, *rhs );
        } );
}









|
<
<
|
<




|
<
<
|
<




|
<
<
|
<




|
<
<
|
<




|
<
<
|
<




|
<
<
|
<




|
<
<
|
<

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
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

bool Module::buildInstruction( State& st, const cir::Add& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateAdd( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::Sub& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateSub( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::Mul& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateMul( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::UDiv& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateUDiv( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::SDiv& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateSDiv( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::URem& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateURem( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::SRem& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateSRem( *lhs, *rhs ); } );

}
Changes to bs/codegen/basicblock.cpp.
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
    if( pLLVMBB->getTerminator() || pBB->codeGenStarted() )
        return pLLVMBB;

    m_llvmBuilder.SetInsertPoint( pLLVMBB );

    auto pCurrentBB = pBB;

    for(;;)
    {
        pCurrentBB->setCodeGenStarted();

        const auto* pInstrs = pCurrentBB->runnableInstructions();
        if( !pInstrs )
            return nullptr;

        for( auto&& instr : *pInstrs )
        {
            if( !buildInstruction( st, instr ) )
                return nullptr;
        }

        // If the terminator is a ghostbranch, directly
        // append the content of the continuation block
        // to avoid unecessary basic blocks splits whenever
        // some ghost code is present
        if( !pCurrentBB->terminator() )
            break;







|








<


<







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
    if( pLLVMBB->getTerminator() || pBB->codeGenStarted() )
        return pLLVMBB;

    m_llvmBuilder.SetInsertPoint( pLLVMBB );

    auto pCurrentBB = pBB;

    for( ;; )
    {
        pCurrentBB->setCodeGenStarted();

        const auto* pInstrs = pCurrentBB->runnableInstructions();
        if( !pInstrs )
            return nullptr;

        for( auto&& instr : *pInstrs )

            if( !buildInstruction( st, instr ) )
                return nullptr;


        // If the terminator is a ghostbranch, directly
        // append the content of the continuation block
        // to avoid unecessary basic blocks splits whenever
        // some ghost code is present
        if( !pCurrentBB->terminator() )
            break;
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

    pCurrentBB->setCodeGenStarted( false );
    return pLLVMBB;
}

bool Module::buildTerminator( State& st, const cir::Terminator& terminator )
{
    return visit( [&]( auto&& e )
    {
        return buildTerminator( st, e );
    }, terminator.content() );
}

bool Module::buildTerminator( State& st, const cir::RetVoid& r )
{
    m_llvmBuilder.CreateRetVoid();
    return true;
}







|
<
<
<







57
58
59
60
61
62
63
64



65
66
67
68
69
70
71

    pCurrentBB->setCodeGenStarted( false );
    return pLLVMBB;
}

bool Module::buildTerminator( State& st, const cir::Terminator& terminator )
{
    return visit( [&]( auto&& e ) { return buildTerminator( st, e ); }, terminator.content() );



}

bool Module::buildTerminator( State& st, const cir::RetVoid& r )
{
    m_llvmBuilder.CreateRetVoid();
    return true;
}
Changes to bs/codegen/codegen.cpp.
16
17
18
19
20
21
22
23
        {
            DiagnosticsManager::GetInstance().emitErrorMessage(
                t.locationId(), "codegen: unable to lower this type.", 0 );
        }

        return cgType;
    }
}







|
16
17
18
19
20
21
22
23
        {
            DiagnosticsManager::GetInstance().emitErrorMessage(
                t.locationId(), "codegen: unable to lower this type.", 0 );
        }

        return cgType;
    }
} // namespace goose::codegen
Changes to bs/codegen/codegen.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
36
37
38
39
40
41
42
#include "sema/sema.h"

namespace goose::builtins
{
    class Func;
    class FuncType;
    struct PointerType;
}

namespace goose::codegen
{
    using namespace eir;
    using namespace cir;
    using namespace sema;

    class Type;

    extern llvm::LLVMContext& GetLLVMContext();
    extern optional< string > Mangle( const Term& identity );

    extern const codegen::Type* GetCodegenType( const eir::Value& t );
}

#include "llvmwrappers.h"
// TODO_SSA reenable
//#include "address.h"
//#include "stack.h"
//#include "module.h"

//#include "module.inl"

#endif







|













|



|
|
|

|


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/sema.h"

namespace goose::builtins
{
    class Func;
    class FuncType;
    struct PointerType;
} // namespace goose::builtins

namespace goose::codegen
{
    using namespace eir;
    using namespace cir;
    using namespace sema;

    class Type;

    extern llvm::LLVMContext& GetLLVMContext();
    extern optional< string > Mangle( const Term& identity );

    extern const codegen::Type* GetCodegenType( const eir::Value& t );
} // namespace goose::codegen

#include "llvmwrappers.h"
// TODO_SSA reenable
// #include "address.h"
// #include "stack.h"
// #include "module.h"

// #include "module.inl"

#endif
Changes to bs/codegen/compareops.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
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

bool Module::buildInstruction( State& st, const cir::Eq& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateICmpEQ( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::Neq& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateICmpNE( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::UGT& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateICmpUGT( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::UGE& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateICmpUGE( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::ULT& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateICmpULT( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::ULE& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateICmpULE( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::SGT& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateICmpSGT( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::SGE& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateICmpSGE( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::SLT& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateICmpSLT( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::SLE& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateICmpSLE( *lhs, *rhs );
        } );
}









|
<
<
|
<




|
<
<
|
<




|
<
<
|
<




|
<
<
|
<




|
<
<
|
<




|
<
<
|
<




|
<
<
|
<




|
<
<
|
<




|
<
<
|
<




|
<
<
|
<

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
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

bool Module::buildInstruction( State& st, const cir::Eq& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpEQ( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::Neq& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpNE( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::UGT& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpUGT( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::UGE& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpUGE( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::ULT& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpULT( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::ULE& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpULE( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::SGT& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpSGT( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::SGE& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpSGE( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::SLT& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpSLT( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::SLE& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpSLE( *lhs, *rhs ); } );

}
Changes to bs/codegen/func.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 "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;

ptr< codegen::Function > Module::buildFuncProto( const Context& c, const builtins::Func& func, const string& name,
    llvm::Function::LinkageTypes linkageType )
{
    auto* pllvmFuncType = GetFuncCodegenType( func.type() );
    if( !pllvmFuncType )
        return {};

    return codegen::Function::Get( pllvmFuncType, linkageType, name, m_llvmModule );
}

ptr< codegen::Function > Module::getOrCreateFunc( const Context& c, const builtins::Func& func )
{
    if( func.isExternal() )
        return getOrCreateFunc( c, func, *func.symbol(), llvm::Function::ExternalLinkage );

    auto name = Mangle( func.cir()->identity() );
    if( !name )
        return {};

    return getOrCreateFunc( c, func, *name, llvm::Function::PrivateLinkage );
}

ptr< codegen::Function > Module::getOrCreateFunc( const Context& c, const builtins::Func& func, const string& name,
    llvm::Function::LinkageTypes linkageType )
{
    auto pCGFunc = codegen::Function::Get( name );
    if( pCGFunc )
        return pCGFunc;

    pCGFunc = buildFuncProto( c, func, name, linkageType );
    if( !pCGFunc )






|
|




















|
|







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 "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;

ptr< codegen::Function > Module::buildFuncProto( const Context& c, const builtins::Func& func,
    const string& name, llvm::Function::LinkageTypes linkageType )
{
    auto* pllvmFuncType = GetFuncCodegenType( func.type() );
    if( !pllvmFuncType )
        return {};

    return codegen::Function::Get( pllvmFuncType, linkageType, name, m_llvmModule );
}

ptr< codegen::Function > Module::getOrCreateFunc( const Context& c, const builtins::Func& func )
{
    if( func.isExternal() )
        return getOrCreateFunc( c, func, *func.symbol(), llvm::Function::ExternalLinkage );

    auto name = Mangle( func.cir()->identity() );
    if( !name )
        return {};

    return getOrCreateFunc( c, func, *name, llvm::Function::PrivateLinkage );
}

ptr< codegen::Function > Module::getOrCreateFunc( const Context& c, const builtins::Func& func,
    const string& name, llvm::Function::LinkageTypes linkageType )
{
    auto pCGFunc = codegen::Function::Get( name );
    if( pCGFunc )
        return pCGFunc;

    pCGFunc = buildFuncProto( c, func, name, linkageType );
    if( !pCGFunc )
70
71
72
73
74
75
76

77
78
79
80
    if( !buildCFG( st, pllvmFunc, func.cir()->body() ) )
        return {};

    llvm::verifyFunction( *pllvmFunc );
    return pCGFunc;
}


llvm::BasicBlock* Module::buildCFG( State& st, llvm::Function* llvmFunc, const ptr< cir::CFG >& pCFG )
{
    return buildBasicBlock( st, pCFG->entryBB() );
}







>
|



70
71
72
73
74
75
76
77
78
79
80
81
    if( !buildCFG( st, pllvmFunc, func.cir()->body() ) )
        return {};

    llvm::verifyFunction( *pllvmFunc );
    return pCGFunc;
}

llvm::BasicBlock* Module::buildCFG(
    State& st, llvm::Function* llvmFunc, const ptr< cir::CFG >& pCFG )
{
    return buildBasicBlock( st, pCFG->entryBB() );
}
Changes to bs/codegen/instructions.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
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

bool Module::buildInstruction( State& st, const cir::InstrSeq& is )
{
    for( auto&& instr : is )
    {
        if( !buildInstruction( st, instr ) )
            return false;
    }

    return true;
}

bool Module::buildInstruction( State& st, const cir::Instruction& instr )
{
    return visit( [&]< typename T >( T&& inst )
    {
        return buildInstruction( st, inst );
    }, instr );
}

bool Module::buildInstruction( State& st, const monostate& )
{
    return false;
}











<


<






|
<
<
<







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
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

bool Module::buildInstruction( State& st, const cir::InstrSeq& is )
{
    for( auto&& instr : is )

        if( !buildInstruction( st, instr ) )
            return false;


    return true;
}

bool Module::buildInstruction( State& st, const cir::Instruction& instr )
{
    return visit( [&]< typename T >( T&& inst ) { return buildInstruction( st, inst ); }, instr );



}

bool Module::buildInstruction( State& st, const monostate& )
{
    return false;
}

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
        auto parg = st.stack.pop( m_llvmBuilder );
        if( !parg )
            return false;

        args[call.numArgs() - i - 1] = **parg;
    }

    st.stack.push( codegen::Value::Get(
        cgFunc->cgFuncType()->returnType(),
        m_llvmBuilder.CreateCall( llvm::FunctionCallee( cgFunc->llvmFunc() ), args ) ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::Constant& ct )
{
    auto val = buildValue( st, ct.value().get() );







|
<







40
41
42
43
44
45
46
47

48
49
50
51
52
53
54
        auto parg = st.stack.pop( m_llvmBuilder );
        if( !parg )
            return false;

        args[call.numArgs() - i - 1] = **parg;
    }

    st.stack.push( codegen::Value::Get( cgFunc->cgFuncType()->returnType(),

        m_llvmBuilder.CreateCall( llvm::FunctionCallee( cgFunc->llvmFunc() ), args ) ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::Constant& ct )
{
    auto val = buildValue( st, ct.value().get() );
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
    if( !ptrVal )
        return false;

    auto* cgType = GetCodegenType( *EIRToValue( load.type().get() ) );
    if( !cgType )
        return false;

    st.stack.push( codegen::Value::Get( cgType,
        m_llvmBuilder.CreateLoad( *cgType, ( *ptrVal )->llvmValue() ) ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::Store& store )
{
    auto ptrVal = st.stack.pop( m_llvmBuilder );
    if( !ptrVal )
        return false;

    auto val = st.stack.pop( m_llvmBuilder );
    if( !val )
        return false;

    if( llvm::isa< llvm::ConstantAggregate >( ( *val )->llvmValue() ) )
    {
        stringstream name;
        name << ".constaggr" << hex << m_nextAggregateID++;

        auto* llvmValue = ( *val )->llvmValue();

        auto pGlob = new llvm::GlobalVariable( m_llvmModule,
            ( *val )->llvmValue()->getType(), true, llvm::GlobalValue::LinkageTypes::PrivateLinkage,
            static_cast< llvm::Constant* >( llvmValue ), name.str() );

        auto size = m_dataLayout.getTypeAllocSize( llvmValue->getType() );

        m_llvmBuilder.CreateMemCpy( **ptrVal, nullopt, pGlob, nullopt,
            llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), size ) );
        return true;







|
|




















|
|







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
    if( !ptrVal )
        return false;

    auto* cgType = GetCodegenType( *EIRToValue( load.type().get() ) );
    if( !cgType )
        return false;

    st.stack.push( codegen::Value::Get(
        cgType, m_llvmBuilder.CreateLoad( *cgType, ( *ptrVal )->llvmValue() ) ) );
    return true;
}

bool Module::buildInstruction( State& st, const cir::Store& store )
{
    auto ptrVal = st.stack.pop( m_llvmBuilder );
    if( !ptrVal )
        return false;

    auto val = st.stack.pop( m_llvmBuilder );
    if( !val )
        return false;

    if( llvm::isa< llvm::ConstantAggregate >( ( *val )->llvmValue() ) )
    {
        stringstream name;
        name << ".constaggr" << hex << m_nextAggregateID++;

        auto* llvmValue = ( *val )->llvmValue();

        auto pGlob = new llvm::GlobalVariable( m_llvmModule, ( *val )->llvmValue()->getType(), true,
            llvm::GlobalValue::LinkageTypes::PrivateLinkage,
            static_cast< llvm::Constant* >( llvmValue ), name.str() );

        auto size = m_dataLayout.getTypeAllocSize( llvmValue->getType() );

        m_llvmBuilder.CreateMemCpy( **ptrVal, nullopt, pGlob, nullopt,
            llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), size ) );
        return true;
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
    auto* pPHI = m_llvmBuilder.CreatePHI( *cgType, p.numIncomings() );
    if( !pPHI )
        return false;

    {
        llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );

        p.forAllIncomings( [&]( auto&& bb, auto&& val )

        {
            auto pIncomingBB = buildBasicBlock( st, bb );
            if( !pIncomingBB )
            {
                pPHI = nullptr;
                return false;
            }

            auto pVal = buildValue( st, val );
            if( !pVal )
            {
                pPHI = nullptr;
                return false;
            }

            pPHI->addIncoming( *pVal, pIncomingBB );
            return true;
        } );
    }

    if( !pPHI )
        return false;

    st.temporaries->set( p.destIndex(), codegen::Value::Get( cgType, pPHI ) );
    return true;







|
>
|
|
|
|
|
|
|

|
|
|
|
|
|

|
|
|







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
    auto* pPHI = m_llvmBuilder.CreatePHI( *cgType, p.numIncomings() );
    if( !pPHI )
        return false;

    {
        llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );

        p.forAllIncomings(
            [&]( auto&& bb, auto&& val )
            {
                auto pIncomingBB = buildBasicBlock( st, bb );
                if( !pIncomingBB )
                {
                    pPHI = nullptr;
                    return false;
                }

                auto pVal = buildValue( st, val );
                if( !pVal )
                {
                    pPHI = nullptr;
                    return false;
                }

                pPHI->addIncoming( *pVal, pIncomingBB );
                return true;
            } );
    }

    if( !pPHI )
        return false;

    st.temporaries->set( p.destIndex(), codegen::Value::Get( cgType, pPHI ) );
    return true;
Changes to bs/codegen/llvmwrappers.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
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
codegen::PointerType::PointerType( const Type* pointedType ) :
    Type( llvm::PointerType::get( GetLLVMContext(), 0 ) ),
    m_pointedType( pointedType )
{
    m_isPointer = true;
}

const FunctionType* FunctionType::Get( const codegen::Type* rt, const ParamTypes& paramTypes, bool isVarArg )

{
    return new FunctionType( rt, paramTypes, isVarArg );
}

FunctionType::FunctionType( const codegen::Type* rt, const ParamTypes& paramTypes, bool isVarArg ) :
    m_returnType( rt ),
    m_paramTypes( paramTypes )
{
    llvm::SmallVector< llvm::Type*, 8 > pt;
    pt.reserve( paramTypes.size() );
    for( auto&& t : paramTypes )
        pt.emplace_back( *t );

    m_llvmType = llvm::FunctionType::get( *rt, pt, isVarArg );
}

ptr< Function > Function::Get(
    const codegen::FunctionType* pType,
    llvm::Function::LinkageTypes linkageType,
    const string& name,
    llvm::Module& llvmModule )
{
    auto pFunc = make_shared< Function >( pType, linkageType, name, llvmModule );
    ms_funcs.emplace( name, pFunc );
    return pFunc;
}

ptr< Function > Function::Get( const string& name )
{
    auto it = ms_funcs.find( name );
    if( it == ms_funcs.end() )
        return {};
    return it->second;
}

Function::Function(
    const codegen::FunctionType* pType,
    llvm::Function::LinkageTypes linkageType,
    const string& name,
    llvm::Module& llvmModule ) :
    Value( pType, llvm::Function::Create( pType->llvmFuncType(), linkageType, name, llvmModule ) )
{
    m_isFunc = true;

    uint32_t paramCount = pType->paramTypes().size();
    m_args.reserve( paramCount );
    auto* lf = llvmFunc();

    uint32_t i = 0;
    for( auto&& llvmArg : lf->args() )
        m_args.emplace_back( codegen::Value::Get( pType->paramTypes()[i++], &llvmArg ) );
}







|
>
















|
<
|
<
<














<
<
|
|
<












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
codegen::PointerType::PointerType( const Type* pointedType ) :
    Type( llvm::PointerType::get( GetLLVMContext(), 0 ) ),
    m_pointedType( pointedType )
{
    m_isPointer = true;
}

const FunctionType* FunctionType::Get(
    const codegen::Type* rt, const ParamTypes& paramTypes, bool isVarArg )
{
    return new FunctionType( rt, paramTypes, isVarArg );
}

FunctionType::FunctionType( const codegen::Type* rt, const ParamTypes& paramTypes, bool isVarArg ) :
    m_returnType( rt ),
    m_paramTypes( paramTypes )
{
    llvm::SmallVector< llvm::Type*, 8 > pt;
    pt.reserve( paramTypes.size() );
    for( auto&& t : paramTypes )
        pt.emplace_back( *t );

    m_llvmType = llvm::FunctionType::get( *rt, pt, isVarArg );
}

ptr< Function > Function::Get( const codegen::FunctionType* pType,

    llvm::Function::LinkageTypes linkageType, const string& name, llvm::Module& llvmModule )


{
    auto pFunc = make_shared< Function >( pType, linkageType, name, llvmModule );
    ms_funcs.emplace( name, pFunc );
    return pFunc;
}

ptr< Function > Function::Get( const string& name )
{
    auto it = ms_funcs.find( name );
    if( it == ms_funcs.end() )
        return {};
    return it->second;
}



Function::Function( const codegen::FunctionType* pType, llvm::Function::LinkageTypes linkageType,
    const string& name, llvm::Module& llvmModule ) :

    Value( pType, llvm::Function::Create( pType->llvmFuncType(), linkageType, name, llvmModule ) )
{
    m_isFunc = true;

    uint32_t paramCount = pType->paramTypes().size();
    m_args.reserve( paramCount );
    auto* lf = llvmFunc();

    uint32_t i = 0;
    for( auto&& llvmArg : lf->args() )
        m_args.emplace_back( codegen::Value::Get( pType->paramTypes()[i++], &llvmArg ) );
}
Changes to bs/codegen/llvmwrappers.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

23
24
25

26
27
28
29

30

31


32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

53
54
55




56

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84


85

86
87
88

89

90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

115
116


117

118
119
120
121
122
123
124
125
126
127
#ifndef GOOSE_CODEGEN_LLVMWRAPPERS_H
#define GOOSE_CODEGEN_LLVMWRAPPERS_H

namespace goose::codegen
{
    // Since llvm 15, llvm pointer types don't know their pointed types anymore.
    // The llvm documentation says:
    // "Frontends need to be adjusted to track pointee types independently of LLVM,
    // insofar as they are necessary for lowering".

    // Here's the sanest way I found to do this "adjustment": wrap every single llvm struct that we use with
    // wrappers that work exactly the same way, with the exception that our pointer type wrapper still
    // tracks the pointed type.

    // So basically this is a whole layer of shit on top of llvm that pretends that
    // things still works the old way.
    class Type
    {
        public:
            static const Type* Get( llvm::Type* llvmType );

            operator llvm::Type*() const { return m_llvmType; }

            operator llvm::Type*() { return m_llvmType; }

            auto* llvmType() const { return m_llvmType; }

            bool isPointer() const { return m_isPointer; }

        protected:
            Type() {}

            Type( llvm::Type* llvmType ) : m_llvmType( llvmType ) {}




            llvm::Type* m_llvmType = nullptr;
            bool m_isPointer = false;
    };

    class PointerType : public Type
    {
        public:
            static const PointerType* Get( const Type* pointedType );

            const Type* pointedType() const { return m_pointedType; }

        private:
            PointerType( const Type* pointedType );
            const Type* m_pointedType = nullptr;
    };

    class FunctionType : public Type
    {
        public:
            using ParamTypes = llvm::SmallVector< const codegen::Type*, 8 >;


            static const FunctionType* Get( const codegen::Type* rt, const ParamTypes& paramTypes, bool isVarArg );

            llvm::FunctionType* llvmFuncType() const { return static_cast< llvm::FunctionType* >( m_llvmType ); }




            const Type* returnType() const { return m_returnType; }

            const auto& paramTypes() const { return m_paramTypes; }

        private:
            FunctionType( const codegen::Type* rt, const ParamTypes& paramTypes, bool isVarArg );

            const Type* m_returnType = nullptr;
            ParamTypes m_paramTypes;
    };

    class Value
    {
        public:
            static ptr< Value > Get( const codegen::Type* cgType, llvm::Value* llvmValue )
            {
                return make_shared< Value >( cgType, llvmValue );
            }

            static ptr< Value > Get( llvm::Value* llvmValue )
            {
                return make_shared< Value >(
                    codegen::Type::Get( llvmValue->getType() ), llvmValue );
            }

            Value( const codegen::Type* cgType, llvm::Value* llvmValue ) :
                m_cgType( cgType ),
                m_llvmValue( llvmValue )
            {}



            operator llvm::Value*() const { return m_llvmValue; }

            operator llvm::Value*() { return m_llvmValue; }

            auto* llvmValue() const { return m_llvmValue; }

            const auto* cgType() const { return m_cgType; }

            bool isFunc() const { return m_isFunc; }

        private:
            const codegen::Type* m_cgType = nullptr;
            llvm::Value* m_llvmValue = nullptr;

        protected:
            bool m_isFunc = false;
    };

    class Function : public codegen::Value
    {
        public:
            static ptr< Function > Get( const string& name );
            static ptr< Function > Get(
                const codegen::FunctionType* pType,
                llvm::Function::LinkageTypes linkaGeType,
                const string& name,
                llvm::Module& llvmModule );

            Function(
                const codegen::FunctionType* pType,
                llvm::Function::LinkageTypes linkaGeType,
                const string& name,
                llvm::Module& llvmModule );


            const FunctionType* cgFuncType() const { return static_cast< const FunctionType* >( cgType() ); }


            llvm::Function* llvmFunc() const { return static_cast< llvm::Function* >( llvmValue() ); }

            const auto& args() const { return m_args; }

        private:
            llvm::SmallVector< ptr< codegen::Value >, 8 > m_args;

            static unordered_map< string, ptr< Function > > ms_funcs;
    };
}

#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
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
#ifndef GOOSE_CODEGEN_LLVMWRAPPERS_H
#define GOOSE_CODEGEN_LLVMWRAPPERS_H

namespace goose::codegen
{
    // Since llvm 15, llvm pointer types don't know their pointed types anymore.
    // The llvm documentation says:
    // "Frontends need to be adjusted to track pointee types independently of LLVM, insofar as they
    // are necessary for lowering".

    // Here's the sanest way I found to do this "adjustment": wrap every single llvm struct that we
    // use with wrappers that work exactly the same way, with the exception that our pointer type
    // wrapper still tracks the pointed type.

    // So basically this is a whole layer of shit on top of llvm that pretends that things still
    // works the old way.
    class Type
    {
      public:
        static const Type* Get( llvm::Type* llvmType );

        operator llvm::Type*() const { return m_llvmType; }

        operator llvm::Type*() { return m_llvmType; }

        auto* llvmType() const { return m_llvmType; }

        bool isPointer() const { return m_isPointer; }

      protected:
        Type() {}

        Type( llvm::Type* llvmType ) :
            m_llvmType( llvmType )
        {
        }

        llvm::Type* m_llvmType = nullptr;
        bool m_isPointer = false;
    };

    class PointerType : public Type
    {
      public:
        static const PointerType* Get( const Type* pointedType );

        const Type* pointedType() const { return m_pointedType; }

      private:
        PointerType( const Type* pointedType );
        const Type* m_pointedType = nullptr;
    };

    class FunctionType : public Type
    {
      public:
        using ParamTypes = llvm::SmallVector< const codegen::Type*, 8 >;

        static const FunctionType* Get(
            const codegen::Type* rt, const ParamTypes& paramTypes, bool isVarArg );

        llvm::FunctionType* llvmFuncType() const
        {
            return static_cast< llvm::FunctionType* >( m_llvmType );
        }

        const Type* returnType() const { return m_returnType; }

        const auto& paramTypes() const { return m_paramTypes; }

      private:
        FunctionType( const codegen::Type* rt, const ParamTypes& paramTypes, bool isVarArg );

        const Type* m_returnType = nullptr;
        ParamTypes m_paramTypes;
    };

    class Value
    {
      public:
        static ptr< Value > Get( const codegen::Type* cgType, llvm::Value* llvmValue )
        {
            return make_shared< Value >( cgType, llvmValue );
        }

        static ptr< Value > Get( llvm::Value* llvmValue )
        {

            return make_shared< Value >( codegen::Type::Get( llvmValue->getType() ), llvmValue );
        }

        Value( const codegen::Type* cgType, llvm::Value* llvmValue ) :
            m_cgType( cgType ),
            m_llvmValue( llvmValue )

        {
        }

        operator llvm::Value*() const { return m_llvmValue; }

        operator llvm::Value*() { return m_llvmValue; }

        auto* llvmValue() const { return m_llvmValue; }

        const auto* cgType() const { return m_cgType; }

        bool isFunc() const { return m_isFunc; }

      private:
        const codegen::Type* m_cgType = nullptr;
        llvm::Value* m_llvmValue = nullptr;

      protected:
        bool m_isFunc = false;
    };

    class Function : public codegen::Value
    {
      public:
        static ptr< Function > Get( const string& name );
        static ptr< Function > Get( const codegen::FunctionType* pType,

            llvm::Function::LinkageTypes linkaGeType, const string& name,

            llvm::Module& llvmModule );



        Function( const codegen::FunctionType* pType, llvm::Function::LinkageTypes linkaGeType,
            const string& name, llvm::Module& llvmModule );

        const FunctionType* cgFuncType() const
        {
            return static_cast< const FunctionType* >( cgType() );
        }

        llvm::Function* llvmFunc() const { return static_cast< llvm::Function* >( llvmValue() ); }

        const auto& args() const { return m_args; }

      private:
        llvm::SmallVector< ptr< codegen::Value >, 8 > m_args;

        static unordered_map< string, ptr< Function > > ms_funcs;
    };
} // namespace goose::codegen

#endif
Changes to bs/codegen/logicops.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
#include "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

bool Module::buildInstruction( State& st, const cir::Not& uo )
{
    return buildUnaryInstruction( st,
        [&]( auto&& operand )
        {

            return m_llvmBuilder.CreateXor( *operand, llvm::ConstantInt::getTrue( GetLLVMContext() ) );
        } );
}

bool Module::buildInstruction( State& st, const cir::And& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateAnd( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::Or& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateOr( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::Xor& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateXor( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::Shl& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateShl( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::LShr& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateLShr( *lhs, *rhs );
        } );
}

bool Module::buildInstruction( State& st, const cir::AShr& bo )
{
    return buildBinaryInstruction( st,
        [&]( auto&& lhs, auto&& rhs )
        {
            return m_llvmBuilder.CreateAShr( *lhs, *rhs );
        } );
}










|
<
>
|





|
<
<
|
<




|
<
<
|
<




|
<
<
|
<




|
<
<
|
<




|
<
<
|
<




|
<
<
|
<

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 "codegen.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;

bool Module::buildInstruction( State& st, const cir::Not& uo )
{
    return buildUnaryInstruction( st,
        [&]( auto&& operand ) {

            return m_llvmBuilder.CreateXor(
                *operand, llvm::ConstantInt::getTrue( GetLLVMContext() ) );
        } );
}

bool Module::buildInstruction( State& st, const cir::And& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateAnd( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::Or& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateOr( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::Xor& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateXor( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::Shl& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateShl( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::LShr& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateLShr( *lhs, *rhs ); } );

}

bool Module::buildInstruction( State& st, const cir::AShr& bo )
{
    return buildBinaryInstruction(


        st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateAShr( *lhs, *rhs ); } );

}
Changes to bs/codegen/mangle.cpp.
19
20
21
22
23
24
25
26
27

28
29
30
31
32
33
34
// Literal elements:
//   i: integer, followed by its value
//   S: string, followed by its length, followed by ':', followed by the string itself.
//   n: anonymous StringId, followed by its unique id
//   s: StringId, followed by its length, followed by ':', followed by the string itself.
//   _ followed by an hex number: vector, whose length is that number,
//     followed by its contained elements, serialized recursively.
//   @ same as above, but the vector is of variable length and is followed by the repetition element.
//   p, P: a void* or ptr< void > term (unfortunately they have legitimate uses in some type identitiers

//      that we do need to be able to mangle)
//   x: an eir term placeholder.
//   X: a bigint, followed by its value
//
// $: compressed string id: it is followed by the 0 based index of a previously encountered
//   StringId literal, in the order they were encountered.
//







|
|
>







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Literal elements:
//   i: integer, followed by its value
//   S: string, followed by its length, followed by ':', followed by the string itself.
//   n: anonymous StringId, followed by its unique id
//   s: StringId, followed by its length, followed by ':', followed by the string itself.
//   _ followed by an hex number: vector, whose length is that number,
//     followed by its contained elements, serialized recursively.
//   @ same as above, but the vector is of variable length and is followed by the repetition
//   element. p, P: a void* or ptr< void > term (unfortunately they have legitimate uses in some
//   type identitiers
//      that we do need to be able to mangle)
//   x: an eir term placeholder.
//   X: a bigint, followed by its value
//
// $: compressed string id: it is followed by the 0 based index of a previously encountered
//   StringId literal, in the order they were encountered.
//
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
282
283
284
285
286
287
288
289
290
291
292
293
294
// unecessary parts. This would require more intelligence to build an identity, although it might
// not necessarily be more complicated than the rule system idea above.

namespace
{
    class Mangler
    {
        public:
            Mangler()
            {
                m_mangled << hex;
            }

            bool mangle( const Term& t )
            {
                return visit( [&]( auto&& t )
                {
                    return mangle( t );
                }, t );
            }

            bool mangle( uint64_t integer )
            {
                m_mangled << 'i' << integer;
                return true;
            }

            bool mangle( const BigInt& bi )
            {
                m_mangled << 'X';
                bi.toHex( m_mangled );
                return true;
            }

            // Locations shouldn't be included in the mangling, but
            // should not cause it to fail either.
            bool mangle( LocationId loc )
            {
                return true;
            }

            bool mangle( const string& str )
            {
                m_mangled << 'S' << str.size() << ':' << str;
                return true;
            }

            bool mangle( const ptr< void >& pv )
            {
                m_mangled << 'P';
                return true;
            }

            bool mangle( void* pv )
            {
                m_mangled << 'p';
                return true;
            }

            bool mangle( StringId strId )
            {
                // Some hard coded abbreviations for super recurrent stuff that ends up being present every time.
                // Not strictly necessary, but makes for shorter symbols.
                if( strId == "value"_sid )
                {
                    m_mangled << 'V';
                    return true;
                }

                if( strId == "constant"_sid )
                {
                    // Use o because we can't use a letter that also counts as an hex digit
                    // if we want to be able to unmangle it
                    m_mangled << 'o';
                    return true;
                }

                if( strId == "void"_sid )
                {
                    m_mangled << 'v';
                    return true;
                }

                if( strId == "type"_sid )
                {
                    m_mangled << 't';
                    return true;
                }

                if( strId == "rt_type"_sid )
                {
                    m_mangled << 'T';
                    return true;
                }

                if( strId == "quote"_sid )
                {
                    m_mangled << 'q';
                    return true;
                }

                if( strId == "func"_sid )
                {
                    m_mangled << 'u';
                    return true;
                }

                if( strId == "param"_sid )
                {
                    m_mangled << 'r';
                    return true;
                }

                if( strId == "predicates"_sid )
                {
                    m_mangled << 'R';
                    return true;
                }

                if( strId == "anykind"_sid )
                {
                    m_mangled << 'n';
                    return true;
                }

                if( strId == "bool"_sid )
                {
                    m_mangled << 'l';
                    return true;
                }

                if( strId == "ct_type"_sid )
                {
                    m_mangled << 'y';
                    return true;
                }

                if( strId == "access_level"_sid )
                {
                    m_mangled << 'z';
                    return true;
                }

                if( strId == "integer"_sid )
                {
                    m_mangled << 'I';
                    return true;
                }

                if( strId == "reference"_sid )
                {
                    m_mangled << 'N';
                    return true;
                }

                // Generic compression scheme to avoid storing the same stringId
                // literals multiple times.
                if( auto it = m_dictionary.find( strId ); it != m_dictionary.end() )
                {
                    m_mangled << '$' << it->second;
                    return true;
                }

                m_dictionary.emplace( strId, m_dictionary.size() );
                m_mangled << 's' << strId.str().size();

                if( strId.isNumerical() )
                    m_mangled << '#' << strId.id() << '#';
                else
                    m_mangled << ':' << strId.str();

                return true;
            }

            bool mangle( const pvec& vec )
            {
                if( vec->repetitionTerm() )
                    m_mangled << '@';
                else
                    m_mangled << '_';

                m_mangled << vec->terms().size();

                for( auto&& t : vec->terms() )
                {
                    if( !mangle( t ) )
                        return false;
                }

                if( vec->repetitionTerm() && !mangle( *vec->repetitionTerm() ) )
                    return false;

                return true;
            }

            bool mangle( const AnyTerm& a )
            {
                m_mangled << 'x';
                return true;
            }

            bool mangle( const Hole& h )
            {
                m_mangled << 'h';
                mangle( h.name() );
                mangle( h.kind() );
                mangle( static_cast< uint64_t >( h.behavior() ) );
                return true;
            }

            template< typename T >
            bool mangle( const T& t )
            {
                return false;
            }

            auto mangled() const
            {
                return m_mangled.str();
            }

        private:
            stringstream m_mangled;
            unordered_map< StringId, size_t > m_dictionary;
    };
}

namespace goose::codegen
{
    optional< string > Mangle( const Term& identity )
    {
        Mangler m;
        if( !m.mangle( builtins::StripPredicateHashesFromIdentity( identity ) ) )
            return nullopt;

        return m.mangled();
    }
}







|
<
<
|
|
<
|
|
|
<
<
<
|

|
|
|
|
|

|
|
|
|
|
|

|
|
|
<
<
<

|
|
|
|
|

|
|
|
|
|

|
|
|
|
|

|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|

|
|
|
|
|

|
|
|
|
|

|
|
|
|
|

|
|
|
|
|

|
|
|
|
|

|
|
|
|
|

|
|
|
|
|

|
|
|
|
|

|
|
|
|
|

|
|
|
|
|

|
|
|
|
|

|
|
|
|
|

|
|
|
|
|

|
|
|
|
|
|
|

|
|

|
|
|
|

|
|

|
|
|
|
|
|

|

|
<
|
|
<

|
|

|
|

|
|
|
|
|

|
|
|
|
|
|
|
|

|
<
|
<
<
<
|
|
<
<
<
|
|
|

|











|
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
// unecessary parts. This would require more intelligence to build an identity, although it might
// not necessarily be more complicated than the rule system idea above.

namespace
{
    class Mangler
    {
      public:


        Mangler() { m_mangled << hex; }


        bool mangle( const Term& t )
        {
            return visit( [&]( auto&& t ) { return mangle( t ); }, t );



        }

        bool mangle( uint64_t integer )
        {
            m_mangled << 'i' << integer;
            return true;
        }

        bool mangle( const BigInt& bi )
        {
            m_mangled << 'X';
            bi.toHex( m_mangled );
            return true;
        }

        // Locations shouldn't be included in the mangling, but
        // should not cause it to fail either.
        bool mangle( LocationId loc ) { return true; }




        bool mangle( const string& str )
        {
            m_mangled << 'S' << str.size() << ':' << str;
            return true;
        }

        bool mangle( const ptr< void >& pv )
        {
            m_mangled << 'P';
            return true;
        }

        bool mangle( void* pv )
        {
            m_mangled << 'p';
            return true;
        }

        bool mangle( StringId strId )
        {
            // Some hard coded abbreviations for super recurrent stuff that ends up being
            // present every time. Not strictly necessary, but makes for shorter symbols.
            if( strId == "value"_sid )
            {
                m_mangled << 'V';
                return true;
            }

            if( strId == "constant"_sid )
            {
                // Use o because we can't use a letter that also counts as an hex digit
                // if we want to be able to unmangle it
                m_mangled << 'o';
                return true;
            }

            if( strId == "void"_sid )
            {
                m_mangled << 'v';
                return true;
            }

            if( strId == "type"_sid )
            {
                m_mangled << 't';
                return true;
            }

            if( strId == "rt_type"_sid )
            {
                m_mangled << 'T';
                return true;
            }

            if( strId == "quote"_sid )
            {
                m_mangled << 'q';
                return true;
            }

            if( strId == "func"_sid )
            {
                m_mangled << 'u';
                return true;
            }

            if( strId == "param"_sid )
            {
                m_mangled << 'r';
                return true;
            }

            if( strId == "predicates"_sid )
            {
                m_mangled << 'R';
                return true;
            }

            if( strId == "anykind"_sid )
            {
                m_mangled << 'n';
                return true;
            }

            if( strId == "bool"_sid )
            {
                m_mangled << 'l';
                return true;
            }

            if( strId == "ct_type"_sid )
            {
                m_mangled << 'y';
                return true;
            }

            if( strId == "access_level"_sid )
            {
                m_mangled << 'z';
                return true;
            }

            if( strId == "integer"_sid )
            {
                m_mangled << 'I';
                return true;
            }

            if( strId == "reference"_sid )
            {
                m_mangled << 'N';
                return true;
            }

            // Generic compression scheme to avoid storing the same stringId
            // literals multiple times.
            if( auto it = m_dictionary.find( strId ); it != m_dictionary.end() )
            {
                m_mangled << '$' << it->second;
                return true;
            }

            m_dictionary.emplace( strId, m_dictionary.size() );
            m_mangled << 's' << strId.str().size();

            if( strId.isNumerical() )
                m_mangled << '#' << strId.id() << '#';
            else
                m_mangled << ':' << strId.str();

            return true;
        }

        bool mangle( const pvec& vec )
        {
            if( vec->repetitionTerm() )
                m_mangled << '@';
            else
                m_mangled << '_';

            m_mangled << vec->terms().size();

            for( auto&& t : vec->terms() )

                if( !mangle( t ) )
                    return false;


            if( vec->repetitionTerm() && !mangle( *vec->repetitionTerm() ) )
                return false;

            return true;
        }

        bool mangle( const AnyTerm& a )
        {
            m_mangled << 'x';
            return true;
        }

        bool mangle( const Hole& h )
        {
            m_mangled << 'h';
            mangle( h.name() );
            mangle( h.kind() );
            mangle( static_cast< uint64_t >( h.behavior() ) );
            return true;
        }

        template< typename T > bool mangle( const T& t ) { return false; }





        auto mangled() const { return m_mangled.str(); }




      private:
        stringstream m_mangled;
        unordered_map< StringId, size_t > m_dictionary;
    };
} // namespace

namespace goose::codegen
{
    optional< string > Mangle( const Term& identity )
    {
        Mangler m;
        if( !m.mangle( builtins::StripPredicateHashesFromIdentity( identity ) ) )
            return nullopt;

        return m.mangled();
    }
} // namespace goose::codegen
Changes to bs/codegen/module.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
using namespace goose;
using namespace goose::codegen;

Module::Module( const string& name ) :
    m_llvmModule( name, GetLLVMContext() ),
    m_dataLayout( &m_llvmModule ),
    m_llvmBuilder( GetLLVMContext() )
{}



void Module::dumpAsLlvmIr( const string& filename ) const
{
    error_code err;
    llvm::raw_fd_ostream out( filename, err );
    if( err )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            format( "Couln't open file '{}': {}.", filename, err.message() ) );
        return;
    }

    m_llvmModule.print( out, nullptr );
    out.flush();
}








<
>
>







|
|







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
using namespace goose;
using namespace goose::codegen;

Module::Module( const string& name ) :
    m_llvmModule( name, GetLLVMContext() ),
    m_dataLayout( &m_llvmModule ),
    m_llvmBuilder( GetLLVMContext() )

{
}

void Module::dumpAsLlvmIr( const string& filename ) const
{
    error_code err;
    llvm::raw_fd_ostream out( filename, err );
    if( err )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage(
            0, format( "Couln't open file '{}': {}.", filename, err.message() ) );
        return;
    }

    m_llvmModule.print( out, nullptr );
    out.flush();
}

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
{
    llvm::LoopAnalysisManager loopAnalysisManager;
    llvm::FunctionAnalysisManager functionAnalysisManager;
    llvm::CGSCCAnalysisManager cGSCCAnalysisManager;
    llvm::ModuleAnalysisManager moduleAnalysisManager;

    llvm::PassBuilder passBuilder( m_targetMachine );
    passBuilder.registerModuleAnalyses(moduleAnalysisManager);
    passBuilder.registerCGSCCAnalyses(cGSCCAnalysisManager);
    passBuilder.registerFunctionAnalyses(functionAnalysisManager);
    passBuilder.registerLoopAnalyses(loopAnalysisManager);

    passBuilder.crossRegisterProxies(loopAnalysisManager, functionAnalysisManager, cGSCCAnalysisManager, moduleAnalysisManager);

    auto optimPipeline = passBuilder.buildPerModuleDefaultPipeline(
        llvm::OptimizationLevel::O3 );

    optimPipeline.run( m_llvmModule, moduleAnalysisManager );
}

bool Module::emitToFile( const string& filename, llvm::CodeGenFileType type )
{
    error_code err;
    llvm::raw_fd_ostream out( filename.c_str(), err );
    if( err )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            format( "Couln't open file '{}': {}.", filename, err.message() ) );
        return false;
    }

    llvm::legacy::PassManager passMan;

    if( m_targetMachine->addPassesToEmitFile( passMan, out, nullptr, type ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "TargetMachine can't emit a file of this type." );
        return false;
    }

    passMan.run( m_llvmModule );
    out.flush();
    return true;
}







|
|
|
|
>
|

|
<










|
|







|
|







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
{
    llvm::LoopAnalysisManager loopAnalysisManager;
    llvm::FunctionAnalysisManager functionAnalysisManager;
    llvm::CGSCCAnalysisManager cGSCCAnalysisManager;
    llvm::ModuleAnalysisManager moduleAnalysisManager;

    llvm::PassBuilder passBuilder( m_targetMachine );
    passBuilder.registerModuleAnalyses( moduleAnalysisManager );
    passBuilder.registerCGSCCAnalyses( cGSCCAnalysisManager );
    passBuilder.registerFunctionAnalyses( functionAnalysisManager );
    passBuilder.registerLoopAnalyses( loopAnalysisManager );
    passBuilder.crossRegisterProxies(
        loopAnalysisManager, functionAnalysisManager, cGSCCAnalysisManager, moduleAnalysisManager );

    auto optimPipeline = passBuilder.buildPerModuleDefaultPipeline( llvm::OptimizationLevel::O3 );


    optimPipeline.run( m_llvmModule, moduleAnalysisManager );
}

bool Module::emitToFile( const string& filename, llvm::CodeGenFileType type )
{
    error_code err;
    llvm::raw_fd_ostream out( filename.c_str(), err );
    if( err )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage(
            0, format( "Couln't open file '{}': {}.", filename, err.message() ) );
        return false;
    }

    llvm::legacy::PassManager passMan;

    if( m_targetMachine->addPassesToEmitFile( passMan, out, nullptr, type ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage(
            0, "TargetMachine can't emit a file of this type." );
        return false;
    }

    passMan.run( m_llvmModule );
    out.flush();
    return true;
}
Changes to bs/codegen/module.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

59
60
61
62
63
64

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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
#ifndef GOOSE_CODEGEN_MODULE_H
#define GOOSE_CODEGEN_MODULE_H

namespace llvm
{
    class TargetMachine;
}

namespace goose::codegen
{
    class Module
    {
        public:
            Module( const string& name );

            bool setupTarget();
            void dumpAsLlvmIr( const string& filename ) const;

            ptr< codegen::Function > getOrCreateFunc( const Context& c, const builtins::Func& func );
            ptr< codegen::Function > getOrCreateFunc( const Context& c, const builtins::Func& func, const string& name,
                llvm::Function::LinkageTypes linkageType );

            void runOptimizationPasses();
            bool emitToFile( const string& filename, llvm::CodeGenFileType type );

        private:
            struct State
            {
                State( const Context& c, size_t numVars, llvm::Function* f ) :
                    context( c ),
                    llvmFunc( f ),
                    temporaries( make_shared< storage_type >( numVars ) )
                {}



                const Context& context;

                llvm::Function* llvmFunc = nullptr;
                llvm::BasicBlock* allocaBasicBlock = nullptr;
                llvm::AllocaInst* lastEmittedAlloca = nullptr;

                using storage_type = cir::TempStorage< ptr< codegen::Value > >;
                ptr< storage_type > temporaries;

                unordered_map< const cir::BasicBlock*, llvm::BasicBlock* > bbMap;

                Stack stack;
            };

            static llvm::BasicBlock* GetOrCreateLLVMBB( State& st, const ptr< cir::BasicBlock >& pBB );

            ptr< codegen::Function > buildFuncProto( const Context& c, const builtins::Func& func, const string& name,
                llvm::Function::LinkageTypes linkageType );

            llvm::Function* getCurrentFunction() const
            {
                return m_llvmBuilder.GetInsertBlock()->getParent();
            }


            llvm::BasicBlock* buildCFG( State& st, llvm::Function* llvmFunc, const ptr< cir::CFG >& pCFG );
            llvm::BasicBlock* buildBasicBlock( State& st, const ptr< cir::BasicBlock >& pBB );

            ptr< codegen::Value > buildValue( State& st, const eir::Value& val );
            ptr< codegen::Value > buildConstant( State& st, const eir::Value& val );
            ptr< codegen::Value > buildConstantPointer( State& st, const builtins::PointerType& ptType, const eir::Value& v );


            template< typename F >
            bool buildUnaryInstruction( State& st, F&& impl );

            template< typename F >
            bool buildBinaryInstruction( State& st, F&& impl );

            bool buildInstruction( State& st, const cir::InstrSeq& is );
            bool buildInstruction( State& st, const cir::Instruction& instr );
            bool buildInstruction( State& st, const monostate& );
            bool buildInstruction( State& st, const cir::Call& call );
            bool buildInstruction( State& st, const cir::Constant& ct );
            bool buildInstruction( State& st, const cir::VarAddr& va );
            bool buildInstruction( State& st, const cir::TempAddr& ta );
            bool buildInstruction( State& st, const cir::Select& s );
            bool buildInstruction( State& st, const cir::CreateTemporary& ct );
            bool buildInstruction( State& st, const cir::GetTemporary& gt );
            bool buildInstruction( State& st, const cir::AllocVar& av );
            bool buildInstruction( State& st, const cir::Load& load );
            bool buildInstruction( State& st, const cir::Store& store );
            bool buildInstruction( State& st, const cir::Phi& p );

            bool buildInstruction( State& st, const cir::Not& uo );
            bool buildInstruction( State& st, const cir::And& bo );
            bool buildInstruction( State& st, const cir::Or& bo );
            bool buildInstruction( State& st, const cir::Xor& bo );
            bool buildInstruction( State& st, const cir::Implies& bo );
            bool buildInstruction( State& st, const cir::IsPrefixOf& bo );

            bool buildInstruction( State& st, const cir::Shl& bo );
            bool buildInstruction( State& st, const cir::LShr& bo );
            bool buildInstruction( State& st, const cir::AShr& bo );

            bool buildInstruction( State& st, const cir::Add& bo );
            bool buildInstruction( State& st, const cir::Sub& bo );
            bool buildInstruction( State& st, const cir::Mul& bo );
            bool buildInstruction( State& st, const cir::UDiv& bo );
            bool buildInstruction( State& st, const cir::SDiv& bo );
            bool buildInstruction( State& st, const cir::URem& bo );
            bool buildInstruction( State& st, const cir::SRem& bo );

            bool buildInstruction( State& st, const cir::Eq& bo );
            bool buildInstruction( State& st, const cir::Neq& bo );
            bool buildInstruction( State& st, const cir::UGT& bo );
            bool buildInstruction( State& st, const cir::UGE& bo );
            bool buildInstruction( State& st, const cir::ULT& bo );
            bool buildInstruction( State& st, const cir::ULE& bo );
            bool buildInstruction( State& st, const cir::SGT& bo );
            bool buildInstruction( State& st, const cir::SGE& bo );
            bool buildInstruction( State& st, const cir::SLT& bo );
            bool buildInstruction( State& st, const cir::SLE& bo );

            bool buildInstruction( State& st, const cir::Assert& );
            bool buildInstruction( State& st, const cir::Placeholder& );
            bool buildInstruction( State& st, const cir::PHOverrideSet& );
            bool buildInstruction( State& st, const cir::PHOverrideClear& );
            bool buildInstruction( State& st, const cir::GhostCall& );
            bool buildInstruction( State& st, const cir::ForAllSetup& );
            bool buildInstruction( State& st, const cir::ForAll& );
            bool buildInstruction( State& st, const cir::PushType& );
            bool buildInstruction( State& st, const cir::PushStringId& );
            bool buildInstruction( State& st, const cir::CallCheck& );
            bool buildInstruction( State& st, const cir::InlineCall& );

            bool buildTerminator( State& st, const cir::Terminator& terminator );
            bool buildTerminator( State& st, const cir::RetVoid& r );
            bool buildTerminator( State& st, const cir::Ret& r );
            bool buildTerminator( State& st, const cir::Branch& b );
            bool buildTerminator( State& st, const cir::CondBranch& cb );
            bool buildTerminator( State& st, const cir::GhostBranch& gb );

            template< typename T >
            bool buildTerminator( State& st, const T& t )
            {
                return false;
            }

            ptr< codegen::Value > buildAlloca( State& st, const codegen::Type* type );

            llvm::Module m_llvmModule;
            llvm::DataLayout m_dataLayout;
            llvm::IRBuilder<> m_llvmBuilder;
            llvm::TargetMachine* m_targetMachine = nullptr;

            unordered_map< string, llvm::GlobalVariable* > m_strings;
            uint32_t m_nextAggregateID = 0;
    };
}


#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
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
#ifndef GOOSE_CODEGEN_MODULE_H
#define GOOSE_CODEGEN_MODULE_H

namespace llvm
{
    class TargetMachine;
}

namespace goose::codegen
{
    class Module
    {
      public:
        Module( const string& name );

        bool setupTarget();
        void dumpAsLlvmIr( const string& filename ) const;

        ptr< codegen::Function > getOrCreateFunc( const Context& c, const builtins::Func& func );
        ptr< codegen::Function > getOrCreateFunc( const Context& c, const builtins::Func& func,
            const string& name, llvm::Function::LinkageTypes linkageType );

        void runOptimizationPasses();
        bool emitToFile( const string& filename, llvm::CodeGenFileType type );

      private:
        struct State
        {
            State( const Context& c, size_t numVars, llvm::Function* f ) :
                context( c ),
                llvmFunc( f ),
                temporaries( make_shared< storage_type >( numVars ) )

            {
            }

            const Context& context;

            llvm::Function* llvmFunc = nullptr;
            llvm::BasicBlock* allocaBasicBlock = nullptr;
            llvm::AllocaInst* lastEmittedAlloca = nullptr;

            using storage_type = cir::TempStorage< ptr< codegen::Value > >;
            ptr< storage_type > temporaries;

            unordered_map< const cir::BasicBlock*, llvm::BasicBlock* > bbMap;

            Stack stack;
        };

        static llvm::BasicBlock* GetOrCreateLLVMBB( State& st, const ptr< cir::BasicBlock >& pBB );

        ptr< codegen::Function > buildFuncProto( const Context& c, const builtins::Func& func,
            const string& name, llvm::Function::LinkageTypes linkageType );

        llvm::Function* getCurrentFunction() const
        {
            return m_llvmBuilder.GetInsertBlock()->getParent();
        }

        llvm::BasicBlock* buildCFG(
            State& st, llvm::Function* llvmFunc, const ptr< cir::CFG >& pCFG );
        llvm::BasicBlock* buildBasicBlock( State& st, const ptr< cir::BasicBlock >& pBB );

        ptr< codegen::Value > buildValue( State& st, const eir::Value& val );
        ptr< codegen::Value > buildConstant( State& st, const eir::Value& val );
        ptr< codegen::Value > buildConstantPointer(
            State& st, const builtins::PointerType& ptType, const eir::Value& v );


        template< typename F > bool buildUnaryInstruction( State& st, F&& impl );


        template< typename F > bool buildBinaryInstruction( State& st, F&& impl );

        bool buildInstruction( State& st, const cir::InstrSeq& is );
        bool buildInstruction( State& st, const cir::Instruction& instr );
        bool buildInstruction( State& st, const monostate& );
        bool buildInstruction( State& st, const cir::Call& call );
        bool buildInstruction( State& st, const cir::Constant& ct );
        bool buildInstruction( State& st, const cir::VarAddr& va );
        bool buildInstruction( State& st, const cir::TempAddr& ta );
        bool buildInstruction( State& st, const cir::Select& s );
        bool buildInstruction( State& st, const cir::CreateTemporary& ct );
        bool buildInstruction( State& st, const cir::GetTemporary& gt );
        bool buildInstruction( State& st, const cir::AllocVar& av );
        bool buildInstruction( State& st, const cir::Load& load );
        bool buildInstruction( State& st, const cir::Store& store );
        bool buildInstruction( State& st, const cir::Phi& p );

        bool buildInstruction( State& st, const cir::Not& uo );
        bool buildInstruction( State& st, const cir::And& bo );
        bool buildInstruction( State& st, const cir::Or& bo );
        bool buildInstruction( State& st, const cir::Xor& bo );
        bool buildInstruction( State& st, const cir::Implies& bo );
        bool buildInstruction( State& st, const cir::IsPrefixOf& bo );

        bool buildInstruction( State& st, const cir::Shl& bo );
        bool buildInstruction( State& st, const cir::LShr& bo );
        bool buildInstruction( State& st, const cir::AShr& bo );

        bool buildInstruction( State& st, const cir::Add& bo );
        bool buildInstruction( State& st, const cir::Sub& bo );
        bool buildInstruction( State& st, const cir::Mul& bo );
        bool buildInstruction( State& st, const cir::UDiv& bo );
        bool buildInstruction( State& st, const cir::SDiv& bo );
        bool buildInstruction( State& st, const cir::URem& bo );
        bool buildInstruction( State& st, const cir::SRem& bo );

        bool buildInstruction( State& st, const cir::Eq& bo );
        bool buildInstruction( State& st, const cir::Neq& bo );
        bool buildInstruction( State& st, const cir::UGT& bo );
        bool buildInstruction( State& st, const cir::UGE& bo );
        bool buildInstruction( State& st, const cir::ULT& bo );
        bool buildInstruction( State& st, const cir::ULE& bo );
        bool buildInstruction( State& st, const cir::SGT& bo );
        bool buildInstruction( State& st, const cir::SGE& bo );
        bool buildInstruction( State& st, const cir::SLT& bo );
        bool buildInstruction( State& st, const cir::SLE& bo );

        bool buildInstruction( State& st, const cir::Assert& );
        bool buildInstruction( State& st, const cir::Placeholder& );
        bool buildInstruction( State& st, const cir::PHOverrideSet& );
        bool buildInstruction( State& st, const cir::PHOverrideClear& );
        bool buildInstruction( State& st, const cir::GhostCall& );
        bool buildInstruction( State& st, const cir::ForAllSetup& );
        bool buildInstruction( State& st, const cir::ForAll& );
        bool buildInstruction( State& st, const cir::PushType& );
        bool buildInstruction( State& st, const cir::PushStringId& );
        bool buildInstruction( State& st, const cir::CallCheck& );
        bool buildInstruction( State& st, const cir::InlineCall& );

        bool buildTerminator( State& st, const cir::Terminator& terminator );
        bool buildTerminator( State& st, const cir::RetVoid& r );
        bool buildTerminator( State& st, const cir::Ret& r );
        bool buildTerminator( State& st, const cir::Branch& b );
        bool buildTerminator( State& st, const cir::CondBranch& cb );
        bool buildTerminator( State& st, const cir::GhostBranch& gb );


        template< typename T > bool buildTerminator( State& st, const T& t ) { return false; }




        ptr< codegen::Value > buildAlloca( State& st, const codegen::Type* type );

        llvm::Module m_llvmModule;
        llvm::DataLayout m_dataLayout;
        llvm::IRBuilder<> m_llvmBuilder;
        llvm::TargetMachine* m_targetMachine = nullptr;

        unordered_map< string, llvm::GlobalVariable* > m_strings;
        uint32_t m_nextAggregateID = 0;
    };

} // namespace goose::codegen

#endif
Changes to bs/codegen/module.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
#ifndef GOOSE_CODEGEN_MODULE_INL
#define GOOSE_CODEGEN_MODULE_INL

namespace goose::codegen
{
    template< typename F >
    bool Module::buildUnaryInstruction( State& st, F&& impl )
    {
        auto operand = st.stack.pop( m_llvmBuilder );
        if( !operand )
            return false;

        st.stack.push( codegen::Value::Get( ( *operand )->cgType(), impl( *operand ) ) );
        return true;
    }

    template< typename F >
    bool Module::buildBinaryInstruction( State& st, F&& impl )
    {
        auto rhs = st.stack.pop( m_llvmBuilder );
        if( !rhs )
            return false;

        auto lhs = st.stack.pop( m_llvmBuilder );
        if( !lhs )
            return false;

        st.stack.push( codegen::Value::Get( ( *lhs )->cgType(), impl( *lhs, *rhs ) ) );
        return true;
    }
}

#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
#ifndef GOOSE_CODEGEN_MODULE_INL
#define GOOSE_CODEGEN_MODULE_INL

namespace goose::codegen
{

    template< typename F > bool Module::buildUnaryInstruction( State& st, F&& impl )
    {
        auto operand = st.stack.pop( m_llvmBuilder );
        if( !operand )
            return false;

        st.stack.push( codegen::Value::Get( ( *operand )->cgType(), impl( *operand ) ) );
        return true;
    }


    template< typename F > bool Module::buildBinaryInstruction( State& st, F&& impl )
    {
        auto rhs = st.stack.pop( m_llvmBuilder );
        if( !rhs )
            return false;

        auto lhs = st.stack.pop( m_llvmBuilder );
        if( !lhs )
            return false;

        st.stack.push( codegen::Value::Get( ( *lhs )->cgType(), impl( *lhs, *rhs ) ) );
        return true;
    }
} // namespace goose::codegen

#endif
Changes to bs/codegen/stack.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
#ifndef GOOSE_CODEGEN_STACK_H
#define GOOSE_CODEGEN_STACK_H

namespace goose::codegen
{
    class Stack
    {
        public:
            using Slot = variant< ptr< codegen::Value >, codegen::Address >;

            template< typename T = ptr< codegen::Value > >
            optional< T > pop( llvm::IRBuilder<>& builder )
            {

                static_assert( is_same_v< T, ptr< codegen::Value > > || is_same_v< T, codegen::Address > );

                if( m_stack.empty() )
                    return nullopt;

                auto result = m_stack.top();
                m_stack.pop();

                if constexpr( is_same_v< T, codegen::Address > )
                {
                    if( !holds_alternative< codegen::Address >( result ) )
                        return nullopt;
                    return get< codegen::Address >( result );
                }
                else if constexpr( is_same_v< T, ptr< codegen::Value > > )
                {
                    if( holds_alternative< codegen::Address >( result ) )
                        return AddressToGEP( builder, get< codegen::Address >( result ) );
                    else
                        return InsertLoadIfNeeded( builder, get< ptr< codegen::Value > >( result ) );
                }

                return nullopt;
            }

            optional< Slot > pop()
            {
                if( m_stack.empty() )
                    return nullopt;

                auto result = m_stack.top();
                m_stack.pop();
                return result;
            }

            template< typename T >
            void push( T&& v )
            {
                m_stack.push( forward< T >( v ) );
            }

        private:
            static ptr< codegen::Value > InsertLoadIfNeeded( llvm::IRBuilder<>& builder, const ptr< codegen::Value >& v )

            {
                if( llvm::isa< llvm::AllocaInst >( v->llvmValue() ) )
                {
                    return codegen::Value::Get( v->cgType(), builder.CreateLoad(

                        llvm::cast< llvm::AllocaInst >( v->llvmValue() )->getAllocatedType(), *v ) );

                }

                return v;
            }

            stack< Slot > m_stack;
    };
}


#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
64
65
66
67
68

69
70
71
#ifndef GOOSE_CODEGEN_STACK_H
#define GOOSE_CODEGEN_STACK_H

namespace goose::codegen
{
    class Stack
    {
      public:
        using Slot = variant< ptr< codegen::Value >, codegen::Address >;

        template< typename T = ptr< codegen::Value > >
        optional< T > pop( llvm::IRBuilder<>& builder )
        {
            static_assert(
                is_same_v< T, ptr< codegen::Value > > || is_same_v< T, codegen::Address > );

            if( m_stack.empty() )
                return nullopt;

            auto result = m_stack.top();
            m_stack.pop();

            if constexpr( is_same_v< T, codegen::Address > )
            {
                if( !holds_alternative< codegen::Address >( result ) )
                    return nullopt;
                return get< codegen::Address >( result );
            }
            else if constexpr( is_same_v< T, ptr< codegen::Value > > )
            {
                if( holds_alternative< codegen::Address >( result ) )
                    return AddressToGEP( builder, get< codegen::Address >( result ) );
                else
                    return InsertLoadIfNeeded( builder, get< ptr< codegen::Value > >( result ) );
            }

            return nullopt;
        }

        optional< Slot > pop()
        {
            if( m_stack.empty() )
                return nullopt;

            auto result = m_stack.top();
            m_stack.pop();
            return result;
        }

        template< typename T > void push( T&& v ) { m_stack.push( forward< T >( v ) ); }





      private:
        static ptr< codegen::Value > InsertLoadIfNeeded(
            llvm::IRBuilder<>& builder, const ptr< codegen::Value >& v )
        {
            if( llvm::isa< llvm::AllocaInst >( v->llvmValue() ) )
            {
                return codegen::Value::Get( v->cgType(),
                    builder.CreateLoad(
                        llvm::cast< llvm::AllocaInst >( v->llvmValue() )->getAllocatedType(),
                        *v ) );
            }

            return v;
        }

        stack< Slot > m_stack;
    };

} // namespace goose::codegen

#endif
Changes to bs/codegen/value.cpp.
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
{
    G_VAL_ASSERT( val, val.isConstant() );

    auto type = *EIRToValue( val.type() );

    if( auto b = FromValue< bool >( val ) )
    {
        return codegen::Value::Get( *b ?
            llvm::ConstantInt::getTrue( GetLLVMContext() ) :
            llvm::ConstantInt::getFalse( GetLLVMContext() ) );
    }
    else if( auto intVal = FromValue< APSInt >( val ) )
    {
        return codegen::Value::Get( llvm::ConstantInt::get( *GetCodegenType( type ),
            *FromValue< APSInt >( val ) ) );
    }
    else if( auto ptType = FromValue< builtins::PointerType >( type ) )
    {
        return buildConstantPointer( st, *ptType, val );
    }
    else if( IsTupleType( type ) )
    {
        llvm::SmallVector< llvm::Constant*, 8 > members;

        bool success = true;
        ForEachInTuple( val, [&]( auto&& v )

        {
            auto pMemberVal = buildConstant( st, v );
            if( !pMemberVal )
            {
                success = false;
                return false;
            }

            members.push_back( static_cast< llvm::Constant* >( pMemberVal->llvmValue() ) );
            return true;
        } );

        if( !success )
            return {};

        return codegen::Value::Get( llvm::ConstantStruct::get( static_cast< llvm::StructType* >
            ( GetCodegenType( type )->llvmType() ), members ) );
    }
    else if( auto funcType = FromValue< FuncType >( type ) )
    {
        if( IsBuiltinFunc( val ) || IsBuiltinIntrinsicFunc( val ) )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage(
                val.locationId(), "builtin functions can't be called at runtime." );
            return {};
        }

         // If it is a plain function, we just need to generate or retrieve it.
        if( val.isConstant() )
        {
            auto f = FromValue< builtins::Func >( val );
            if( !f )
                return {};

            llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );
            return getOrCreateFunc( st.context, *f );
        }
        else
        {
            // TODO: function pointers
            assert( false );
            return {};
        }
    }

    DiagnosticsManager::GetInstance().emitErrorMessage(
        val.locationId(), "constants with compile-time types are not supported by code generation.", 0 );
    return {};
}

ptr< codegen::Value > Module::buildConstantPointer( State& st, const builtins::PointerType& ptType, const eir::Value& v )

{
    // We currently have two types of pointer constants: nullptr,
    // and pointers to string literals.
    if( auto str = get_if< string >( &v.val() ) )
    {
        llvm::GlobalVariable* pGlob = nullptr;

        auto it = m_strings.find( *str );
        if( it == m_strings.end() )
        {
            stringstream name;
            name << ".str" << hex << m_strings.size();

            auto* arType = llvm::ArrayType::get( llvm::IntegerType::getInt8Ty( GetLLVMContext() ),
                str->size() + 1 );

            pGlob = new llvm::GlobalVariable( m_llvmModule,
                arType, true, llvm::GlobalValue::LinkageTypes::PrivateLinkage,
                llvm::ConstantDataArray::getString( GetLLVMContext(), *str ),
                name.str() );

            m_strings.emplace( *str, pGlob );
        }
        else
            pGlob = it->second;

        return codegen::Value::Get( pGlob );
    }

    return codegen::Value::Get( llvm::ConstantPointerNull::get( static_cast< llvm::PointerType* >
        ( GetCodegenType( *EIRToValue( v.type() ) )->llvmType() ) ) );
}







|
<
|



|
|










|
>
|
|
|
|
|
|
|

|
|
|




|
|










|

















|
|



|
>













|
|

|
|
|
<









|
|

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
{
    G_VAL_ASSERT( val, val.isConstant() );

    auto type = *EIRToValue( val.type() );

    if( auto b = FromValue< bool >( val ) )
    {
        return codegen::Value::Get( *b ? llvm::ConstantInt::getTrue( GetLLVMContext() ) :

                                         llvm::ConstantInt::getFalse( GetLLVMContext() ) );
    }
    else if( auto intVal = FromValue< APSInt >( val ) )
    {
        return codegen::Value::Get(
            llvm::ConstantInt::get( *GetCodegenType( type ), *FromValue< APSInt >( val ) ) );
    }
    else if( auto ptType = FromValue< builtins::PointerType >( type ) )
    {
        return buildConstantPointer( st, *ptType, val );
    }
    else if( IsTupleType( type ) )
    {
        llvm::SmallVector< llvm::Constant*, 8 > members;

        bool success = true;
        ForEachInTuple( val,
            [&]( auto&& v )
            {
                auto pMemberVal = buildConstant( st, v );
                if( !pMemberVal )
                {
                    success = false;
                    return false;
                }

                members.push_back( static_cast< llvm::Constant* >( pMemberVal->llvmValue() ) );
                return true;
            } );

        if( !success )
            return {};

        return codegen::Value::Get( llvm::ConstantStruct::get(
            static_cast< llvm::StructType* >( GetCodegenType( type )->llvmType() ), members ) );
    }
    else if( auto funcType = FromValue< FuncType >( type ) )
    {
        if( IsBuiltinFunc( val ) || IsBuiltinIntrinsicFunc( val ) )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage(
                val.locationId(), "builtin functions can't be called at runtime." );
            return {};
        }

        // If it is a plain function, we just need to generate or retrieve it.
        if( val.isConstant() )
        {
            auto f = FromValue< builtins::Func >( val );
            if( !f )
                return {};

            llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );
            return getOrCreateFunc( st.context, *f );
        }
        else
        {
            // TODO: function pointers
            assert( false );
            return {};
        }
    }

    DiagnosticsManager::GetInstance().emitErrorMessage( val.locationId(),
        "constants with compile-time types are not supported by code generation.", 0 );
    return {};
}

ptr< codegen::Value > Module::buildConstantPointer(
    State& st, const builtins::PointerType& ptType, const eir::Value& v )
{
    // We currently have two types of pointer constants: nullptr,
    // and pointers to string literals.
    if( auto str = get_if< string >( &v.val() ) )
    {
        llvm::GlobalVariable* pGlob = nullptr;

        auto it = m_strings.find( *str );
        if( it == m_strings.end() )
        {
            stringstream name;
            name << ".str" << hex << m_strings.size();

            auto* arType = llvm::ArrayType::get(
                llvm::IntegerType::getInt8Ty( GetLLVMContext() ), str->size() + 1 );

            pGlob = new llvm::GlobalVariable( m_llvmModule, arType, true,
                llvm::GlobalValue::LinkageTypes::PrivateLinkage,
                llvm::ConstantDataArray::getString( GetLLVMContext(), *str ), name.str() );


            m_strings.emplace( *str, pGlob );
        }
        else
            pGlob = it->second;

        return codegen::Value::Get( pGlob );
    }

    return codegen::Value::Get( llvm::ConstantPointerNull::get( static_cast< llvm::PointerType* >(
        GetCodegenType( *EIRToValue( v.type() ) )->llvmType() ) ) );
}
Changes to bs/compile/compiler.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "compiler.h"
#include "builtins/builtins.h"
#include "g0api/g0api.h"
#include "sema/sema.h"
// TODO_SSA reenable
//#include "verify/verify.h"
#include "execute/execute.h"
#include "diagnostics/diagnostics.h"

#include "llvm/InitializePasses.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/TargetSelect.h"






|







1
2
3
4
5
6
7
8
9
10
11
12
13
#include "compiler.h"
#include "builtins/builtins.h"
#include "g0api/g0api.h"
#include "sema/sema.h"
// TODO_SSA reenable
// #include "verify/verify.h"
#include "execute/execute.h"
#include "diagnostics/diagnostics.h"

#include "llvm/InitializePasses.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/TargetSelect.h"

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
            cmdArgs = cmdArgs + argv[i];
        }

        SetupBuiltins( *m_pEnv );
        parse::SetupParsingBuiltins( *m_pEnv );
        g0api::SetupG0Api( *m_pEnv );

        RegisterBuiltinFunc< string() >( *m_pEnv, "Args"_sid,
            [cmdArgs]()
            {
                return cmdArgs;
            } );

        RegisterBuiltinFunc< void ( string ) >( *m_pEnv, "Print"_sid,
            []( const string& str )
            {
                cout << str;
            } );

        llvm::InitializeNativeTarget();
        llvm::InitializeNativeTargetAsmPrinter();
    }

    uint32_t Compiler::execute( const string& filename )
    {
        auto locVarsIdentity = AppendToVectorTerm( builtins::RootG0Identity(), TSID( 0_locvars ) );
        m_pEnv->addVisibilityRule( builtins::RootG0Identity(), locVarsIdentity );
        auto result = LoadAndExecuteFile( m_pEnv, filename, locVarsIdentity, GetValueType< uint32_t >(), ToValue< uint32_t >( 1 ) );


        if( DiagnosticsManager::GetInstance().errorsWereEmitted() )
            return 1;

        if( !result )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( 0,
                format( "{}: could not be executed.", filename ) );
            return 1;
        }

        if( result->isPoison() )
            return 1;

        return *FromValue< uint32_t >( *result );
    }

    optional< Value > Compiler::LoadAndExecuteFile( const ptr< Env >& e, const string& filename, const Term& identity,
        const Term& returnType, optional< Value > defRetVal )
    {
        ProfileZoneScoped;
        ProfileZoneName( filename.c_str(), filename.size() );

        auto cfg = LoadAndParseFile( e, filename, identity, returnType, defRetVal );
        if( !cfg || cfg->isPoisoned() )
            return PoisonValue();

        if( !cfg->entryBB()->canBeExecuted() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( 0,
                format( "{}: can not be executed.", filename ) );
            return PoisonValue();
        }

        sema::Context c( e, identity, returnType );
        Context::DefaultContextGuard dfg( c );

        execute::VM vm;
        return vm.execute( *cfg );
    }

    ptr< cir::CFG > Compiler::LoadAndParseFile( const ptr< Env >& e, const string& filename, const Term& identity,
        const Term& returnType, optional< Value > defRetVal )
    {
        ProfileZoneScoped;
        ProfileZoneName( filename.c_str(), filename.size() );

        auto& dm = DiagnosticsManager::GetInstance();

        assert( returnType == GetValueType< void >() || defRetVal );

        ifstream sourcefile( filename.c_str() );
        if( !sourcefile.good() )
        {
            dm.emitErrorMessage( 0,
                format( "can't open '{}'.", filename ) );
            return nullptr;
        }

        sema::Context c( e, identity, returnType );
        Context::DefaultContextGuard dfg( c );

        DiagnosticsContext dc( 0, true );
        VerbosityContext vc( Verbosity::Normal, true );

        auto cfg = make_shared< cir::CFG >( 0 );
        cfg->createBB();
        cfg->setCurrentBB( cfg->entryBB() );

        auto cb = make_shared< CodeBuilder >( cfg );
        c.setBuilder( ToValue( TypeWrapper< ptr< CodeBuilder > >( cb ) ) );

        auto r = make_shared< parse::Resolver >(
            make_shared< lex::Lexer >( sourcefile, filename ), c );
        parse::Parser p( r );

        p.parseSequence();

        if( cfg->isPoisoned() )
            return cfg;








|
<
|
<
<
<
|
|
<
<
<









|
>






|
|









|
|










|
|










|
|











|
<
















|
|







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
            cmdArgs = cmdArgs + argv[i];
        }

        SetupBuiltins( *m_pEnv );
        parse::SetupParsingBuiltins( *m_pEnv );
        g0api::SetupG0Api( *m_pEnv );

        RegisterBuiltinFunc< string() >( *m_pEnv, "Args"_sid, [cmdArgs]() { return cmdArgs; } );





        RegisterBuiltinFunc< void( string ) >(
            *m_pEnv, "Print"_sid, []( const string& str ) { cout << str; } );




        llvm::InitializeNativeTarget();
        llvm::InitializeNativeTargetAsmPrinter();
    }

    uint32_t Compiler::execute( const string& filename )
    {
        auto locVarsIdentity = AppendToVectorTerm( builtins::RootG0Identity(), TSID( 0_locvars ) );
        m_pEnv->addVisibilityRule( builtins::RootG0Identity(), locVarsIdentity );
        auto result = LoadAndExecuteFile( m_pEnv, filename, locVarsIdentity,
            GetValueType< uint32_t >(), ToValue< uint32_t >( 1 ) );

        if( DiagnosticsManager::GetInstance().errorsWereEmitted() )
            return 1;

        if( !result )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage(
                0, format( "{}: could not be executed.", filename ) );
            return 1;
        }

        if( result->isPoison() )
            return 1;

        return *FromValue< uint32_t >( *result );
    }

    optional< Value > Compiler::LoadAndExecuteFile( const ptr< Env >& e, const string& filename,
        const Term& identity, const Term& returnType, optional< Value > defRetVal )
    {
        ProfileZoneScoped;
        ProfileZoneName( filename.c_str(), filename.size() );

        auto cfg = LoadAndParseFile( e, filename, identity, returnType, defRetVal );
        if( !cfg || cfg->isPoisoned() )
            return PoisonValue();

        if( !cfg->entryBB()->canBeExecuted() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage(
                0, format( "{}: can not be executed.", filename ) );
            return PoisonValue();
        }

        sema::Context c( e, identity, returnType );
        Context::DefaultContextGuard dfg( c );

        execute::VM vm;
        return vm.execute( *cfg );
    }

    ptr< cir::CFG > Compiler::LoadAndParseFile( const ptr< Env >& e, const string& filename,
        const Term& identity, const Term& returnType, optional< Value > defRetVal )
    {
        ProfileZoneScoped;
        ProfileZoneName( filename.c_str(), filename.size() );

        auto& dm = DiagnosticsManager::GetInstance();

        assert( returnType == GetValueType< void >() || defRetVal );

        ifstream sourcefile( filename.c_str() );
        if( !sourcefile.good() )
        {
            dm.emitErrorMessage( 0, format( "can't open '{}'.", filename ) );

            return nullptr;
        }

        sema::Context c( e, identity, returnType );
        Context::DefaultContextGuard dfg( c );

        DiagnosticsContext dc( 0, true );
        VerbosityContext vc( Verbosity::Normal, true );

        auto cfg = make_shared< cir::CFG >( 0 );
        cfg->createBB();
        cfg->setCurrentBB( cfg->entryBB() );

        auto cb = make_shared< CodeBuilder >( cfg );
        c.setBuilder( ToValue( TypeWrapper< ptr< CodeBuilder > >( cb ) ) );

        auto r =
            make_shared< parse::Resolver >( make_shared< lex::Lexer >( sourcefile, filename ), c );
        parse::Parser p( r );

        p.parseSequence();

        if( cfg->isPoisoned() )
            return cfg;

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
            {
                auto converted = ConvertValueToType( c, *defRetVal, returnType );
                if( holds_alternative< ValUnifyError >( converted ) )
                {
                    switch( get< ValUnifyError >( converted ) )
                    {
                        case ValUnifyError::NoSolution:

                            dm.emitErrorMessage( defRetVal->locationId(), "default return value type mismatch." );
                            break;

                        case ValUnifyError::Ambiguous:
                            dm.emitErrorMessage( defRetVal->locationId(), "ambiguous default return value conversion." );

                            break;
                    }

                    return nullptr;
                }

                cfg->currentBB()->append( get< Value >( converted ) );
                cfg->emitTerminator( r->currentLocation(), cir::Ret( r->currentLocation() ) );
            }
        }

        // TODO_SSA reenable or not
        //ReindexVars( cfg );

        // TODO_SSA reenable
        /*verify::Func fv( c, cfg, returnType );
        if( !fv.verify() )
            return nullptr;*/

        return cfg;
    }
}







>
|



|
>












|








|
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
            {
                auto converted = ConvertValueToType( c, *defRetVal, returnType );
                if( holds_alternative< ValUnifyError >( converted ) )
                {
                    switch( get< ValUnifyError >( converted ) )
                    {
                        case ValUnifyError::NoSolution:
                            dm.emitErrorMessage(
                                defRetVal->locationId(), "default return value type mismatch." );
                            break;

                        case ValUnifyError::Ambiguous:
                            dm.emitErrorMessage( defRetVal->locationId(),
                                "ambiguous default return value conversion." );
                            break;
                    }

                    return nullptr;
                }

                cfg->currentBB()->append( get< Value >( converted ) );
                cfg->emitTerminator( r->currentLocation(), cir::Ret( r->currentLocation() ) );
            }
        }

        // TODO_SSA reenable or not
        // ReindexVars( cfg );

        // TODO_SSA reenable
        /*verify::Func fv( c, cfg, returnType );
        if( !fv.verify() )
            return nullptr;*/

        return cfg;
    }
} // namespace goose::compile
Changes to bs/compile/compiler.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_COMPILER_H
#define GOOSE_COMPILER_H

#include "sema/sema.h"

namespace goose::compile
{
    using namespace std;

    class Compiler
    {
        public:
            Compiler( int argc, char** argv );
            uint32_t execute( const string& filename );

            static optional< eir::Value > LoadAndExecuteFile( const util::ptr< sema::Env >& e, const string& filename,
                const eir::Term& identity, const eir::Term& returnType, optional< eir::Value > defRetVal );


            static util::ptr< cir::CFG > LoadAndParseFile( const util::ptr< sema::Env >& e, const string& filename,
                const eir::Term& identity, const eir::Term& returnType, optional< eir::Value > defRetVal );


        private:
            util::ptr< sema::Env > m_pEnv;
    };
}


#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
#ifndef GOOSE_COMPILER_H
#define GOOSE_COMPILER_H

#include "sema/sema.h"

namespace goose::compile
{
    using namespace std;

    class Compiler
    {
      public:
        Compiler( int argc, char** argv );
        uint32_t execute( const string& filename );

        static optional< eir::Value > LoadAndExecuteFile( const util::ptr< sema::Env >& e,
            const string& filename, const eir::Term& identity, const eir::Term& returnType,
            optional< eir::Value > defRetVal );

        static util::ptr< cir::CFG > LoadAndParseFile( const util::ptr< sema::Env >& e,
            const string& filename, const eir::Term& identity, const eir::Term& returnType,
            optional< eir::Value > defRetVal );

      private:
        util::ptr< sema::Env > m_pEnv;
    };

} // namespace goose::compile

#endif
Changes to bs/diagnostics/diagnosticscontext.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
#ifndef GOOSE_DIAGNOSTICS_DISAGNOSTICSCONTEXT_H
#define GOOSE_DIAGNOSTICS_DISAGNOSTICSCONTEXT_H

namespace goose::diagnostics
{
    class DiagnosticsContext
    {
        public:
            template< typename S >
            DiagnosticsContext( LocationId location, S&& desc, bool rootContext = false )
            {
                DiagnosticsManager::GetInstance().pushDiagnosticsContext(
                    make_shared< ContextInfos >( forward< S >( desc ), location, rootContext ) );
            }

            DiagnosticsContext( LocationId location, bool rootContext = false )
            {
                DiagnosticsManager::GetInstance().pushDiagnosticsContext(
                    make_shared< ContextInfos >( "", location, rootContext ) );
            }

            DiagnosticsContext( const DiagnosticsContext& ) = delete;

            ~DiagnosticsContext()
            {
                DiagnosticsManager::GetInstance().popDiagnosticsContext();
            }
    };
}


#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
#ifndef GOOSE_DIAGNOSTICS_DISAGNOSTICSCONTEXT_H
#define GOOSE_DIAGNOSTICS_DISAGNOSTICSCONTEXT_H

namespace goose::diagnostics
{
    class DiagnosticsContext
    {
      public:
        template< typename S >
        DiagnosticsContext( LocationId location, S&& desc, bool rootContext = false )
        {
            DiagnosticsManager::GetInstance().pushDiagnosticsContext(
                make_shared< ContextInfos >( forward< S >( desc ), location, rootContext ) );
        }

        DiagnosticsContext( LocationId location, bool rootContext = false )
        {
            DiagnosticsManager::GetInstance().pushDiagnosticsContext(
                make_shared< ContextInfos >( "", location, rootContext ) );
        }

        DiagnosticsContext( const DiagnosticsContext& ) = delete;



        ~DiagnosticsContext() { DiagnosticsManager::GetInstance().popDiagnosticsContext(); }

    };

} // namespace goose::diagnostics

#endif
Changes to bs/diagnostics/diagnosticsmanager.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
75
76
77
78

79
80
81

82
83
84
85
86
87
88
89
    {
        if( m_vContextStack.empty() )
            return;

        m_vContextStack.back()->setVerbosity( v );
    }

    void DiagnosticsManager::emitErrorMessage( LocationId locationId, const string& message, uint32_t contextLevels )

    {
        if( !m_vContextStack.empty() && m_vContextStack.back()->verbosity() != Verbosity::Normal )
            return;

        emitError( locationId, message, contextLevels );
    }


    void DiagnosticsManager::emitErrorMessage( LocationId locationId, StringId errId, const string& message, uint32_t contextLevels )
    {
        if( !m_vContextStack.empty() && m_vContextStack.back()->verbosity() != Verbosity::Normal )
            return;

        auto it = m_customDiagnostics.find( make_pair( locationId, errId ) );
        if( it == m_customDiagnostics.end() )
            emitError( locationId, message, contextLevels );
        else
        {
            auto&& [msg, contexts] = it->second;
            for( auto it = contexts.rbegin(); it != contexts.rend(); ++it )
                m_contextStack.emplace_back( *it );

            emitError( locationId, msg.empty() ? message : msg, contextLevels );

            m_contextStack.resize( m_contextStack.size() - contexts.size() );
        }
    }

    void DiagnosticsManager::emitSyntaxErrorMessage( LocationId locationId, const string& message, uint32_t contextLevels )

    {
        emitErrorMessage( locationId, message, contextLevels );
        setCurrentVerbosityLevel( Verbosity::Silent );
    }

    void DiagnosticsManager::emitLexerErrorMessage( LocationId locationId, const string& message )
    {
        emitErrorMessage( locationId, message, 0 );
    }

    void DiagnosticsManager::emitTraceMessage( LocationId locationId, const string& message )
    {
        Renderer r( llvm::outs() );

        r.addContext( locationId, Renderer::Squiggles,
            message, Renderer::Colors::Trace );
    }

    void DiagnosticsManager::defineCustomDiagnostic( LocationId locationId, StringId errId, const string& message )

    {
        vector< ptr< ContextInfos > > contexts;
        contexts.reserve( m_contextStack.size() );
        for( auto it = m_contextStack.rbegin(); it != m_contextStack.rend(); ++it )
        {
            bool isRootContext = get< 2 >( **it );
            get< 2 >( **it ) = false;
            contexts.emplace_back( *it );
            if( isRootContext )
                break;
        }


        m_customDiagnostics.emplace( make_pair( locationId, errId ), make_pair( message, move( contexts ) ) );
    }


    void DiagnosticsManager::emitError( LocationId locationId, const string& message, uint32_t contextLevels )
    {
        if( m_usedUpErrorLocations.find( locationId ) != m_usedUpErrorLocations.end() )
            return;
        m_usedUpErrorLocations.emplace( locationId );

        if( m_emittedFirstError )
            llvm::errs() << '\n';







|
>







>
|



















|
>














|
<


|
>












>
|


>
|







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
    {
        if( m_vContextStack.empty() )
            return;

        m_vContextStack.back()->setVerbosity( v );
    }

    void DiagnosticsManager::emitErrorMessage(
        LocationId locationId, const string& message, uint32_t contextLevels )
    {
        if( !m_vContextStack.empty() && m_vContextStack.back()->verbosity() != Verbosity::Normal )
            return;

        emitError( locationId, message, contextLevels );
    }

    void DiagnosticsManager::emitErrorMessage(
        LocationId locationId, StringId errId, const string& message, uint32_t contextLevels )
    {
        if( !m_vContextStack.empty() && m_vContextStack.back()->verbosity() != Verbosity::Normal )
            return;

        auto it = m_customDiagnostics.find( make_pair( locationId, errId ) );
        if( it == m_customDiagnostics.end() )
            emitError( locationId, message, contextLevels );
        else
        {
            auto&& [msg, contexts] = it->second;
            for( auto it = contexts.rbegin(); it != contexts.rend(); ++it )
                m_contextStack.emplace_back( *it );

            emitError( locationId, msg.empty() ? message : msg, contextLevels );

            m_contextStack.resize( m_contextStack.size() - contexts.size() );
        }
    }

    void DiagnosticsManager::emitSyntaxErrorMessage(
        LocationId locationId, const string& message, uint32_t contextLevels )
    {
        emitErrorMessage( locationId, message, contextLevels );
        setCurrentVerbosityLevel( Verbosity::Silent );
    }

    void DiagnosticsManager::emitLexerErrorMessage( LocationId locationId, const string& message )
    {
        emitErrorMessage( locationId, message, 0 );
    }

    void DiagnosticsManager::emitTraceMessage( LocationId locationId, const string& message )
    {
        Renderer r( llvm::outs() );

        r.addContext( locationId, Renderer::Squiggles, message, Renderer::Colors::Trace );

    }

    void DiagnosticsManager::defineCustomDiagnostic(
        LocationId locationId, StringId errId, const string& message )
    {
        vector< ptr< ContextInfos > > contexts;
        contexts.reserve( m_contextStack.size() );
        for( auto it = m_contextStack.rbegin(); it != m_contextStack.rend(); ++it )
        {
            bool isRootContext = get< 2 >( **it );
            get< 2 >( **it ) = false;
            contexts.emplace_back( *it );
            if( isRootContext )
                break;
        }

        m_customDiagnostics.emplace(
            make_pair( locationId, errId ), make_pair( message, move( contexts ) ) );
    }

    void DiagnosticsManager::emitError(
        LocationId locationId, const string& message, uint32_t contextLevels )
    {
        if( m_usedUpErrorLocations.find( locationId ) != m_usedUpErrorLocations.end() )
            return;
        m_usedUpErrorLocations.emplace( locationId );

        if( m_emittedFirstError )
            llvm::errs() << '\n';
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
            if( contextLevels == 0 )
                break;

            auto&& [desc, location, isRootContext] = **it;

            // Highlight all context as error until the first one that have a valid location.
            // This is because sometimes the error originates someplace abstract, such as resolving
            // an internally created overload set for an operator and we only get a source code position
            // in the parent context, and we want that, as the place where the actual faulty code has been written,
            // to be displayed in red.
            if( locationId.invalid() && !location.invalid() )
            {
                locationId = location;
                r.addContext( location, Renderer::Caret | Renderer::Squiggles | Renderer::Highlight,
                    desc, Renderer::Colors::Error );
            }
            else
            {
                r.addContext( location, Renderer::Squiggles,
                    desc, Renderer::Colors::Context );
            }

            if( isRootContext )
                break;

            if( contextLevels != ~0U )
                --contextLevels;







|
|
|








|
<







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
            if( contextLevels == 0 )
                break;

            auto&& [desc, location, isRootContext] = **it;

            // Highlight all context as error until the first one that have a valid location.
            // This is because sometimes the error originates someplace abstract, such as resolving
            // an internally created overload set for an operator and we only get a source code
            // position in the parent context, and we want that, as the place where the actual
            // faulty code has been written, to be displayed in red.
            if( locationId.invalid() && !location.invalid() )
            {
                locationId = location;
                r.addContext( location, Renderer::Caret | Renderer::Squiggles | Renderer::Highlight,
                    desc, Renderer::Colors::Error );
            }
            else
            {
                r.addContext( location, Renderer::Squiggles, desc, Renderer::Colors::Context );

            }

            if( isRootContext )
                break;

            if( contextLevels != ~0U )
                --contextLevels;
165
166
167
168
169
170
171
172
        m_vContextStack.emplace_back( vc );
    }

    void DiagnosticsManager::popVerbosityContext()
    {
        m_vContextStack.resize( m_vContextStack.size() - 1 );
    }
}







|
169
170
171
172
173
174
175
176
        m_vContextStack.emplace_back( vc );
    }

    void DiagnosticsManager::popVerbosityContext()
    {
        m_vContextStack.resize( m_vContextStack.size() - 1 );
    }
} // namespace goose::diagnostics
Changes to bs/diagnostics/diagnosticsmanager.h.
1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
20
21
22
23
24

25
26
27

28
29
30
31

32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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
#ifndef GOOSE_DIAGNOSTICS_DIAGNOSTICSMANAGER_H
#define GOOSE_DIAGNOSTICS_DIAGNOSTICSMANAGER_H

namespace goose::diagnostics
{
    class DiagnosticsContext;
    class VerbosityContext;

    enum class Verbosity
    {
        Normal,         // Display all errors normally
        Silent          // Stop displaying errors for the current context and all non-root sub child contexts.

    };

    using ContextInfos = tuple< string, LocationId, bool >;

    class DiagnosticsManager
    {
        friend class DiagnosticsContext;
        friend class VerbosityContext;

        public:
            static DiagnosticsManager& GetInstance();


            void emitErrorMessage( LocationId locationId, const string& message, uint32_t contextLevels = ~0 );

            void emitErrorMessage( LocationId locationId, StringId errId, const string& message, uint32_t contextLevels = ~0 );


            // This is similar to standard error message, but it also set the verbosity level for the
            // current context to "Silent", as syntax errors are likely to cause cascading errors
            // for the rest of the scope.

            void emitSyntaxErrorMessage( LocationId locationId, const string& message, uint32_t contextLevels = ~0 );

            // This is similar to emitErrorMessage, except it nevers display any context.
            void emitLexerErrorMessage( LocationId locationId, const string& message );

            void emitTraceMessage( LocationId locationId, const string& message );


            void defineCustomDiagnostic( LocationId locationId, StringId errId, const string& message = ""s );

            // When enabled, all diagnostics contexts will be pretty printed on the standard output.
            void setTraceMode( bool enable )
            {
                m_traceMode = enable;
            }

            bool forceColors() const { return m_forceColors; }

            void setForceColors( bool enable )
            {
                m_forceColors = enable;
            }

            void setCurrentVerbosityLevel( Verbosity v );

            bool errorsWereEmitted() const { return m_emittedFirstError; }

            template< typename C >
            void pushDiagnosticsContext( C&& ci )
            {
                m_contextStack.emplace_back( forward< C >( ci ) );

                if( m_traceMode )
                {
                    Renderer r( llvm::outs() );

                    auto&& [desc, location, isRootContext] = *ci;
                    r.addContext( location, Renderer::Squiggles,
                        desc, Renderer::Colors::Trace );
                }
            }

            void popDiagnosticsContext();

        private:
            void emitError( LocationId locationId, const string& message, uint32_t contextLevels );

            void pushVerbosityContext( VerbosityContext* vc );
            void popVerbosityContext();

            vector< ptr< ContextInfos > >   m_contextStack;
            vector< VerbosityContext* >     m_vContextStack;

            using CustomDiagIdentifier = pair< LocationId, StringId >;
            using CustomDiag = pair< string, vector< ptr< ContextInfos > > >;
            unordered_map< CustomDiagIdentifier, CustomDiag > m_customDiagnostics;

            // Keep track of which location IDs have already been used to emit an error,
            // to avoid duplicate error messages (for situations such as template where
            // we parse the same piece of code several times, for instance)
            unordered_set< LocationId > m_usedUpErrorLocations;

            bool m_emittedFirstError = false;
            bool m_forceColors = false;
            bool m_traceMode = false;
    };
}

#define G_ERROR( message ) \
    { \
        diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( 0, \
            format( "internal: {}:{}: {}", __FILE__, __LINE__, message ) ); \
        abort(); \
    }

#define G_VAL_ERROR( val, message ) \
    { \
        diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( (val).locationId(), \
            format( "internal: {}:{}: {}", __FILE__, __LINE__, message ) ); \
        abort(); \
    }

#define G_VAL_ASSERT( val, cond ) \
    if( !( cond ) ) \
    { \
        stringstream sstr; \
        sstr << (val); \
        diagnostics::DiagnosticsContext dc( (val).locationId(), format( "Value: {}",  sstr.str() ) ); \

        diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( (val).locationId(), \
            format( "internal: {}:{}: assertion " #cond " failed", __FILE__, __LINE__ ) ); \
        abort(); \
    }

#define G_LOC_ERROR( loc, message ) \
    { \
        diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( (loc), \
            format( "internal: {}:{}: {}", __FILE__, __LINE__, message ) ); \
        abort(); \
    }

#define G_LOC_ASSERT( loc, cond ) \
    if( !( cond ) ) \
    { \
        diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( (loc), \
            format( "internal: {}:{}: assertion " #cond " failed", __FILE__, __LINE__ ) ); \
        abort(); \
    }

#define G_TRACE_VAL( val ) \
    { \
        stringstream sstr; \
        sstr << val; \
        diagnostics::DiagnosticsManager::GetInstance().emitTraceMessage( (val).locationId(), \
            sstr.str() ); \
    }

#define G_TRACE( loc, message ) \
    { \
        diagnostics::DiagnosticsManager::GetInstance().emitTraceMessage( loc, \
            message ); \
    }

#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
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
#ifndef GOOSE_DIAGNOSTICS_DIAGNOSTICSMANAGER_H
#define GOOSE_DIAGNOSTICS_DIAGNOSTICSMANAGER_H

namespace goose::diagnostics
{
    class DiagnosticsContext;
    class VerbosityContext;

    enum class Verbosity
    {
        Normal, // Display all errors normally
        Silent // Stop displaying errors for the current context and all non-root sub child
               // contexts.
    };

    using ContextInfos = tuple< string, LocationId, bool >;

    class DiagnosticsManager
    {
        friend class DiagnosticsContext;
        friend class VerbosityContext;

      public:
        static DiagnosticsManager& GetInstance();

        void emitErrorMessage(
            LocationId locationId, const string& message, uint32_t contextLevels = ~0 );

        void emitErrorMessage( LocationId locationId, StringId errId, const string& message,
            uint32_t contextLevels = ~0 );

        // This is similar to standard error message, but it also set the verbosity level for
        // the current context to "Silent", as syntax errors are likely to cause cascading
        // errors for the rest of the scope.
        void emitSyntaxErrorMessage(
            LocationId locationId, const string& message, uint32_t contextLevels = ~0 );

        // This is similar to emitErrorMessage, except it nevers display any context.
        void emitLexerErrorMessage( LocationId locationId, const string& message );

        void emitTraceMessage( LocationId locationId, const string& message );

        void defineCustomDiagnostic(
            LocationId locationId, StringId errId, const string& message = ""s );

        // When enabled, all diagnostics contexts will be pretty printed on the standard output.
        void setTraceMode( bool enable ) { m_traceMode = enable; }




        bool forceColors() const { return m_forceColors; }

        void setForceColors( bool enable ) { m_forceColors = enable; }




        void setCurrentVerbosityLevel( Verbosity v );

        bool errorsWereEmitted() const { return m_emittedFirstError; }

        template< typename C > void pushDiagnosticsContext( C&& ci )

        {
            m_contextStack.emplace_back( forward< C >( ci ) );

            if( m_traceMode )
            {
                Renderer r( llvm::outs() );

                auto&& [desc, location, isRootContext] = *ci;
                r.addContext( location, Renderer::Squiggles, desc, Renderer::Colors::Trace );

            }
        }

        void popDiagnosticsContext();

      private:
        void emitError( LocationId locationId, const string& message, uint32_t contextLevels );

        void pushVerbosityContext( VerbosityContext* vc );
        void popVerbosityContext();

        vector< ptr< ContextInfos > > m_contextStack;
        vector< VerbosityContext* > m_vContextStack;

        using CustomDiagIdentifier = pair< LocationId, StringId >;
        using CustomDiag = pair< string, vector< ptr< ContextInfos > > >;
        unordered_map< CustomDiagIdentifier, CustomDiag > m_customDiagnostics;

        // Keep track of which location IDs have already been used to emit an error, to avoid
        // duplicate error messages (for situations such as template where we parse the same
        // piece of code several times, for instance)
        unordered_set< LocationId > m_usedUpErrorLocations;

        bool m_emittedFirstError = false;
        bool m_forceColors = false;
        bool m_traceMode = false;
    };
} // namespace goose::diagnostics

#define G_ERROR( message ) \
    { \
        diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( \
            0, format( "internal: {}:{}: {}", __FILE__, __LINE__, message ) ); \
        abort(); \
    }

#define G_VAL_ERROR( val, message ) \
    { \
        diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( \
            ( val ).locationId(), format( "internal: {}:{}: {}", __FILE__, __LINE__, message ) ); \
        abort(); \
    }

#define G_VAL_ASSERT( val, cond ) \
    if( !( cond ) ) \
    { \
        stringstream sstr; \
        sstr << ( val ); \
        diagnostics::DiagnosticsContext dc( \
            ( val ).locationId(), format( "Value: {}", sstr.str() ) ); \
        diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( ( val ).locationId(), \
            format( "internal: {}:{}: assertion " #cond " failed", __FILE__, __LINE__ ) ); \
        abort(); \
    }

#define G_LOC_ERROR( loc, message ) \
    { \
        diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( \
            ( loc ), format( "internal: {}:{}: {}", __FILE__, __LINE__, message ) ); \
        abort(); \
    }

#define G_LOC_ASSERT( loc, cond ) \
    if( !( cond ) ) \
    { \
        diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( ( loc ), \
            format( "internal: {}:{}: assertion " #cond " failed", __FILE__, __LINE__ ) ); \
        abort(); \
    }

#define G_TRACE_VAL( val ) \
    { \
        stringstream sstr; \
        sstr << val; \
        diagnostics::DiagnosticsManager::GetInstance().emitTraceMessage( \
            ( val ).locationId(), sstr.str() ); \
    }

#define G_TRACE( loc, message ) \
    { \
        diagnostics::DiagnosticsManager::GetInstance().emitTraceMessage( loc, message ); \

    }

#endif
Changes to bs/diagnostics/renderer.cpp.
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
    const char* Renderer::Colors::Context = "\x1b[0m\x1b[38;5;128m";
    const char* Renderer::Colors::Trace = "\x1b[0m\x1b[38;5;46m";
    const char* Renderer::Colors::Error = "\x1b[0m\x1b[1;38;5;197m";
    const char* Renderer::Colors::Source = "\x1b[0m\x1b[38;5;253m";

    bool Renderer::useColors() const
    {
        return DiagnosticsManager::GetInstance().forceColors()
            || m_output.has_colors();
    }

    void Renderer::addContext( LocationId location, uint8_t styleFlags, const char* color )
    {
        addItem( { "", location, true, styleFlags, color } );
    }


    void Renderer::addContext( LocationId location, uint8_t styleFlags, const string& message, const char* color )
    {
        addItem( { message, location, true, styleFlags, color } );
    }

    void Renderer::addItem( Item&& item )
    {
        auto loc = Location::Get( item.location );







|
<







>
|







8
9
10
11
12
13
14
15

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    const char* Renderer::Colors::Context = "\x1b[0m\x1b[38;5;128m";
    const char* Renderer::Colors::Trace = "\x1b[0m\x1b[38;5;46m";
    const char* Renderer::Colors::Error = "\x1b[0m\x1b[1;38;5;197m";
    const char* Renderer::Colors::Source = "\x1b[0m\x1b[38;5;253m";

    bool Renderer::useColors() const
    {
        return DiagnosticsManager::GetInstance().forceColors() || m_output.has_colors();

    }

    void Renderer::addContext( LocationId location, uint8_t styleFlags, const char* color )
    {
        addItem( { "", location, true, styleFlags, color } );
    }

    void Renderer::addContext(
        LocationId location, uint8_t styleFlags, const string& message, const char* color )
    {
        addItem( { message, location, true, styleFlags, color } );
    }

    void Renderer::addItem( Item&& item )
    {
        auto loc = Location::Get( item.location );
48
49
50
51
52
53
54
55
56

57
58
59
60
61
62
63
                m_lastLineNumber = loc->line();
            }

            m_output << item.message << '\n';
            return;
        }

        // Try to merge the context information with the pending context information, so that it is displayed
        // on the same line. In case of conflict, flush what we have and started a new context line.

        bool needFlush = false;

        // If there is no pending filename, it means that our pending context have no location.
        // So this shouldn't be a factor against merging it.
        if( !m_pendingContextFilename.empty() )
        {
            // We can only merge context that are for the same line and file.







|
|
>







48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
                m_lastLineNumber = loc->line();
            }

            m_output << item.message << '\n';
            return;
        }

        // Try to merge the context information with the pending context information, so that it is
        // displayed on the same line. In case of conflict, flush what we have and started a new
        // context line.
        bool needFlush = false;

        // If there is no pending filename, it means that our pending context have no location.
        // So this shouldn't be a factor against merging it.
        if( !m_pendingContextFilename.empty() )
        {
            // We can only merge context that are for the same line and file.
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
        if( needFlush )
            flushContext();

        addToPendingContext( loc, item.styleFlags, item.color, item.message );
    }

    // Add the provided context information to the pending context information.
    void Renderer::addToPendingContext( const optional< Location >& loc, uint8_t styleFlags, const char* pColor, const string& message )

    {
        // Pending color is set to null to indicate that the pending context is clear.
        if( !m_pendingContextMessageColor )
        {
            if( m_pendingContextMessage.empty() )
                m_pendingContextMessage = message;
            m_pendingContextMessageColor = pColor;
            m_pendingCaret = false;
        }

        m_pendingCaret = m_pendingCaret || ( styleFlags & Style::Caret );

        if( !loc )
            return;

        if( m_pendingContextFilename.empty()  )
        {
            m_pendingContextFilename = loc->filename();

            m_pendingContextOffset = loc->isOffsetValid() ? ( loc->offset() - loc->column() + 1 ) : ~0U;
            m_pendingContextLineNumber = loc->line();

            m_pendingContextMessageXPosStart = loc->column() - 1;
            m_pendingContextMessageXPosEnd = loc->column() - 1 + loc->length();

            m_pendingContextMessageColor = pColor;
        }

        m_pendingHighlightSpans.emplace_back( SpanVertex{ loc->column(), styleFlags, pColor } );

        m_pendingHighlightSpans.emplace_back( SpanVertex{ loc->column() + loc->length(), styleFlags, nullptr } );

        sort( m_pendingHighlightSpans.begin(), m_pendingHighlightSpans.end(),
            []( auto&& a, auto&& b )
            {
                return a.column < b.column;
            } );
    }

    // Render the pending context line and clear the pending context state.
    void Renderer::flushContext()
    {
        if( !m_pendingContextMessageColor )
            return;







|
>















|


>
|









>
|


|
<
<
<







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
        if( needFlush )
            flushContext();

        addToPendingContext( loc, item.styleFlags, item.color, item.message );
    }

    // Add the provided context information to the pending context information.
    void Renderer::addToPendingContext( const optional< Location >& loc, uint8_t styleFlags,
        const char* pColor, const string& message )
    {
        // Pending color is set to null to indicate that the pending context is clear.
        if( !m_pendingContextMessageColor )
        {
            if( m_pendingContextMessage.empty() )
                m_pendingContextMessage = message;
            m_pendingContextMessageColor = pColor;
            m_pendingCaret = false;
        }

        m_pendingCaret = m_pendingCaret || ( styleFlags & Style::Caret );

        if( !loc )
            return;

        if( m_pendingContextFilename.empty() )
        {
            m_pendingContextFilename = loc->filename();
            m_pendingContextOffset =
                loc->isOffsetValid() ? ( loc->offset() - loc->column() + 1 ) : ~0U;
            m_pendingContextLineNumber = loc->line();

            m_pendingContextMessageXPosStart = loc->column() - 1;
            m_pendingContextMessageXPosEnd = loc->column() - 1 + loc->length();

            m_pendingContextMessageColor = pColor;
        }

        m_pendingHighlightSpans.emplace_back( SpanVertex{ loc->column(), styleFlags, pColor } );
        m_pendingHighlightSpans.emplace_back(
            SpanVertex{ loc->column() + loc->length(), styleFlags, nullptr } );

        sort( m_pendingHighlightSpans.begin(), m_pendingHighlightSpans.end(),
            []( auto&& a, auto&& b ) { return a.column < b.column; } );



    }

    // Render the pending context line and clear the pending context state.
    void Renderer::flushContext()
    {
        if( !m_pendingContextMessageColor )
            return;
192
193
194
195
196
197
198
199

200
201
202
203
204
205
206
207

208
209
210
211
212
213
214
        m_output << applyColor( Colors::Location );

        // Fetch the line from the file if we can, otherwise
        // just describe the location
        ifstream sourcefile( m_pendingContextFilename );
        if( m_pendingContextOffset == ~0U || !sourcefile.good() )
        {
            m_output << format( "{}:{}:{}: ", m_pendingContextFilename, m_pendingContextLineNumber, m_pendingContextMessageXPosStart + 1 );

            m_output << applyColor( m_pendingContextMessageColor );
            m_output << m_pendingContextMessage << '\n' << applyColor( Colors::Reset );
        }
        else
        {
            if( m_pendingContextFilename != m_lastFilename )
                m_output << format( "{}:\n", m_pendingContextFilename );
            else if( m_pendingContextLineNumber != m_lastLineNumber && m_pendingContextLineNumber != ( m_lastLineNumber + 1 ) )

                m_output << format( "     ...\n" );

            m_lastFilename = m_pendingContextFilename;
            m_lastLineNumber = m_pendingContextLineNumber;

            m_output << format( "{:>5} | ", m_pendingContextLineNumber );
            m_output << applyColor( Colors::Source );







|
>







|
>







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
        m_output << applyColor( Colors::Location );

        // Fetch the line from the file if we can, otherwise
        // just describe the location
        ifstream sourcefile( m_pendingContextFilename );
        if( m_pendingContextOffset == ~0U || !sourcefile.good() )
        {
            m_output << format( "{}:{}:{}: ", m_pendingContextFilename, m_pendingContextLineNumber,
                m_pendingContextMessageXPosStart + 1 );
            m_output << applyColor( m_pendingContextMessageColor );
            m_output << m_pendingContextMessage << '\n' << applyColor( Colors::Reset );
        }
        else
        {
            if( m_pendingContextFilename != m_lastFilename )
                m_output << format( "{}:\n", m_pendingContextFilename );
            else if( m_pendingContextLineNumber != m_lastLineNumber
                && m_pendingContextLineNumber != ( m_lastLineNumber + 1 ) )
                m_output << format( "     ...\n" );

            m_lastFilename = m_pendingContextFilename;
            m_lastLineNumber = m_pendingContextLineNumber;

            m_output << format( "{:>5} | ", m_pendingContextLineNumber );
            m_output << applyColor( Colors::Source );
227
228
229
230
231
232
233

234
235
236
237
238
239
240
241
            uint32_t start = 0;

            for( auto&& sv : m_pendingHighlightSpans )
            {
                // Output the span of the source string up to that highlight vertex
                m_output << string( sview.substr( start, sv.column - 1 - start ) );


                uint32_t count = min( sv.column - size_t{ 1 } - start, max( sview.size(), size_t{ 1 } ) - start );

                // Generate decorations
                while( count-- )
                    decoration << ( inCaretSpan ? '^' : decChar );

                start = sv.column - 1;








>
|







230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
            uint32_t start = 0;

            for( auto&& sv : m_pendingHighlightSpans )
            {
                // Output the span of the source string up to that highlight vertex
                m_output << string( sview.substr( start, sv.column - 1 - start ) );

                uint32_t count = min(
                    sv.column - size_t{ 1 } - start, max( sview.size(), size_t{ 1 } ) - start );

                // Generate decorations
                while( count-- )
                    decoration << ( inCaretSpan ? '^' : decChar );

                start = sv.column - 1;

284
285
286
287
288
289
290
291
292

293
294
295
296

297

298
299
300
301
302

303
304
305
306
307
308
309
310

            if( start <= sview.size() )
                m_output << string( sview.substr( start, sview.size() - start ) ) << '\n';
            else
                m_output << '\n';

            auto decStr = decoration.str();
            if( !decStr.empty()  )
                m_output << applyColor( Colors::Location ) << "      | " << applyColor( Colors::Reset ) << decStr << '\n';


            if( !m_pendingContextMessage.empty() )
            {
                m_output << applyColor( Colors::Location ) << "      | " << applyColor( m_pendingContextMessageColor );



                int32_t maxpos = min( m_pendingContextMessageXPosEnd, static_cast< uint32_t >( line.size() ) );
                int32_t xpos = maxpos - m_pendingContextMessageXPosStart;
                xpos -= m_pendingContextMessage.size();
                xpos /= 2;


                uint32_t count = max( xpos + static_cast< int32_t >( m_pendingContextMessageXPosStart ), 0 );
                while( count-- )
                    m_output << ' ';

                m_output << m_pendingContextMessage << '\n';
                m_output << applyColor( Colors::Reset );
            }
        }







|
|
>



|
>

>
|




>
|







288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318

            if( start <= sview.size() )
                m_output << string( sview.substr( start, sview.size() - start ) ) << '\n';
            else
                m_output << '\n';

            auto decStr = decoration.str();
            if( !decStr.empty() )
                m_output << applyColor( Colors::Location ) << "      | "
                         << applyColor( Colors::Reset ) << decStr << '\n';

            if( !m_pendingContextMessage.empty() )
            {
                m_output << applyColor( Colors::Location ) << "      | "
                         << applyColor( m_pendingContextMessageColor );

                int32_t maxpos =
                    min( m_pendingContextMessageXPosEnd, static_cast< uint32_t >( line.size() ) );
                int32_t xpos = maxpos - m_pendingContextMessageXPosStart;
                xpos -= m_pendingContextMessage.size();
                xpos /= 2;

                uint32_t count =
                    max( xpos + static_cast< int32_t >( m_pendingContextMessageXPosStart ), 0 );
                while( count-- )
                    m_output << ' ';

                m_output << m_pendingContextMessage << '\n';
                m_output << applyColor( Colors::Reset );
            }
        }
318
319
320
321
322
323
324
325

    const char* Renderer::applyColor( const char* pColor )
    {
        if( useColors() )
            return pColor;
        return "";
    }
}







|
326
327
328
329
330
331
332
333

    const char* Renderer::applyColor( const char* pColor )
    {
        if( useColors() )
            return pColor;
        return "";
    }
} // namespace goose::diagnostics
Changes to bs/diagnostics/renderer.h.
1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

44
45

46
47
48
49
50

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#ifndef GOOSE_DIAGNOSTICS_RENDERER_H
#define GOOSE_DIAGNOSTICS_RENDERER_H

namespace goose::diagnostics
{
    class Renderer
    {
        public:
            Renderer( llvm::raw_ostream& output ) :
                m_output( output )
            {}

            ~Renderer()

            {
                flushContext();
            }

            enum Style
            {
                None        = 0,

                Caret       = 1 << 0,
                Squiggles   = 1 << 1,
                Highlight   = 1 << 2
            };

            struct Colors
            {
                static const char* Reset;

                static const char* Location;            // File name, line number gutter
                static const char* Context;             // Squiggles and messages providing contextual informations
                static const char* Trace;               // Squiggles and messages displayed during tracing
                static const char* Error;               // Error highlight and "error" keyword
                static const char* Source;              // Source code
            };

            template< typename... A >
            void addMessage( LocationId location, const string& message, A&&... args );

            template< typename... A >
            void addContext( const string&  message, A&&... args );


            void addContext( LocationId location, uint8_t styleFlags, const char* color = Colors::Reset );
            void addContext( LocationId location, uint8_t styleFlags, const string& message, const char* color = Colors::Reset );


        private:
            bool useColors() const;

            void addToPendingContext( const optional< Location >& loc, uint8_t styleFlags, const char* pColor, const string& message );

            void flushContext();
            llvm::raw_ostream& m_output;

            struct Item
            {
                string message;

                LocationId location = 0;

                // The following values are only relevant if the location is valid.
                bool quoteSource = false;
                uint8_t styleFlags = 0;

                // Color is only relevant if styleFlags != 0
                const char* color = "";
            };

            void addItem( Item&& item );

            const char* applyColor( const char* pColor );

            string m_lastFilename;
            uint32_t m_lastLineNumber = 0;

            // The pending context line informations.
            string m_pendingContextFilename;
            uint32_t m_pendingContextOffset = 0;
            uint32_t m_pendingContextLineNumber = 0;

            // Describe the starting or ending point of a span to be
            // highlighted. color = nullptr means it's an ending point.
            struct SpanVertex
            {
                uint32_t column = 0;
                uint8_t styleFlags = 0;
                const char* color = nullptr;
            };

            // The pending highlighting vertices.
            llvm::SmallVector< SpanVertex, 8 > m_pendingHighlightSpans;

            // The pending context message.
            string m_pendingContextMessage;

            // The color and position of the pending context message.
            const char* m_pendingContextMessageColor = nullptr;
            uint32_t m_pendingContextMessageXPosStart = 0;
            uint32_t m_pendingContextMessageXPosEnd = 0;

            // True if there is a pending caret highlighting.
            // (we only allow one per displayed context line)
            bool m_pendingCaret = false;
    };
}

#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
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
#ifndef GOOSE_DIAGNOSTICS_RENDERER_H
#define GOOSE_DIAGNOSTICS_RENDERER_H

namespace goose::diagnostics
{
    class Renderer
    {
      public:
        Renderer( llvm::raw_ostream& output ) :
            m_output( output )

        {

        }

        ~Renderer() { flushContext(); }


        enum Style
        {
            None = 0,

            Caret = 1 << 0,
            Squiggles = 1 << 1,
            Highlight = 1 << 2
        };

        struct Colors
        {
            static const char* Reset;

            static const char* Location; // File name, line number gutter
            static const char* Context; // Squiggles and messages providing contextual informations
            static const char* Trace; // Squiggles and messages displayed during tracing
            static const char* Error; // Error highlight and "error" keyword
            static const char* Source; // Source code
        };

        template< typename... A >
        void addMessage( LocationId location, const string& message, A&&... args );

        template< typename... A > void addContext( const string& message, A&&... args );


        void addContext(
            LocationId location, uint8_t styleFlags, const char* color = Colors::Reset );
        void addContext( LocationId location, uint8_t styleFlags, const string& message,
            const char* color = Colors::Reset );

      private:
        bool useColors() const;

        void addToPendingContext( const optional< Location >& loc, uint8_t styleFlags,
            const char* pColor, const string& message );
        void flushContext();
        llvm::raw_ostream& m_output;

        struct Item
        {
            string message;

            LocationId location = 0;

            // The following values are only relevant if the location is valid.
            bool quoteSource = false;
            uint8_t styleFlags = 0;

            // Color is only relevant if styleFlags != 0
            const char* color = "";
        };

        void addItem( Item&& item );

        const char* applyColor( const char* pColor );

        string m_lastFilename;
        uint32_t m_lastLineNumber = 0;

        // The pending context line informations.
        string m_pendingContextFilename;
        uint32_t m_pendingContextOffset = 0;
        uint32_t m_pendingContextLineNumber = 0;

        // Describe the starting or ending point of a span to be
        // highlighted. color = nullptr means it's an ending point.
        struct SpanVertex
        {
            uint32_t column = 0;
            uint8_t styleFlags = 0;
            const char* color = nullptr;
        };

        // The pending highlighting vertices.
        llvm::SmallVector< SpanVertex, 8 > m_pendingHighlightSpans;

        // The pending context message.
        string m_pendingContextMessage;

        // The color and position of the pending context message.
        const char* m_pendingContextMessageColor = nullptr;
        uint32_t m_pendingContextMessageXPosStart = 0;
        uint32_t m_pendingContextMessageXPosEnd = 0;

        // True if there is a pending caret highlighting.
        // (we only allow one per displayed context line)
        bool m_pendingCaret = false;
    };
} // namespace goose::diagnostics

#endif
Changes to bs/diagnostics/renderer.inl.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

20
21
#ifndef GOOSE_DIAGNOSTICS_RENDERER_INL
#define GOOSE_DIAGNOSTICS_RENDERER_INL

namespace goose::diagnostics
{
    template<typename... A >
    void Renderer::addMessage( LocationId location, const string& message, A&&... args )
    {
        addItem( { format( message, forward< A >( args )... ), location,
            false, Style::None } );
    }

    template< typename... A >
    void Renderer::addContext( const string& message, A&&... args )
    {
        addItem( { format( message, forward< A >( args )... ),
            0, false, Style::None } );
    }
}


#endif





|


|
<


<
|

|
<

<
>


1
2
3
4
5
6
7
8
9

10
11

12
13
14

15

16
17
18
#ifndef GOOSE_DIAGNOSTICS_RENDERER_INL
#define GOOSE_DIAGNOSTICS_RENDERER_INL

namespace goose::diagnostics
{
    template< typename... A >
    void Renderer::addMessage( LocationId location, const string& message, A&&... args )
    {
        addItem( { format( message, forward< A >( args )... ), location, false, Style::None } );

    }


    template< typename... A > void Renderer::addContext( const string& message, A&&... args )
    {
        addItem( { format( message, forward< A >( args )... ), 0, false, Style::None } );

    }

} // namespace goose::diagnostics

#endif
Changes to bs/diagnostics/verbositycontext.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
#ifndef GOOSE_DIAGNOSTICS_VERBOSITYCONTEXT_H
#define GOOSE_DIAGNOSTICS_VERBOSITYCONTEXT_H

namespace goose::diagnostics
{
    class VerbosityContext
    {
        public:
            VerbosityContext( Verbosity v, bool rootContext = false ) :
                m_verbosity( v ),
                m_isRootContext( rootContext )
            {
                DiagnosticsManager::GetInstance().pushVerbosityContext( this );
            }

            ~VerbosityContext()
            {
                DiagnosticsManager::GetInstance().popVerbosityContext();
            }

            bool isRootContext() const { return m_isRootContext; }

            auto verbosity() const { return m_verbosity; }

            void setVerbosity( Verbosity v )
            {
                if( v > m_verbosity )
                    m_verbosity = v;
            }

        private:
            Verbosity m_verbosity = Verbosity::Normal;
            bool m_isRootContext = false;
    };
}


#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
#ifndef GOOSE_DIAGNOSTICS_VERBOSITYCONTEXT_H
#define GOOSE_DIAGNOSTICS_VERBOSITYCONTEXT_H

namespace goose::diagnostics
{
    class VerbosityContext
    {
      public:
        VerbosityContext( Verbosity v, bool rootContext = false ) :
            m_verbosity( v ),
            m_isRootContext( rootContext )
        {
            DiagnosticsManager::GetInstance().pushVerbosityContext( this );
        }



        ~VerbosityContext() { DiagnosticsManager::GetInstance().popVerbosityContext(); }


        bool isRootContext() const { return m_isRootContext; }

        auto verbosity() const { return m_verbosity; }

        void setVerbosity( Verbosity v )
        {
            if( v > m_verbosity )
                m_verbosity = v;
        }

      private:
        Verbosity m_verbosity = Verbosity::Normal;
        bool m_isRootContext = false;
    };

} // namespace goose::diagnostics

#endif
Changes to bs/eir/anyterm.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
#ifndef GOOSE_EIR_ANYTERM_H
#define GOOSE_EIR_ANYTERM_H

namespace goose::eir
{
    class AnyTerm
    {
        public:
            AnyTerm( StringId varName ) :
                m_varName( varName )
            {}



            const auto& varName() const { return m_varName; }

            friend ostream& ToString( ostream& out, const AnyTerm& v );

            friend ostream& operator<<( ostream& out, const AnyTerm& x )
            {
                return out << x.m_varName;
            }

            auto operator<=>( const AnyTerm& ) const = default;

        private:
            StringId m_varName;
    };
}


namespace std
{
    template<> struct hash< goose::eir::AnyTerm >
    {
        size_t operator()( const goose::eir::AnyTerm& x ) const
        {
            return hash< goose::util::StringId >()( x.varName() );
        }
    };
}

#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
#ifndef GOOSE_EIR_ANYTERM_H
#define GOOSE_EIR_ANYTERM_H

namespace goose::eir
{
    class AnyTerm
    {
      public:
        AnyTerm( StringId varName ) :
            m_varName( varName )

        {
        }

        const auto& varName() const { return m_varName; }

        friend ostream& ToString( ostream& out, const AnyTerm& v );

        friend ostream& operator<<( ostream& out, const AnyTerm& x ) { return out << x.m_varName; }




        auto operator<=>( const AnyTerm& ) const = default;

      private:
        StringId m_varName;
    };

} // namespace goose::eir

namespace std
{
    template<> struct hash< goose::eir::AnyTerm >
    {
        size_t operator()( const goose::eir::AnyTerm& x ) const
        {
            return hash< goose::util::StringId >()( x.varName() );
        }
    };
} // namespace std

#endif
Changes to bs/eir/bridge.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
#ifndef GOOSE_EIR_BRIDGE_H
#define GOOSE_EIR_BRIDGE_H

namespace goose::eir
{
    template< typename T >
    struct Bridge

    {};

    template< typename T, typename... P >
    auto GetValueType( P&&... params )
    {
        return Bridge< T >::Type( forward< P >( params )... );
    }

    template< typename T, typename... ARGS >
    Value ToDeclValue( T&& x, ARGS&&... args )
    {
        using TT = remove_cvref_t< T >;
        return Bridge< TT >::ToDeclValue( forward< T >( x ), forward< ARGS >( args )... );
    }

    template< typename TT, typename... T >
    Value ToDeclValue( T&&... x )
    {
        return Bridge< TT >::ToDeclValue( forward< T >( x )... );
    }

    template< typename T, typename... ARGS >
    Value ToValue( T&& x, ARGS&&... args )
    {
        using TT = remove_cvref_t< T >;
        return Bridge< TT >::ToValue( forward< T >( x ), forward< ARGS >( args )... );
    }

    template< typename TT, typename... T >
    Value ToValue( T&&... x )
    {
        return Bridge< TT >::ToValue( forward< T >( x )... );
    }

    template< typename T >
    auto FromValue( const Value& val )
    {
        return Bridge< T >::FromValue( val );
    }
}

#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
#ifndef GOOSE_EIR_BRIDGE_H
#define GOOSE_EIR_BRIDGE_H

namespace goose::eir
{
    template< typename T > struct Bridge

    {
    };

    template< typename T, typename... P > auto GetValueType( P&&... params )

    {
        return Bridge< T >::Type( forward< P >( params )... );
    }

    template< typename T, typename... ARGS > Value ToDeclValue( T&& x, ARGS&&... args )

    {
        using TT = remove_cvref_t< T >;
        return Bridge< TT >::ToDeclValue( forward< T >( x ), forward< ARGS >( args )... );
    }

    template< typename TT, typename... T > Value ToDeclValue( T&&... x )

    {
        return Bridge< TT >::ToDeclValue( forward< T >( x )... );
    }

    template< typename T, typename... ARGS > Value ToValue( T&& x, ARGS&&... args )

    {
        using TT = remove_cvref_t< T >;
        return Bridge< TT >::ToValue( forward< T >( x ), forward< ARGS >( args )... );
    }

    template< typename TT, typename... T > Value ToValue( T&&... x )

    {
        return Bridge< TT >::ToValue( forward< T >( x )... );
    }

    template< typename T > auto FromValue( const Value& val )

    {
        return Bridge< T >::FromValue( val );
    }
} // namespace goose::eir

#endif
Changes to bs/eir/compare.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

#include "eir.h"

namespace goose::eir
{
    bool operator==( const Term& lhs, const Term& rhs )
    {
        if( lhs.index() != rhs.index() )
            return false;


        return visit( [&]< typename T >( const T& l )
        {
            const auto& r = get< T >( rhs );

            if constexpr( is_same_v< pvec, T > )
                return *l == *r;
            else if constexpr( is_same_v< LocationId, T > )
                return true;
            else if constexpr( is_same_v< ptr< void >, T > )
                return true;
            else if constexpr( is_same_v< void*, T > )
                return true;
            else if constexpr( is_same_v< APSInt, T >)
                return l.isSigned() == r.isSigned() && l.getBitWidth() == r.getBitWidth() && l == r;

            else
                return l == r;

        }, lhs );
    }

    bool operator!=( const Term& lhs, const Term& rhs )
    {
        return !( lhs == rhs );
    }

    bool Vector::operator==( const Vector& rhs ) const
    {
        return m_repetitionTerm == rhs.m_repetitionTerm
            && m_terms == rhs.m_terms;
    }

    bool Hole::operator<( const Hole& rhs ) const
    {
        if( name() != rhs.name() )
            return name() < rhs.name();

        if( behavior() != rhs.behavior() )
            return behavior() < rhs.behavior();

        return kind() < rhs.kind();
    }

    bool Hole::operator==( const Hole& rhs ) const
    {
        return name() == rhs.name()
            && behavior() == rhs.behavior()
            && kind() == rhs.kind();
    }
}










>
|
|
|

|
|
|
|
|
|
|
|
|
|
>
|
|
>
|









|
<















|
<
<

<
>
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
#include "eir.h"

namespace goose::eir
{
    bool operator==( const Term& lhs, const Term& rhs )
    {
        if( lhs.index() != rhs.index() )
            return false;

        return visit(
            [&]< typename T >( const T& l )
            {
                const auto& r = get< T >( rhs );

                if constexpr( is_same_v< pvec, T > )
                    return *l == *r;
                else if constexpr( is_same_v< LocationId, T > )
                    return true;
                else if constexpr( is_same_v< ptr< void >, T > )
                    return true;
                else if constexpr( is_same_v< void*, T > )
                    return true;
                else if constexpr( is_same_v< APSInt, T > )
                    return l.isSigned() == r.isSigned() && l.getBitWidth() == r.getBitWidth()
                        && l == r;
                else
                    return l == r;
            },
            lhs );
    }

    bool operator!=( const Term& lhs, const Term& rhs )
    {
        return !( lhs == rhs );
    }

    bool Vector::operator==( const Vector& rhs ) const
    {
        return m_repetitionTerm == rhs.m_repetitionTerm && m_terms == rhs.m_terms;

    }

    bool Hole::operator<( const Hole& rhs ) const
    {
        if( name() != rhs.name() )
            return name() < rhs.name();

        if( behavior() != rhs.behavior() )
            return behavior() < rhs.behavior();

        return kind() < rhs.kind();
    }

    bool Hole::operator==( const Hole& rhs ) const
    {
        return name() == rhs.name() && behavior() == rhs.behavior() && kind() == rhs.kind();


    }

} // namespace goose::eir
Changes to bs/eir/decompose.h.
1
2
3
4
5
6
7
8
9
10
11
12
13



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

54
55
56
57
58
59


60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77


78
79
#ifndef GOOSE_EIR_DECOMPOSE_H
#define GOOSE_EIR_DECOMPOSE_H

namespace goose::eir
{
    template< typename T >
    struct LiteralSpec
    {
        using return_type = bool;

        template< typename TT >
        LiteralSpec( TT&& val ) : m_val( forward< TT >( val ) )
        {}




        T m_val;
    };

    template< typename T >
    auto Lit( T&& val );

    template< typename T >
    struct Val
    {
        using return_type = T;
    };

    struct SubTerm
    {
        using return_type = Term;
    };

    template< typename... T >
    struct VecDecompositionReturnTypeBuilder

    {};

    template< typename... TU >
    struct VecDecompositionReturnTypeBuilder< tuple< TU... > >
    {
        using return_type = tuple< TU... >;
    };

    template< typename... TU, typename HS, typename... TS >
    struct VecDecompositionReturnTypeBuilder< tuple< TU... >, HS, TS... >
    {
        using return_type = conditional_t< is_same_v< bool, typename HS::return_type >,
            typename VecDecompositionReturnTypeBuilder< tuple< TU... >, TS... >::return_type,
            typename VecDecompositionReturnTypeBuilder< tuple< TU..., typename HS::return_type >, TS... >::return_type
        >;
    };

    template< typename... S >
    struct VectorSpec
    {

        using return_type = typename VecDecompositionReturnTypeBuilder< tuple<>, S... >::return_type;

        template< typename... T >
        VectorSpec( T&&... specs ) :
            m_specs( forward< T >( specs )... )
        {}



        tuple< S... > m_specs;
    };

    template< typename... S >
    auto Vec( S&&... specs );

    template< typename T >
    bool Decompose( const Term& t, const LiteralSpec< T >& spec );

    template< typename T >
    optional< reference_wrapper< const T > > Decompose( const Term& t, const Val< T >& spec );

    static inline const Term& Decompose( const Term& t, const SubTerm& spec );

    template< typename... S >
    optional< typename VectorSpec< S... >::return_type > Decompose( const Term& t, const VectorSpec< S... >& spec );
}



#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

64

65
66
67
68
69
70
71
72
73

74
75
76
77
#ifndef GOOSE_EIR_DECOMPOSE_H
#define GOOSE_EIR_DECOMPOSE_H

namespace goose::eir
{
    template< typename T > struct LiteralSpec

    {
        using return_type = bool;

        template< typename TT >
        LiteralSpec( TT&& val ) :

            m_val( forward< TT >( val ) )
        {
        }

        T m_val;
    };

    template< typename T > auto Lit( T&& val );


    template< typename T > struct Val

    {
        using return_type = T;
    };

    struct SubTerm
    {
        using return_type = Term;
    };

    template< typename... T > struct VecDecompositionReturnTypeBuilder

    {
    };


    template< typename... TU > struct VecDecompositionReturnTypeBuilder< tuple< TU... > >
    {
        using return_type = tuple< TU... >;
    };

    template< typename... TU, typename HS, typename... TS >
    struct VecDecompositionReturnTypeBuilder< tuple< TU... >, HS, TS... >
    {
        using return_type = conditional_t< is_same_v< bool, typename HS::return_type >,
            typename VecDecompositionReturnTypeBuilder< tuple< TU... >, TS... >::return_type,
            typename VecDecompositionReturnTypeBuilder< tuple< TU..., typename HS::return_type >,
                TS... >::return_type >;
    };

    template< typename... S > struct VectorSpec

    {
        using return_type =
            typename VecDecompositionReturnTypeBuilder< tuple<>, S... >::return_type;

        template< typename... T >
        VectorSpec( T&&... specs ) :
            m_specs( forward< T >( specs )... )

        {
        }

        tuple< S... > m_specs;
    };

    template< typename... S > auto Vec( S&&... specs );



    template< typename T > bool Decompose( const Term& t, const LiteralSpec< T >& spec );

    template< typename T >
    optional< reference_wrapper< const T > > Decompose( const Term& t, const Val< T >& spec );

    static inline const Term& Decompose( const Term& t, const SubTerm& spec );

    template< typename... S >
    optional< typename VectorSpec< S... >::return_type > Decompose(

        const Term& t, const VectorSpec< S... >& spec );
} // namespace goose::eir

#endif
Changes to bs/eir/decompose.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
#ifndef GOOSE_EIR_DECOMPOSE_INL
#define GOOSE_EIR_DECOMPOSE_INL

namespace goose::eir
{
    template< typename T >
    auto Lit( T&& val )
    {
        return LiteralSpec< T >( forward< T >( val ) );
    }

    template< typename... S >
    auto Vec( S&&... specs )
    {
        return VectorSpec< S... >( forward< S >( specs )... );
    }

    template< typename T >
    bool Decompose( const Term& t, const LiteralSpec< T >& spec )
    {
        const auto* pContent = get_if< T >( &t );
        if( !pContent )
            return false;

        return *pContent == spec.m_val;
    }





|
<




|
<




<
|







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_EIR_DECOMPOSE_INL
#define GOOSE_EIR_DECOMPOSE_INL

namespace goose::eir
{
    template< typename T > auto Lit( T&& val )

    {
        return LiteralSpec< T >( forward< T >( val ) );
    }

    template< typename... S > auto Vec( S&&... specs )

    {
        return VectorSpec< S... >( forward< S >( specs )... );
    }


    template< typename T > bool Decompose( const Term& t, const LiteralSpec< T >& spec )
    {
        const auto* pContent = get_if< T >( &t );
        if( !pContent )
            return false;

        return *pContent == spec.m_val;
    }
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

    const Term& Decompose( const Term& t, const SubTerm& spec )
    {
        return t;
    }

    template< size_t IR, size_t IS, typename... S, typename... T >
    bool Decompose( const Vector::container_type< Term >& terms, const tuple< S... >& specs, tuple< T... >& result )

    {
        auto res = Decompose( terms[IS], get< IS >( specs ) );

        if constexpr( is_same_v< decltype( res ), bool > )
        {
            if( !res )
                return false;

            // For specs that are just predicates, we have no result to insert
            // so don't increment the result index.
            if constexpr( IS < ( sizeof... ( S ) - 1 ) )
                return Decompose< IR, IS + 1 >( terms, specs, result );
            else
                return true;
        }
        else if constexpr( is_optional_v< decltype( res ) > )
        {
            if( !res )
                return false;

            get< IR >( result ) = move( *res );

            if constexpr( IS < ( sizeof... ( S ) - 1 ) )
                return Decompose< IR + 1, IS + 1 >( terms, specs, result );
            else
                return true;
        }
        else
        {
            get< IR >( result ) = move( res );

            if constexpr( IS < ( sizeof... ( S ) - 1 ) )
                return Decompose< IR + 1, IS + 1 >( terms, specs, result );

            return true;
        }
    }

    template< typename... S >
    optional< typename VectorSpec< S... >::return_type > Decompose( const Term& t, const VectorSpec< S... >& spec )

    {
        const auto* ppVec = get_if< pvec >( &t );
        if( !ppVec )
            return nullopt;

        const auto& terms = ( *ppVec )->terms();
        if( terms.size() != sizeof...( S ) )
            return nullopt;

        typename VectorSpec< S... >::return_type result;
        if( !Decompose< 0, 0 >( terms, spec.m_specs, result ) )
            return nullopt;

        return result;
    }
}

#endif







|
>










|











|








|







|
>















|


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

    const Term& Decompose( const Term& t, const SubTerm& spec )
    {
        return t;
    }

    template< size_t IR, size_t IS, typename... S, typename... T >
    bool Decompose( const Vector::container_type< Term >& terms, const tuple< S... >& specs,
        tuple< T... >& result )
    {
        auto res = Decompose( terms[IS], get< IS >( specs ) );

        if constexpr( is_same_v< decltype( res ), bool > )
        {
            if( !res )
                return false;

            // For specs that are just predicates, we have no result to insert
            // so don't increment the result index.
            if constexpr( IS < ( sizeof...( S ) - 1 ) )
                return Decompose< IR, IS + 1 >( terms, specs, result );
            else
                return true;
        }
        else if constexpr( is_optional_v< decltype( res ) > )
        {
            if( !res )
                return false;

            get< IR >( result ) = move( *res );

            if constexpr( IS < ( sizeof...( S ) - 1 ) )
                return Decompose< IR + 1, IS + 1 >( terms, specs, result );
            else
                return true;
        }
        else
        {
            get< IR >( result ) = move( res );

            if constexpr( IS < ( sizeof...( S ) - 1 ) )
                return Decompose< IR + 1, IS + 1 >( terms, specs, result );

            return true;
        }
    }

    template< typename... S >
    optional< typename VectorSpec< S... >::return_type > Decompose(
        const Term& t, const VectorSpec< S... >& spec )
    {
        const auto* ppVec = get_if< pvec >( &t );
        if( !ppVec )
            return nullopt;

        const auto& terms = ( *ppVec )->terms();
        if( terms.size() != sizeof...( S ) )
            return nullopt;

        typename VectorSpec< S... >::return_type result;
        if( !Decompose< 0, 0 >( terms, spec.m_specs, result ) )
            return nullopt;

        return result;
    }
} // namespace goose::eir

#endif
Changes to bs/eir/eir.h.
1
2
3
4
5
6
7
8
9
10
11
12
13

14


15
16
17
18
19
20
21
#ifndef GOOSE_EIR_H
#define GOOSE_EIR_H

#include "util/util.h"

namespace goose::eir
{
    using namespace util;

    struct EmptyPayload {};

    template< typename U = EmptyPayload >
    class Trie;

}



#include "anyterm.h"
#include "vecoflength.h"
#include "term.h"
#include "trie.h"
#include "tostring.h"
#include "vector.h"









|
|
<
<
>
|
>
>







1
2
3
4
5
6
7
8
9
10
11


12
13
14
15
16
17
18
19
20
21
22
#ifndef GOOSE_EIR_H
#define GOOSE_EIR_H

#include "util/util.h"

namespace goose::eir
{
    using namespace util;

    struct EmptyPayload
    {


    };

    template< typename U = EmptyPayload > class Trie;
} // namespace goose::eir

#include "anyterm.h"
#include "vecoflength.h"
#include "term.h"
#include "trie.h"
#include "tostring.h"
#include "vector.h"
Changes to bs/eir/enumerate.cpp.
1
2
3
4
5
6
7
8
9
10
#include "eir.h"

namespace goose::eir
{
    Generator< Term > Enumerate( const Trie<>& trie )
    {
        for( auto&& [t,p] : Enumerate<>( trie ) )
            co_yield move( t );
    }
}






|


|
1
2
3
4
5
6
7
8
9
10
#include "eir.h"

namespace goose::eir
{
    Generator< Term > Enumerate( const Trie<>& trie )
    {
        for( auto&& [t, p] : Enumerate<>( trie ) )
            co_yield move( t );
    }
} // namespace goose::eir
Changes to bs/eir/enumerate.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
#ifndef GOOSE_EIR_ENUMERATE_INL
#define GOOSE_EIR_ENUMERATE_INL

namespace goose::eir
{
    //--------------------------------------------------------------------------------------------
    // Primitive types
    //--------------------------------------------------------------------------------------------
    template< typename T, typename U >
    static Generator< pair< Term, const U& > > Enumerate( const ValueTrieNode< T, U >& trie )
    {
        for( auto&& [k,v] : trie.m_branches )
            co_yield { TERM( k ), v };
    }

    //--------------------------------------------------------------------------------------------
    // Hole
    //--------------------------------------------------------------------------------------------
    template< typename U >
    static Generator< pair< Hole::Behavior, const U& > > Enumerate( const HoleBehaviorNode< U >& rdn )

    {
        if( rdn.m_standardBhvBranch )
            co_yield { Hole::Behavior::Standard, *rdn.m_standardBhvBranch };

        if( rdn.m_packBhvBranch )
            co_yield { Hole::Behavior::Pack, *rdn.m_packBhvBranch };

        if( rdn.m_anyBhvBranch )
            co_yield { Hole::Behavior::Any, *rdn.m_anyBhvBranch };
    }

    template< typename U >
    static Generator< pair< StringId, const HoleBehaviorNode< U >& > > Enumerate( const HoleKindNode< U >& hkn )

    {
        for( auto&& [kind,v] : hkn.m_branches )
            co_yield { kind, *v };

        if( hkn.m_anyKindBranch )
            co_yield { ""_sid, *hkn.m_anyKindBranch };
    }

    template< typename U >
    static Generator< pair< Term, const U& > > Enumerate( const TrieNode< Hole, U >& trie )
    {
        for( auto&& [name,hkn] : trie.m_branches )
            for( auto&& [kind,bhvn] : Enumerate( *hkn ) )
                for( auto&& [bhv,v] : Enumerate( bhvn ) )
                    co_yield { HOLE( move( name ), move( kind ), bhv ), v };

        if( trie.m_anyNameBranch )
            for( auto&& [kind,bhvn] : Enumerate( *trie.m_anyNameBranch ) )
                for( auto&& [bhv,v] : Enumerate( bhvn ) )
                    co_yield { HOLE( ""_sid, move( kind ), bhv ), v };
    }

    //--------------------------------------------------------------------------------------------
    // LocationId
    //--------------------------------------------------------------------------------------------
    template< typename U >











|







|
>












|
>

|









|
|
|



|
|







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
#ifndef GOOSE_EIR_ENUMERATE_INL
#define GOOSE_EIR_ENUMERATE_INL

namespace goose::eir
{
    //--------------------------------------------------------------------------------------------
    // Primitive types
    //--------------------------------------------------------------------------------------------
    template< typename T, typename U >
    static Generator< pair< Term, const U& > > Enumerate( const ValueTrieNode< T, U >& trie )
    {
        for( auto&& [k, v] : trie.m_branches )
            co_yield { TERM( k ), v };
    }

    //--------------------------------------------------------------------------------------------
    // Hole
    //--------------------------------------------------------------------------------------------
    template< typename U >
    static Generator< pair< Hole::Behavior, const U& > > Enumerate(
        const HoleBehaviorNode< U >& rdn )
    {
        if( rdn.m_standardBhvBranch )
            co_yield { Hole::Behavior::Standard, *rdn.m_standardBhvBranch };

        if( rdn.m_packBhvBranch )
            co_yield { Hole::Behavior::Pack, *rdn.m_packBhvBranch };

        if( rdn.m_anyBhvBranch )
            co_yield { Hole::Behavior::Any, *rdn.m_anyBhvBranch };
    }

    template< typename U >
    static Generator< pair< StringId, const HoleBehaviorNode< U >& > > Enumerate(
        const HoleKindNode< U >& hkn )
    {
        for( auto&& [kind, v] : hkn.m_branches )
            co_yield { kind, *v };

        if( hkn.m_anyKindBranch )
            co_yield { ""_sid, *hkn.m_anyKindBranch };
    }

    template< typename U >
    static Generator< pair< Term, const U& > > Enumerate( const TrieNode< Hole, U >& trie )
    {
        for( auto&& [name, hkn] : trie.m_branches )
            for( auto&& [kind, bhvn] : Enumerate( *hkn ) )
                for( auto&& [bhv, v] : Enumerate( bhvn ) )
                    co_yield { HOLE( move( name ), move( kind ), bhv ), v };

        if( trie.m_anyNameBranch )
            for( auto&& [kind, bhvn] : Enumerate( *trie.m_anyNameBranch ) )
                for( auto&& [bhv, v] : Enumerate( bhvn ) )
                    co_yield { HOLE( ""_sid, move( kind ), bhv ), v };
    }

    //--------------------------------------------------------------------------------------------
    // LocationId
    //--------------------------------------------------------------------------------------------
    template< typename U >
80
81
82
83
84
85
86
87

88
89
90
91
92
93
94
        co_yield { static_cast< void* >( nullptr ), trie.m_next };
    }

    //--------------------------------------------------------------------------------------------
    // Containers
    //--------------------------------------------------------------------------------------------
    template< typename U >
    static Generator< pair< Term, const U& > > Enumerate( Vector&& vec, const TrieContainerBranch_t& contBranch )

    {
        if( contBranch.index() == 0 )
        {
            for( auto&& [t, payload] : Enumerate( get< 0 >( contBranch )->m_trie ) )
            {
                auto newVec = Vector::MakeAppend( vec, t );
                co_yield Enumerate< U >( move( newVec ), payload );







|
>







82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
        co_yield { static_cast< void* >( nullptr ), trie.m_next };
    }

    //--------------------------------------------------------------------------------------------
    // Containers
    //--------------------------------------------------------------------------------------------
    template< typename U >
    static Generator< pair< Term, const U& > > Enumerate(
        Vector&& vec, const TrieContainerBranch_t& contBranch )
    {
        if( contBranch.index() == 0 )
        {
            for( auto&& [t, payload] : Enumerate( get< 0 >( contBranch )->m_trie ) )
            {
                auto newVec = Vector::MakeAppend( vec, t );
                co_yield Enumerate< U >( move( newVec ), payload );
144
145
146
147
148
149
150
151
152
153
    }

    template< typename U >
    static Generator< pair< Term, const U& > > Enumerate( const Trie< U >& trie )
    {
        return Enumerate< 0 >( trie );
    }
}

#endif







|


147
148
149
150
151
152
153
154
155
156
    }

    template< typename U >
    static Generator< pair< Term, const U& > > Enumerate( const Trie< U >& trie )
    {
        return Enumerate< 0 >( trie );
    }
} // namespace goose::eir

#endif
Changes to bs/eir/graphviz.cpp.
1
2
3
4
5
6
7
8
9
#include "eir.h"

namespace goose::eir
{
    void GraphVizDump( const char* pFilename, const Trie<>& trie )
    {
        GraphVizDump<>( pFilename, trie );
    }
}








|
1
2
3
4
5
6
7
8
9
#include "eir.h"

namespace goose::eir
{
    void GraphVizDump( const char* pFilename, const Trie<>& trie )
    {
        GraphVizDump<>( pFilename, trie );
    }
} // namespace goose::eir
Changes to bs/eir/graphviz.h.
1
2
3
4
5
6
7
8
9
10
11
12
#ifndef GOOSE_EIR_GRAPHVIZ_H
#define GOOSE_EIR_GRAPHVIZ_H

namespace goose::eir
{
    template< typename U >
    static void GraphVizDump( const char* pFilename, const Trie< U >& trie );

    extern void GraphVizDump( const char* pFilename, const Trie<>& trie );
}

#endif





<
|





1
2
3
4
5

6
7
8
9
10
11
#ifndef GOOSE_EIR_GRAPHVIZ_H
#define GOOSE_EIR_GRAPHVIZ_H

namespace goose::eir
{

    template< typename U > static void GraphVizDump( const char* pFilename, const Trie< U >& trie );

    extern void GraphVizDump( const char* pFilename, const Trie<>& trie );
}

#endif
Changes to bs/eir/graphviz.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
#ifndef GOOSE_EIR_GRAPHVIZ_INL
#define GOOSE_EIR_GRAPHVIZ_INL

namespace goose::eir
{
    template< typename U, typename F >
    static void GraphVizDump( GraphVizBuilder& builder, const Trie< U >& trie, F&& next );

    //--------------------------------------------------------------------------------------------
    // Primitive types
    //--------------------------------------------------------------------------------------------
    template< typename T, typename U, typename F >

    static void GraphVizDump( GraphVizBuilder& builder, const char* pLabel, const ValueTrieNode< T, U >& trie, F&& next )
    {
        GraphVizBuilder::Node n( builder, &trie, ( pLabel + " node"s ).c_str() );

        for( auto&& [k,v] : trie.m_branches )
        {
            GraphVizBuilder::Row row( builder );

            {
                GraphVizBuilder::Cell cell( builder );
                GraphVizBuilder::Color col( builder );
                ToString( builder.output(), k );
            }

            next( v );
        }
    }

    //--------------------------------------------------------------------------------------------
    // LocationId
    //--------------------------------------------------------------------------------------------
    template< typename U, typename F >
    static void GraphVizDump( GraphVizBuilder& builder, const char* pLabel, const TrieNode< LocationId, U >& trie, F&& next )

    {
        GraphVizBuilder::Node n( builder, &trie, ( pLabel + " node"s ).c_str() );
        GraphVizBuilder::Row row( builder );
        {
            GraphVizBuilder::Cell cell( builder );
            GraphVizBuilder::Color col( builder );
            builder.output() << "next";
        }
        next( trie.m_next );
    }

    //--------------------------------------------------------------------------------------------
    // Holes
    //--------------------------------------------------------------------------------------------
    template< typename U, typename F >

    static void GraphVizDump( GraphVizBuilder& builder, const HoleBehaviorNode< U >& trie, F&& next )
    {
        GraphVizBuilder::Node n( builder, &trie, "Hole behavior node" );

        if( trie.m_standardBhvBranch )
        {
            auto id = builder.getNodeId( trie.m_standardBhvBranch.get() );
            GraphVizBuilder::Row row( builder );












>
|



|

















|
>















>
|







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
#ifndef GOOSE_EIR_GRAPHVIZ_INL
#define GOOSE_EIR_GRAPHVIZ_INL

namespace goose::eir
{
    template< typename U, typename F >
    static void GraphVizDump( GraphVizBuilder& builder, const Trie< U >& trie, F&& next );

    //--------------------------------------------------------------------------------------------
    // Primitive types
    //--------------------------------------------------------------------------------------------
    template< typename T, typename U, typename F >
    static void GraphVizDump(
        GraphVizBuilder& builder, const char* pLabel, const ValueTrieNode< T, U >& trie, F&& next )
    {
        GraphVizBuilder::Node n( builder, &trie, ( pLabel + " node"s ).c_str() );

        for( auto&& [k, v] : trie.m_branches )
        {
            GraphVizBuilder::Row row( builder );

            {
                GraphVizBuilder::Cell cell( builder );
                GraphVizBuilder::Color col( builder );
                ToString( builder.output(), k );
            }

            next( v );
        }
    }

    //--------------------------------------------------------------------------------------------
    // LocationId
    //--------------------------------------------------------------------------------------------
    template< typename U, typename F >
    static void GraphVizDump( GraphVizBuilder& builder, const char* pLabel,
        const TrieNode< LocationId, U >& trie, F&& next )
    {
        GraphVizBuilder::Node n( builder, &trie, ( pLabel + " node"s ).c_str() );
        GraphVizBuilder::Row row( builder );
        {
            GraphVizBuilder::Cell cell( builder );
            GraphVizBuilder::Color col( builder );
            builder.output() << "next";
        }
        next( trie.m_next );
    }

    //--------------------------------------------------------------------------------------------
    // Holes
    //--------------------------------------------------------------------------------------------
    template< typename U, typename F >
    static void GraphVizDump(
        GraphVizBuilder& builder, const HoleBehaviorNode< U >& trie, F&& next )
    {
        GraphVizBuilder::Node n( builder, &trie, "Hole behavior node" );

        if( trie.m_standardBhvBranch )
        {
            auto id = builder.getNodeId( trie.m_standardBhvBranch.get() );
            GraphVizBuilder::Row row( builder );
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
282
283
284
285
286
287
288
289

290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344

345
346
347
            GraphVizBuilder::Row row( builder );
            GraphVizBuilder::Cell cell( builder );
            GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );

            builder.output() << "&lt;any&gt;";
            builder.addEdge( builder.currentNodeId(), id );

            builder.queueWork( [&, next]
            {
                GraphVizDump( builder, *trie.m_anyKindBranch, next );
            } );
        }

        for( auto&& [k,v] : trie.m_branches )
        {
            auto id = builder.getNodeId( v.get() );
            GraphVizBuilder::Row row( builder );
            GraphVizBuilder::Cell cell( builder );
            GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );

            ToString( builder.output(), k );
            builder.addEdge( builder.currentNodeId(), id );

            const auto& nextNode = *v;

            builder.queueWork( [&, next]
            {
                GraphVizDump( builder, nextNode, next );
            } );
        }
    }

    template< typename U, typename F >

    static void GraphVizDump( GraphVizBuilder& builder, const char* pLabel, const TrieNode< Hole, U >& trie, F&& next )
    {
        GraphVizBuilder::Node n( builder, &trie, ( pLabel + " node"s ).c_str() );

        if( trie.m_anyNameBranch )
        {
            auto id = builder.getNodeId( trie.m_anyNameBranch.get() );
            GraphVizBuilder::Row row( builder );
            GraphVizBuilder::Cell cell( builder );
            GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );

            builder.output() << "&lt;any&gt;";
            builder.addEdge( builder.currentNodeId(), id );

            builder.queueWork( [&, next]
            {
                GraphVizDump( builder, *trie.m_anyNameBranch, next );
            } );
        }

        for( auto&& [k,v] : trie.m_branches )
        {
            auto id = builder.getNodeId( v.get() );
            GraphVizBuilder::Row row( builder );
            GraphVizBuilder::Cell cell( builder );
            GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );

            ToString( builder.output(), k );
            builder.addEdge( builder.currentNodeId(), id );

            const auto& nextNode = *v;

            builder.queueWork( [&, next]
            {
                GraphVizDump( builder, nextNode, next );
            } );
        }
    }

    //--------------------------------------------------------------------------------------------
    // ptr< void >
    //--------------------------------------------------------------------------------------------
    template< typename U, typename F >
    static void GraphVizDump( GraphVizBuilder& builder, const char* pLabel, const TrieNode< ptr< void >, U >& trie, F&& next )

    {
        GraphVizBuilder::Node n( builder, &trie, ( pLabel + " node"s ).c_str() );
        GraphVizBuilder::Row row( builder );
        {
            GraphVizBuilder::Cell cell( builder );
            GraphVizBuilder::Color col( builder );
            builder.output() << "next";
        }
        next( trie.m_next );
    }

    //--------------------------------------------------------------------------------------------
    // void*
    //--------------------------------------------------------------------------------------------
    template< typename U, typename F >

    static void GraphVizDump( GraphVizBuilder& builder, const char* pLabel, const TrieNode< void*, U >& trie, F&& next )
    {
        GraphVizBuilder::Node n( builder, &trie, ( pLabel + " node"s ).c_str() );
        GraphVizBuilder::Row row( builder );
        {
            GraphVizBuilder::Cell cell( builder );
            GraphVizBuilder::Color col( builder );
            builder.output() << "next";
        }
        next( trie.m_next );
    }

    //--------------------------------------------------------------------------------------------
    // Containers
    //--------------------------------------------------------------------------------------------
    template< typename U, typename F >

    static void GraphVizDump( GraphVizBuilder& builder, const TrieContainerNode& contNode, F&& next )
    {
        auto id = builder.getNodeId( &contNode.m_trie );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );
        builder.output() << id;
        builder.addEdge( builder.currentNodeId(), id );

        builder.queueWork( [&, next]
        {
            GraphVizDump( builder, contNode.m_trie, next );
        } );
    }

    template< typename U, typename F >

    static void GraphVizDump( GraphVizBuilder& builder, const TrieContainerBranch_t& contBranch, F&& next )
    {
        if( contBranch.index() == 0 )
        {
            function< void ( const TrieContainerBranch_t& ) > f( [&, next]( auto&& payload )
            {
                GraphVizDump< U >( builder, payload, next );
            } );

            GraphVizDump< U >( builder, *get< 0 >( contBranch ), move( f ) );
        }
        else
        {
            const auto& contEnd = get< 1 >( contBranch );
            next( *any_cast< U >( &contEnd.m_payload ) );
        }
    }

    template< typename U, typename F >

    static void GraphVizDump( GraphVizBuilder& builder, const char* pLabel, const TrieNode< pvec, U >& trie, F&& next )
    {
        GraphVizBuilder::Node n( builder, &trie, "Vector node" );

        for( auto&& [k,contBranch] : trie.m_fixedLengthBranches )
        {
            GraphVizBuilder::Row row( builder );

            {
                GraphVizBuilder::Cell cell( builder );
                GraphVizBuilder::Color col( builder );
                builder.output() << "length " << k;
            }

            GraphVizDump< U >( builder, contBranch, next );
        }

        for( auto&& [k,contBranch] : trie.m_variableLengthBranches )
        {
            GraphVizBuilder::Row row( builder );

            {
                GraphVizBuilder::Cell cell( builder );
                GraphVizBuilder::Color col( builder );
                builder.output() << "length " << k << '+';
            }

            using rpt_node = ptr< TrieContainerRepetitionNode< U > >;
            GraphVizDump< rpt_node >( builder, contBranch, [&, next]( auto&& payload )

            {
                auto id = builder.getNodeId( &payload->m_repetition );
                GraphVizBuilder::Cell cell( builder );
                GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );
                builder.output() << id;
                builder.addEdge( builder.currentNodeId(), id );

                builder.queueWork( [&, payload, next]
                {
                    GraphVizDump( builder, payload->m_repetition, next );
                } );
            }  );
        }
    }

    //--------------------------------------------------------------------------------------------
    // Terms
    //--------------------------------------------------------------------------------------------
    template< typename T, typename F >

    static void GraphVizDumpTermRow( GraphVizBuilder& builder, const char* pLabel, const T& ptr, F&& next )
    {
        if( !ptr )
            return;

        GraphVizBuilder::Row row( builder );

        {
            GraphVizBuilder::Cell cell( builder );
            GraphVizBuilder::Color col( builder );
            builder.output() << pLabel;
        }

        {
            auto id = builder.getNodeId( ptr.get() );

            GraphVizBuilder::Cell cell( builder );
            GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );
            builder.output() << id;
            builder.addEdge( builder.currentNodeId(), id );

            builder.queueWork( [&,pLabel,ptr,next]
            {
                GraphVizDump( builder, pLabel, *ptr, next );
            } );
        }
    }

    template< typename U, typename F >
    static void GraphVizDump( GraphVizBuilder& builder, const Trie< U >& trie, F&& next )
    {
        GraphVizBuilder::Node n( builder, &trie, "Term node" );

        GraphVizDumpTermRow( builder, "uint32_t",           get< 0 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "LocationId",         get< 1 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "string",             get< 2 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "StringId",           get< 3 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "Delimiter",          get< 4 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "Hole",               get< 5 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "AnyTerm",            get< 6 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "VecOfLength",        get< 7 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "ptr&lt; void &gt;",  get< 8 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "void*",              get< 9 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "Vector",             get< 10 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "BigInt",             get< 11 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "APSInt",             get< 12 >( trie.branches() ), next );
    }

    template< typename U >
    static void GraphVizDump( const char* pFilename, const Trie< U >& trie )
    {
        ofstream file( pFilename );
        GraphVizBuilder builder( file );
        GraphVizDump( builder, trie, []( auto&& ){} );
    }

}

#endif







|
<
|
<


|











|
<
<
<




>
|













|
<
|
<


|











|
<
<
<







|
>















>
|















>
|







|
<
<
<



>
|



|
<
|
<











>
|



|












|










|
>
|
|
|
|
|
|

|
<
|

<







>
|




















|
<
|
<








|
|
|
|
|
|
|
|
|
|
|
|
|


<
|



|

>
|
<

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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305

306

307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329

330
331
332
333
334
335
336
337

338
            GraphVizBuilder::Row row( builder );
            GraphVizBuilder::Cell cell( builder );
            GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );

            builder.output() << "&lt;any&gt;";
            builder.addEdge( builder.currentNodeId(), id );

            builder.queueWork(

                [&, next] { GraphVizDump( builder, *trie.m_anyKindBranch, next ); } );

        }

        for( auto&& [k, v] : trie.m_branches )
        {
            auto id = builder.getNodeId( v.get() );
            GraphVizBuilder::Row row( builder );
            GraphVizBuilder::Cell cell( builder );
            GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );

            ToString( builder.output(), k );
            builder.addEdge( builder.currentNodeId(), id );

            const auto& nextNode = *v;

            builder.queueWork( [&, next] { GraphVizDump( builder, nextNode, next ); } );



        }
    }

    template< typename U, typename F >
    static void GraphVizDump(
        GraphVizBuilder& builder, const char* pLabel, const TrieNode< Hole, U >& trie, F&& next )
    {
        GraphVizBuilder::Node n( builder, &trie, ( pLabel + " node"s ).c_str() );

        if( trie.m_anyNameBranch )
        {
            auto id = builder.getNodeId( trie.m_anyNameBranch.get() );
            GraphVizBuilder::Row row( builder );
            GraphVizBuilder::Cell cell( builder );
            GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );

            builder.output() << "&lt;any&gt;";
            builder.addEdge( builder.currentNodeId(), id );

            builder.queueWork(

                [&, next] { GraphVizDump( builder, *trie.m_anyNameBranch, next ); } );

        }

        for( auto&& [k, v] : trie.m_branches )
        {
            auto id = builder.getNodeId( v.get() );
            GraphVizBuilder::Row row( builder );
            GraphVizBuilder::Cell cell( builder );
            GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );

            ToString( builder.output(), k );
            builder.addEdge( builder.currentNodeId(), id );

            const auto& nextNode = *v;

            builder.queueWork( [&, next] { GraphVizDump( builder, nextNode, next ); } );



        }
    }

    //--------------------------------------------------------------------------------------------
    // ptr< void >
    //--------------------------------------------------------------------------------------------
    template< typename U, typename F >
    static void GraphVizDump( GraphVizBuilder& builder, const char* pLabel,
        const TrieNode< ptr< void >, U >& trie, F&& next )
    {
        GraphVizBuilder::Node n( builder, &trie, ( pLabel + " node"s ).c_str() );
        GraphVizBuilder::Row row( builder );
        {
            GraphVizBuilder::Cell cell( builder );
            GraphVizBuilder::Color col( builder );
            builder.output() << "next";
        }
        next( trie.m_next );
    }

    //--------------------------------------------------------------------------------------------
    // void*
    //--------------------------------------------------------------------------------------------
    template< typename U, typename F >
    static void GraphVizDump(
        GraphVizBuilder& builder, const char* pLabel, const TrieNode< void*, U >& trie, F&& next )
    {
        GraphVizBuilder::Node n( builder, &trie, ( pLabel + " node"s ).c_str() );
        GraphVizBuilder::Row row( builder );
        {
            GraphVizBuilder::Cell cell( builder );
            GraphVizBuilder::Color col( builder );
            builder.output() << "next";
        }
        next( trie.m_next );
    }

    //--------------------------------------------------------------------------------------------
    // Containers
    //--------------------------------------------------------------------------------------------
    template< typename U, typename F >
    static void GraphVizDump(
        GraphVizBuilder& builder, const TrieContainerNode& contNode, F&& next )
    {
        auto id = builder.getNodeId( &contNode.m_trie );
        GraphVizBuilder::Cell cell( builder );
        GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );
        builder.output() << id;
        builder.addEdge( builder.currentNodeId(), id );

        builder.queueWork( [&, next] { GraphVizDump( builder, contNode.m_trie, next ); } );



    }

    template< typename U, typename F >
    static void GraphVizDump(
        GraphVizBuilder& builder, const TrieContainerBranch_t& contBranch, F&& next )
    {
        if( contBranch.index() == 0 )
        {
            function< void( const TrieContainerBranch_t& ) > f(

                [&, next]( auto&& payload ) { GraphVizDump< U >( builder, payload, next ); } );


            GraphVizDump< U >( builder, *get< 0 >( contBranch ), move( f ) );
        }
        else
        {
            const auto& contEnd = get< 1 >( contBranch );
            next( *any_cast< U >( &contEnd.m_payload ) );
        }
    }

    template< typename U, typename F >
    static void GraphVizDump(
        GraphVizBuilder& builder, const char* pLabel, const TrieNode< pvec, U >& trie, F&& next )
    {
        GraphVizBuilder::Node n( builder, &trie, "Vector node" );

        for( auto&& [k, contBranch] : trie.m_fixedLengthBranches )
        {
            GraphVizBuilder::Row row( builder );

            {
                GraphVizBuilder::Cell cell( builder );
                GraphVizBuilder::Color col( builder );
                builder.output() << "length " << k;
            }

            GraphVizDump< U >( builder, contBranch, next );
        }

        for( auto&& [k, contBranch] : trie.m_variableLengthBranches )
        {
            GraphVizBuilder::Row row( builder );

            {
                GraphVizBuilder::Cell cell( builder );
                GraphVizBuilder::Color col( builder );
                builder.output() << "length " << k << '+';
            }

            using rpt_node = ptr< TrieContainerRepetitionNode< U > >;
            GraphVizDump< rpt_node >( builder, contBranch,
                [&, next]( auto&& payload )
                {
                    auto id = builder.getNodeId( &payload->m_repetition );
                    GraphVizBuilder::Cell cell( builder );
                    GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );
                    builder.output() << id;
                    builder.addEdge( builder.currentNodeId(), id );

                    builder.queueWork( [&, payload, next]

                        { GraphVizDump( builder, payload->m_repetition, next ); } );
                } );

        }
    }

    //--------------------------------------------------------------------------------------------
    // Terms
    //--------------------------------------------------------------------------------------------
    template< typename T, typename F >
    static void GraphVizDumpTermRow(
        GraphVizBuilder& builder, const char* pLabel, const T& ptr, F&& next )
    {
        if( !ptr )
            return;

        GraphVizBuilder::Row row( builder );

        {
            GraphVizBuilder::Cell cell( builder );
            GraphVizBuilder::Color col( builder );
            builder.output() << pLabel;
        }

        {
            auto id = builder.getNodeId( ptr.get() );

            GraphVizBuilder::Cell cell( builder );
            GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );
            builder.output() << id;
            builder.addEdge( builder.currentNodeId(), id );

            builder.queueWork(

                [&, pLabel, ptr, next] { GraphVizDump( builder, pLabel, *ptr, next ); } );

        }
    }

    template< typename U, typename F >
    static void GraphVizDump( GraphVizBuilder& builder, const Trie< U >& trie, F&& next )
    {
        GraphVizBuilder::Node n( builder, &trie, "Term node" );

        GraphVizDumpTermRow( builder, "uint32_t", get< 0 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "LocationId", get< 1 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "string", get< 2 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "StringId", get< 3 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "Delimiter", get< 4 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "Hole", get< 5 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "AnyTerm", get< 6 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "VecOfLength", get< 7 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "ptr&lt; void &gt;", get< 8 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "void*", get< 9 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "Vector", get< 10 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "BigInt", get< 11 >( trie.branches() ), next );
        GraphVizDumpTermRow( builder, "APSInt", get< 12 >( trie.branches() ), next );
    }


    template< typename U > static void GraphVizDump( const char* pFilename, const Trie< U >& trie )
    {
        ofstream file( pFilename );
        GraphVizBuilder builder( file );
        GraphVizDump( builder, trie, []( auto&& ) {} );
    }
} // namespace goose::eir


#endif
Changes to bs/eir/hash.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
#include "eir.h"
#include "cir/cir.h"

namespace std
{
    size_t hash< goose::eir::Hole >::operator()( const goose::eir::Hole& x ) const
    {
        return llvm::hash_combine(
            goose::util::ComputeHash( x.name() ),
            goose::util::ComputeHash( x.kind() ),
            goose::util::ComputeHash( x.behavior() )
        );
    }

    size_t hash< goose::eir::Vector >::operator()( const goose::eir::Vector& v ) const
    {
        auto g = ContainerHashGenerator( v.terms() );
        return llvm::hash_combine_range( g.begin(), g.end() );
    }

    size_t hash< goose::eir::Term >::operator()( const goose::eir::Term& x ) const
    {

        return visit( [&]< typename T >( T&& t )
        {
            using TT = remove_cvref_t< T >;

            if constexpr( is_same_v< TT, goose::util::ptr< void > > || is_same_v< TT, void* > )
            {
                return llvm::hash_combine( goose::util::ComputeHash( x.index() ), goose::util::ComputeHash( nullptr ) );

            }
            else if constexpr( is_same_v< TT, goose::eir::pvec > )
            {

                return llvm::hash_combine( goose::util::ComputeHash( x.index() ), goose::util::ComputeHash( *t ) );
            }
            else if constexpr( is_same_v< TT, goose::cir::LocationId > )
            {

                return llvm::hash_combine( goose::util::ComputeHash( x.index() ), goose::util::ComputeHash( 0UL ) );
            }
            else
            {

                return llvm::hash_combine( goose::util::ComputeHash( x.index() ), goose::util::ComputeHash( t ) );
            }

        }, x );
    }

    size_t hash< goose::eir::Value >::operator()( const goose::eir::Value& x ) const
    {
        if( x.isConstant() )

            return llvm::hash_combine( goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.val() ) );
        else

            return llvm::hash_combine( goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( *x.cir() ) );
    }
}







|
<
|
<
<










>
|
|
|

|
|
|
>
|
|
|
>
|
|
|
|
>
|
|
|
|
>
|
|
>
|





>
|

>
|

|
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 "eir.h"
#include "cir/cir.h"

namespace std
{
    size_t hash< goose::eir::Hole >::operator()( const goose::eir::Hole& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.name() ),

            goose::util::ComputeHash( x.kind() ), goose::util::ComputeHash( x.behavior() ) );


    }

    size_t hash< goose::eir::Vector >::operator()( const goose::eir::Vector& v ) const
    {
        auto g = ContainerHashGenerator( v.terms() );
        return llvm::hash_combine_range( g.begin(), g.end() );
    }

    size_t hash< goose::eir::Term >::operator()( const goose::eir::Term& x ) const
    {
        return visit(
            [&]< typename T >( T&& t )
            {
                using TT = remove_cvref_t< T >;

                if constexpr( is_same_v< TT, goose::util::ptr< void > > || is_same_v< TT, void* > )
                {
                    return llvm::hash_combine( goose::util::ComputeHash( x.index() ),
                        goose::util::ComputeHash( nullptr ) );
                }
                else if constexpr( is_same_v< TT, goose::eir::pvec > )
                {
                    return llvm::hash_combine(
                        goose::util::ComputeHash( x.index() ), goose::util::ComputeHash( *t ) );
                }
                else if constexpr( is_same_v< TT, goose::cir::LocationId > )
                {
                    return llvm::hash_combine(
                        goose::util::ComputeHash( x.index() ), goose::util::ComputeHash( 0UL ) );
                }
                else
                {
                    return llvm::hash_combine(
                        goose::util::ComputeHash( x.index() ), goose::util::ComputeHash( t ) );
                }
            },
            x );
    }

    size_t hash< goose::eir::Value >::operator()( const goose::eir::Value& x ) const
    {
        if( x.isConstant() )
            return llvm::hash_combine(
                goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.val() ) );
        else
            return llvm::hash_combine(
                goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( *x.cir() ) );
    }
} // namespace std
Changes to bs/eir/hash.h.
18
19
20
21
22
23
24
25
26
27
        size_t operator()( const goose::eir::Term& x ) const;
    };

    template<> struct hash< goose::eir::Value >
    {
        size_t operator()( const goose::eir::Value& x ) const;
    };
}

#endif







|


18
19
20
21
22
23
24
25
26
27
        size_t operator()( const goose::eir::Term& x ) const;
    };

    template<> struct hash< goose::eir::Value >
    {
        size_t operator()( const goose::eir::Value& x ) const;
    };
} // namespace std

#endif
Changes to bs/eir/helpers.cpp.
43
44
45
46
47
48
49
50

    TermGen ForEachInVectorTerm( const Term& vectorTerm )
    {
        const auto& vec = *get< pvec >( vectorTerm );
        for( auto&& t : vec.terms() )
            co_yield &t;
    }
}







|
43
44
45
46
47
48
49
50

    TermGen ForEachInVectorTerm( const Term& vectorTerm )
    {
        const auto& vec = *get< pvec >( vectorTerm );
        for( auto&& t : vec.terms() )
            co_yield &t;
    }
} // namespace goose::eir
Changes to bs/eir/helpers.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
#ifndef GOOSE_EIR_HELPERS_H
#define GOOSE_EIR_HELPERS_H

namespace goose::eir
{
    static inline auto VecSize( const Term& vectorTerm )
    {
        return get< pvec >( vectorTerm )->terms().size();
    }

    template< typename... T >
    Term AppendToVectorTerm( const Term& vectorTerm, T&&... t );

    template< typename... T >
    Term PrependToVectorTerm( const Term& vectorTerm, T&&... t );

    extern Term ConcatenateVectorTerms( const Term& vector1, const Term& vector2 );

    extern Term TakeVectorTerm( const Term& vectorTerm, size_t n );
    extern Term DropVectorTerm( const Term& vectorTerm, size_t n );

    extern Term DuplicateVectorTerm( const Term& vectorTerm );

    template< typename F >
    void ForEachInVectorTerm( const Term& vectorTerm, F&& func );

    template< typename F >
    bool ForEachInVectorTerms( const Term& vectorTerm1, const Term& vectorTerm2, F&& func );

    using TermGen = Generator< const Term* >;
    extern TermGen ForEachInVectorTerm( const Term& vectorTerm );

    extern bool AreVecLengthsCompatible( const VecLength& vl1, const VecLength& vl2 );
}

#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
#ifndef GOOSE_EIR_HELPERS_H
#define GOOSE_EIR_HELPERS_H

namespace goose::eir
{
    static inline auto VecSize( const Term& vectorTerm )
    {
        return get< pvec >( vectorTerm )->terms().size();
    }


    template< typename... T > Term AppendToVectorTerm( const Term& vectorTerm, T&&... t );


    template< typename... T > Term PrependToVectorTerm( const Term& vectorTerm, T&&... t );

    extern Term ConcatenateVectorTerms( const Term& vector1, const Term& vector2 );

    extern Term TakeVectorTerm( const Term& vectorTerm, size_t n );
    extern Term DropVectorTerm( const Term& vectorTerm, size_t n );

    extern Term DuplicateVectorTerm( const Term& vectorTerm );


    template< typename F > void ForEachInVectorTerm( const Term& vectorTerm, F&& func );

    template< typename F >
    bool ForEachInVectorTerms( const Term& vectorTerm1, const Term& vectorTerm2, F&& func );

    using TermGen = Generator< const Term* >;
    extern TermGen ForEachInVectorTerm( const Term& vectorTerm );

    extern bool AreVecLengthsCompatible( const VecLength& vl1, const VecLength& vl2 );
} // namespace goose::eir

#endif
Changes to bs/eir/helpers.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
#ifndef GOOSE_EIR_HELPERS_INL
#define GOOSE_EIR_HELPERS_INL

namespace goose::eir
{
    template< typename... T >
    Term AppendToVectorTerm( const Term& vectorTerm, T&&... t )
    {
        const auto& vec = *get< pvec >( vectorTerm );
        auto newVec = Vector::MakeAppend( vec, forward< T >( t )... );
        return TERM( make_shared< Vector >( move( newVec ) ) );
    }

    template< typename... T >
    Term PrependToVectorTerm( const Term& vectorTerm, T&&... t )
    {
        const auto& vec = *get< pvec >( vectorTerm );
        auto newVec = Vector::MakePrepend( vec, forward< T >( t )... );
        return TERM( make_shared< Vector >( move( newVec ) ) );
    }

    template< typename F >
    void ForEachInVectorTerm( const Term& vectorTerm, F&& func )
    {
        auto& vec = *get< pvec >( vectorTerm );
        for( auto&& t : vec.terms() )
        {
            if( !func( t ) )
                break;
        }
    }

    template< typename F >
    bool ForEachInVectorTerms( const Term& vectorTerm1, const Term& vectorTerm2, F&& func )
    {
        // We allow the lambda to mutate the content even though we are taking constrefs to the terms:
        // I tried to make a diff overload with mutable refs and somehow it doesn't resolve to it
        // and c++ is a pain in the ass
        // In goose I could just capture the constness of the inputs refs as a template param and
        // apply it to those inner refs. It's probably possible in C++ through some clown show ref
        // type wrapper but fuck that
        auto& vec1 = *get< pvec >( vectorTerm1 );
        auto& vec2 = *get< pvec >( vectorTerm2 );

        if( vec1.terms().size() != vec2.terms().size() )
            return false;

        for( size_t i = 0; i < vec1.terms().size(); ++i )
        {
            if( !func( vec1[i], vec2[i] ) )
                break;
        }

        return true;
    }
}

#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
#ifndef GOOSE_EIR_HELPERS_INL
#define GOOSE_EIR_HELPERS_INL

namespace goose::eir
{

    template< typename... T > Term AppendToVectorTerm( const Term& vectorTerm, T&&... t )
    {
        const auto& vec = *get< pvec >( vectorTerm );
        auto newVec = Vector::MakeAppend( vec, forward< T >( t )... );
        return TERM( make_shared< Vector >( move( newVec ) ) );
    }


    template< typename... T > Term PrependToVectorTerm( const Term& vectorTerm, T&&... t )
    {
        const auto& vec = *get< pvec >( vectorTerm );
        auto newVec = Vector::MakePrepend( vec, forward< T >( t )... );
        return TERM( make_shared< Vector >( move( newVec ) ) );
    }


    template< typename F > void ForEachInVectorTerm( const Term& vectorTerm, F&& func )
    {
        auto& vec = *get< pvec >( vectorTerm );
        for( auto&& t : vec.terms() )

            if( !func( t ) )
                break;

    }

    template< typename F >
    bool ForEachInVectorTerms( const Term& vectorTerm1, const Term& vectorTerm2, F&& func )
    {
        // We allow the lambda to mutate the content even though we are taking constrefs to the
        // terms: I tried to make a diff overload with mutable refs and somehow it doesn't resolve

        // to it and c++ is a pain in the ass In goose I could just capture the constness of the
        // inputs refs as a template param and apply it to those inner refs. It's probably possible
        // in C++ through some clown show ref type wrapper but fuck that
        auto& vec1 = *get< pvec >( vectorTerm1 );
        auto& vec2 = *get< pvec >( vectorTerm2 );

        if( vec1.terms().size() != vec2.terms().size() )
            return false;

        for( size_t i = 0; i < vec1.terms().size(); ++i )

            if( !func( vec1[i], vec2[i] ) )
                break;


        return true;
    }
} // namespace goose::eir

#endif
Changes to bs/eir/match.cpp.
13
14
15
16
17
18
19
20
    void MatchSolution::setupVars()
    {
        if( !m_pVars )
            m_pVars = make_shared< unordered_map< StringId, any > >();
        else if( m_pVars.use_count() > 1 )
            m_pVars = make_shared< unordered_map< StringId, any > >( *m_pVars );
    }
}







|
13
14
15
16
17
18
19
20
    void MatchSolution::setupVars()
    {
        if( !m_pVars )
            m_pVars = make_shared< unordered_map< StringId, any > >();
        else if( m_pVars.use_count() > 1 )
            m_pVars = make_shared< unordered_map< StringId, any > >( *m_pVars );
    }
} // namespace goose::eir
Changes to bs/eir/match.h.
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

            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( StringId name ) const
            {
                if( name == "_"_sid )
                    return nullptr;

                if( !m_pVars )
                    return nullptr;

                auto it = m_pVars->find( name );
                if( it == m_pVars->end() )
                    return nullptr;

                return any_cast< T >( &it->second );
            }

            // Tries to set the variable. If it already exists but have a different
            // value, returns false.
            template< typename T >
            bool setVar( StringId name, T&& val )
            {
                if( name == "_"_sid )
                    return true;

                if( m_pVars )
                {
                    auto it = m_pVars->find( name );
                    if( it != m_pVars->end() )
                    {
                        using TT = remove_cvref_t< T >;
                        if( const auto* pExistingVal = any_cast< TT >( &it->second ) )
                        {
                            if constexpr( is_same_v< TT, VecLength > )
                            {
                                if( !AreVecLengthsCompatible( *pExistingVal, val ) )
                                    return false;

                                it->second = VecLength(
                                    max( pExistingVal->minLength(), val.minLength() ),
                                    pExistingVal->isVariable() && val.isVariable()
                                );

                                return true;
                            }
                            else
                                return *pExistingVal == val;
                        }

                        return false;
                    }
                }

                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
    };

    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match( const Term& expression, const Trie< U >& patterns );
}



#endif







|
|
>
|

>
>
|
|
|
|
|
|
|

|
|

|
|
|

|
|

|
|
|
<
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
<

|
|
|
|
|

|
|
|

|
|
|
|

|

|
|

|
|
|



|
<
>
>


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

            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( StringId name ) const
        {
            if( name == "_"_sid )
                return nullptr;

            if( !m_pVars )
                return nullptr;

            auto it = m_pVars->find( name );
            if( it == m_pVars->end() )
                return nullptr;

            return any_cast< T >( &it->second );
        }

        // Tries to set the variable. If it already exists but have a different
        // value, returns false.
        template< typename T > bool setVar( StringId name, T&& val )

        {
            if( name == "_"_sid )
                return true;

            if( m_pVars )
            {
                auto it = m_pVars->find( name );
                if( it != m_pVars->end() )
                {
                    using TT = remove_cvref_t< T >;
                    if( const auto* pExistingVal = any_cast< TT >( &it->second ) )
                    {
                        if constexpr( is_same_v< TT, VecLength > )
                        {
                            if( !AreVecLengthsCompatible( *pExistingVal, val ) )
                                return false;

                            it->second =
                                VecLength( max( pExistingVal->minLength(), val.minLength() ),
                                    pExistingVal->isVariable() && val.isVariable() );


                            return true;
                        }
                        else
                            return *pExistingVal == val;
                    }

                    return false;
                }
            }

            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
    };

    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match(

        const Term& expression, const Trie< U >& patterns );
} // namespace goose::eir

#endif
Changes to bs/eir/match.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
#ifndef GOOSE_EIR_MATCH_INL
#define GOOSE_EIR_MATCH_INL

namespace goose::eir
{
    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match( MatchSolution&& s, const Term& expression, const Trie< U >& patterns );


    //--------------------------------------------------------------------------------------------
    // AnyTerm
    //--------------------------------------------------------------------------------------------
    // Term/Trie
    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match( MatchSolution&& s, const Term& expression, const ValueTrieNode< AnyTerm, U >& trie )

    {
        for( auto&& [at, payload] : trie.m_branches )
        {
            auto sol = s;
            if( sol.setVar( at.varName(), expression ) )
                co_yield { move( sol ), payload };
        }
    }

    //--------------------------------------------------------------------------------------------
    // VecOfLength
    //--------------------------------------------------------------------------------------------
    // Term/Trie
    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match( MatchSolution&& s, const pvec& vec, const ValueTrieNode< VecOfLength, U >& trie )

    {
        auto vecLen = vec->length();

        s.addComplexity( 1 );

        for( auto&& [vol, payload] : trie.m_branches )
        {
            auto sol = s;
            if( sol.setVar( vol.varName(), vecLen ) )
                co_yield { move( sol ), payload };
        }
    }

    //--------------------------------------------------------------------------------------------
    // Primitive types
    //--------------------------------------------------------------------------------------------
    // Term/Trie
    template< typename T, typename U >
    static Generator< pair< MatchSolution, const U& > > Match( MatchSolution&& s, const T& val, const ValueTrieNode< T, U >& trie )

    {
        auto it = trie.m_branches.find( val );
        if( it != trie.m_branches.end() )
        {
            s.addComplexity( 2 );
            co_yield { move( s ), it->second };
        }
    }

    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match( MatchSolution&& s, const APSInt& val, const ValueTrieNode< FixedInt, U >& trie )

    {
        auto it = trie.m_branches.find( val );
        if( it != trie.m_branches.end() )
        {
            s.addComplexity( 2 );
            co_yield { move( s ), it->second };
        }
    }

    //--------------------------------------------------------------------------------------------
    // Hole
    //--------------------------------------------------------------------------------------------
    // Term/Trie
    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match( MatchSolution&& s, const Hole& h, const HoleBehaviorNode< U >& trie )

    {
        switch( h.behavior() )
        {
            case Hole::Behavior::Standard:
            {
                if( !trie.m_standardBhvBranch )
                    break;






|
>






|
>














|
>


















|
>










|
>














|
>







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
#ifndef GOOSE_EIR_MATCH_INL
#define GOOSE_EIR_MATCH_INL

namespace goose::eir
{
    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match(
        MatchSolution&& s, const Term& expression, const Trie< U >& patterns );

    //--------------------------------------------------------------------------------------------
    // AnyTerm
    //--------------------------------------------------------------------------------------------
    // Term/Trie
    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match(
        MatchSolution&& s, const Term& expression, const ValueTrieNode< AnyTerm, U >& trie )
    {
        for( auto&& [at, payload] : trie.m_branches )
        {
            auto sol = s;
            if( sol.setVar( at.varName(), expression ) )
                co_yield { move( sol ), payload };
        }
    }

    //--------------------------------------------------------------------------------------------
    // VecOfLength
    //--------------------------------------------------------------------------------------------
    // Term/Trie
    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match(
        MatchSolution&& s, const pvec& vec, const ValueTrieNode< VecOfLength, U >& trie )
    {
        auto vecLen = vec->length();

        s.addComplexity( 1 );

        for( auto&& [vol, payload] : trie.m_branches )
        {
            auto sol = s;
            if( sol.setVar( vol.varName(), vecLen ) )
                co_yield { move( sol ), payload };
        }
    }

    //--------------------------------------------------------------------------------------------
    // Primitive types
    //--------------------------------------------------------------------------------------------
    // Term/Trie
    template< typename T, typename U >
    static Generator< pair< MatchSolution, const U& > > Match(
        MatchSolution&& s, const T& val, const ValueTrieNode< T, U >& trie )
    {
        auto it = trie.m_branches.find( val );
        if( it != trie.m_branches.end() )
        {
            s.addComplexity( 2 );
            co_yield { move( s ), it->second };
        }
    }

    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match(
        MatchSolution&& s, const APSInt& val, const ValueTrieNode< FixedInt, U >& trie )
    {
        auto it = trie.m_branches.find( val );
        if( it != trie.m_branches.end() )
        {
            s.addComplexity( 2 );
            co_yield { move( s ), it->second };
        }
    }

    //--------------------------------------------------------------------------------------------
    // Hole
    //--------------------------------------------------------------------------------------------
    // Term/Trie
    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match(
        MatchSolution&& s, const Hole& h, const HoleBehaviorNode< U >& trie )
    {
        switch( h.behavior() )
        {
            case Hole::Behavior::Standard:
            {
                if( !trie.m_standardBhvBranch )
                    break;
116
117
118
119
120
121
122
123

124
125
126
127
128
129
130
        {
            s.addComplexity( 1 );
            co_yield { move( s ), *trie.m_anyBhvBranch };
        }
    }

    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match( MatchSolution&& s, const Hole& h, const HoleKindNode< U >& trie )

    {
        if( h.kind() == ""_sid )
        {
            s.addComplexity( 1 );

            for( auto&& x : trie.m_branches )
            {







|
>







122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
        {
            s.addComplexity( 1 );
            co_yield { move( s ), *trie.m_anyBhvBranch };
        }
    }

    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match(
        MatchSolution&& s, const Hole& h, const HoleKindNode< U >& trie )
    {
        if( h.kind() == ""_sid )
        {
            s.addComplexity( 1 );

            for( auto&& x : trie.m_branches )
            {
150
151
152
153
154
155
156
157

158
159
160
161
162
163
164
        {
            s.addComplexity( 1 );
            co_yield Match( move( s ), h, *trie.m_anyKindBranch );
        }
    }

    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match( MatchSolution&& s, const Hole& h, const TrieNode< Hole, U >& trie )

    {
        if( h.name() == ""_sid )
        {
            s.addComplexity( 1 );

            for( auto&& x : trie.m_branches )
            {







|
>







157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
        {
            s.addComplexity( 1 );
            co_yield Match( move( s ), h, *trie.m_anyKindBranch );
        }
    }

    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match(
        MatchSolution&& s, const Hole& h, const TrieNode< Hole, U >& trie )
    {
        if( h.name() == ""_sid )
        {
            s.addComplexity( 1 );

            for( auto&& x : trie.m_branches )
            {
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295

296
297
298
299
300
301
302
303
304

305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325

326
327
328
329
330
331
332
    }

    //--------------------------------------------------------------------------------------------
    // LocationId
    //--------------------------------------------------------------------------------------------
    // Term/Trie
    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match( MatchSolution&& s, const LocationId& val, const TrieNode< LocationId, U >& trie )

    {
        auto sol = s;
        co_yield { move( sol ), trie.m_next };
    }

    //--------------------------------------------------------------------------------------------
    // ptr< void >
    //--------------------------------------------------------------------------------------------
    // Term/Trie
    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match( MatchSolution&& s, const ptr< void >& val, const TrieNode< ptr< void >, U >& trie )

    {
        auto sol = s;
        co_yield { move( sol ), trie.m_next };
    }

    //--------------------------------------------------------------------------------------------
    // void*
    //--------------------------------------------------------------------------------------------
    // Term/Trie
    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match( MatchSolution&& s, const void* val, const TrieNode< void*, U >& trie )

    {
        auto sol = s;
        co_yield { move( sol ), trie.m_next };
    }

    //--------------------------------------------------------------------------------------------
    // Containers
    //--------------------------------------------------------------------------------------------
    template< typename U >
    Generator< pair< MatchSolution, const U& > > MatchRepetition( MatchSolution&& s, VecGenerator exprCont,
        const TrieContainerRepetitionNode< U >& rptNode )
    {
        if( exprCont.finished() )
            co_yield { move( s ), rptNode.m_next };
        else
        {
            for( auto&& [s, payload] : Match( move( s ), exprCont(), rptNode.m_repetition ) )
                co_yield MatchRepetition( move( s ), exprCont, rptNode );
        }
    }

    template< typename U >
    Generator< tuple< MatchSolution, const U&, VecGenerator > > Match( MatchSolution&& s, VecGenerator exprCont, const TrieContainerBranch_t& contBranch )

    {
        if( contBranch.index() == 0 )
        {

            for( auto&& [s, payload] : Match( move( s ), exprCont(), get< 0 >( contBranch )->m_trie ) )
                co_yield Match< U >( move( s ), exprCont, payload );
        }
        else
        {
            const auto& contEnd = get< 1 >( contBranch );
            co_yield { move( s ), *any_cast< U >( &contEnd.m_payload ), exprCont };
        }
    }

    // Term/Trie
    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match( MatchSolution&& s, const pvec& vec, const TrieNode< pvec, U >& trie )

    {
        auto exprLen = vec->length();

        // Ignore variable length vectors in expressions. They can't be matched.
        // We don't need it and it's much simpler this way.
        if( exprLen.isVariable() )
            co_return;

        s.addComplexity( 1 );

        if( auto it = trie.m_fixedLengthBranches.find( exprLen.minLength() ); it != trie.m_fixedLengthBranches.end() )

        {
            auto sol = s;
            VecGenerator gen( *vec );
            for( auto&& [s,content,g] : Match< U >( move( sol ), gen, it->second ) )
                co_yield { s, content };
        }

        using rpt_node = ptr< TrieContainerRepetitionNode< U > >;

        auto it = trie.m_variableLengthBranches.begin();
        auto end = trie.m_variableLengthBranches.upper_bound( exprLen.minLength() );
        while( it != end )
        {
            auto sol = s;
            VecGenerator gen( *vec );
            for( auto&& [s,pRptNode,g] : Match< rpt_node >( move( sol ), gen, it->second ) )
                co_yield MatchRepetition( move( s ), g, *pRptNode );

            ++it;
        }
    }

    //--------------------------------------------------------------------------------------------
    // Terms
    //--------------------------------------------------------------------------------------------
    // Term/Trie
    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match( MatchSolution&& s, const Term& expression, const Trie< U >& patterns )

    {
        return visit( [&]( auto&& t )
        {
            return Match( move( s ), t, patterns );
        }, expression );
    }

    template< typename T, typename U >
    static Generator< pair< MatchSolution, const U& > > Match( MatchSolution&& s, const T& t, const Trie< U >& patterns )

    {
        static_assert( !is_same_v< T, Term > );

        if( auto pBranch = get< ptr< TrieNode< T, U > > >( patterns.branches() ) )
            co_yield Match( MatchSolution( s ), t, *pBranch );

        if constexpr( is_same_v< T, pvec > )
        {
            if( auto pBranch = get< ptr< TrieNode< VecOfLength, U > > >( patterns.branches() ) )
                co_yield Match( MatchSolution( s ), t, *pBranch );
        }

        if constexpr( !is_same_v< T, AnyTerm > )
        {
            if( auto pBranch = get< ptr< TrieNode< AnyTerm, U > > >( patterns.branches() ) )
                co_yield Match( move( s ), t, *pBranch );
        }
    }

    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match( const Term& expression, const Trie< U >& patterns )

    {
        MatchSolution s;
        co_yield Match( move( s ), expression, patterns );
    }
}

#endif







|
>










|
>










|
>









|
|




<


<



|
>



>
|











|
>










|
>



|











|











|
>

|
<
<
<



|
>




















|
>




|


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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311



312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
    }

    //--------------------------------------------------------------------------------------------
    // LocationId
    //--------------------------------------------------------------------------------------------
    // Term/Trie
    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match(
        MatchSolution&& s, const LocationId& val, const TrieNode< LocationId, U >& trie )
    {
        auto sol = s;
        co_yield { move( sol ), trie.m_next };
    }

    //--------------------------------------------------------------------------------------------
    // ptr< void >
    //--------------------------------------------------------------------------------------------
    // Term/Trie
    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match(
        MatchSolution&& s, const ptr< void >& val, const TrieNode< ptr< void >, U >& trie )
    {
        auto sol = s;
        co_yield { move( sol ), trie.m_next };
    }

    //--------------------------------------------------------------------------------------------
    // void*
    //--------------------------------------------------------------------------------------------
    // Term/Trie
    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match(
        MatchSolution&& s, const void* val, const TrieNode< void*, U >& trie )
    {
        auto sol = s;
        co_yield { move( sol ), trie.m_next };
    }

    //--------------------------------------------------------------------------------------------
    // Containers
    //--------------------------------------------------------------------------------------------
    template< typename U >
    Generator< pair< MatchSolution, const U& > > MatchRepetition(
        MatchSolution&& s, VecGenerator exprCont, const TrieContainerRepetitionNode< U >& rptNode )
    {
        if( exprCont.finished() )
            co_yield { move( s ), rptNode.m_next };
        else

            for( auto&& [s, payload] : Match( move( s ), exprCont(), rptNode.m_repetition ) )
                co_yield MatchRepetition( move( s ), exprCont, rptNode );

    }

    template< typename U >
    Generator< tuple< MatchSolution, const U&, VecGenerator > > Match(
        MatchSolution&& s, VecGenerator exprCont, const TrieContainerBranch_t& contBranch )
    {
        if( contBranch.index() == 0 )
        {
            for( auto&& [s, payload] :
                Match( move( s ), exprCont(), get< 0 >( contBranch )->m_trie ) )
                co_yield Match< U >( move( s ), exprCont, payload );
        }
        else
        {
            const auto& contEnd = get< 1 >( contBranch );
            co_yield { move( s ), *any_cast< U >( &contEnd.m_payload ), exprCont };
        }
    }

    // Term/Trie
    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match(
        MatchSolution&& s, const pvec& vec, const TrieNode< pvec, U >& trie )
    {
        auto exprLen = vec->length();

        // Ignore variable length vectors in expressions. They can't be matched.
        // We don't need it and it's much simpler this way.
        if( exprLen.isVariable() )
            co_return;

        s.addComplexity( 1 );

        if( auto it = trie.m_fixedLengthBranches.find( exprLen.minLength() );
            it != trie.m_fixedLengthBranches.end() )
        {
            auto sol = s;
            VecGenerator gen( *vec );
            for( auto&& [s, content, g] : Match< U >( move( sol ), gen, it->second ) )
                co_yield { s, content };
        }

        using rpt_node = ptr< TrieContainerRepetitionNode< U > >;

        auto it = trie.m_variableLengthBranches.begin();
        auto end = trie.m_variableLengthBranches.upper_bound( exprLen.minLength() );
        while( it != end )
        {
            auto sol = s;
            VecGenerator gen( *vec );
            for( auto&& [s, pRptNode, g] : Match< rpt_node >( move( sol ), gen, it->second ) )
                co_yield MatchRepetition( move( s ), g, *pRptNode );

            ++it;
        }
    }

    //--------------------------------------------------------------------------------------------
    // Terms
    //--------------------------------------------------------------------------------------------
    // Term/Trie
    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match(
        MatchSolution&& s, const Term& expression, const Trie< U >& patterns )
    {
        return visit( [&]( auto&& t ) { return Match( move( s ), t, patterns ); }, expression );



    }

    template< typename T, typename U >
    static Generator< pair< MatchSolution, const U& > > Match(
        MatchSolution&& s, const T& t, const Trie< U >& patterns )
    {
        static_assert( !is_same_v< T, Term > );

        if( auto pBranch = get< ptr< TrieNode< T, U > > >( patterns.branches() ) )
            co_yield Match( MatchSolution( s ), t, *pBranch );

        if constexpr( is_same_v< T, pvec > )
        {
            if( auto pBranch = get< ptr< TrieNode< VecOfLength, U > > >( patterns.branches() ) )
                co_yield Match( MatchSolution( s ), t, *pBranch );
        }

        if constexpr( !is_same_v< T, AnyTerm > )
        {
            if( auto pBranch = get< ptr< TrieNode< AnyTerm, U > > >( patterns.branches() ) )
                co_yield Match( move( s ), t, *pBranch );
        }
    }

    template< typename U >
    static Generator< pair< MatchSolution, const U& > > Match(
        const Term& expression, const Trie< U >& patterns )
    {
        MatchSolution s;
        co_yield Match( move( s ), expression, patterns );
    }
} // namespace goose::eir

#endif
Changes to bs/eir/merge.cpp.
1
2
3
4
5
6
7
8
9
#include "eir.h"

namespace goose::eir
{
    Trie<> Merge( const Trie<>& trie, const Term& term )
    {
        return Merge( trie, term, []( auto&& ){ return EmptyPayload(); } );
    }
}






|

|
1
2
3
4
5
6
7
8
9
#include "eir.h"

namespace goose::eir
{
    Trie<> Merge( const Trie<>& trie, const Term& term )
    {
        return Merge( trie, term, []( auto&& ) { return EmptyPayload(); } );
    }
} // namespace goose::eir
Changes to bs/eir/merge.inl.
1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
#ifndef GOOSE_EIR_MERGE_INL
#define GOOSE_EIR_MERGE_INL

namespace goose::eir
{
    //--------------------------------------------------------------------------------------------
    // Primitive types
    //--------------------------------------------------------------------------------------------
    template< typename T, typename U, typename F >
    static ptr< TrieNode< T, U > > Merge( const ptr< TrieNode< T, U > >& lhs, const T& rhs, F&& next )

    {
        if constexpr( is_same_v< T, void* > )
        {
            auto pNewNode = make_shared< TrieNode< void*, U > >();
            pNewNode->m_next = lhs ? next( lhs->m_next ) : next( U() );
            return pNewNode;
        }









|
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef GOOSE_EIR_MERGE_INL
#define GOOSE_EIR_MERGE_INL

namespace goose::eir
{
    //--------------------------------------------------------------------------------------------
    // Primitive types
    //--------------------------------------------------------------------------------------------
    template< typename T, typename U, typename F >
    static ptr< TrieNode< T, U > > Merge(
        const ptr< TrieNode< T, U > >& lhs, const T& rhs, F&& next )
    {
        if constexpr( is_same_v< T, void* > )
        {
            auto pNewNode = make_shared< TrieNode< void*, U > >();
            pNewNode->m_next = lhs ? next( lhs->m_next ) : next( U() );
            return pNewNode;
        }
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
        }
    }

    //--------------------------------------------------------------------------------------------
    // Holes
    //--------------------------------------------------------------------------------------------
    template< typename U, typename F >
    static ptr< HoleBehaviorNode< U > > Merge( const ptr< HoleBehaviorNode< U > >& lhs, const Hole& rhs, F&& next )

    {
        ptr< HoleBehaviorNode< U > > pNewNode;

        if( lhs )
            pNewNode = make_shared< HoleBehaviorNode< U > >( *lhs );
        else
            pNewNode = make_shared< HoleBehaviorNode< U > >();

        switch( rhs.behavior() )
        {
            case Hole::Behavior::Standard:
                pNewNode->m_standardBhvBranch = make_shared< U >(
                    pNewNode->m_standardBhvBranch ? next( *pNewNode->m_standardBhvBranch ) : next( U() ) );

                break;

            case Hole::Behavior::Pack:
                pNewNode->m_packBhvBranch = make_shared< U >(
                    pNewNode->m_packBhvBranch ? next( *pNewNode->m_packBhvBranch ) : next( U() ) );
                break;








|
>











|
|
>







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
        }
    }

    //--------------------------------------------------------------------------------------------
    // Holes
    //--------------------------------------------------------------------------------------------
    template< typename U, typename F >
    static ptr< HoleBehaviorNode< U > > Merge(
        const ptr< HoleBehaviorNode< U > >& lhs, const Hole& rhs, F&& next )
    {
        ptr< HoleBehaviorNode< U > > pNewNode;

        if( lhs )
            pNewNode = make_shared< HoleBehaviorNode< U > >( *lhs );
        else
            pNewNode = make_shared< HoleBehaviorNode< U > >();

        switch( rhs.behavior() )
        {
            case Hole::Behavior::Standard:
                pNewNode->m_standardBhvBranch = make_shared< U >( pNewNode->m_standardBhvBranch ?
                        next( *pNewNode->m_standardBhvBranch ) :
                        next( U() ) );
                break;

            case Hole::Behavior::Pack:
                pNewNode->m_packBhvBranch = make_shared< U >(
                    pNewNode->m_packBhvBranch ? next( *pNewNode->m_packBhvBranch ) : next( U() ) );
                break;

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
    static ptr< HoleKindNode< U > > Merge( const HoleKindNode< U >& lhs, const Hole& rhs, F&& next )
    {
        ptr< HoleKindNode< U > > pNewNode;

        pNewNode = make_shared< HoleKindNode< U > >( lhs );

        if( rhs.kind() == ""_sid )

            pNewNode->m_anyKindBranch = Merge( pNewNode->m_anyKindBranch, rhs, forward< F >( next ) );
        else
        {
            auto it = pNewNode->m_branches.find( rhs.kind() );
            if( it == pNewNode->m_branches.end() )
                pNewNode->m_branches.emplace( rhs.kind(), Merge( ptr< HoleBehaviorNode< U > >(), rhs, forward< F >( next ) ) );

            else
                it->second = Merge( it->second, rhs, forward< F >( next ) );
        }

        return pNewNode;
    }

    template< typename U, typename F >
    static ptr< TrieNode< Hole, U > > Merge( const ptr< TrieNode< Hole, U > >& lhs, const Hole& rhs, F&& next )

    {
        ptr< TrieNode< Hole, U > > pNewNode;

        if( lhs )
            pNewNode = make_shared< TrieNode< Hole, U > >( *lhs );
        else
            pNewNode = make_shared< TrieNode< Hole, U > >();

        if( rhs.name() == ""_sid )
        {
            if( !pNewNode->m_anyNameBranch )
                pNewNode->m_anyNameBranch = Merge( HoleKindNode< U >(), rhs, forward< F >( next ) );
            else

                pNewNode->m_anyNameBranch = Merge( *pNewNode->m_anyNameBranch, rhs, forward< F >( next ) );
        }
        else
        {
            auto it = pNewNode->m_branches.find( rhs.name() );
            if( it == pNewNode->m_branches.end() )

                pNewNode->m_branches.emplace( rhs.name(), Merge( HoleKindNode< U >(), rhs, forward< F >( next ) ) );
            else
                it->second = Merge( *it->second, rhs, forward< F >( next ) );
        }

        return pNewNode;
    }

    //--------------------------------------------------------------------------------------------
    // LocationId
    //--------------------------------------------------------------------------------------------
    template< typename U, typename F >
    static ptr< TrieNode< LocationId, U > > Merge( const ptr< TrieNode< LocationId, U > >& lhs, const LocationId& rhs, F&& next )

    {
        auto pNewNode = make_shared< TrieNode< LocationId, U > >();
        pNewNode->m_next = lhs ? next( lhs->m_next ) : next( U() );
        return pNewNode;
    }

    //--------------------------------------------------------------------------------------------
    // ptr< void >
    //--------------------------------------------------------------------------------------------
    template< typename U, typename F >
    static ptr< TrieNode< ptr< void >, U > > Merge( const ptr< TrieNode< ptr< void >, U > >& lhs, const ptr< void >& rhs, F&& next )

    {
        auto pNewNode = make_shared< TrieNode< ptr< void >, U > >();
        pNewNode->m_next = lhs ? next( lhs->m_next ) : next( U() );
        return pNewNode;
    }

    //--------------------------------------------------------------------------------------------
    // Containers
    //--------------------------------------------------------------------------------------------
    template< typename U, typename IT, typename F >

    static TrieContainerBranch_t Merge( const TrieContainerBranch_t& lhs, const IT& it, const pvec& vec, F&& next )
    {
        if( it == vec->terms().end() )
        {
            if( lhs.index() == 1 )
            {
                const auto& contEnd = get< 1 >( lhs );
                return TrieContainerEnd
                {
                    any( next( *any_cast< U >( &contEnd.m_payload ) ) ),
                    vec->weight()
                };
            }

            return TrieContainerEnd
            {
                any( next( U() ) ),
                vec->weight()
            };
        }

        assert( lhs.index() == 0 );

        ptr< TrieContainerNode > pNewNode;
        const auto& pNode = get< 0 >( lhs );

        if( pNode )
            pNewNode = make_shared< TrieContainerNode >( *pNode );
        else
            pNewNode = make_shared< TrieContainerNode >();

        function< TrieContainerBranch_t ( const TrieContainerBranch_t& ) > f( [&]( auto& payload )

        {
            auto nextIt = it;
            ++nextIt;

            return Merge< U >( payload, nextIt, vec, forward< F >( next ) );
        } );

        pNewNode->m_trie = Merge( pNewNode->m_trie, *it, move( f ) );
        return pNewNode;
    }

    template< typename U, typename F >
    static ptr< TrieContainerRepetitionNode< U > > Merge( const ptr< TrieContainerRepetitionNode< U > >& lhs, const Term& rhs, F&& next )

    {
        auto pNewNode = make_shared< TrieContainerRepetitionNode< U > >();

        pNewNode->m_next = next( lhs ? lhs->m_next : U() );

        if( lhs )
            pNewNode->m_repetition = lhs->m_repetition;

        pNewNode->m_repetition = Merge( pNewNode->m_repetition, rhs, forward< F >( next ) );
        return pNewNode;
    }

    template< typename U, typename F >
    static ptr< TrieNode< pvec, U > > Merge( const ptr< TrieNode< pvec, U > >& lhs, const pvec& rhs, F&& next )

    {
        ptr< TrieNode< pvec, U > > pNewNode;

        if( lhs )
            pNewNode = make_shared< TrieNode< pvec, U > >( *lhs );
        else
            pNewNode = make_shared< TrieNode< pvec, U > >();

        auto length = rhs->length();

        if( length.isVariable() )
        {
            using rpt_node = ptr< TrieContainerRepetitionNode< U > >;

            auto f = [&]( auto&& payload )
            {
                // Merge the repetition
                return Merge( payload, *rhs->repetitionTerm(),
                    forward< F >( next ) );
            };

            auto it = pNewNode->m_variableLengthBranches.find( length.minLength() );
            if( it == pNewNode->m_variableLengthBranches.end() )
            {
                pNewNode->m_variableLengthBranches.emplace( length.minLength(),
                    Merge< rpt_node >( TrieContainerBranch_t(), rhs->terms().begin(), rhs,
                    move( f ) ) );
            }
            else
            {
                it->second = Merge< rpt_node >( it->second, rhs->terms().begin(), rhs,
                    move( f ) );
            }
        }
        else
        {
            auto it = pNewNode->m_fixedLengthBranches.find( length.minLength() );
            if( it == pNewNode->m_fixedLengthBranches.end() )
            {
                pNewNode->m_fixedLengthBranches.emplace( length.minLength(),
                    Merge< U >( TrieContainerBranch_t(), rhs->terms().begin(), rhs,
                    forward< F >( next ) ) );
            }
            else
            {
                it->second = Merge< U >( it->second, rhs->terms().begin(), rhs,
                    forward< F >( next ) );
            }
        }

        return pNewNode;
    }

    //--------------------------------------------------------------------------------------------
    // Terms
    //--------------------------------------------------------------------------------------------
    template< typename U, typename F >
    static Trie< U > Merge( const Trie< U >& trie, const Term& term, F&& next )
    {

        return visit( [&]< typename T >( const T& t )
        {
            auto newTrie = trie;
            auto& pNode = get< ptr< TrieNode< T, U > > >( newTrie.branches() );

            if( pNode )
                pNode = Merge( pNode, t, forward< F >( next ) );
            else
                pNode = Merge( ptr< TrieNode< T, U > >(), t, forward< F >( next ) );

            return newTrie;
        }, term );
    }

}


#endif







>
|




|
>








|
>













>
|





>
|











|
>










|
>










>
|






<
<
|
|
<


|
<
<
<
<












|
>
|
|
|

|
|






|
>













|
>

















|
<






|
|



|
<









|



|
|












>
|
|
|
|

|
|
|
|

|
<
|
>
|
>


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

282
283
284
285
286
287
    static ptr< HoleKindNode< U > > Merge( const HoleKindNode< U >& lhs, const Hole& rhs, F&& next )
    {
        ptr< HoleKindNode< U > > pNewNode;

        pNewNode = make_shared< HoleKindNode< U > >( lhs );

        if( rhs.kind() == ""_sid )
            pNewNode->m_anyKindBranch =
                Merge( pNewNode->m_anyKindBranch, rhs, forward< F >( next ) );
        else
        {
            auto it = pNewNode->m_branches.find( rhs.kind() );
            if( it == pNewNode->m_branches.end() )
                pNewNode->m_branches.emplace( rhs.kind(),
                    Merge( ptr< HoleBehaviorNode< U > >(), rhs, forward< F >( next ) ) );
            else
                it->second = Merge( it->second, rhs, forward< F >( next ) );
        }

        return pNewNode;
    }

    template< typename U, typename F >
    static ptr< TrieNode< Hole, U > > Merge(
        const ptr< TrieNode< Hole, U > >& lhs, const Hole& rhs, F&& next )
    {
        ptr< TrieNode< Hole, U > > pNewNode;

        if( lhs )
            pNewNode = make_shared< TrieNode< Hole, U > >( *lhs );
        else
            pNewNode = make_shared< TrieNode< Hole, U > >();

        if( rhs.name() == ""_sid )
        {
            if( !pNewNode->m_anyNameBranch )
                pNewNode->m_anyNameBranch = Merge( HoleKindNode< U >(), rhs, forward< F >( next ) );
            else
                pNewNode->m_anyNameBranch =
                    Merge( *pNewNode->m_anyNameBranch, rhs, forward< F >( next ) );
        }
        else
        {
            auto it = pNewNode->m_branches.find( rhs.name() );
            if( it == pNewNode->m_branches.end() )
                pNewNode->m_branches.emplace(
                    rhs.name(), Merge( HoleKindNode< U >(), rhs, forward< F >( next ) ) );
            else
                it->second = Merge( *it->second, rhs, forward< F >( next ) );
        }

        return pNewNode;
    }

    //--------------------------------------------------------------------------------------------
    // LocationId
    //--------------------------------------------------------------------------------------------
    template< typename U, typename F >
    static ptr< TrieNode< LocationId, U > > Merge(
        const ptr< TrieNode< LocationId, U > >& lhs, const LocationId& rhs, F&& next )
    {
        auto pNewNode = make_shared< TrieNode< LocationId, U > >();
        pNewNode->m_next = lhs ? next( lhs->m_next ) : next( U() );
        return pNewNode;
    }

    //--------------------------------------------------------------------------------------------
    // ptr< void >
    //--------------------------------------------------------------------------------------------
    template< typename U, typename F >
    static ptr< TrieNode< ptr< void >, U > > Merge(
        const ptr< TrieNode< ptr< void >, U > >& lhs, const ptr< void >& rhs, F&& next )
    {
        auto pNewNode = make_shared< TrieNode< ptr< void >, U > >();
        pNewNode->m_next = lhs ? next( lhs->m_next ) : next( U() );
        return pNewNode;
    }

    //--------------------------------------------------------------------------------------------
    // Containers
    //--------------------------------------------------------------------------------------------
    template< typename U, typename IT, typename F >
    static TrieContainerBranch_t Merge(
        const TrieContainerBranch_t& lhs, const IT& it, const pvec& vec, F&& next )
    {
        if( it == vec->terms().end() )
        {
            if( lhs.index() == 1 )
            {
                const auto& contEnd = get< 1 >( lhs );


                return TrieContainerEnd{ any( next( *any_cast< U >( &contEnd.m_payload ) ) ),
                    vec->weight() };

            }

            return TrieContainerEnd{ any( next( U() ) ), vec->weight() };




        }

        assert( lhs.index() == 0 );

        ptr< TrieContainerNode > pNewNode;
        const auto& pNode = get< 0 >( lhs );

        if( pNode )
            pNewNode = make_shared< TrieContainerNode >( *pNode );
        else
            pNewNode = make_shared< TrieContainerNode >();

        function< TrieContainerBranch_t( const TrieContainerBranch_t& ) > f(
            [&]( auto& payload )
            {
                auto nextIt = it;
                ++nextIt;

                return Merge< U >( payload, nextIt, vec, forward< F >( next ) );
            } );

        pNewNode->m_trie = Merge( pNewNode->m_trie, *it, move( f ) );
        return pNewNode;
    }

    template< typename U, typename F >
    static ptr< TrieContainerRepetitionNode< U > > Merge(
        const ptr< TrieContainerRepetitionNode< U > >& lhs, const Term& rhs, F&& next )
    {
        auto pNewNode = make_shared< TrieContainerRepetitionNode< U > >();

        pNewNode->m_next = next( lhs ? lhs->m_next : U() );

        if( lhs )
            pNewNode->m_repetition = lhs->m_repetition;

        pNewNode->m_repetition = Merge( pNewNode->m_repetition, rhs, forward< F >( next ) );
        return pNewNode;
    }

    template< typename U, typename F >
    static ptr< TrieNode< pvec, U > > Merge(
        const ptr< TrieNode< pvec, U > >& lhs, const pvec& rhs, F&& next )
    {
        ptr< TrieNode< pvec, U > > pNewNode;

        if( lhs )
            pNewNode = make_shared< TrieNode< pvec, U > >( *lhs );
        else
            pNewNode = make_shared< TrieNode< pvec, U > >();

        auto length = rhs->length();

        if( length.isVariable() )
        {
            using rpt_node = ptr< TrieContainerRepetitionNode< U > >;

            auto f = [&]( auto&& payload )
            {
                // Merge the repetition
                return Merge( payload, *rhs->repetitionTerm(), forward< F >( next ) );

            };

            auto it = pNewNode->m_variableLengthBranches.find( length.minLength() );
            if( it == pNewNode->m_variableLengthBranches.end() )
            {
                pNewNode->m_variableLengthBranches.emplace( length.minLength(),
                    Merge< rpt_node >(
                        TrieContainerBranch_t(), rhs->terms().begin(), rhs, move( f ) ) );
            }
            else
            {
                it->second = Merge< rpt_node >( it->second, rhs->terms().begin(), rhs, move( f ) );

            }
        }
        else
        {
            auto it = pNewNode->m_fixedLengthBranches.find( length.minLength() );
            if( it == pNewNode->m_fixedLengthBranches.end() )
            {
                pNewNode->m_fixedLengthBranches.emplace( length.minLength(),
                    Merge< U >( TrieContainerBranch_t(), rhs->terms().begin(), rhs,
                        forward< F >( next ) ) );
            }
            else
            {
                it->second =
                    Merge< U >( it->second, rhs->terms().begin(), rhs, forward< F >( next ) );
            }
        }

        return pNewNode;
    }

    //--------------------------------------------------------------------------------------------
    // Terms
    //--------------------------------------------------------------------------------------------
    template< typename U, typename F >
    static Trie< U > Merge( const Trie< U >& trie, const Term& term, F&& next )
    {
        return visit(
            [&]< typename T >( const T& t )
            {
                auto newTrie = trie;
                auto& pNode = get< ptr< TrieNode< T, U > > >( newTrie.branches() );

                if( pNode )
                    pNode = Merge( pNode, t, forward< F >( next ) );
                else
                    pNode = Merge( ptr< TrieNode< T, U > >(), t, forward< F >( next ) );

                return newTrie;

            },
            term );
    }
} // namespace goose::eir

#endif
Changes to bs/eir/pretty.cpp.
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
    static PrettyPrinter instance;
    return instance;
}

void PrettyPrinter::addRule( const Term& pat, PrintFunc f )
{
    m_printingRules = Merge( m_printingRules, pat, [&]( auto&& ){ return f; } );
}

bool PrettyPrinter::print( ostream& out, const Term& t )
{
    MatchScore bestScore;
    PrintFunc bestFunc;
    bool ambiguous = false;







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
    static PrettyPrinter instance;
    return instance;
}

void PrettyPrinter::addRule( const Term& pat, PrintFunc f )
{
    m_printingRules = Merge( m_printingRules, pat, [&]( auto&& ) { return f; } );
}

bool PrettyPrinter::print( ostream& out, const Term& t )
{
    MatchScore bestScore;
    PrintFunc bestFunc;
    bool ambiguous = false;
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
{
    PrettyPrinter::GetInstance().print( out, t );
    return out;
}

PrettyPrinter::PrettyPrinter()
{
    addRule( ANYTERM(_), []( auto&& out, auto&& t )

    {
        // Don't print locationIds
        if( holds_alternative< LocationId >( t ) )
            return false;

        ToString( out, t );
        return true;
    } );

    addRule( VECOFLENGTH(_), [&]( auto&& out, auto&& t )

    {
        bool isEmpty = true;

        const auto& v = get< pvec >( t );
        out << '<';

        for( auto&& x : v->terms() )
        {
            if( isEmpty )
                out << ' ';
            if( print( out, x ) )
                out << ' ';

            isEmpty = false;
        }

        if( v->repetitionTerm() )
        {
            if( isEmpty )
                out << ' ';

            out << '*';

            if( print( out, *v->repetitionTerm() ) )
                out << ' ';

            isEmpty = false;
        }

        out << '>';
        return true;
    } );
}







|
>
|
|
|
|

|
|
|

|
>
|
|

|
|

|
|
|
|
|
|

|
|

|
|
|
|

|

|
|

|
|

|
|
|

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
{
    PrettyPrinter::GetInstance().print( out, t );
    return out;
}

PrettyPrinter::PrettyPrinter()
{
    addRule( ANYTERM( _ ),
        []( auto&& out, auto&& t )
        {
            // Don't print locationIds
            if( holds_alternative< LocationId >( t ) )
                return false;

            ToString( out, t );
            return true;
        } );

    addRule( VECOFLENGTH( _ ),
        [&]( auto&& out, auto&& t )
        {
            bool isEmpty = true;

            const auto& v = get< pvec >( t );
            out << '<';

            for( auto&& x : v->terms() )
            {
                if( isEmpty )
                    out << ' ';
                if( print( out, x ) )
                    out << ' ';

                isEmpty = false;
            }

            if( v->repetitionTerm() )
            {
                if( isEmpty )
                    out << ' ';

                out << '*';

                if( print( out, *v->repetitionTerm() ) )
                    out << ' ';

                isEmpty = false;
            }

            out << '>';
            return true;
        } );
}
Changes to bs/eir/pretty.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
#ifndef GOOSE_EIR_PRETTYPRINTER_H
#define GOOSE_EIR_PRETTYPRINTER_H

namespace goose::eir
{
    class PrettyPrinter
    {
        public:
            static PrettyPrinter& GetInstance();
            using PrintFunc = function< bool ( ostream& out, const Term& t ) >;

            void addRule( const Term& pat, PrintFunc f );
            bool print( ostream& out, const Term& t );

        private:
            PrettyPrinter();

            Trie< PrintFunc > m_printingRules;
    };
}

namespace std
{
    template< class CharT >
    struct std::formatter< goose::eir::Term, CharT > :
        goose::util::SStreamFormatter< goose::eir::Term, CharT >

    {};
}

#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
#ifndef GOOSE_EIR_PRETTYPRINTER_H
#define GOOSE_EIR_PRETTYPRINTER_H

namespace goose::eir
{
    class PrettyPrinter
    {
      public:
        static PrettyPrinter& GetInstance();
        using PrintFunc = function< bool( ostream& out, const Term& t ) >;

        void addRule( const Term& pat, PrintFunc f );
        bool print( ostream& out, const Term& t );

      private:
        PrettyPrinter();

        Trie< PrintFunc > m_printingRules;
    };
} // namespace goose::eir

namespace std
{
    template< class CharT >
    struct std::formatter< goose::eir::Term, CharT >
        : goose::util::SStreamFormatter< goose::eir::Term, CharT >
    {
    };
} // namespace std

#endif
Changes to bs/eir/term.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

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
        CloseBracket
    };

    struct STerm;

    class Hole
    {
        public:
            enum class Behavior
            {
                Standard,
                Pack,
                Any
            };

            Hole() = default;

            Hole( StringId name, StringId kind = ""_sid, Behavior bhv = Behavior::Standard ) :
                m_name( name ),
                m_kind( kind ),
                m_bhv( bhv )
            {}



            const auto& name() const { return m_name; }

            const auto& kind() const { return m_kind; }

            auto behavior() const { return m_bhv; }

            bool operator<( const Hole& rhs ) const;
            bool operator==( const Hole& rhs ) const;

        private:
            StringId m_name;
            StringId m_kind;
            Behavior m_bhv = Behavior::Standard;
    };

    using Term = variant
    <
        uint64_t,
        LocationId,
        string,
        StringId,
        Delimiter,
        Hole,
        AnyTerm,
        VecOfLength,
        pvec,

        // Representation for ct_int, the compile time only integers
        // with "unlimited" precision
        BigInt,

        // Compile time representation for normal, fixed size integers
        APSInt,

        ptr< void >,
        void*
    >;

    extern bool operator==( const Term& lhs, const Term& rhs );
    extern bool operator!=( const Term& lhs, const Term& rhs );
    extern ostream& operator<<( ostream& out, const Term& t );

    // A term associated with a location id.
    // Used to represent tokens and tokens/values coming out of the resolver.
    using TermLoc = pair< eir::Term, LocationId >;
}

#define TERM( x )           eir::Term( x )
#define TSTR( x )           TERM( string( x ) )
#define TSID( x )           TERM( #x##_sid )
#define HOLE( ... )         TERM( ( eir::Hole{ __VA_ARGS__ } ) )
#define ANYTERM( x )        TERM( eir::AnyTerm( #x##_sid ) )
#define VECOFLENGTH( x )    TERM( eir::VecOfLength( #x##_sid ) )
#define VEC( ... )          TERM( eir::Vector::Make( __VA_ARGS__ ) )
#define REPEAT( x )         eir::Repetition( x )

#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
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
        CloseBracket
    };

    struct STerm;

    class Hole
    {
      public:
        enum class Behavior
        {
            Standard,
            Pack,
            Any
        };

        Hole() = default;

        Hole( StringId name, StringId kind = ""_sid, Behavior bhv = Behavior::Standard ) :
            m_name( name ),
            m_kind( kind ),
            m_bhv( bhv )

        {
        }

        const auto& name() const { return m_name; }

        const auto& kind() const { return m_kind; }

        auto behavior() const { return m_bhv; }

        bool operator<( const Hole& rhs ) const;
        bool operator==( const Hole& rhs ) const;

      private:
        StringId m_name;
        StringId m_kind;
        Behavior m_bhv = Behavior::Standard;
    };

    using Term = variant< uint64_t, LocationId, string, StringId, Delimiter, Hole, AnyTerm,








        VecOfLength, pvec,


        // Representation for ct_int, the compile time only integers
        // with "unlimited" precision
        BigInt,

        // Compile time representation for normal, fixed size integers
        APSInt,

        ptr< void >, void* >;



    extern bool operator==( const Term& lhs, const Term& rhs );
    extern bool operator!=( const Term& lhs, const Term& rhs );
    extern ostream& operator<<( ostream& out, const Term& t );

    // A term associated with a location id.
    // Used to represent tokens and tokens/values coming out of the resolver.
    using TermLoc = pair< eir::Term, LocationId >;
} // namespace goose::eir

#define TERM( x ) eir::Term( x )
#define TSTR( x ) TERM( string( x ) )
#define TSID( x ) TERM( #x##_sid )
#define HOLE( ... ) TERM( ( eir::Hole{ __VA_ARGS__ } ) )
#define ANYTERM( x ) TERM( eir::AnyTerm( #x##_sid ) )
#define VECOFLENGTH( x ) TERM( eir::VecOfLength( #x##_sid ) )
#define VEC( ... ) TERM( eir::Vector::Make( __VA_ARGS__ ) )
#define REPEAT( x ) eir::Repetition( x )

#endif
Changes to bs/eir/tests/match-terms-trie.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
#include <catch2/catch_all.hpp>
#include "eir/eir.h"

using namespace std;
using namespace goose;
using namespace goose::eir;

SCENARIO( "Match works", "[match]" )
{
    WHEN( "Matching various expressions against various patterns stored in a trie" )
    {
        auto pat0 = ANYTERM( a );

        auto pat1 = VEC(
            VEC( TSTR( "foo" ), TSTR( "bar" ) ),
            VEC(),
            VEC( TSTR( "foo" ), TSTR( "bar" ) ),
            VEC( TSTR( "foo" ), TSTR( "bar" ) )
        );

        auto expr1 = pat1;

        auto pat2 = VEC(
            VEC( ANYTERM( a ), TSTR( "bar" ) ),
            VEC(),
            VEC( ANYTERM( a ), TSTR( "bar" ) ),
            VEC( ANYTERM( a ), TSTR( "bar" ) )
        );

        auto expr2 = VEC(
            VEC( TSTR( "meh" ), TSTR( "bar" ) ),
            VEC(),
            VEC( TSTR( "meh" ), TSTR( "bar" ) ),
            VEC( TSTR( "meh" ), TSTR( "bar" ) )
        );

        auto pat3 = VEC(
            ANYTERM( x ),
            VEC( ANYTERM( a ), TSTR( "bar" ) ),
            ANYTERM( z ),
            ANYTERM( z )
        );

        auto expr3 = VEC(
            TSID( a ),
            VEC( TSTR( "meh" ), TSTR( "bar" ) ),
            TSID( b ),
            TSID( b )
        );

        auto pat4 = VEC(
            VEC( ANYTERM( a ), TSTR( "bar" ) ),
            ANYTERM( y ),
            ANYTERM( y ),
            ANYTERM( y )
        );

        auto expr4 = VEC(
            VEC( TSTR( "huh" ), TSTR( "bar" ) ),
            VEC(),
            VEC(),
            VEC()
        );

        auto pat5 = HOLE( ""_sid, ""_sid );
        auto expr5 = HOLE( "gg"_sid );

        auto pat6 = HOLE( "blah"_sid, ""_sid );
        auto expr6 = HOLE( "blah"_sid );

        auto pat7 = HOLE( "blah"_sid, "gg"_sid );
        auto expr7 = HOLE( "blah"_sid, "gg"_sid );

        auto pat8 = HOLE( ""_sid, "gg"_sid );
        auto expr8 = HOLE( "blah"_sid, "huh"_sid );

        Trie< string > testTrie1;
        testTrie1 = Merge( testTrie1, pat0, []( auto&& ){ return "pat0"s; } );
        testTrie1 = Merge( testTrie1, pat1, []( auto&& ){ return "pat1"s; } );
        testTrie1 = Merge( testTrie1, pat2, []( auto&& ){ return "pat2"s; } );
        testTrie1 = Merge( testTrie1, pat3, []( auto&& ){ return "pat3"s; } );
        testTrie1 = Merge( testTrie1, pat4, []( auto&& ){ return "pat4"s; } );
        testTrie1 = Merge( testTrie1, pat5, []( auto&& ){ return "pat5"s; } );
        testTrie1 = Merge( testTrie1, pat6, []( auto&& ){ return "pat6"s; } );
        testTrie1 = Merge( testTrie1, pat7, []( auto&& ){ return "pat7"s; } );
        testTrie1 = Merge( testTrie1, pat8, []( auto&& ){ return "pat8"s; } );

        THEN( "Matching the terms and the trie work as expected" )
        {
            vector< pair< MatchSolution, string > > solutions;
            for( auto&& [sol, str] : Match( expr1, testTrie1 ) )
                solutions.emplace_back( sol, str );

            REQUIRE( solutions.size() == 3 );
            REQUIRE( solutions[0].second == "pat1"s ); REQUIRE( solutions[0].first.complexity() == 17 ); REQUIRE( solutions[0].first.numVars() == 0 );


            REQUIRE( solutions[1].second == "pat2"s ); REQUIRE( solutions[1].first.complexity() == 11 ); REQUIRE( solutions[1].first.numVars() == 1 );


            REQUIRE( solutions[2].second == "pat0"s ); REQUIRE( solutions[2].first.complexity() == 0 ); REQUIRE( solutions[2].first.numVars() == 1 );



            solutions.clear();
            for( auto&& [sol, str] : Match( expr2, testTrie1 ) )
                solutions.emplace_back( sol, str );

            REQUIRE( solutions.size() == 2 );
            REQUIRE( solutions[0].second == "pat2"s ); REQUIRE( solutions[0].first.complexity() == 11 ); REQUIRE( solutions[0].first.numVars() == 1 );


            REQUIRE( solutions[1].second == "pat0"s ); REQUIRE( solutions[1].first.complexity() == 0 ); REQUIRE( solutions[1].first.numVars() == 1 );



            solutions.clear();
            for( auto&& [sol, str] : Match( expr3, testTrie1 ) )
                solutions.emplace_back( sol, str );

            REQUIRE( solutions.size() == 2 );
            REQUIRE( solutions[0].second == "pat3"s ); REQUIRE( solutions[0].first.complexity() == 4 ); REQUIRE( solutions[0].first.numVars() == 3 );


            REQUIRE( solutions[1].second == "pat0"s ); REQUIRE( solutions[1].first.complexity() == 0 ); REQUIRE( solutions[1].first.numVars() == 1 );



            solutions.clear();
            for( auto&& [sol, str] : Match( expr4, testTrie1 ) )
                solutions.emplace_back( sol, str );

            REQUIRE( solutions.size() == 2 );
            REQUIRE( solutions[0].second == "pat4"s ); REQUIRE( solutions[0].first.complexity() == 4 ); REQUIRE( solutions[0].first.numVars() == 2 );


            REQUIRE( solutions[1].second == "pat0"s ); REQUIRE( solutions[1].first.complexity() == 0 ); REQUIRE( solutions[1].first.numVars() == 1 );



            solutions.clear();
            for( auto&& [sol, str] : Match( expr5, testTrie1 ) )
                solutions.emplace_back( sol, str );

            REQUIRE( solutions.size() == 3 );
            REQUIRE( solutions[0].second == "pat8"s ); REQUIRE( solutions[0].first.complexity() == 4 ); REQUIRE( solutions[0].first.numVars() == 0 );


            REQUIRE( solutions[1].second == "pat5"s ); REQUIRE( solutions[1].first.complexity() == 4 ); REQUIRE( solutions[1].first.numVars() == 0 );


            REQUIRE( solutions[2].second == "pat0"s ); REQUIRE( solutions[2].first.complexity() == 0 ); REQUIRE( solutions[2].first.numVars() == 1 );



            solutions.clear();
            for( auto&& [sol, str] : Match( expr6, testTrie1 ) )
                solutions.emplace_back( sol, str );

            REQUIRE( solutions.size() == 5 );
            REQUIRE( solutions[0].second == "pat7"s ); REQUIRE( solutions[0].first.complexity() == 5 ); REQUIRE( solutions[0].first.numVars() == 0 );


            REQUIRE( solutions[1].second == "pat6"s ); REQUIRE( solutions[1].first.complexity() == 5 ); REQUIRE( solutions[1].first.numVars() == 0 );


            REQUIRE( solutions[2].second == "pat8"s ); REQUIRE( solutions[2].first.complexity() == 4 ); REQUIRE( solutions[2].first.numVars() == 0 );


            REQUIRE( solutions[3].second == "pat5"s ); REQUIRE( solutions[3].first.complexity() == 4 ); REQUIRE( solutions[3].first.numVars() == 0 );


            REQUIRE( solutions[4].second == "pat0"s ); REQUIRE( solutions[4].first.complexity() == 0 ); REQUIRE( solutions[4].first.numVars() == 1 );



            solutions.clear();
            for( auto&& [sol, str] : Match( expr7, testTrie1 ) )
                solutions.emplace_back( sol, str );

            REQUIRE( solutions.size() == 5 );
            REQUIRE( solutions[0].second == "pat7"s ); REQUIRE( solutions[0].first.complexity() == 6 ); REQUIRE( solutions[0].first.numVars() == 0 );


            REQUIRE( solutions[1].second == "pat6"s ); REQUIRE( solutions[1].first.complexity() == 5 ); REQUIRE( solutions[1].first.numVars() == 0 );


            REQUIRE( solutions[2].second == "pat8"s ); REQUIRE( solutions[2].first.complexity() == 5 ); REQUIRE( solutions[2].first.numVars() == 0 );


            REQUIRE( solutions[3].second == "pat5"s ); REQUIRE( solutions[3].first.complexity() == 4 ); REQUIRE( solutions[3].first.numVars() == 0 );


            REQUIRE( solutions[4].second == "pat0"s ); REQUIRE( solutions[4].first.complexity() == 0 ); REQUIRE( solutions[4].first.numVars() == 1 );



            solutions.clear();
            for( auto&& [sol, str] : Match( expr8, testTrie1 ) )
                solutions.emplace_back( sol, str );

            REQUIRE( solutions.size() == 3 );
            REQUIRE( solutions[0].second == "pat6"s ); REQUIRE( solutions[0].first.complexity() == 5 ); REQUIRE( solutions[0].first.numVars() == 0 );


            REQUIRE( solutions[1].second == "pat5"s ); REQUIRE( solutions[1].first.complexity() == 4 ); REQUIRE( solutions[1].first.numVars() == 0 );


            REQUIRE( solutions[2].second == "pat0"s ); REQUIRE( solutions[2].first.complexity() == 0 ); REQUIRE( solutions[2].first.numVars() == 1 );


        }
    }
}













<
|
<
|
<
<



<
|
<
|
<
<

<
|
<
|
<
<

|
<
|
<
<
<

<
<
|
<
<
<

|
|
<
<
<
<

<
|
<
<
<
<














|
|
|
|
|
|
|
|
|








|
>
>
|
>
>
|
|
>






|
>
>
|
|
>






|
>
>
|
|
>






|
>
>
|
|
>






|
>
>
|
>
>
|
|
>






|
>
>
|
>
>
|
>
>
|
>
>
|
|
>






|
>
>
|
>
>
|
>
>
|
>
>
|
|
>






|
>
>
|
>
>
|
>
>



1
2
3
4
5
6
7
8
9
10
11
12
13

14

15


16
17
18

19

20


21

22

23


24
25

26



27


28



29
30
31




32

33




34
35
36
37
38
39
40
41
42
43
44
45
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
#include <catch2/catch_all.hpp>
#include "eir/eir.h"

using namespace std;
using namespace goose;
using namespace goose::eir;

SCENARIO( "Match works", "[match]" )
{
    WHEN( "Matching various expressions against various patterns stored in a trie" )
    {
        auto pat0 = ANYTERM( a );


        auto pat1 = VEC( VEC( TSTR( "foo" ), TSTR( "bar" ) ), VEC(),

            VEC( TSTR( "foo" ), TSTR( "bar" ) ), VEC( TSTR( "foo" ), TSTR( "bar" ) ) );



        auto expr1 = pat1;


        auto pat2 = VEC( VEC( ANYTERM( a ), TSTR( "bar" ) ), VEC(),

            VEC( ANYTERM( a ), TSTR( "bar" ) ), VEC( ANYTERM( a ), TSTR( "bar" ) ) );




        auto expr2 = VEC( VEC( TSTR( "meh" ), TSTR( "bar" ) ), VEC(),

            VEC( TSTR( "meh" ), TSTR( "bar" ) ), VEC( TSTR( "meh" ), TSTR( "bar" ) ) );



        auto pat3 =

            VEC( ANYTERM( x ), VEC( ANYTERM( a ), TSTR( "bar" ) ), ANYTERM( z ), ANYTERM( z ) );






        auto expr3 = VEC( TSID( a ), VEC( TSTR( "meh" ), TSTR( "bar" ) ), TSID( b ), TSID( b ) );




        auto pat4 =
            VEC( VEC( ANYTERM( a ), TSTR( "bar" ) ), ANYTERM( y ), ANYTERM( y ), ANYTERM( y ) );






        auto expr4 = VEC( VEC( TSTR( "huh" ), TSTR( "bar" ) ), VEC(), VEC(), VEC() );





        auto pat5 = HOLE( ""_sid, ""_sid );
        auto expr5 = HOLE( "gg"_sid );

        auto pat6 = HOLE( "blah"_sid, ""_sid );
        auto expr6 = HOLE( "blah"_sid );

        auto pat7 = HOLE( "blah"_sid, "gg"_sid );
        auto expr7 = HOLE( "blah"_sid, "gg"_sid );

        auto pat8 = HOLE( ""_sid, "gg"_sid );
        auto expr8 = HOLE( "blah"_sid, "huh"_sid );

        Trie< string > testTrie1;
        testTrie1 = Merge( testTrie1, pat0, []( auto&& ) { return "pat0"s; } );
        testTrie1 = Merge( testTrie1, pat1, []( auto&& ) { return "pat1"s; } );
        testTrie1 = Merge( testTrie1, pat2, []( auto&& ) { return "pat2"s; } );
        testTrie1 = Merge( testTrie1, pat3, []( auto&& ) { return "pat3"s; } );
        testTrie1 = Merge( testTrie1, pat4, []( auto&& ) { return "pat4"s; } );
        testTrie1 = Merge( testTrie1, pat5, []( auto&& ) { return "pat5"s; } );
        testTrie1 = Merge( testTrie1, pat6, []( auto&& ) { return "pat6"s; } );
        testTrie1 = Merge( testTrie1, pat7, []( auto&& ) { return "pat7"s; } );
        testTrie1 = Merge( testTrie1, pat8, []( auto&& ) { return "pat8"s; } );

        THEN( "Matching the terms and the trie work as expected" )
        {
            vector< pair< MatchSolution, string > > solutions;
            for( auto&& [sol, str] : Match( expr1, testTrie1 ) )
                solutions.emplace_back( sol, str );

            REQUIRE( solutions.size() == 3 );
            REQUIRE( solutions[0].second == "pat1"s );
            REQUIRE( solutions[0].first.complexity() == 17 );
            REQUIRE( solutions[0].first.numVars() == 0 );
            REQUIRE( solutions[1].second == "pat2"s );
            REQUIRE( solutions[1].first.complexity() == 11 );
            REQUIRE( solutions[1].first.numVars() == 1 );
            REQUIRE( solutions[2].second == "pat0"s );
            REQUIRE( solutions[2].first.complexity() == 0 );
            REQUIRE( solutions[2].first.numVars() == 1 );

            solutions.clear();
            for( auto&& [sol, str] : Match( expr2, testTrie1 ) )
                solutions.emplace_back( sol, str );

            REQUIRE( solutions.size() == 2 );
            REQUIRE( solutions[0].second == "pat2"s );
            REQUIRE( solutions[0].first.complexity() == 11 );
            REQUIRE( solutions[0].first.numVars() == 1 );
            REQUIRE( solutions[1].second == "pat0"s );
            REQUIRE( solutions[1].first.complexity() == 0 );
            REQUIRE( solutions[1].first.numVars() == 1 );

            solutions.clear();
            for( auto&& [sol, str] : Match( expr3, testTrie1 ) )
                solutions.emplace_back( sol, str );

            REQUIRE( solutions.size() == 2 );
            REQUIRE( solutions[0].second == "pat3"s );
            REQUIRE( solutions[0].first.complexity() == 4 );
            REQUIRE( solutions[0].first.numVars() == 3 );
            REQUIRE( solutions[1].second == "pat0"s );
            REQUIRE( solutions[1].first.complexity() == 0 );
            REQUIRE( solutions[1].first.numVars() == 1 );

            solutions.clear();
            for( auto&& [sol, str] : Match( expr4, testTrie1 ) )
                solutions.emplace_back( sol, str );

            REQUIRE( solutions.size() == 2 );
            REQUIRE( solutions[0].second == "pat4"s );
            REQUIRE( solutions[0].first.complexity() == 4 );
            REQUIRE( solutions[0].first.numVars() == 2 );
            REQUIRE( solutions[1].second == "pat0"s );
            REQUIRE( solutions[1].first.complexity() == 0 );
            REQUIRE( solutions[1].first.numVars() == 1 );

            solutions.clear();
            for( auto&& [sol, str] : Match( expr5, testTrie1 ) )
                solutions.emplace_back( sol, str );

            REQUIRE( solutions.size() == 3 );
            REQUIRE( solutions[0].second == "pat8"s );
            REQUIRE( solutions[0].first.complexity() == 4 );
            REQUIRE( solutions[0].first.numVars() == 0 );
            REQUIRE( solutions[1].second == "pat5"s );
            REQUIRE( solutions[1].first.complexity() == 4 );
            REQUIRE( solutions[1].first.numVars() == 0 );
            REQUIRE( solutions[2].second == "pat0"s );
            REQUIRE( solutions[2].first.complexity() == 0 );
            REQUIRE( solutions[2].first.numVars() == 1 );

            solutions.clear();
            for( auto&& [sol, str] : Match( expr6, testTrie1 ) )
                solutions.emplace_back( sol, str );

            REQUIRE( solutions.size() == 5 );
            REQUIRE( solutions[0].second == "pat7"s );
            REQUIRE( solutions[0].first.complexity() == 5 );
            REQUIRE( solutions[0].first.numVars() == 0 );
            REQUIRE( solutions[1].second == "pat6"s );
            REQUIRE( solutions[1].first.complexity() == 5 );
            REQUIRE( solutions[1].first.numVars() == 0 );
            REQUIRE( solutions[2].second == "pat8"s );
            REQUIRE( solutions[2].first.complexity() == 4 );
            REQUIRE( solutions[2].first.numVars() == 0 );
            REQUIRE( solutions[3].second == "pat5"s );
            REQUIRE( solutions[3].first.complexity() == 4 );
            REQUIRE( solutions[3].first.numVars() == 0 );
            REQUIRE( solutions[4].second == "pat0"s );
            REQUIRE( solutions[4].first.complexity() == 0 );
            REQUIRE( solutions[4].first.numVars() == 1 );

            solutions.clear();
            for( auto&& [sol, str] : Match( expr7, testTrie1 ) )
                solutions.emplace_back( sol, str );

            REQUIRE( solutions.size() == 5 );
            REQUIRE( solutions[0].second == "pat7"s );
            REQUIRE( solutions[0].first.complexity() == 6 );
            REQUIRE( solutions[0].first.numVars() == 0 );
            REQUIRE( solutions[1].second == "pat6"s );
            REQUIRE( solutions[1].first.complexity() == 5 );
            REQUIRE( solutions[1].first.numVars() == 0 );
            REQUIRE( solutions[2].second == "pat8"s );
            REQUIRE( solutions[2].first.complexity() == 5 );
            REQUIRE( solutions[2].first.numVars() == 0 );
            REQUIRE( solutions[3].second == "pat5"s );
            REQUIRE( solutions[3].first.complexity() == 4 );
            REQUIRE( solutions[3].first.numVars() == 0 );
            REQUIRE( solutions[4].second == "pat0"s );
            REQUIRE( solutions[4].first.complexity() == 0 );
            REQUIRE( solutions[4].first.numVars() == 1 );

            solutions.clear();
            for( auto&& [sol, str] : Match( expr8, testTrie1 ) )
                solutions.emplace_back( sol, str );

            REQUIRE( solutions.size() == 3 );
            REQUIRE( solutions[0].second == "pat6"s );
            REQUIRE( solutions[0].first.complexity() == 5 );
            REQUIRE( solutions[0].first.numVars() == 0 );
            REQUIRE( solutions[1].second == "pat5"s );
            REQUIRE( solutions[1].first.complexity() == 4 );
            REQUIRE( solutions[1].first.numVars() == 0 );
            REQUIRE( solutions[2].second == "pat0"s );
            REQUIRE( solutions[2].first.complexity() == 0 );
            REQUIRE( solutions[2].first.numVars() == 1 );
        }
    }
}
Changes to bs/eir/tests/merge.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
#include <catch2/catch_all.hpp>
#include "eir/eir.h"

using namespace std;
using namespace goose;
using namespace goose::eir;

SCENARIO( "Merge works", "[merge]" )
{
    WHEN( "Merging multiple terms into a trie" )
    {
        auto pat0 = ANYTERM( a );

        auto pat1 = VEC(
            VEC( TSTR( "foo" ), TSTR( "bar" ) ),
            VEC(),
            VEC( TSTR( "foo" ), TSTR( "bar" ) ),
            VEC( TSTR( "foo" ), TSTR( "bar" ) )
        );

        auto pat2 = VEC(
            VEC( ANYTERM( a ), TSTR( "bar" ) ),
            VEC(),
            VEC( ANYTERM( a ), TSTR( "bar" ) ),
            VEC( ANYTERM( a ), TSTR( "bar" ) )
        );

        auto pat3 = VEC(
            ANYTERM( x ),
            VEC( ANYTERM( a ), TSTR( "bar" ) ),
            ANYTERM( z ),
            ANYTERM( z )
        );

        auto pat4 = VEC(
            VEC( ANYTERM( a ), TSTR( "bar" ) ),
            ANYTERM( y ),
            ANYTERM( y ),
            ANYTERM( y )
        );

        auto pat5 = VEC(
            VEC( TSTR( "foo" ), ANYTERM( b ) ),
            ANYTERM( y ),
            ANYTERM( y ),
            VEC( TSTR( "foo" ), ANYTERM( b ) )
        );

        auto pat6 = VEC(
            ANYTERM( x ),
            VEC( TSTR( "foo" ), TSTR( "bar" ) ),
            VEC( TSTR( "foo" ), TSTR( "bar" ) ),
            VEC( TSTR( "foo" ), TSTR( "baz" ) )
        );

        auto pat7 = VEC(
            ANYTERM( v ),
            ANYTERM( w ),
            ANYTERM( v ),
            VEC( TSTR( "foo" ), TSTR( "bar" ) )
        );

        auto pat8 = VEC(
            ANYTERM( x ),
            VEC( ANYTERM( a ), TSTR( "bar" ) ),
            ANYTERM( x ),
            VEC( TSTR( "foo" ), ANYTERM( b ) )
        );

        auto pat9 = VEC(
            ANYTERM( x ),
            VEC( HOLE( ""_sid, ""_sid ), TSTR( "bar" ) ),
            ANYTERM( x ),
            VEC( TSTR( "foo" ), ANYTERM( b ) )
        );

        auto pat10 = VEC(
            ANYTERM( x ),
            VEC( HOLE( "gg"_sid, ""_sid ), TSTR( "bar" ) ),
            ANYTERM( x ),
            VEC( TSTR( "foo" ), ANYTERM( b ) )
        );

        auto pat11 = VEC(
            ANYTERM( x ),
            VEC( HOLE( ""_sid, "meh"_sid ), TSTR( "bar" ) ),
            ANYTERM( x ),
            VEC( TSTR( "foo" ), ANYTERM( b ) )
        );

        auto pat12 = VEC(
            ANYTERM( x ),
            VEC( HOLE( "gg"_sid, "meh"_sid ), TSTR( "bar" ) ),
            ANYTERM( x ),
            VEC( TSTR( "foo" ), ANYTERM( b ) )
        );

        Trie<> testTrie1;
        testTrie1 = Merge( testTrie1, pat0 );
        testTrie1 = Merge( testTrie1, pat1 );
        testTrie1 = Merge( testTrie1, pat2 );
        testTrie1 = Merge( testTrie1, pat3 );
        testTrie1 = Merge( testTrie1, pat4 );













<
|
<
|
<
<

<
|
<
|
<
<

|
<
|
<
<
<

|
|
<
<
<
<

<
|
<
<
|
<

<
<
|
|
<
<

|
<
<
<
|
<

<
<
|
<
|
<

<
<
|
<
|
<

<
<
|
<
|
<

<
<
|
<
|
<

<
<
|
<
|
<







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
#include <catch2/catch_all.hpp>
#include "eir/eir.h"

using namespace std;
using namespace goose;
using namespace goose::eir;

SCENARIO( "Merge works", "[merge]" )
{
    WHEN( "Merging multiple terms into a trie" )
    {
        auto pat0 = ANYTERM( a );


        auto pat1 = VEC( VEC( TSTR( "foo" ), TSTR( "bar" ) ), VEC(),

            VEC( TSTR( "foo" ), TSTR( "bar" ) ), VEC( TSTR( "foo" ), TSTR( "bar" ) ) );




        auto pat2 = VEC( VEC( ANYTERM( a ), TSTR( "bar" ) ), VEC(),

            VEC( ANYTERM( a ), TSTR( "bar" ) ), VEC( ANYTERM( a ), TSTR( "bar" ) ) );



        auto pat3 =

            VEC( ANYTERM( x ), VEC( ANYTERM( a ), TSTR( "bar" ) ), ANYTERM( z ), ANYTERM( z ) );




        auto pat4 =
            VEC( VEC( ANYTERM( a ), TSTR( "bar" ) ), ANYTERM( y ), ANYTERM( y ), ANYTERM( y ) );






        auto pat5 = VEC( VEC( TSTR( "foo" ), ANYTERM( b ) ), ANYTERM( y ), ANYTERM( y ),


            VEC( TSTR( "foo" ), ANYTERM( b ) ) );




        auto pat6 = VEC( ANYTERM( x ), VEC( TSTR( "foo" ), TSTR( "bar" ) ),
            VEC( TSTR( "foo" ), TSTR( "bar" ) ), VEC( TSTR( "foo" ), TSTR( "baz" ) ) );



        auto pat7 =



            VEC( ANYTERM( v ), ANYTERM( w ), ANYTERM( v ), VEC( TSTR( "foo" ), TSTR( "bar" ) ) );




        auto pat8 = VEC( ANYTERM( x ), VEC( ANYTERM( a ), TSTR( "bar" ) ), ANYTERM( x ),

            VEC( TSTR( "foo" ), ANYTERM( b ) ) );




        auto pat9 = VEC( ANYTERM( x ), VEC( HOLE( ""_sid, ""_sid ), TSTR( "bar" ) ), ANYTERM( x ),

            VEC( TSTR( "foo" ), ANYTERM( b ) ) );




        auto pat10 = VEC( ANYTERM( x ), VEC( HOLE( "gg"_sid, ""_sid ), TSTR( "bar" ) ),

            ANYTERM( x ), VEC( TSTR( "foo" ), ANYTERM( b ) ) );




        auto pat11 = VEC( ANYTERM( x ), VEC( HOLE( ""_sid, "meh"_sid ), TSTR( "bar" ) ),

            ANYTERM( x ), VEC( TSTR( "foo" ), ANYTERM( b ) ) );




        auto pat12 = VEC( ANYTERM( x ), VEC( HOLE( "gg"_sid, "meh"_sid ), TSTR( "bar" ) ),

            ANYTERM( x ), VEC( TSTR( "foo" ), ANYTERM( b ) ) );


        Trie<> testTrie1;
        testTrie1 = Merge( testTrie1, pat0 );
        testTrie1 = Merge( testTrie1, pat1 );
        testTrie1 = Merge( testTrie1, pat2 );
        testTrie1 = Merge( testTrie1, pat3 );
        testTrie1 = Merge( testTrie1, pat4 );
Changes to bs/eir/tostring.cpp.
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
        llvm::SmallString< 16 > s;
        i.toString( s, 10 );
        return out << "APSint(" << s.c_str() << ')';
    }

    ostream& ToString( ostream& out, const Term& t )
    {
        visit( [&]( auto&& t )
        {
            ToString( out, t );
        }, t );

        return out;
    }
}







|
<
<
<



|
153
154
155
156
157
158
159
160



161
162
163
164
        llvm::SmallString< 16 > s;
        i.toString( s, 10 );
        return out << "APSint(" << s.c_str() << ')';
    }

    ostream& ToString( ostream& out, const Term& t )
    {
        visit( [&]( auto&& t ) { ToString( out, t ); }, t );




        return out;
    }
} // namespace goose::eir
Changes to bs/eir/tostring.h.
18
19
20
21
22
23
24
25
26
27
    extern ostream& ToString( ostream& out, const void* x );
    extern ostream& ToString( ostream& out, const AnyTerm& at );
    extern ostream& ToString( ostream& out, const VecOfLength& v );
    extern ostream& ToString( ostream& out, const ptr< Vector >& v );

    extern ostream& ToString( ostream& out, const BigInt& t );
    extern ostream& ToString( ostream& out, const APSInt& t );
}

#endif







|


18
19
20
21
22
23
24
25
26
27
    extern ostream& ToString( ostream& out, const void* x );
    extern ostream& ToString( ostream& out, const AnyTerm& at );
    extern ostream& ToString( ostream& out, const VecOfLength& v );
    extern ostream& ToString( ostream& out, const ptr< Vector >& v );

    extern ostream& ToString( ostream& out, const BigInt& t );
    extern ostream& ToString( ostream& out, const APSInt& t );
} // namespace goose::eir

#endif
Changes to bs/eir/trie.h.
1
2
3
4
5
6
7

8
9
10
11
12
13
14
15
16
17

18
19
20
21
22
23
24
25
26
27

28
29
30
31

32
33
34
35

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

61
62
63
64

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

81
82
83
84

85
86
87
88
89
90
91
92
#ifndef GOOSE_EIR_TRIE_H
#define GOOSE_EIR_TRIE_H

namespace goose::eir
{
    template< typename T, typename U >
    struct TrieNode

    {};

    template< typename T, typename U >
    struct ValueTrieNode
    {
        unordered_map< T, U > m_branches;
    };

    template< typename U >
    struct TrieNode< uint64_t, U > : public ValueTrieNode< uint64_t, U >

    {};

    template< typename U >
    struct TrieNode< LocationId, U >
    {
        U m_next;
    };

    template< typename U >
    struct TrieNode< string, U > : public ValueTrieNode< string, U >

    {};

    template< typename U >
    struct TrieNode< StringId, U > : public ValueTrieNode< StringId, U >

    {};

    template< typename U >
    struct TrieNode< Delimiter, U > : public ValueTrieNode< Delimiter, U >

    {};

    template< typename U >
    struct HoleBehaviorNode
    {
        ptr< U > m_standardBhvBranch;
        ptr< U > m_packBhvBranch;
        ptr< U > m_anyBhvBranch;
    };

    template< typename U >
    struct HoleKindNode : public ValueTrieNode< StringId, ptr< HoleBehaviorNode< U > > >
    {
        ptr< HoleBehaviorNode< U > > m_anyKindBranch;
    };

    template< typename U >
    struct TrieNode< Hole, U > : public ValueTrieNode< StringId,
        ptr< HoleKindNode< U > > >
    {
        ptr< HoleKindNode< U > > m_anyNameBranch;
    };

    template< typename U >
    struct TrieNode< AnyTerm, U > : public ValueTrieNode< AnyTerm, U >

    {};

    template< typename U >
    struct TrieNode< VecOfLength, U > : public ValueTrieNode< VecOfLength, U >

    {};

    template< typename U >
    struct TrieNode< ptr< void >, U >
    {
        U m_next;
    };

    template< typename U >
    struct TrieNode< void*, U >
    {
        U m_next;
    };

    template< typename U >
    struct TrieNode< BigInt, U > : public ValueTrieNode< BigInt, U >

    {};

    template< typename U >
    struct TrieNode< APSInt, U > : public ValueTrieNode< FixedInt, U >

    {};

    struct TrieContainerNode;

    // Since Vector terms now carry around their weight (...),
    // and since it can be overriden with an arbitrary value, we need
    // to preserve it in the trie to be able to restore it (for instance
    // when enumerating all the elements). So the final node of a container branch





|
<
>
|

|
<




<
|
>
|

|
<




<
|
>
|

<
|
>
|

<
|
>
|

|
<













|
<




<
|
>
|



>
|

|
<




|
<




<
|
>
|

<
|
>
|







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
#ifndef GOOSE_EIR_TRIE_H
#define GOOSE_EIR_TRIE_H

namespace goose::eir
{
    template< typename T, typename U > struct TrieNode

    {
    };

    template< typename T, typename U > struct ValueTrieNode

    {
        unordered_map< T, U > m_branches;
    };


    template< typename U > struct TrieNode< uint64_t, U > : public ValueTrieNode< uint64_t, U >
    {
    };

    template< typename U > struct TrieNode< LocationId, U >

    {
        U m_next;
    };


    template< typename U > struct TrieNode< string, U > : public ValueTrieNode< string, U >
    {
    };


    template< typename U > struct TrieNode< StringId, U > : public ValueTrieNode< StringId, U >
    {
    };


    template< typename U > struct TrieNode< Delimiter, U > : public ValueTrieNode< Delimiter, U >
    {
    };

    template< typename U > struct HoleBehaviorNode

    {
        ptr< U > m_standardBhvBranch;
        ptr< U > m_packBhvBranch;
        ptr< U > m_anyBhvBranch;
    };

    template< typename U >
    struct HoleKindNode : public ValueTrieNode< StringId, ptr< HoleBehaviorNode< U > > >
    {
        ptr< HoleBehaviorNode< U > > m_anyKindBranch;
    };

    template< typename U >
    struct TrieNode< Hole, U > : public ValueTrieNode< StringId, ptr< HoleKindNode< U > > >

    {
        ptr< HoleKindNode< U > > m_anyNameBranch;
    };


    template< typename U > struct TrieNode< AnyTerm, U > : public ValueTrieNode< AnyTerm, U >
    {
    };

    template< typename U >
    struct TrieNode< VecOfLength, U > : public ValueTrieNode< VecOfLength, U >
    {
    };

    template< typename U > struct TrieNode< ptr< void >, U >

    {
        U m_next;
    };

    template< typename U > struct TrieNode< void*, U >

    {
        U m_next;
    };


    template< typename U > struct TrieNode< BigInt, U > : public ValueTrieNode< BigInt, U >
    {
    };


    template< typename U > struct TrieNode< APSInt, U > : public ValueTrieNode< FixedInt, U >
    {
    };

    struct TrieContainerNode;

    // Since Vector terms now carry around their weight (...),
    // and since it can be overriden with an arbitrary value, we need
    // to preserve it in the trie to be able to restore it (for instance
    // when enumerating all the elements). So the final node of a container branch
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
    {
        any m_payload;
        int32_t vecWeight = 0;
    };

    using TrieContainerBranch_t = variant< ptr< TrieContainerNode >, TrieContainerEnd >;

    template< typename U >
    struct TrieNode< pvec, U >
    {
        // All fixed length branches, by length.
        unordered_map< size_t, TrieContainerBranch_t > m_fixedLengthBranches;

        // All variable length branches, by min length.
        map< size_t, TrieContainerBranch_t > m_variableLengthBranches;
    };

    template< typename U >
    class Trie
    {
        public:
            using BranchesTuple = tuple
            <
                ptr< TrieNode< uint64_t, U > >,
                ptr< TrieNode< LocationId, U > >,
                ptr< TrieNode< string, U > >,
                ptr< TrieNode< StringId, U > >,
                ptr< TrieNode< Delimiter, U > >,
                ptr< TrieNode< Hole, U > >,
                ptr< TrieNode< AnyTerm, U > >,
                ptr< TrieNode< VecOfLength, U > >,
                ptr< TrieNode< ptr< void >, U > >,
                ptr< TrieNode< void*, U > >,
                ptr< TrieNode< pvec, U > >,
                ptr< TrieNode< BigInt, U > >,
                ptr< TrieNode< APSInt, U > >
            >;

            const auto& branches() const { return m_branches; }

            auto& branches() { return m_branches; }

        private:
            BranchesTuple m_branches;
    };

    struct TrieContainerNode
    {
        Trie< TrieContainerBranch_t > m_trie;
    };

    template< typename U >
    struct TrieContainerRepetitionNode
    {
        Trie< U > m_repetition;
        U m_next;
    };
}

#endif







|
<








|
<

|
|
<
<
|
<
|
<
|
<
|
<
|
<
<
|
<

|
>
|

|
|







|
<




|


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
    {
        any m_payload;
        int32_t vecWeight = 0;
    };

    using TrieContainerBranch_t = variant< ptr< TrieContainerNode >, TrieContainerEnd >;

    template< typename U > struct TrieNode< pvec, U >

    {
        // All fixed length branches, by length.
        unordered_map< size_t, TrieContainerBranch_t > m_fixedLengthBranches;

        // All variable length branches, by min length.
        map< size_t, TrieContainerBranch_t > m_variableLengthBranches;
    };

    template< typename U > class Trie

    {
      public:
        using BranchesTuple = tuple< ptr< TrieNode< uint64_t, U > >,


            ptr< TrieNode< LocationId, U > >, ptr< TrieNode< string, U > >,

            ptr< TrieNode< StringId, U > >, ptr< TrieNode< Delimiter, U > >,

            ptr< TrieNode< Hole, U > >, ptr< TrieNode< AnyTerm, U > >,

            ptr< TrieNode< VecOfLength, U > >, ptr< TrieNode< ptr< void >, U > >,

            ptr< TrieNode< void*, U > >, ptr< TrieNode< pvec, U > >, ptr< TrieNode< BigInt, U > >,


            ptr< TrieNode< APSInt, U > > >;


        const auto& branches() const { return m_branches; }

        auto& branches() { return m_branches; }

      private:
        BranchesTuple m_branches;
    };

    struct TrieContainerNode
    {
        Trie< TrieContainerBranch_t > m_trie;
    };

    template< typename U > struct TrieContainerRepetitionNode

    {
        Trie< U > m_repetition;
        U m_next;
    };
} // namespace goose::eir

#endif
Changes to bs/eir/value.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
#include "eir.h"

namespace goose::eir
{
    bool Value::isType() const
    {
        return type() == TypeType() || isMetaType();
    }

    bool Value::isMetaType() const
    {
        auto result = Decompose( type(),
            Vec(
                Lit( "type"_sid ),
                Val< uint64_t >(),
                Val< LocationId >()
            )
        );

        return !!result;
    }

    const ptr< cir::InstrSeq >& Value::cir() const
    {
        static ptr< cir::InstrSeq > null;











|
<
<
<
|
<
<







1
2
3
4
5
6
7
8
9
10
11
12



13


14
15
16
17
18
19
20
#include "eir.h"

namespace goose::eir
{
    bool Value::isType() const
    {
        return type() == TypeType() || isMetaType();
    }

    bool Value::isMetaType() const
    {
        auto result =



            Decompose( type(), Vec( Lit( "type"_sid ), Val< uint64_t >(), Val< LocationId >() ) );



        return !!result;
    }

    const ptr< cir::InstrSeq >& Value::cir() const
    {
        static ptr< cir::InstrSeq > null;
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
            return ~0;

        return *pIndex;
    }

    const Term& TypeType()
    {
        static auto typeType = ValueToEIR( Value( VEC( TSID( type ), TERM( 1U ),
            static_cast< LocationId >( 0 ) ), TERM( 0U ) ) );
        return typeType;
    }

    // A generic poisoned value of "type" type.
    Value PoisonType( LocationId loc )
    {
        static auto poisonType = Value( TypeType(), TSID( poison ) ).setPoison();







|
|







44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
            return ~0;

        return *pIndex;
    }

    const Term& TypeType()
    {
        static auto typeType = ValueToEIR(
            Value( VEC( TSID( type ), TERM( 1U ), static_cast< LocationId >( 0 ) ), TERM( 0U ) ) );
        return typeType;
    }

    // A generic poisoned value of "type" type.
    Value PoisonType( LocationId loc )
    {
        static auto poisonType = Value( TypeType(), TSID( poison ) ).setPoison();
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
    Term ValueToEIR( const Value& v )
    {
        if( v.isConstant() )
        {
            // Special case for type's type
            if( v.isMetaType() )
            {
                auto result = Decompose( v.val(),
                    Val< uint64_t >()
                );

                if( result && result->get() > 0 )
                {
                    return VEC( TSID( type ), TERM( result->get() ),
                        static_cast< LocationId >( v.locationId() ) );
                }
            }

            return VEC( TSID( value ), TSID( constant ), v.type(), v.val(),
                static_cast< LocationId >( v.locationId() ) );
        }

        return VEC( TSID( value ), TSID( computed ), v.type(),
            TERM( static_pointer_cast< void >( v.cir() ) ),
            static_cast< LocationId >( v.locationId() ) );
    }

    optional< Value > EIRToValue( const Term& t )
    {
        // Special case for type's type
        auto typedecomp = Decompose( t,
            Vec(
                Lit( "type"_sid ),
                Val< uint64_t >(),
                Val< LocationId >()
            )
        );

        if( typedecomp )
        {
            auto&& [universeIndex, loc] = *typedecomp;
            return Value( VEC( TSID( type ), TERM( universeIndex + 1 ),
                LocationId() ), TERM( universeIndex ), loc );
        }

        auto result = Decompose( t,
            Vec(
               Lit( "value"_sid ),
               Val< StringId >(),
               SubTerm(),
               SubTerm(),
               Val< LocationId >()
            )
        );

        if( !result )
            return nullopt;

        auto&& [sort, type, val, locationId] = *result;
        if( sort == "constant"_sid )
            return Value( type, val, locationId );

        auto cir = Decompose( val, Val< ptr< void > >() );
        if( !cir )
            return nullopt;


        return Value( type, static_pointer_cast< cir::InstrSeq >( ptr< void >( *cir ) ), locationId );
    }

    Term ValueToEIR( const ValuePattern& v )
    {
        return VEC( TSID( value ), v.sort(), v.type(), v.val(), v.locationId() );
    }

    optional< ValuePattern > EIRToValuePattern( const Term& t )
    {
        auto result = Decompose( t,
            Vec(
               Lit( "value"_sid ),
               SubTerm(),
               SubTerm(),
               SubTerm(),
               Val< LocationId >()
            )
        );

        if( !result )
            return nullopt;

        auto&& [sort, type, val, locationId] = *result;
        return ValuePattern( sort, type, val, locationId );
    }
}







|
<
<




















|
<
<
<
|
<
<




|
|



<
|
<
<
<
|
<
<












>
|









|
<
|
<
<
<
<
<
<







|
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
    Term ValueToEIR( const Value& v )
    {
        if( v.isConstant() )
        {
            // Special case for type's type
            if( v.isMetaType() )
            {
                auto result = Decompose( v.val(), Val< uint64_t >() );



                if( result && result->get() > 0 )
                {
                    return VEC( TSID( type ), TERM( result->get() ),
                        static_cast< LocationId >( v.locationId() ) );
                }
            }

            return VEC( TSID( value ), TSID( constant ), v.type(), v.val(),
                static_cast< LocationId >( v.locationId() ) );
        }

        return VEC( TSID( value ), TSID( computed ), v.type(),
            TERM( static_pointer_cast< void >( v.cir() ) ),
            static_cast< LocationId >( v.locationId() ) );
    }

    optional< Value > EIRToValue( const Term& t )
    {
        // Special case for type's type
        auto typedecomp =



            Decompose( t, Vec( Lit( "type"_sid ), Val< uint64_t >(), Val< LocationId >() ) );



        if( typedecomp )
        {
            auto&& [universeIndex, loc] = *typedecomp;
            return Value( VEC( TSID( type ), TERM( universeIndex + 1 ), LocationId() ),
                TERM( universeIndex ), loc );
        }

        auto result = Decompose( t,

            Vec( Lit( "value"_sid ), Val< StringId >(), SubTerm(), SubTerm(),



                Val< LocationId >() ) );



        if( !result )
            return nullopt;

        auto&& [sort, type, val, locationId] = *result;
        if( sort == "constant"_sid )
            return Value( type, val, locationId );

        auto cir = Decompose( val, Val< ptr< void > >() );
        if( !cir )
            return nullopt;

        return Value(
            type, static_pointer_cast< cir::InstrSeq >( ptr< void >( *cir ) ), locationId );
    }

    Term ValueToEIR( const ValuePattern& v )
    {
        return VEC( TSID( value ), v.sort(), v.type(), v.val(), v.locationId() );
    }

    optional< ValuePattern > EIRToValuePattern( const Term& t )
    {
        auto result = Decompose(

            t, Vec( Lit( "value"_sid ), SubTerm(), SubTerm(), SubTerm(), Val< LocationId >() ) );







        if( !result )
            return nullopt;

        auto&& [sort, type, val, locationId] = *result;
        return ValuePattern( sort, type, val, locationId );
    }
} // namespace goose::eir
Changes to bs/eir/value.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
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
    extern optional< Value > EIRToValue( const Term& t );

    extern Term ValueToEIR( const ValuePattern& v );
    extern optional< ValuePattern > EIRToValuePattern( const Term& t );

    class Value
    {
        public:
            Value() = default;

            template< typename T, typename VL >
            Value( T&& type, VL&& valOrCIR, LocationId loc = LocationId() ) :
                m_type( forward< T >( type ) ),
                m_payload( forward< VL >( valOrCIR ) ),
                m_locationId( loc )
            {}



            const auto& type() const { return m_type; }

            const auto& payload() const { return m_payload; }

            const auto& val() const { return get< Term >( m_payload ); }

            auto& type() { return m_type; }

            auto& val() { return get< Term >( m_payload ); }

            const ptr< cir::InstrSeq >& cir() const;
            ptr< cir::InstrSeq >& cir();

            uint32_t cirIndex() const;

            template< typename T >
            void setCIR( T&& pInstrSeq )
            {
                m_payload = forward< T >( pInstrSeq );
            }

            auto locationId() const { return m_locationId; }

            auto&& setLocationId( LocationId id )
            {
                if( m_locationId.isPoison() )
                    id.setPoison();
                m_locationId = id;
                return *this;
            }

            bool isPoison() const { return m_locationId.isPoison(); }

            auto&& setPoison() { m_locationId.setPoison(); return *this; }





            bool isConstant() const { return holds_alternative< Term >( m_payload ); }

            bool isIndex() const { return holds_alternative< uint32_t >( m_payload ); }

            bool isType() const;
            bool isMetaType() const;

            friend ostream& operator<<( ostream& out, const Value& val )
            {
                return out << ValueToEIR( val );
            }

            bool operator==( const Value& rhs ) const
            {
                return m_type == rhs.m_type && m_payload == rhs.m_payload;
            }

            bool operator!=( const Value& rhs ) const
            {
                return m_type != rhs.m_type || m_payload != rhs.m_payload;
            }

            bool operator<( const Value& rhs ) const
            {
                if( m_type != rhs.m_type )
                    return m_type < rhs.m_type;
                return m_payload < rhs.m_payload;
            }

        private:
            Term m_type;
            variant< Term, ptr< cir::InstrSeq >, uint32_t > m_payload;
            LocationId m_locationId = LocationId::Poison();
    };

    class ValuePattern
    {
        public:
            template< typename S, typename T, typename V >
            ValuePattern( S&& sort, T&& type, V&& v, LocationId locationId = LocationId() ) :
                m_sort( forward< T >( sort ) ),
                m_type( forward< T >( type ) ),
                m_val( forward< V >( v ) ),
                m_locationId( locationId )
            {}



            auto locationId() const { return m_locationId; }

            auto&& setLocationId( LocationId id )
            {
                if( !m_locationId.isPoison() )
                    m_locationId = id;
                return *this;
            }

            bool isPoison() const { return m_locationId.isPoison(); }

            void setPoison() { m_locationId.setPoison(); }

            const auto& sort() const { return m_sort; }

            const auto& type() const { return m_type; }

            const auto& val() const { return m_val; }

            auto& sort() { return m_sort; }

            auto& type() { return m_type; }

            auto& val() { return m_val; }

        private:
            Term m_sort;
            Term m_type;
            Term m_val;

            LocationId m_locationId = 0;
    };

    // A generic poisoned value of "type" type.
    extern Value PoisonType( LocationId loc = Location::Create( source_location::current() ) );

    // A generic poisoned value of "poisontype" type.
    extern Value PoisonValue( LocationId loc = Location::Create( source_location::current() ) );

    using ValueVec = llvm::SmallVector< Value, 4 >;
}

#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
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
    extern optional< Value > EIRToValue( const Term& t );

    extern Term ValueToEIR( const ValuePattern& v );
    extern optional< ValuePattern > EIRToValuePattern( const Term& t );

    class Value
    {
      public:
        Value() = default;

        template< typename T, typename VL >
        Value( T&& type, VL&& valOrCIR, LocationId loc = LocationId() ) :
            m_type( forward< T >( type ) ),
            m_payload( forward< VL >( valOrCIR ) ),
            m_locationId( loc )

        {
        }

        const auto& type() const { return m_type; }

        const auto& payload() const { return m_payload; }

        const auto& val() const { return get< Term >( m_payload ); }

        auto& type() { return m_type; }

        auto& val() { return get< Term >( m_payload ); }

        const ptr< cir::InstrSeq >& cir() const;
        ptr< cir::InstrSeq >& cir();

        uint32_t cirIndex() const;

        template< typename T > void setCIR( T&& pInstrSeq )

        {
            m_payload = forward< T >( pInstrSeq );
        }

        auto locationId() const { return m_locationId; }

        auto&& setLocationId( LocationId id )
        {
            if( m_locationId.isPoison() )
                id.setPoison();
            m_locationId = id;
            return *this;
        }

        bool isPoison() const { return m_locationId.isPoison(); }

        auto&& setPoison()
        {
            m_locationId.setPoison();
            return *this;
        }

        bool isConstant() const { return holds_alternative< Term >( m_payload ); }

        bool isIndex() const { return holds_alternative< uint32_t >( m_payload ); }

        bool isType() const;
        bool isMetaType() const;

        friend ostream& operator<<( ostream& out, const Value& val )
        {
            return out << ValueToEIR( val );
        }

        bool operator==( const Value& rhs ) const
        {
            return m_type == rhs.m_type && m_payload == rhs.m_payload;
        }

        bool operator!=( const Value& rhs ) const
        {
            return m_type != rhs.m_type || m_payload != rhs.m_payload;
        }

        bool operator<( const Value& rhs ) const
        {
            if( m_type != rhs.m_type )
                return m_type < rhs.m_type;
            return m_payload < rhs.m_payload;
        }

      private:
        Term m_type;
        variant< Term, ptr< cir::InstrSeq >, uint32_t > m_payload;
        LocationId m_locationId = LocationId::Poison();
    };

    class ValuePattern
    {
      public:
        template< typename S, typename T, typename V >
        ValuePattern( S&& sort, T&& type, V&& v, LocationId locationId = LocationId() ) :
            m_sort( forward< T >( sort ) ),
            m_type( forward< T >( type ) ),
            m_val( forward< V >( v ) ),
            m_locationId( locationId )

        {
        }

        auto locationId() const { return m_locationId; }

        auto&& setLocationId( LocationId id )
        {
            if( !m_locationId.isPoison() )
                m_locationId = id;
            return *this;
        }

        bool isPoison() const { return m_locationId.isPoison(); }

        void setPoison() { m_locationId.setPoison(); }

        const auto& sort() const { return m_sort; }

        const auto& type() const { return m_type; }

        const auto& val() const { return m_val; }

        auto& sort() { return m_sort; }

        auto& type() { return m_type; }

        auto& val() { return m_val; }

      private:
        Term m_sort;
        Term m_type;
        Term m_val;

        LocationId m_locationId = 0;
    };

    // A generic poisoned value of "type" type.
    extern Value PoisonType( LocationId loc = Location::Create( source_location::current() ) );

    // A generic poisoned value of "poisontype" type.
    extern Value PoisonValue( LocationId loc = Location::Create( source_location::current() ) );

    using ValueVec = llvm::SmallVector< Value, 4 >;
} // namespace goose::eir

#endif
Changes to bs/eir/vecgenerator.cpp.
12
13
14
15
16
17
18
19

20
21
22
23
24
25
26
27
28
29
30
31
32
33
    {
        return m_index >= m_cont.terms().size();
    }

    size_t VecGenerator::repetitionIndex() const
    {
        return ( fixedPartFinished() && m_cont.repetitionTerm() ) ?
            m_index - m_cont.terms().size() : 0;

    }

    const Term& VecGenerator::operator()()
    {
        if( m_cont.repetitionTerm() && m_index >= m_cont.terms().size() )
        {
            ++m_index;
            return *m_cont.repetitionTerm();
        }

        assert( !finished() );
        return m_cont.terms()[m_index++];
    }
}







|
>













|
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
    {
        return m_index >= m_cont.terms().size();
    }

    size_t VecGenerator::repetitionIndex() const
    {
        return ( fixedPartFinished() && m_cont.repetitionTerm() ) ?
            m_index - m_cont.terms().size() :
            0;
    }

    const Term& VecGenerator::operator()()
    {
        if( m_cont.repetitionTerm() && m_index >= m_cont.terms().size() )
        {
            ++m_index;
            return *m_cont.repetitionTerm();
        }

        assert( !finished() );
        return m_cont.terms()[m_index++];
    }
} // namespace goose::eir
Changes to bs/eir/vecgenerator.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
#ifndef GOOSE_EIR_VECGENERATOR_H
#define GOOSE_EIR_VECGENERATOR_H

namespace goose::eir
{
    // Generator that outputs the elements of a vector term, and then the repetition term (if any) forever.

    class VecGenerator
    {
        public:
            VecGenerator( const Vector& cont ) : m_cont( cont ) {}




            bool finished() const;
            bool fixedPartFinished() const;
            size_t repetitionIndex() const;

            const Term& operator()();

        private:
            const Vector& m_cont;
            size_t m_index = 0;
    };
}


#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
#ifndef GOOSE_EIR_VECGENERATOR_H
#define GOOSE_EIR_VECGENERATOR_H

namespace goose::eir
{
    // Generator that outputs the elements of a vector term, and then the repetition term (if any)
    // forever.
    class VecGenerator
    {
      public:
        VecGenerator( const Vector& cont ) :
            m_cont( cont )
        {
        }

        bool finished() const;
        bool fixedPartFinished() const;
        size_t repetitionIndex() const;

        const Term& operator()();

      private:
        const Vector& m_cont;
        size_t m_index = 0;
    };

} // namespace goose::eir

#endif
Changes to bs/eir/vecoflength.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
#ifndef GOOSE_EIR_VECOFLENGTH_H
#define GOOSE_EIR_VECOFLENGTH_H

namespace goose::eir
{
    class VecOfLength
    {
        public:
            VecOfLength( StringId varName ) :
                m_varName( varName )
            {}



            const auto& varName() const { return m_varName; }

            friend ostream& ToString( ostream& out, const VecOfLength& v );

            friend ostream& operator<<( ostream& out, const VecOfLength& x )
            {
                return out << x.m_varName;
            }

            auto operator<=>( const VecOfLength& ) const = default;

        private:
            StringId m_varName;
    };
}


namespace std
{
    template<> struct hash< goose::eir::VecOfLength >
    {
        size_t operator()( const goose::eir::VecOfLength& x ) const
        {
            return hash< goose::util::StringId >()( x.varName() );
        }
    };
}

#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
#ifndef GOOSE_EIR_VECOFLENGTH_H
#define GOOSE_EIR_VECOFLENGTH_H

namespace goose::eir
{
    class VecOfLength
    {
      public:
        VecOfLength( StringId varName ) :
            m_varName( varName )

        {
        }

        const auto& varName() const { return m_varName; }

        friend ostream& ToString( ostream& out, const VecOfLength& v );

        friend ostream& operator<<( ostream& out, const VecOfLength& x )
        {
            return out << x.m_varName;
        }

        auto operator<=>( const VecOfLength& ) const = default;

      private:
        StringId m_varName;
    };

} // namespace goose::eir

namespace std
{
    template<> struct hash< goose::eir::VecOfLength >
    {
        size_t operator()( const goose::eir::VecOfLength& x ) const
        {
            return hash< goose::util::StringId >()( x.varName() );
        }
    };
} // namespace std

#endif
Changes to bs/eir/vector.h.
1
2
3
4
5
6
7
8
9
10
11
12


13
14
15
16


17
18
19
20
21
22
23
24
25
26
27
28


29

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

48

49
50
51
52
53
54
55

56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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
#ifndef GOOSE_EIR_VECTOR_H
#define GOOSE_EIR_VECTOR_H

namespace goose::eir
{
    static inline int32_t GetWeight( const Term& term );

    struct Repetition
    {
        Repetition( const Term& t ) :
            m_term( t )
        {}



        Repetition( Term&& t ) :
            m_term( move( t ) )
        {}



        Term m_term;
    };

    class VecLength
    {
        public:
            VecLength( size_t length, bool isVariable ) :
                m_length( length ),
                m_isVariable( isVariable )
            {}



            auto minLength() const { return m_length; }

            auto isVariable() const { return m_isVariable; }

            auto operator<=>( const VecLength& ) const = default;

        private:
            size_t  m_length = 0;
            bool    m_isVariable = false;
    };

    class Vector
    {
        public:
            Vector() {}

            template< typename T >
            using container_type = llvm::SmallVector< T, 4 >;

            const auto& terms() const { return m_terms; }

            auto& terms() { return m_terms; }

            int32_t weight() const { return m_weight + m_weightOverride; }
            void setWeight( int32_t w )
            {
                m_weightOverride = w - m_weight;
            }

            int32_t weightOverride() const { return m_weightOverride; }

            void setWeightOverride( int32_t wo ) { m_weightOverride = wo; }

            void reserve( size_t n )
            {
                m_terms.reserve( n );
            }

            void resize( size_t n )
            {
                m_terms.resize( n );
            }

            template< typename T >
            void append( T&& term )
            {
                if constexpr( !is_same_v< Repetition, remove_cvref_t< T > > )
                {
                    // We want the outer structures to have more
                    // weight during typechecking. We achieve
                    // this by using the sum of the weights of the
                    // children, multiplied by two. This should ensure
                    // that typechecking solutions keeping the outer
                    // structure intact will be used over solutions that
                    // don't.
                    m_weight += GetWeight( term ) * 2;
                    m_terms.emplace_back( forward< T >( term ) );
                }
                else
                {
                    setRepetitionTerm( term.m_term );
                }
            }

            template< typename T >
            void set( size_t index, T&& term )
            {
                m_weight -= GetWeight( m_terms[index] );
                m_weight += GetWeight( term ) * 2;
                m_terms[index] = term;
            }

            template< typename... T >
            static auto Make( T&&... terms )
            {
                auto v = make_shared< Vector >();
                v->reserve( sizeof... ( T ) );
                ( v->append( terms ), ... );
                return v;
            }

            template< typename... T >
            static Vector MakeAppend( const Vector& vec, T&&... terms )
            {
                Vector v( vec );
                v.reserve( v.m_terms.size() + sizeof... ( T ) );
                ( v.append( terms ), ... );
                return v;
            }

            template< typename... T >
            static Vector MakePrepend( const Vector& vec, T&&... terms )
            {
                Vector v;
                v.reserve( v.m_terms.size() + sizeof... ( T ) );

                ( v.append( terms ), ... );

                for( auto&& t : vec.terms() )
                    v.append( t );

                return v;
            }

            template< typename... T >
            static Vector MakeConcat( const Vector& vec1, const Vector& vec2 )
            {
                Vector v( vec1 );
                v.reserve( vec1.m_terms.size() + vec2.m_terms.size() );

                for( auto&& t : vec2.terms() )
                    v.append( t );

                return v;
            }

            friend ostream& ToString( ostream& out, const pvec& v );

            Vector( container_type< Term >&& terms ) :
                m_terms( move( terms ) )
            {}



            template< typename V >
            Vector( V&& terms, Term&& repetition ) :
                m_terms( forward< V >( terms ) ),
                m_repetitionTerm( move( repetition ) )
            {}



            auto length() const { return VecLength( m_terms.size(), !!m_repetitionTerm ); }

            const auto& repetitionTerm() const { return m_repetitionTerm; }

            bool empty() const { return m_terms.empty(); }

            Generator< reference_wrapper< const Term > > forEachTerm() const
            {
                for( auto&& t : terms() )
                    co_yield t;

                if( m_repetitionTerm )
                    co_yield *m_repetitionTerm;
            }

            template< typename F >
            pvec transform( F&& func ) const
            {
                auto v = make_shared< Vector >();
                v->reserve( m_terms.size() );

                for( auto&& x : m_terms )
                {
                    auto newX = func( x );
                    if( !newX )
                        return nullptr;

                    v->append( move( *newX ) );
                }

                return v;
            }

            Vector take( size_t n ) const
            {
                Vector v;
                v.reserve( max( n, m_terms.size() ) );

                auto it = m_terms.begin();

                while( n-- )
                    v.append( *it++ );

                return v;
            }

            Vector drop( size_t n ) const
            {
                Vector v;
                v.reserve( m_terms.size() > n ? m_terms.size() - n : 0 );

                auto it = m_terms.begin();
                advance( it, n );

                while( it != m_terms.end() )
                    v.append( *it++ );

                return v;
            }

            template< typename T >
            void setRepetitionTerm( T&& term )
            {
                uint32_t c = weight();
                if( m_repetitionTerm )
                    c -= GetWeight( *m_repetitionTerm );

                m_repetitionTerm = forward< T >( term );

                c += GetWeight( *m_repetitionTerm );
                setWeight( c );
            }

            bool operator==( const Vector& rhs ) const;

            const auto& operator[]( size_t i ) const
            {
                return m_terms[i];
            }

            auto& operator[]( size_t i )
            {
                return m_terms[i];
            }

        private:
            container_type< Term >  m_terms;
            optional< Term >        m_repetitionTerm;
            int32_t                 m_weight = 1;
            int32_t                 m_weightOverride = 0;
    };

    template< typename T >
    static inline auto&& SetWeight( T&& term, int32_t weight )
    {

        visit( [&]< typename TT >( const TT& t )
        {
            if constexpr( is_same_v< TT, pvec > )
                return t->setWeight( weight );
            else
                assert( weight == 0 );

        }, term );

        return forward< T >( term );
    }

    static inline int32_t GetWeight( const Term& term )
    {

        return visit( []< typename T >( const T& t ) -> uint32_t
        {
            if constexpr( is_same_v< T, pvec > )
                return t->weight();
            else
                return 0;
        }, term );
    }

}


#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



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
#ifndef GOOSE_EIR_VECTOR_H
#define GOOSE_EIR_VECTOR_H

namespace goose::eir
{
    static inline int32_t GetWeight( const Term& term );

    struct Repetition
    {
        Repetition( const Term& t ) :
            m_term( t )

        {
        }

        Repetition( Term&& t ) :
            m_term( move( t ) )

        {
        }

        Term m_term;
    };

    class VecLength
    {
      public:
        VecLength( size_t length, bool isVariable ) :
            m_length( length ),
            m_isVariable( isVariable )

        {
        }

        auto minLength() const { return m_length; }

        auto isVariable() const { return m_isVariable; }

        auto operator<=>( const VecLength& ) const = default;

      private:
        size_t m_length = 0;
        bool m_isVariable = false;
    };

    class Vector
    {
      public:
        Vector() {}


        template< typename T > using container_type = llvm::SmallVector< T, 4 >;

        const auto& terms() const { return m_terms; }

        auto& terms() { return m_terms; }

        int32_t weight() const { return m_weight + m_weightOverride; }


        void setWeight( int32_t w ) { m_weightOverride = w - m_weight; }


        int32_t weightOverride() const { return m_weightOverride; }

        void setWeightOverride( int32_t wo ) { m_weightOverride = wo; }

        void reserve( size_t n ) { m_terms.reserve( n ); }




        void resize( size_t n ) { m_terms.resize( n ); }




        template< typename T > void append( T&& term )

        {
            if constexpr( !is_same_v< Repetition, remove_cvref_t< T > > )
            {
                // We want the outer structures to have more weight during typechecking. We

                // achieve this by using the sum of the weights of the children, multiplied by

                // two. This should ensure that typechecking solutions keeping the outer
                // structure intact will be used over solutions that don't.

                m_weight += GetWeight( term ) * 2;
                m_terms.emplace_back( forward< T >( term ) );
            }
            else
            {
                setRepetitionTerm( term.m_term );
            }
        }

        template< typename T > void set( size_t index, T&& term )

        {
            m_weight -= GetWeight( m_terms[index] );
            m_weight += GetWeight( term ) * 2;
            m_terms[index] = term;
        }

        template< typename... T > static auto Make( T&&... terms )

        {
            auto v = make_shared< Vector >();
            v->reserve( sizeof...( T ) );
            ( v->append( terms ), ... );
            return v;
        }


        template< typename... T > static Vector MakeAppend( const Vector& vec, T&&... terms )
        {
            Vector v( vec );
            v.reserve( v.m_terms.size() + sizeof...( T ) );
            ( v.append( terms ), ... );
            return v;
        }


        template< typename... T > static Vector MakePrepend( const Vector& vec, T&&... terms )
        {
            Vector v;
            v.reserve( v.m_terms.size() + sizeof...( T ) );

            ( v.append( terms ), ... );

            for( auto&& t : vec.terms() )
                v.append( t );

            return v;
        }


        template< typename... T > static Vector MakeConcat( const Vector& vec1, const Vector& vec2 )
        {
            Vector v( vec1 );
            v.reserve( vec1.m_terms.size() + vec2.m_terms.size() );

            for( auto&& t : vec2.terms() )
                v.append( t );

            return v;
        }

        friend ostream& ToString( ostream& out, const pvec& v );

        Vector( container_type< Term >&& terms ) :
            m_terms( move( terms ) )

        {
        }

        template< typename V >
        Vector( V&& terms, Term&& repetition ) :
            m_terms( forward< V >( terms ) ),
            m_repetitionTerm( move( repetition ) )

        {
        }

        auto length() const { return VecLength( m_terms.size(), !!m_repetitionTerm ); }

        const auto& repetitionTerm() const { return m_repetitionTerm; }

        bool empty() const { return m_terms.empty(); }

        Generator< reference_wrapper< const Term > > forEachTerm() const
        {
            for( auto&& t : terms() )
                co_yield t;

            if( m_repetitionTerm )
                co_yield *m_repetitionTerm;
        }

        template< typename F > pvec transform( F&& func ) const

        {
            auto v = make_shared< Vector >();
            v->reserve( m_terms.size() );

            for( auto&& x : m_terms )
            {
                auto newX = func( x );
                if( !newX )
                    return nullptr;

                v->append( move( *newX ) );
            }

            return v;
        }

        Vector take( size_t n ) const
        {
            Vector v;
            v.reserve( max( n, m_terms.size() ) );

            auto it = m_terms.begin();

            while( n-- )
                v.append( *it++ );

            return v;
        }

        Vector drop( size_t n ) const
        {
            Vector v;
            v.reserve( m_terms.size() > n ? m_terms.size() - n : 0 );

            auto it = m_terms.begin();
            advance( it, n );

            while( it != m_terms.end() )
                v.append( *it++ );

            return v;
        }

        template< typename T > void setRepetitionTerm( T&& term )

        {
            uint32_t c = weight();
            if( m_repetitionTerm )
                c -= GetWeight( *m_repetitionTerm );

            m_repetitionTerm = forward< T >( term );

            c += GetWeight( *m_repetitionTerm );
            setWeight( c );
        }

        bool operator==( const Vector& rhs ) const;

        const auto& operator[]( size_t i ) const { return m_terms[i]; }




        auto& operator[]( size_t i ) { return m_terms[i]; }




      private:
        container_type< Term > m_terms;
        optional< Term > m_repetitionTerm;
        int32_t m_weight = 1;
        int32_t m_weightOverride = 0;
    };


    template< typename T > static inline auto&& SetWeight( T&& term, int32_t weight )
    {
        visit(
            [&]< typename TT >( const TT& t )
            {
                if constexpr( is_same_v< TT, pvec > )
                    return t->setWeight( weight );
                else
                    assert( weight == 0 );
            },
            term );

        return forward< T >( term );
    }

    static inline int32_t GetWeight( const Term& term )
    {
        return visit(
            []< typename T >( const T& t ) -> uint32_t
            {
                if constexpr( is_same_v< T, pvec > )
                    return t->weight();
                else
                    return 0;

            },
            term );
    }
} // namespace goose::eir

#endif
Changes to bs/execute/binaryops.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
#include "execute.h"
#include "builtins/builtins.h"
#include "binaryops.inl"

using namespace goose;
using namespace goose::execute;
using namespace goose::builtins;

bool VM::execute( const cir::And& bo )
{
    return executeLogicBinOp( bo, []< typename T >( const T& lhs, const T& rhs )

    {
        if constexpr( is_same_v< T, bool > )
        {
            return lhs && rhs;
        }
        else
        {
            return lhs & rhs;
        }
    } );
}

bool VM::execute( const cir::Or& bo )
{
    return executeLogicBinOp( bo, []< typename T >( const T& lhs, const T& rhs )

    {
        if constexpr( is_same_v< T, bool > )
        {
            return lhs || rhs;
        }
        else
        {
            return lhs | rhs;
        }
    } );
}

bool VM::execute( const cir::Xor& bo )
{
    return executeLogicBinOp( bo, []< typename T >( const T& lhs, const T& rhs )

    {
        if constexpr( is_same_v< T, bool > )
        {
            auto l = static_cast< uint8_t >( lhs );
            auto r = static_cast< uint8_t >( rhs );
            return !!( l ^ r );
        }
        else
        {
            return lhs ^ rhs;
        }
    } );
}

bool VM::execute( const cir::Shl& bo )
{
    return executeShiftBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs.shl( rhs );
    } );
}

bool VM::execute( const cir::LShr& bo )
{
    return executeShiftBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs.lshr( rhs );
    } );
}

bool VM::execute( const cir::AShr& bo )
{
    return executeShiftBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs.ashr( rhs );
    } );
}

bool VM::execute( const cir::Add& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs + rhs;
    } );
}

bool VM::execute( const cir::Sub& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs - rhs;
    } );
}

bool VM::execute( const cir::Mul& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs * rhs;
    } );
}

bool VM::execute( const cir::UDiv& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs / rhs;
    } );
}

bool VM::execute( const cir::SDiv& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs / rhs;
    } );
}

bool VM::execute( const cir::URem& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs % rhs;
    } );
}

bool VM::execute( const cir::SRem& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs % rhs;
    } );
}

bool VM::execute( const cir::Eq& bo )
{
    return executeEqualityBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs == rhs;
    } );
}

bool VM::execute( const cir::Neq& bo )
{
    return executeEqualityBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs != rhs;
    } );
}

bool VM::execute( const cir::UGT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs > rhs;
    } );
}

bool VM::execute( const cir::UGE& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs >= rhs;
    } );
}

bool VM::execute( const cir::ULT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs < rhs;
    } );
}

bool VM::execute( const cir::ULE& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs <= rhs;
    } );
}

bool VM::execute( const cir::SGT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs > rhs;
    } );
}

bool VM::execute( const cir::SGE& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs >= rhs;
    } );
}

bool VM::execute( const cir::SLT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs < rhs;
    } );
}

bool VM::execute( const cir::SLE& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
    {
        return lhs <= rhs;
    } );
}










|
>
|
|
<
|
<
|
<
|
<
|




|
>
|
|
<
|
<
|
<
|
<
|




|
>
|
|
|
|
|
|
|
|
|
|
|
|




|
<
<
<




|
<
<
<




|
<
<
<




|
<
<
<




|
<
<
<




|
<
<
<




|
<
<
<




|
<
<
<




|
<
<
<




|
<
<
<




|
<
<
<




|
<
<
<




|
<
<
<




|
<
<
<




|
<
<
<




|
<
<
<




|
<
<
<




|
<
<
<




|
<
<
<




|
<
<
<

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
#include "execute.h"
#include "builtins/builtins.h"
#include "binaryops.inl"

using namespace goose;
using namespace goose::execute;
using namespace goose::builtins;

bool VM::execute( const cir::And& bo )
{
    return executeLogicBinOp( bo,
        []< typename T >( const T& lhs, const T& rhs )
        {
            if constexpr( is_same_v< T, bool > )

                return lhs && rhs;

            else

                return lhs & rhs;

        } );
}

bool VM::execute( const cir::Or& bo )
{
    return executeLogicBinOp( bo,
        []< typename T >( const T& lhs, const T& rhs )
        {
            if constexpr( is_same_v< T, bool > )

                return lhs || rhs;

            else

                return lhs | rhs;

        } );
}

bool VM::execute( const cir::Xor& bo )
{
    return executeLogicBinOp( bo,
        []< typename T >( const T& lhs, const T& rhs )
        {
            if constexpr( is_same_v< T, bool > )
            {
                auto l = static_cast< uint8_t >( lhs );
                auto r = static_cast< uint8_t >( rhs );
                return !!( l ^ r );
            }
            else
            {
                return lhs ^ rhs;
            }
        } );
}

bool VM::execute( const cir::Shl& bo )
{
    return executeShiftBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs.shl( rhs ); } );



}

bool VM::execute( const cir::LShr& bo )
{
    return executeShiftBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs.lshr( rhs ); } );



}

bool VM::execute( const cir::AShr& bo )
{
    return executeShiftBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs.ashr( rhs ); } );



}

bool VM::execute( const cir::Add& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs + rhs; } );



}

bool VM::execute( const cir::Sub& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs - rhs; } );



}

bool VM::execute( const cir::Mul& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs * rhs; } );



}

bool VM::execute( const cir::UDiv& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs / rhs; } );



}

bool VM::execute( const cir::SDiv& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs / rhs; } );



}

bool VM::execute( const cir::URem& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs % rhs; } );



}

bool VM::execute( const cir::SRem& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs % rhs; } );



}

bool VM::execute( const cir::Eq& bo )
{
    return executeEqualityBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs == rhs; } );



}

bool VM::execute( const cir::Neq& bo )
{
    return executeEqualityBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs != rhs; } );



}

bool VM::execute( const cir::UGT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs > rhs; } );



}

bool VM::execute( const cir::UGE& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs >= rhs; } );



}

bool VM::execute( const cir::ULT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs < rhs; } );



}

bool VM::execute( const cir::ULE& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs <= rhs; } );



}

bool VM::execute( const cir::SGT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs > rhs; } );



}

bool VM::execute( const cir::SGE& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs >= rhs; } );



}

bool VM::execute( const cir::SLT& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs < rhs; } );



}

bool VM::execute( const cir::SLE& bo )
{
    return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs <= rhs; } );



}
Changes to bs/execute/binaryops.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
#ifndef GOOSE_EXECUTE_BINARYOPS_INL
#define GOOSE_EXECUTE_BINARYOPS_INL

namespace goose::execute
{
    template< typename F >
    bool VM::executeEqualityBinOp( const cir::BinaryOp& bo, F&& func )
    {
        auto rhs = pop();
        auto lhs = pop();
        if( !lhs || !rhs )
            return false;
        if( lhs->isPoison() || rhs->isPoison() )
            return false;

        assert( lhs->type() == rhs->type() );
        if( lhs->type() != rhs->type() )
            return false;

        auto lval = Evaluate( *lhs, *this );
        if( lval.isPoison() )
            return false;
        if( !lval.isConstant() )
            return false;

        auto rval = Evaluate( *rhs, *this );
        if( rval.isPoison()  )
            return false;
        if( !rval.isConstant() )
            return false;

        if( auto lbool = FromValue< bool >( lval ) )
        {
            push( ToValue( func( *lbool, *FromValue< bool >( rval ) ) ) );





<
|



















|







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
#ifndef GOOSE_EXECUTE_BINARYOPS_INL
#define GOOSE_EXECUTE_BINARYOPS_INL

namespace goose::execute
{

    template< typename F > bool VM::executeEqualityBinOp( const cir::BinaryOp& bo, F&& func )
    {
        auto rhs = pop();
        auto lhs = pop();
        if( !lhs || !rhs )
            return false;
        if( lhs->isPoison() || rhs->isPoison() )
            return false;

        assert( lhs->type() == rhs->type() );
        if( lhs->type() != rhs->type() )
            return false;

        auto lval = Evaluate( *lhs, *this );
        if( lval.isPoison() )
            return false;
        if( !lval.isConstant() )
            return false;

        auto rval = Evaluate( *rhs, *this );
        if( rval.isPoison() )
            return false;
        if( !rval.isConstant() )
            return false;

        if( auto lbool = FromValue< bool >( lval ) )
        {
            push( ToValue( func( *lbool, *FromValue< bool >( rval ) ) ) );
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
            const auto& rstr = *FromValue< string >( rval );
            push( ToValue( func( *lstr, rstr ) ) );
            return true;
        }

        G_TRACE_VAL( lval )
        G_TRACE_VAL( rval )
        G_ERROR( "binop execution failure")
        return false;
    }

    template< typename F >
    bool VM::executeLogicBinOp( const cir::BinaryOp& bo, F&& func )
    {
        auto rhs = pop();
        auto lhs = pop();
        if( !lhs || !rhs )
            return false;
        if( lhs->isPoison() || rhs->isPoison() )
            return false;

        assert( lhs->type() == rhs->type() );
        if( lhs->type() != rhs->type() )
            return false;

        auto lval = Evaluate( *lhs, *this );
        if( lval.isPoison()  )
            return false;
        if( !lval.isConstant() )
            return false;

        auto rval = Evaluate( *rhs, *this );
        if( rval.isPoison()  )
            return false;
        if( !rval.isConstant() )
            return false;

        if( auto lbool = FromValue< bool >( lval ) )
        {
            push( ToValue( func( *lbool, *FromValue< bool >( rval ) ) ) );







|



<
|













|





|







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
            const auto& rstr = *FromValue< string >( rval );
            push( ToValue( func( *lstr, rstr ) ) );
            return true;
        }

        G_TRACE_VAL( lval )
        G_TRACE_VAL( rval )
        G_ERROR( "binop execution failure" )
        return false;
    }


    template< typename F > bool VM::executeLogicBinOp( const cir::BinaryOp& bo, F&& func )
    {
        auto rhs = pop();
        auto lhs = pop();
        if( !lhs || !rhs )
            return false;
        if( lhs->isPoison() || rhs->isPoison() )
            return false;

        assert( lhs->type() == rhs->type() );
        if( lhs->type() != rhs->type() )
            return false;

        auto lval = Evaluate( *lhs, *this );
        if( lval.isPoison() )
            return false;
        if( !lval.isConstant() )
            return false;

        auto rval = Evaluate( *rhs, *this );
        if( rval.isPoison() )
            return false;
        if( !rval.isConstant() )
            return false;

        if( auto lbool = FromValue< bool >( lval ) )
        {
            push( ToValue( func( *lbool, *FromValue< bool >( rval ) ) ) );
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
            auto rint = *FromValue< BigInt >( rval );
            push( ToValue( func( *lint, move( rint ) ) ) );
            return true;
        }

        G_TRACE_VAL( lval )
        G_TRACE_VAL( rval )
        G_ERROR( "binop execution failure")
        return false;
    }

    template< typename F >
    bool VM::executeBinOp( const cir::BinaryOp& bo, F&& func )
    {
        auto rhs = pop();
        auto lhs = pop();
        if( !lhs || !rhs )
            return false;
        if( lhs->isPoison() || rhs->isPoison() )
            return false;

        assert( lhs->type() == rhs->type() );
        if( lhs->type() != rhs->type() )
            return false;

        auto lval = Evaluate( *lhs, *this );
        if( lval.isPoison()  )
            return false;
        if( !lval.isConstant() )
            return false;

        auto rval = Evaluate( *rhs, *this );
        if( rval.isPoison()  )
            return false;
        if( !rval.isConstant() )
            return false;

        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            push( ToValue( func( *lint, move( rint ) ) ) );
            return true;
        }

        if( auto lint = FromValue< BigInt >( lval ) )
        {
            const auto& rint = *FromValue< BigInt >( rval );
            push( ToValue( func( *lint, rint ) ) );
            return true;
        }

        G_TRACE_VAL( lval )
        G_TRACE_VAL( rval )
        G_ERROR( "binop execution failure")
        return false;
    }

    template< typename F >
    bool VM::executeShiftBinOp( const cir::BinaryOp& bo, F&& func )
    {
        auto rhs = pop();
        auto lhs = pop();
        if( !lhs || !rhs )
            return false;
        if( lhs->isPoison() || rhs->isPoison() )
            return false;

        auto lval = Evaluate( *lhs, *this );
         if( lval.isPoison()  )
            return false;
        if( !lval.isConstant() )
            return false;

        auto rval = Evaluate( *rhs, *this );
        if( rval.isPoison()  )
            return false;
        if( !rval.isConstant() )
            return false;

        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            push( ToValue( APSInt( func( *lint, move( rint ) ), lint->isUnsigned() ) ) );
            return true;
        }

        if( auto lint = FromValue< BigInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            push( ToValue( BigInt( func( *lint, move( rint ) ) ) ) );
            return true;
        }

        G_TRACE_VAL( lval )
        G_TRACE_VAL( rval )
        G_ERROR( "binop execution failure")
        return false;
    }
}

#endif







|



<
|













|





|




















|



<
|









|





|




















|


|


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
            auto rint = *FromValue< BigInt >( rval );
            push( ToValue( func( *lint, move( rint ) ) ) );
            return true;
        }

        G_TRACE_VAL( lval )
        G_TRACE_VAL( rval )
        G_ERROR( "binop execution failure" )
        return false;
    }


    template< typename F > bool VM::executeBinOp( const cir::BinaryOp& bo, F&& func )
    {
        auto rhs = pop();
        auto lhs = pop();
        if( !lhs || !rhs )
            return false;
        if( lhs->isPoison() || rhs->isPoison() )
            return false;

        assert( lhs->type() == rhs->type() );
        if( lhs->type() != rhs->type() )
            return false;

        auto lval = Evaluate( *lhs, *this );
        if( lval.isPoison() )
            return false;
        if( !lval.isConstant() )
            return false;

        auto rval = Evaluate( *rhs, *this );
        if( rval.isPoison() )
            return false;
        if( !rval.isConstant() )
            return false;

        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            push( ToValue( func( *lint, move( rint ) ) ) );
            return true;
        }

        if( auto lint = FromValue< BigInt >( lval ) )
        {
            const auto& rint = *FromValue< BigInt >( rval );
            push( ToValue( func( *lint, rint ) ) );
            return true;
        }

        G_TRACE_VAL( lval )
        G_TRACE_VAL( rval )
        G_ERROR( "binop execution failure" )
        return false;
    }


    template< typename F > bool VM::executeShiftBinOp( const cir::BinaryOp& bo, F&& func )
    {
        auto rhs = pop();
        auto lhs = pop();
        if( !lhs || !rhs )
            return false;
        if( lhs->isPoison() || rhs->isPoison() )
            return false;

        auto lval = Evaluate( *lhs, *this );
        if( lval.isPoison() )
            return false;
        if( !lval.isConstant() )
            return false;

        auto rval = Evaluate( *rhs, *this );
        if( rval.isPoison() )
            return false;
        if( !rval.isConstant() )
            return false;

        if( auto lint = FromValue< APSInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            push( ToValue( APSInt( func( *lint, move( rint ) ), lint->isUnsigned() ) ) );
            return true;
        }

        if( auto lint = FromValue< BigInt >( lval ) )
        {
            auto rint = *FromValue< APSInt >( rval );
            push( ToValue( BigInt( func( *lint, move( rint ) ) ) ) );
            return true;
        }

        G_TRACE_VAL( lval )
        G_TRACE_VAL( rval )
        G_ERROR( "binop execution failure" )
        return false;
    }
} // namespace goose::execute

#endif
Changes to bs/execute/eval.cpp.
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

        if( !v.isConstant() )
        {
            const auto& cir = val.cir();
            if( !cir )
                return v.setPoison();

            // Execution may fail: there are some cases when we can't really
            // be sure that eager evaluation is possible until we actually try.
            // In this case we forget about the eager evaluation and return the
            // value as is.
            if( !vm.execute( *cir ) )
                return v;

            // Not returning a value isn't a failure, for instance
            // if the value we just evaluated was a call to a function returning void.
            // So we return a void typed value in this case, otherwise the original
            // value might get evaluated again. And since void function are usually
            // called for their side effects, we don't want to evaluate them twice.

            // HOW ABOUT I ACTUALLY DO THE THING I SAID IN THE COMMENT TO
            // ACTUALLY AVOID THE PROBLEM I DESCRIBED FFS
            auto result = vm.pop();
            if( !result )
                return Value( GetValueType< void >(), 0U ); // FFS

            if( result->isPoison() )
                return *result;

            v = move( *result );
            v.setLocationId( val.locationId() );
        }

        if( IsTuple( v ) )
        {
            Vector vals;
            vals.reserve( TupleSize( v ) );

            bool poison = false;

            ForEachInTuple( v, [&]( auto&& v )

            {
                auto newV = Evaluate( v, vm );
                if( newV.isPoison() )
                    poison = true;

                vals.append( ValueToEIR( move( newV ) ) );
                return true;
            } );

            Value newTup( v.type(), make_shared< Vector >( move( vals ) ) );
            if( poison )
                newTup.setPoison();

            return newTup;
        }

        return v;
    }
}







|
|
|
<



|
|
<
|
|

|
|


















|
>
|
|
|
|

|
|
|










|
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

        if( !v.isConstant() )
        {
            const auto& cir = val.cir();
            if( !cir )
                return v.setPoison();

            // Execution may fail: there are some cases when we can't really be sure that eager
            // evaluation is possible until we actually try. In this case we forget about the eager
            // evaluation and return the value as is.

            if( !vm.execute( *cir ) )
                return v;

            // Not returning a value isn't a failure, for instance if the value we just evaluated
            // was a call to a function returning void. So we return a void typed value in this

            // case, otherwise the original value might get evaluated again. And since void function
            // are usually called for their side effects, we don't want to evaluate them twice.

            // HOW ABOUT I ACTUALLY DO THE THING I SAID IN THE COMMENT TO ACTUALLY AVOID THE PROBLEM
            // I DESCRIBED FFS
            auto result = vm.pop();
            if( !result )
                return Value( GetValueType< void >(), 0U ); // FFS

            if( result->isPoison() )
                return *result;

            v = move( *result );
            v.setLocationId( val.locationId() );
        }

        if( IsTuple( v ) )
        {
            Vector vals;
            vals.reserve( TupleSize( v ) );

            bool poison = false;

            ForEachInTuple( v,
                [&]( auto&& v )
                {
                    auto newV = Evaluate( v, vm );
                    if( newV.isPoison() )
                        poison = true;

                    vals.append( ValueToEIR( move( newV ) ) );
                    return true;
                } );

            Value newTup( v.type(), make_shared< Vector >( move( vals ) ) );
            if( poison )
                newTup.setPoison();

            return newTup;
        }

        return v;
    }
} // namespace goose::execute
Changes to bs/execute/execute.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

17
18
19
20
21
#ifndef GOOSE_EXECUTE_H
#define GOOSE_EXECUTE_H

#include "cir/cir.h"

namespace goose::execute
{
    using namespace eir;
    using namespace cir;

    class VM;

    // If the provided value isn't constant, execute its cir
    // to turn it into a constant.
    extern Value Evaluate( const Value& val, VM& vm );
}


#include "vm.h"
#include "termaddr.h"

#endif












|
<

<
>





1
2
3
4
5
6
7
8
9
10
11
12
13

14

15
16
17
18
19
20
#ifndef GOOSE_EXECUTE_H
#define GOOSE_EXECUTE_H

#include "cir/cir.h"

namespace goose::execute
{
    using namespace eir;
    using namespace cir;

    class VM;

    // If the provided value isn't constant, execute its cir to turn it into a constant.

    extern Value Evaluate( const Value& val, VM& vm );

} // namespace goose::execute

#include "vm.h"
#include "termaddr.h"

#endif
Changes to bs/execute/termaddr.cpp.
1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
17
#include "execute.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::execute;
using namespace goose::builtins;

const Term& Bridge< eir::Term* >::Type()
{

    static auto type = ValueToEIR( Value( TypeType(), MkStdType( TSID( ct_type ), TSID( comptime_addr ) ) ) );
    return type;
}

Value Bridge< eir::Term* >::ToValue( eir::Term* x )
{
    return Value( Type(), TERM( static_cast< void* >( x ) ) );
}









>
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "execute.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::execute;
using namespace goose::builtins;

const Term& Bridge< eir::Term* >::Type()
{
    static auto type =
        ValueToEIR( Value( TypeType(), MkStdType( TSID( ct_type ), TSID( comptime_addr ) ) ) );
    return type;
}

Value Bridge< eir::Term* >::ToValue( eir::Term* x )
{
    return Value( Type(), TERM( static_cast< void* >( x ) ) );
}
Changes to bs/execute/termaddr.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef GOOSE_EXECUTE_TERMADDR_H
#define GOOSE_EXECUTE_TERMADDR_H

namespace goose::eir
{
    template<>
    struct Bridge< eir::Term* >
    {
        static const Term& Type();
        static Value ToValue( eir::Term* x );
        static eir::Term* FromValue( const Value& v );
    };
}

#endif





<
|





|


1
2
3
4
5

6
7
8
9
10
11
12
13
14
#ifndef GOOSE_EXECUTE_TERMADDR_H
#define GOOSE_EXECUTE_TERMADDR_H

namespace goose::eir
{

    template<> struct Bridge< eir::Term* >
    {
        static const Term& Type();
        static Value ToValue( eir::Term* x );
        static eir::Term* FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/execute/vm.cpp.
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
    while( bb )
    {
        const auto* pInstrs = bb->runnableInstructions();
        if( !pInstrs )
            return false;

        for( auto&& instr : *pInstrs )
        {
            if( !execute( instr ) )
                return false;
        }

        if( !bb->terminator() )
            break;

        m_pPreviousBB = bb;
        bb = executeTerminator( *bb->terminator() );
    }

    m_pPreviousBB = pbbBackup;
    if( m_retVal )
        push( *m_retVal );
    return true;
}

bool VM::execute( const cir::InstrSeq& is )
{
    for( const auto& instr : is )
    {
        if( !execute( instr ) )
            return false;
    }

    return true;
}

bool VM::execute( const cir::Instruction& instr )
{
    return visit( [&]( auto&& e )
    {
        return execute( e );
    }, instr );
}

bool VM::execute( const cir::Call& call )
{
    if( !( ms_remainingBranchInstExecutions ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "execute: compilation time execution budget exceeded." );
        return false;
    }

    --ms_remainingBranchInstExecutions;

    auto callFunc = pop();
    if( !callFunc )
        return false;

    if( callFunc->isPoison() )
        DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );

    auto func = Evaluate( *callFunc, *this );
    if( func.isPoison() )
        return false;

    if( !func.isConstant() )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "execute: function evaluation failed." );
        return false;
    }

    if( IsExternalFunc( func ) )
        return false;

    auto argCount = call.numArgs();







<


<

















<


<






|
<
<
<






|
|


















|
|







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
    while( bb )
    {
        const auto* pInstrs = bb->runnableInstructions();
        if( !pInstrs )
            return false;

        for( auto&& instr : *pInstrs )

            if( !execute( instr ) )
                return false;


        if( !bb->terminator() )
            break;

        m_pPreviousBB = bb;
        bb = executeTerminator( *bb->terminator() );
    }

    m_pPreviousBB = pbbBackup;
    if( m_retVal )
        push( *m_retVal );
    return true;
}

bool VM::execute( const cir::InstrSeq& is )
{
    for( const auto& instr : is )

        if( !execute( instr ) )
            return false;


    return true;
}

bool VM::execute( const cir::Instruction& instr )
{
    return visit( [&]( auto&& e ) { return execute( e ); }, instr );



}

bool VM::execute( const cir::Call& call )
{
    if( !( ms_remainingBranchInstExecutions ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage(
            0, "execute: compilation time execution budget exceeded." );
        return false;
    }

    --ms_remainingBranchInstExecutions;

    auto callFunc = pop();
    if( !callFunc )
        return false;

    if( callFunc->isPoison() )
        DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );

    auto func = Evaluate( *callFunc, *this );
    if( func.isPoison() )
        return false;

    if( !func.isConstant() )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage(
            0, "execute: function evaluation failed." );
        return false;
    }

    if( IsExternalFunc( func ) )
        return false;

    auto argCount = call.numArgs();
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
    if( !pvoid )
        return false;

    auto val = EIRToValue( *static_cast< Term* >( pvoid ) );
    if( !val )
        return false;

    // We only support tuples now. In the future, Select will also be used to
    // add an offset to another pointer.
    // Everything else (structs, classes, containers, etc.) should build on top
    // of those two fundamental types.
    if( !IsTuple( *val ) )
    {
        G_VAL_ERROR( *val, "execute: select: value is not a tuple" );
        return false;
    }

    assert( val->isConstant() );

    push( ToValue( &GetConstantTupleElement( *val, s.memberIndex() ) ) );
    return true;
}

bool VM::execute( const cir::AllocVar& av )
{
    auto stackIndex = m_currentFrameStart + ( av.index() & 0x7fffffff );

    if( m_stack.size() <= stackIndex )
        m_stack.resize( stackIndex + 1 );

    m_stack[stackIndex] = BuildUninitializedValue( *EIRToValue( av.type().get() ) );
    return true;
}

bool VM::execute( const cir::Load& l )
{
    auto baseAddr = pop();
    if( !baseAddr )
        return false;

    auto addr = FromValue< Term* >( *baseAddr );
    if( !addr )
        return false;

    if( *addr == TSID( UNINITIALIZED ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( l.locationId(),
            "execute: attempting to load from an uninitialized address" );
        return false;
    }

    auto val = EIRToValue( *addr );
    if( !val )
        return false;








|
<
|
|














|




















|
|







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
    if( !pvoid )
        return false;

    auto val = EIRToValue( *static_cast< Term* >( pvoid ) );
    if( !val )
        return false;

    // We only support tuples now. In the future, Select will also be used to add an offset to

    // another pointer. Everything else (structs, classes, containers, etc.) should build on top of
    // those two fundamental types.
    if( !IsTuple( *val ) )
    {
        G_VAL_ERROR( *val, "execute: select: value is not a tuple" );
        return false;
    }

    assert( val->isConstant() );

    push( ToValue( &GetConstantTupleElement( *val, s.memberIndex() ) ) );
    return true;
}

bool VM::execute( const cir::AllocVar& av )
{
    auto stackIndex = m_currentFrameStart + ( av.index() & 0x7fff'ffff );

    if( m_stack.size() <= stackIndex )
        m_stack.resize( stackIndex + 1 );

    m_stack[stackIndex] = BuildUninitializedValue( *EIRToValue( av.type().get() ) );
    return true;
}

bool VM::execute( const cir::Load& l )
{
    auto baseAddr = pop();
    if( !baseAddr )
        return false;

    auto addr = FromValue< Term* >( *baseAddr );
    if( !addr )
        return false;

    if( *addr == TSID( UNINITIALIZED ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage(
            l.locationId(), "execute: attempting to load from an uninitialized address" );
        return false;
    }

    auto val = EIRToValue( *addr );
    if( !val )
        return false;

268
269
270
271
272
273
274
275

276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
    return true;
}

bool VM::execute( const cir::Phi& p )
{
    bool success = false;

    p.forAllIncomings( [&]( auto&& bb, auto&& val )

    {
        if( bb == m_pPreviousBB )
        {
            m_temporaries.set( p.destIndex(), Evaluate( val, *this ) );
            success = true;
            return false;
        }

        return true;
    } );

    return success;
}

bool VM::execute( const cir::Not& uo )
{
    auto operand = pop();







|
>
|
|
|
|
|
|
|

|
|







260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
    return true;
}

bool VM::execute( const cir::Phi& p )
{
    bool success = false;

    p.forAllIncomings(
        [&]( auto&& bb, auto&& val )
        {
            if( bb == m_pPreviousBB )
            {
                m_temporaries.set( p.destIndex(), Evaluate( val, *this ) );
                success = true;
                return false;
            }

            return true;
        } );

    return success;
}

bool VM::execute( const cir::Not& uo )
{
    auto operand = pop();
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
{
    G_ERROR( "InlineCall encountered during execution" );
    return false;
}

ptr< BasicBlock > VM::executeTerminator( const cir::Terminator& terminator )
{
    return visit( [&]( auto&& e )
    {
        return executeTerminator( e );
    }, terminator.content() );
}

ptr< BasicBlock > VM::executeTerminator( const cir::RetVoid& r )
{
    return nullptr;
}








|
<
<
<







308
309
310
311
312
313
314
315



316
317
318
319
320
321
322
{
    G_ERROR( "InlineCall encountered during execution" );
    return false;
}

ptr< BasicBlock > VM::executeTerminator( const cir::Terminator& terminator )
{
    return visit( [&]( auto&& e ) { return executeTerminator( e ); }, terminator.content() );



}

ptr< BasicBlock > VM::executeTerminator( const cir::RetVoid& r )
{
    return nullptr;
}

340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413

414
415
416
417
418

419
420
    return nullptr;
}

ptr< BasicBlock > VM::executeTerminator( const cir::Branch& b )
{
    if( !( ms_remainingBranchInstExecutions ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "execute: compilation time execution budget exceeded." );
        m_retVal = PoisonValue();
        return nullptr;
    }

    --ms_remainingBranchInstExecutions;
    return b.dest().lock();
}

ptr< BasicBlock > VM::executeTerminator( const cir::CondBranch& cb )
{
    if( !( ms_remainingBranchInstExecutions ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "execute: compilation time execution budget exceeded." );
        m_retVal = PoisonValue();
        return nullptr;
    }

    --ms_remainingBranchInstExecutions;

    auto condVal = pop();
    if( !condVal )
        return nullptr;

    auto cond = Evaluate( *condVal, *this );
    if( cond.isPoison() )
        return nullptr;

    if( !cond.isConstant() )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( condVal->locationId(),
            "execute: branch condition evaluation failed." );
        m_retVal = PoisonValue();
        return nullptr;
    }

    if( *FromValue< bool >( cond ) )
        return cb.trueDest().lock();

    return cb.falseDest().lock();
}

ptr< BasicBlock > VM::executeTerminator( const cir::GhostBranch& gb )
{
    if( !( ms_remainingBranchInstExecutions ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( 0,
            "execute: compilation time execution budget exceeded." );
        m_retVal = PoisonValue();
        return nullptr;
    }

    --ms_remainingBranchInstExecutions;
    return gb.continuation().lock();
}

ptr< Term > VM::BuildUninitializedValue( const Value& type )
{
    if( !IsTupleType( type ) )
        return make_shared< Term >( TSID( UNINITIALIZED ) );

    auto tupContent = make_shared< Vector >();
    tupContent->reserve( TupleTypeSize( type ) );

    ForEachInTupleType( type, [&]( auto&& t )

    {
        tupContent->append( *BuildUninitializedValue( *EIRToValue( t ) ) );
        return true;
    } );


    return make_shared< Term >( ValueToEIR( Value( ValueToEIR( type ), TERM( move( tupContent ) ) ) ) );
}







|
|












|
|
















|
|














|
|
















|
>
|
|
|
|

>
|

330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
    return nullptr;
}

ptr< BasicBlock > VM::executeTerminator( const cir::Branch& b )
{
    if( !( ms_remainingBranchInstExecutions ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage(
            0, "execute: compilation time execution budget exceeded." );
        m_retVal = PoisonValue();
        return nullptr;
    }

    --ms_remainingBranchInstExecutions;
    return b.dest().lock();
}

ptr< BasicBlock > VM::executeTerminator( const cir::CondBranch& cb )
{
    if( !( ms_remainingBranchInstExecutions ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage(
            0, "execute: compilation time execution budget exceeded." );
        m_retVal = PoisonValue();
        return nullptr;
    }

    --ms_remainingBranchInstExecutions;

    auto condVal = pop();
    if( !condVal )
        return nullptr;

    auto cond = Evaluate( *condVal, *this );
    if( cond.isPoison() )
        return nullptr;

    if( !cond.isConstant() )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage(
            condVal->locationId(), "execute: branch condition evaluation failed." );
        m_retVal = PoisonValue();
        return nullptr;
    }

    if( *FromValue< bool >( cond ) )
        return cb.trueDest().lock();

    return cb.falseDest().lock();
}

ptr< BasicBlock > VM::executeTerminator( const cir::GhostBranch& gb )
{
    if( !( ms_remainingBranchInstExecutions ) )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage(
            0, "execute: compilation time execution budget exceeded." );
        m_retVal = PoisonValue();
        return nullptr;
    }

    --ms_remainingBranchInstExecutions;
    return gb.continuation().lock();
}

ptr< Term > VM::BuildUninitializedValue( const Value& type )
{
    if( !IsTupleType( type ) )
        return make_shared< Term >( TSID( UNINITIALIZED ) );

    auto tupContent = make_shared< Vector >();
    tupContent->reserve( TupleTypeSize( type ) );

    ForEachInTupleType( type,
        [&]( auto&& t )
        {
            tupContent->append( *BuildUninitializedValue( *EIRToValue( t ) ) );
            return true;
        } );

    return make_shared< Term >(
        ValueToEIR( Value( ValueToEIR( type ), TERM( move( tupContent ) ) ) ) );
}
Changes to bs/execute/vm.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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
#ifndef GOOSE_EXECUTE_VM_H
#define GOOSE_EXECUTE_VM_H

namespace goose::execute
{
    class VM
    {
        friend class ExecutionBudgetGuard;

        public:
            static void SetExecutionBudget( uint32_t b )
            {
                ms_remainingBranchInstExecutions = b;
            }

            optional< Value > execute( CFG& cfg );

            bool execute( ptr< BasicBlock > bb );
            bool execute( const cir::InstrSeq& is );
            bool execute( const cir::Instruction& instr );
            bool execute( const cir::Call& call );
            bool execute( const cir::Constant& cst );
            bool execute( const cir::Select& s );
            bool execute( const cir::AllocVar& av );
            bool execute( const cir::Load& l );
            bool execute( const cir::Store& s );
            bool execute( const cir::Phi& p );

            bool execute( const cir::Not& uo );
            bool execute( const cir::And& bo );
            bool execute( const cir::Or& bo );
            bool execute( const cir::Xor& bo );
            bool execute( const cir::Shl& bo );
            bool execute( const cir::LShr& bo );
            bool execute( const cir::AShr& bo );

            bool execute( const cir::Add& bo );
            bool execute( const cir::Sub& bo );
            bool execute( const cir::Mul& bo );
            bool execute( const cir::UDiv& bo );
            bool execute( const cir::SDiv& bo );
            bool execute( const cir::URem& bo );
            bool execute( const cir::SRem& bo );

            bool execute( const cir::Eq& bo );
            bool execute( const cir::Neq& bo );
            bool execute( const cir::UGT& bo );
            bool execute( const cir::UGE& bo );
            bool execute( const cir::ULT& bo );
            bool execute( const cir::ULE& bo );
            bool execute( const cir::SGT& bo );
            bool execute( const cir::SGE& bo );
            bool execute( const cir::SLT& bo );
            bool execute( const cir::SLE& bo );

            bool execute( const cir::InlineCall& ic );

            template< typename T >
            bool execute( const T& )
            {
                return false;
            }

            ptr< BasicBlock > executeTerminator( const cir::Terminator& terminator );
            ptr< BasicBlock > executeTerminator( const cir::RetVoid& r );
            ptr< BasicBlock > executeTerminator( const cir::Ret& r );
            ptr< BasicBlock > executeTerminator( const cir::Branch& b );
            ptr< BasicBlock > executeTerminator( const cir::CondBranch& cb );
            ptr< BasicBlock > executeTerminator( const cir::GhostBranch& gb );

            template< typename T >
            ptr< BasicBlock > executeTerminator( const T& )
            {
                return nullptr;
            }

            void push( const Value& v )
            {
                m_stack.emplace_back( make_shared< Term >( ValueToEIR( v ) ) );
            }

            optional< Value > pop()
            {
                if( m_stack.empty() || m_stack.size() <= m_currentFrameStart )
                    return nullopt;

                assert( m_stack.back() );

                auto result = move( *m_stack.back() );
                m_stack.resize( m_stack.size() - 1 );
                return EIRToValue( result );
            }

            optional< Term > popTerm()
            {
                if( m_stack.empty() || m_stack.size() <= m_currentFrameStart )
                    return nullopt;

                assert( m_stack.back() );

                auto result = move( *m_stack.back() );
                m_stack.resize( m_stack.size() - 1 );
                return result;
            }

        private:
            struct StackFrameHelper
            {
                StackFrameHelper( VM& vm, CFG& cfg, size_t numArgs = 0 );
                ~StackFrameHelper();

                template< typename T >
                void set( size_t index, T&& val )
                {
                    vm.m_stack[index + savedStackSize] = forward< T >( val );
                }

                size_t savedStackSize = 0;
                size_t savedFrameStart = 0;
                VM& vm;
            };

            static optional< Value > ExecuteBuiltinFuncCall( const Value& func, const Term& args );
            static ptr< Term > BuildUninitializedValue( const Value& type );

            template< typename F >
            bool executeEqualityBinOp( const cir::BinaryOp& bo, F&& func );

            template< typename F >
            bool executeLogicBinOp( const cir::BinaryOp& bo, F&& func );

            template< typename F >
            bool executeBinOp( const cir::BinaryOp& bo, F&& func );

            template< typename F >
            bool executeShiftBinOp( const cir::BinaryOp& bo, F&& func );

            llvm::SmallVector< ptr< Term >, 8 > m_stack;
            cir::TempStorage< Value > m_temporaries;
            optional< Value > m_retVal;
            size_t m_currentFrameStart = 0;

            // Used to interpret Phi instructions.
            ptr< BasicBlock > m_pPreviousBB;

            // To avoid compile time code to get stuck, we have a limitation on the
            // total number of call and loop instructions that we are willing to execute
            // before giving up.
            static uint32_t ms_remainingBranchInstExecutions;
    };

    // To avoid using up the execution budget on failed non mandatory compile time execution attempts

    class ExecutionBudgetGuard
    {
        public:
            ExecutionBudgetGuard() :
                m_savedBudget( VM::ms_remainingBranchInstExecutions )
            {}

            ~ExecutionBudgetGuard()

            {
                VM::ms_remainingBranchInstExecutions = m_savedBudget;
            }

            // If the budget use should be kept, call this before destruction.
            void commit()
            {
                m_savedBudget = VM::ms_remainingBranchInstExecutions;
            }

        private:
            uint32_t m_savedBudget = 0;
    };
}


#endif









|
|
<
<
<

|

|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|

|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|

|

|
<
|
<
<
<
|
|
|
|
|
|

<
|
<
<
<

|
|
|
|

|
|
|
|

|

|
|
|
|

|
|
|
|

|

|
|
|
|

|
|
|
|
|

|
<
|
|
|

|
|
|
|

|
|

<
|

<
|

<
|

<
|

|
|
|
|

|
|

|
|
<
|


|
>


|
|
|
<
|
<
>
|
|
|
<
|
<
<
|
|
<
|
|

<
>


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
#ifndef GOOSE_EXECUTE_VM_H
#define GOOSE_EXECUTE_VM_H

namespace goose::execute
{
    class VM
    {
        friend class ExecutionBudgetGuard;

      public:
        static void SetExecutionBudget( uint32_t b ) { ms_remainingBranchInstExecutions = b; }




        optional< Value > execute( CFG& cfg );

        bool execute( ptr< BasicBlock > bb );
        bool execute( const cir::InstrSeq& is );
        bool execute( const cir::Instruction& instr );
        bool execute( const cir::Call& call );
        bool execute( const cir::Constant& cst );
        bool execute( const cir::Select& s );
        bool execute( const cir::AllocVar& av );
        bool execute( const cir::Load& l );
        bool execute( const cir::Store& s );
        bool execute( const cir::Phi& p );

        bool execute( const cir::Not& uo );
        bool execute( const cir::And& bo );
        bool execute( const cir::Or& bo );
        bool execute( const cir::Xor& bo );
        bool execute( const cir::Shl& bo );
        bool execute( const cir::LShr& bo );
        bool execute( const cir::AShr& bo );

        bool execute( const cir::Add& bo );
        bool execute( const cir::Sub& bo );
        bool execute( const cir::Mul& bo );
        bool execute( const cir::UDiv& bo );
        bool execute( const cir::SDiv& bo );
        bool execute( const cir::URem& bo );
        bool execute( const cir::SRem& bo );

        bool execute( const cir::Eq& bo );
        bool execute( const cir::Neq& bo );
        bool execute( const cir::UGT& bo );
        bool execute( const cir::UGE& bo );
        bool execute( const cir::ULT& bo );
        bool execute( const cir::ULE& bo );
        bool execute( const cir::SGT& bo );
        bool execute( const cir::SGE& bo );
        bool execute( const cir::SLT& bo );
        bool execute( const cir::SLE& bo );

        bool execute( const cir::InlineCall& ic );

        template< typename T > bool execute( const T& ) { return false; }





        ptr< BasicBlock > executeTerminator( const cir::Terminator& terminator );
        ptr< BasicBlock > executeTerminator( const cir::RetVoid& r );
        ptr< BasicBlock > executeTerminator( const cir::Ret& r );
        ptr< BasicBlock > executeTerminator( const cir::Branch& b );
        ptr< BasicBlock > executeTerminator( const cir::CondBranch& cb );
        ptr< BasicBlock > executeTerminator( const cir::GhostBranch& gb );


        template< typename T > ptr< BasicBlock > executeTerminator( const T& ) { return nullptr; }




        void push( const Value& v )
        {
            m_stack.emplace_back( make_shared< Term >( ValueToEIR( v ) ) );
        }

        optional< Value > pop()
        {
            if( m_stack.empty() || m_stack.size() <= m_currentFrameStart )
                return nullopt;

            assert( m_stack.back() );

            auto result = move( *m_stack.back() );
            m_stack.resize( m_stack.size() - 1 );
            return EIRToValue( result );
        }

        optional< Term > popTerm()
        {
            if( m_stack.empty() || m_stack.size() <= m_currentFrameStart )
                return nullopt;

            assert( m_stack.back() );

            auto result = move( *m_stack.back() );
            m_stack.resize( m_stack.size() - 1 );
            return result;
        }

      private:
        struct StackFrameHelper
        {
            StackFrameHelper( VM& vm, CFG& cfg, size_t numArgs = 0 );
            ~StackFrameHelper();

            template< typename T > void set( size_t index, T&& val )

            {
                vm.m_stack[index + savedStackSize] = forward< T >( val );
            }

            size_t savedStackSize = 0;
            size_t savedFrameStart = 0;
            VM& vm;
        };

        static optional< Value > ExecuteBuiltinFuncCall( const Value& func, const Term& args );
        static ptr< Term > BuildUninitializedValue( const Value& type );


        template< typename F > bool executeEqualityBinOp( const cir::BinaryOp& bo, F&& func );


        template< typename F > bool executeLogicBinOp( const cir::BinaryOp& bo, F&& func );


        template< typename F > bool executeBinOp( const cir::BinaryOp& bo, F&& func );


        template< typename F > bool executeShiftBinOp( const cir::BinaryOp& bo, F&& func );

        llvm::SmallVector< ptr< Term >, 8 > m_stack;
        cir::TempStorage< Value > m_temporaries;
        optional< Value > m_retVal;
        size_t m_currentFrameStart = 0;

        // Used to interpret Phi instructions.
        ptr< BasicBlock > m_pPreviousBB;

        // To avoid compile time code to get stuck, we have a limitation on the total number of
        // call and loop instructions that we are willing to execute before giving up.

        static uint32_t ms_remainingBranchInstExecutions;
    };

    // To avoid using up the execution budget on failed non mandatory compile time execution
    // attempts
    class ExecutionBudgetGuard
    {
      public:
        ExecutionBudgetGuard() :
            m_savedBudget( VM::ms_remainingBranchInstExecutions )

        {

        }

        ~ExecutionBudgetGuard() { VM::ms_remainingBranchInstExecutions = m_savedBudget; }


        // If the budget use should be kept, call this before destruction.


        void commit() { m_savedBudget = VM::ms_remainingBranchInstExecutions; }


      private:
        uint32_t m_savedBudget = 0;
    };

} // namespace goose::execute

#endif
Changes to bs/g0api/cgapi/cgapi.h.
12
13
14
15
16
17
18
19
20
21
    static inline void SetupApiCodeGen( Env& e )
    {
        SetupApiCGModule( e );
        SetupApiCGMangle( e );
        SetupApiCGFunc( e );
        SetupApiCGLinker( e );
    }
}

#endif







|


12
13
14
15
16
17
18
19
20
21
    static inline void SetupApiCodeGen( Env& e )
    {
        SetupApiCGModule( e );
        SetupApiCGMangle( e );
        SetupApiCGFunc( e );
        SetupApiCGLinker( e );
    }
} // namespace goose::g0api

#endif
Changes to bs/g0api/cgapi/func.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
#include "g0api/g0api.h"
#include "eir/eir.h"

using namespace goose;

namespace goose::g0api
{
    void SetupApiCGFunc( Env& e )
    {
        weak_ptr< Env > pEnv = e.shared_from_this();


        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< codegen::Module > >, CustomPattern< Value, FuncPattern > ) >( e, "CGGenerateFunction"_sid,
            [pEnv]( const TypeWrapper< ptr< codegen::Module > >& module, const Value& f )
            {
                ProfileZoneScopedN( "CGGenerateFunction" );

                if( !IsFuncType( *EIRToValue( f.type() ) ) )
                    return false;

                sema::Context c( pEnv.lock(), RootG0Identity() );

                DiagnosticsContext dc( 0, true );
                VerbosityContext vc( Verbosity::Normal, true );

                auto func = CompileFunc( c, f );
                if( func.isPoison() || DiagnosticsManager::GetInstance().errorsWereEmitted() )
                    return false;

                auto llvmFunc = module->getOrCreateFunc( c, *FromValue< builtins::Func >( func ) );
                return !!llvmFunc;
            } );


        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< codegen::Module > >, CustomPattern< Value, FuncPattern >, string ) >( e, "CGGenerateFunction"_sid,
            [pEnv]( const TypeWrapper< ptr< codegen::Module > >& module, const Value& f, const string& name )

            {
                ProfileZoneScopedN( "CGGenerateFunction" );

                if( !IsFuncType( *EIRToValue( f.type() ) ) )
                    return false;

                sema::Context c( pEnv.lock(), RootG0Identity() );

                DiagnosticsContext dc( 0, true );
                VerbosityContext vc( Verbosity::Normal, true );

                auto func = CompileFunc( c, f );
                if( func.isPoison() || DiagnosticsManager::GetInstance().errorsWereEmitted() )
                    return false;

                auto llvmFunc = module->getOrCreateFunc( c, *FromValue< builtins::Func >( func ), name, llvm::Function::ExternalLinkage );

                return !!llvmFunc;
            } );
    }
}











>
|




















>
|
|
>















|
>



|
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 "g0api/g0api.h"
#include "eir/eir.h"

using namespace goose;

namespace goose::g0api
{
    void SetupApiCGFunc( Env& e )
    {
        weak_ptr< Env > pEnv = e.shared_from_this();

        RegisterBuiltinFunc< bool( TypeWrapper< ptr< codegen::Module > >,
            CustomPattern< Value, FuncPattern > ) >( e, "CGGenerateFunction"_sid,
            [pEnv]( const TypeWrapper< ptr< codegen::Module > >& module, const Value& f )
            {
                ProfileZoneScopedN( "CGGenerateFunction" );

                if( !IsFuncType( *EIRToValue( f.type() ) ) )
                    return false;

                sema::Context c( pEnv.lock(), RootG0Identity() );

                DiagnosticsContext dc( 0, true );
                VerbosityContext vc( Verbosity::Normal, true );

                auto func = CompileFunc( c, f );
                if( func.isPoison() || DiagnosticsManager::GetInstance().errorsWereEmitted() )
                    return false;

                auto llvmFunc = module->getOrCreateFunc( c, *FromValue< builtins::Func >( func ) );
                return !!llvmFunc;
            } );

        RegisterBuiltinFunc< bool( TypeWrapper< ptr< codegen::Module > >,
            CustomPattern< Value, FuncPattern >, string ) >( e, "CGGenerateFunction"_sid,
            [pEnv]( const TypeWrapper< ptr< codegen::Module > >& module, const Value& f,
                const string& name )
            {
                ProfileZoneScopedN( "CGGenerateFunction" );

                if( !IsFuncType( *EIRToValue( f.type() ) ) )
                    return false;

                sema::Context c( pEnv.lock(), RootG0Identity() );

                DiagnosticsContext dc( 0, true );
                VerbosityContext vc( Verbosity::Normal, true );

                auto func = CompileFunc( c, f );
                if( func.isPoison() || DiagnosticsManager::GetInstance().errorsWereEmitted() )
                    return false;

                auto llvmFunc = module->getOrCreateFunc( c, *FromValue< builtins::Func >( func ),
                    name, llvm::Function::ExternalLinkage );
                return !!llvmFunc;
            } );
    }
} // namespace goose::g0api
Changes to bs/g0api/cgapi/linker.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
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "lld/Common/Driver.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/Support/raw_os_ostream.h"

LLD_HAS_DRIVER(coff)
LLD_HAS_DRIVER(elf)
LLD_HAS_DRIVER(mingw)
LLD_HAS_DRIVER(macho)
LLD_HAS_DRIVER(wasm)

using namespace goose;
using namespace goose::eir;
using namespace goose::g0api;

namespace
{
    bool BuildArgArrayFromTuple( const Value& tup, vector< string >& array )
    {
        if( !IsTuple( tup ) )
            return false;

        array.reserve( TupleSize( tup ) );

        bool success = true;
        ForEachInTuple( tup, [&]( auto&& t )

        {
            auto arg = FromValue< string >( t );
            if( !arg )
            {
                success = false;
                return false;
            }

            array.emplace_back( move( *arg ) );
            return true;
        } );

        return success;
    }

    void BuildArgPtrArray( vector< string >& args, vector< const char* >& argPtrs )
    {
        argPtrs.reserve( args.size() + 1 );
        argPtrs.emplace_back( "lld" );
        for( auto&& a : args )
            argPtrs.emplace_back( a.c_str() );
    }
}

namespace goose::g0api
{
    void SetupApiCGLinker( Env& e )
    {
        RegisterBuiltinFunc< int32_t ( Value ) >( e, "CGLinkerLLD"_sid,
            []( const Value& argTup )
            {
                ProfileZoneScopedN( "CGLinkerLLD" );

                vector< string > args;
                if( !BuildArgArrayFromTuple( argTup, args ) )
                    return 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
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "lld/Common/Driver.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/Support/raw_os_ostream.h"

LLD_HAS_DRIVER( coff )
LLD_HAS_DRIVER( elf )
LLD_HAS_DRIVER( mingw )
LLD_HAS_DRIVER( macho )
LLD_HAS_DRIVER( wasm )

using namespace goose;
using namespace goose::eir;
using namespace goose::g0api;

namespace
{
    bool BuildArgArrayFromTuple( const Value& tup, vector< string >& array )
    {
        if( !IsTuple( tup ) )
            return false;

        array.reserve( TupleSize( tup ) );

        bool success = true;
        ForEachInTuple( tup,
            [&]( auto&& t )
            {
                auto arg = FromValue< string >( t );
                if( !arg )
                {
                    success = false;
                    return false;
                }

                array.emplace_back( move( *arg ) );
                return true;
            } );

        return success;
    }

    void BuildArgPtrArray( vector< string >& args, vector< const char* >& argPtrs )
    {
        argPtrs.reserve( args.size() + 1 );
        argPtrs.emplace_back( "lld" );
        for( auto&& a : args )
            argPtrs.emplace_back( a.c_str() );
    }
} // namespace

namespace goose::g0api
{
    void SetupApiCGLinker( Env& e )
    {
        RegisterBuiltinFunc< int32_t( Value ) >( e, "CGLinkerLLD"_sid,
            []( const Value& argTup )
            {
                ProfileZoneScopedN( "CGLinkerLLD" );

                vector< string > args;
                if( !BuildArgArrayFromTuple( argTup, args ) )
                    return 0;
71
72
73
74
75
76
77
78

                if( !r.canRunAgain )
                    lld::exitLld( r.retCode );

                return r.retCode;
            } );
    }
}







|
72
73
74
75
76
77
78
79

                if( !r.canRunAgain )
                    lld::exitLld( r.retCode );

                return r.retCode;
            } );
    }
} // namespace goose::g0api
Changes to bs/g0api/cgapi/mangle.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
#include "g0api/g0api.h"
#include "eir/eir.h"

using namespace goose;

namespace goose::g0api
{
    void SetupApiCGMangle( Env& e )
    {
        RegisterBuiltinFunc< string ( Value ) >( e, "CGMangle"_sid,
            []( const Value& v ) -> string
            {
                auto func = FromValue< builtins::Func >( v );
                if( !func )
                    return "";

                if( !func->cir() )
                    return "";

                auto result = codegen::Mangle( func->cir()->identity() );
                if( !result )
                    return "";

                return *result;
            } );
    }
}









|
















|
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
#include "g0api/g0api.h"
#include "eir/eir.h"

using namespace goose;

namespace goose::g0api
{
    void SetupApiCGMangle( Env& e )
    {
        RegisterBuiltinFunc< string( Value ) >( e, "CGMangle"_sid,
            []( const Value& v ) -> string
            {
                auto func = FromValue< builtins::Func >( v );
                if( !func )
                    return "";

                if( !func->cir() )
                    return "";

                auto result = codegen::Mangle( func->cir()->identity() );
                if( !result )
                    return "";

                return *result;
            } );
    }
} // namespace goose::g0api
Changes to bs/g0api/cgapi/module.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
#include "g0api/g0api.h"
#include "eir/eir.h"

namespace goose::g0api
{
    void SetupApiCGModule( Env& e )
    {
        RegisterBuiltinFunc< TypeWrapper< ptr< codegen::Module > > ( string ) >( e, "CGModuleCreate"_sid,
            []( const string& name ) -> TypeWrapper< ptr< codegen::Module > >
            {
                return make_shared< codegen::Module >( name );
            } );

        // TODO add parameters to choose the cpu type, the target for cross compilation etc
        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< codegen::Module > > ) >( e, "CGModuleSetupTarget"_sid,
            []( const TypeWrapper< ptr< codegen::Module > >& module )
            {
                return module->setupTarget();
            } );

        // TODO add parameters to control the optimization level etc
        RegisterBuiltinFunc< void ( TypeWrapper< ptr< codegen::Module > > ) >( e, "CGModuleOptimize"_sid,

            []( const TypeWrapper< ptr< codegen::Module > >& module )
            {
                ProfileZoneScopedN( "CGModuleOptimize" );
                module->runOptimizationPasses();
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< codegen::Module > >, string ) >( e, "CGModuleEmitLLVMIr"_sid,

            []( const TypeWrapper< ptr< codegen::Module > >& module, const string& filename )
            {
                ProfileZoneScopedN( "CGModuleEmitLLVMIr" );
                module->dumpAsLlvmIr( filename );
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< codegen::Module > >, string ) >( e, "CGModuleEmitAsm"_sid,

            []( const TypeWrapper< ptr< codegen::Module > >& module, const string& filename )
            {
                ProfileZoneScopedN( "CGModuleEmitAsm" );
                return module->emitToFile( filename, llvm::CGFT_AssemblyFile );
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< codegen::Module > >, string ) >( e, "CGModuleEmitObj"_sid,

            []( const TypeWrapper< ptr< codegen::Module > >& module, const string& filename )
            {
                ProfileZoneScopedN( "CGModuleEmitObj" );
                return module->emitToFile( filename, llvm::CGFT_ObjectFile );
            } );
    }
}







|
|
<
|
<


|
|
<
|
<


|
>






|
>






|
>






|
>






|
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
#include "g0api/g0api.h"
#include "eir/eir.h"

namespace goose::g0api
{
    void SetupApiCGModule( Env& e )
    {
        RegisterBuiltinFunc< TypeWrapper< ptr< codegen::Module > >( string ) >( e,
            "CGModuleCreate"_sid, []( const string& name ) -> TypeWrapper< ptr< codegen::Module > >

            { return make_shared< codegen::Module >( name ); } );


        // TODO add parameters to choose the cpu type, the target for cross compilation etc
        RegisterBuiltinFunc< bool( TypeWrapper< ptr< codegen::Module > > ) >( e,
            "CGModuleSetupTarget"_sid, []( const TypeWrapper< ptr< codegen::Module > >& module )

            { return module->setupTarget(); } );


        // TODO add parameters to control the optimization level etc
        RegisterBuiltinFunc< void( TypeWrapper< ptr< codegen::Module > > ) >( e,
            "CGModuleOptimize"_sid,
            []( const TypeWrapper< ptr< codegen::Module > >& module )
            {
                ProfileZoneScopedN( "CGModuleOptimize" );
                module->runOptimizationPasses();
            } );

        RegisterBuiltinFunc< void( TypeWrapper< ptr< codegen::Module > >, string ) >( e,
            "CGModuleEmitLLVMIr"_sid,
            []( const TypeWrapper< ptr< codegen::Module > >& module, const string& filename )
            {
                ProfileZoneScopedN( "CGModuleEmitLLVMIr" );
                module->dumpAsLlvmIr( filename );
            } );

        RegisterBuiltinFunc< bool( TypeWrapper< ptr< codegen::Module > >, string ) >( e,
            "CGModuleEmitAsm"_sid,
            []( const TypeWrapper< ptr< codegen::Module > >& module, const string& filename )
            {
                ProfileZoneScopedN( "CGModuleEmitAsm" );
                return module->emitToFile( filename, llvm::CGFT_AssemblyFile );
            } );

        RegisterBuiltinFunc< bool( TypeWrapper< ptr< codegen::Module > >, string ) >( e,
            "CGModuleEmitObj"_sid,
            []( const TypeWrapper< ptr< codegen::Module > >& module, const string& filename )
            {
                ProfileZoneScopedN( "CGModuleEmitObj" );
                return module->emitToFile( filename, llvm::CGFT_ObjectFile );
            } );
    }
} // namespace goose::g0api
Changes to bs/g0api/compiler.cpp.
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
    void SetupApiCompiler( Env& e )
    {
        DefineConstant( e, "TargetOSName"_sid, ValueToEIR( ToValue( string( TARGET_OS_NAME ) ) ) );

        wptr< Env > pEnv = e.shared_from_this();

        RegisterBuiltinFunc< void ( bool ) >( e, "DiagnosticsForceColors"_sid,
            []( bool enable )
            {
                DiagnosticsManager::GetInstance().setForceColors( enable );
            } );

        // TODO_SSA_VERIFICATION reenable
        /*RegisterBuiltinFunc< Intrinsic< void ( bool ) > >( e, "#VerificationTraceMode"_sid,
            []( auto&& c, const Value& enable )
            {
                if( !enable.isConstant() )
                {







|
<
<
|
<







11
12
13
14
15
16
17
18


19

20
21
22
23
24
25
26
{
    void SetupApiCompiler( Env& e )
    {
        DefineConstant( e, "TargetOSName"_sid, ValueToEIR( ToValue( string( TARGET_OS_NAME ) ) ) );

        wptr< Env > pEnv = e.shared_from_this();

        RegisterBuiltinFunc< void( bool ) >( e, "DiagnosticsForceColors"_sid,


            []( bool enable ) { DiagnosticsManager::GetInstance().setForceColors( enable ); } );


        // TODO_SSA_VERIFICATION reenable
        /*RegisterBuiltinFunc< Intrinsic< void ( bool ) > >( e, "#VerificationTraceMode"_sid,
            []( auto&& c, const Value& enable )
            {
                if( !enable.isConstant() )
                {
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296

                        "this doesn't evaluate to a constant." );
                    return;
                }

                verify::Func::SetEnableVerification( *FromValue< bool >( enable ) );
            } );

        RegisterBuiltinFunc< Intrinsic< void ( bool ) > >( e, "#VerificationDumpSolverOnFailure"_sid,

            []( auto&& c, const Value& enable )
            {
                if( !enable.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( enable.locationId(),
                        "this doesn't evaluate to a constant." );
                    return;
                }

                verify::Func::SetDumpSolverOnFailure( *FromValue< bool >( enable ) );
            } );

        RegisterBuiltinFunc< Intrinsic< void ( bool ) > >( e, "#VerificationDumpSolverOnSuccess"_sid,

            []( auto&& c, const Value& enable )
            {
                if( !enable.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( enable.locationId(),
                        "this doesn't evaluate to a constant." );
                    return;
                }

                verify::Func::SetDumpSolverOnSuccess( *FromValue< bool >( enable ) );
            } );*/

        RegisterBuiltinFunc< Intrinsic< void ( CustomPattern< Value, FuncPattern >, string ) > >( e, "#DumpFunctionCFG"_sid,

            []( auto&& c, const Value& funcVal, const Value& filename )
            {
                if( !funcVal.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( funcVal.locationId(),
                        "this doesn't evaluate to a constant." );
                    return;
                }

                if( !filename.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( filename.locationId(),
                        "this doesn't evaluate to a constant." );
                    return;
                }

                auto name = *FromValue< string >( filename );
                auto func = CompileFunc( c, funcVal );
                if( func.isPoison() || DiagnosticsManager::GetInstance().errorsWereEmitted() )
                    return;

                auto f = *FromValue< builtins::Func >( func );
                if( !f.cir() )
                    return;

                ofstream out( name.c_str() );
                out << *f.cir();
            } );

        RegisterBuiltinFunc< Eager< void > ( CustomPattern< Value, ValuePatternT > ) >( e, "#DumpValue"_sid,
            []( const auto& v )
            {
                G_TRACE_VAL( v );
            } );

        RegisterBuiltinFunc< Intrinsic< void ( uint32_t ) > >( e, "#SetExecutionBudget"_sid,
            []( auto&& c, const Value& budget )
            {
                static bool used = false;

                if( !budget.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( budget.locationId(),
                        "this doesn't evaluate to a constant." );
                    return;
                }

                if( used )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( 0,
                        "#SetExecutionBudget can only be used once." );
                    return;
                }

                used = true;
                execute::VM::SetExecutionBudget( *FromValue< uint32_t >( budget ) );
            } );

        RegisterBuiltinFunc< Eager< Value > ( Value, string ) >( e, "ExternalFunction"_sid,
            []( const Value& f, const string& symbol )
            {
                auto ft = FromValue< FuncType >( f );
                if( !ft )
                    return PoisonValue();

                return ToValue( BuildExternalFunc( *ft, symbol ) );
            } );

        RegisterBuiltinFunc< Eager< Value > ( Value, string ) >( e, "ExternalVarArgFunction"_sid,
            []( const Value& f, const string& symbol )
            {
                auto ft = FromValue< FuncType >( f );
                if( !ft )
                    return PoisonValue();

                return ToValue( BuildExternalFunc( *ft, symbol, true ) );
            } );

        // TODO: Clean: this is no longer needed now that we have an api to access the Env
        RegisterBuiltinFunc< void ( string, Value ) >( e, "CreateConstant"_sid,
            [pEnv]( const string& name, const Value& v )
            {
                if( !v.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( v.locationId(),
                        "this doesn't evaluate to a constant." );
                    return;
                }

                pEnv.lock()->storeValue(
                    AppendToVectorTerm( RootG0Identity(), StringId( name.c_str() ) ),
                    ANYTERM( _ ), ValueToEIR( v ) );
            } );

        RegisterBuiltinFunc< Intrinsic< uint32_t ( string ) > >( e, "#Include"_sid,
            [pEnv]( auto&& c, const Value& fnameval ) -> Value
            {
                execute::VM vm;
                auto fname = Evaluate( fnameval, vm );
                auto filename = FromValue< string >( fname );
                if( !filename )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( fnameval.locationId(),
                        "can't evaluate this to a string." );
                    return PoisonValue();
                }

                auto identity = TakeVectorTerm( c.identity(), VecSize( c.identity() ) - 1 );
                get< pvec >( identity )->terms().back() = TSID( 0_locvars );

                auto result = Compiler::LoadAndExecuteFile( pEnv.lock(), *filename, identity,
                    GetValueType< uint32_t >(), ToValue< uint32_t >( 0 ) );

                if( !result )
                    return ToValue< uint32_t >( 0 );

                return *result;
            } );


        RegisterBuiltinFunc< Eager< void > ( ptr< OverloadSet >, bool ) >( e, "#SetVerboseResolution"_sid,
            [pEnv]( const ptr< OverloadSet >& pOvl, bool b )
            {
                pOvl->setVerboseResolution( b );
            } );

        RegisterBuiltinFunc< uint32_t ( string ) >( e, "ExecuteFile"_sid,
            [pEnv]( const string& filename ) -> uint32_t
            {
                auto identity = AppendToVectorTerm( RootG0Identity(),
                    TERM( StringId( Env::NewUniqueId() ) ) );
                auto locVarsIdentity = AppendToVectorTerm( identity, TSID( 0_locvars ) );

                auto env = pEnv.lock();
                env->addVisibilityRule( builtins::RootG0Identity(), identity );
                env->addVisibilityRule( identity, locVarsIdentity );

                auto result = Compiler::LoadAndExecuteFile( env, filename, locVarsIdentity,
                    GetValueType< uint32_t >(), ToValue< uint32_t >( 0 ) );

                if( !result || result->isPoison() )
                    return 0;

                return *FromValue< uint32_t >( *result );
            } );


        RegisterBuiltinFunc< Intrinsic< Value ( string, TypeWrapper< Term >, Value, Value, Value ) > >( e, "#CompileFileToFunction"_sid,

            [pEnv]( auto&& c, const Value& filenameVal, const Value& identityVal, const Value& rt, const Value& defReturnValue, const Value& params ) -> Value

            {
                ProfileZoneScopedN( "#CompileFileToFunction" );

                if( !filenameVal.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( filenameVal.locationId(),
                        "this doesn't evaluate to a constant." );
                    return PoisonValue();
                }

                auto filename = *FromValue< string >( filenameVal );
                ProfileZoneName( filename.c_str(), filename.size() );

                if( !identityVal.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( identityVal.locationId(),
                        "this doesn't evaluate to a constant." );
                    return PoisonValue();
                }

                auto identity = *FromValue< TypeWrapper< Term > >( identityVal );

                // Validate those generic value inputs
                if( !IsType( c, rt ) )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( rt.locationId(),
                        "type expected.", 0 );
                    return PoisonValue();
                }

                if( CheckParamListKind( params ) != ParamListKind::Regular )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( params.locationId(),
                        "template parameter lists are not supported here.", 0 );
                    return PoisonValue();
                }

                sema::Context localC( pEnv.lock(), identity, ValueToEIR( rt ) );
                auto ftype = BuildFuncType( localC, rt, params );
                if( !ftype )
                    return PoisonValue();

                auto funcIdentity = AppendToVectorTerm( identity,
                    TERM( StringId( Env::NewUniqueId() ) ) );
                c.env()->addVisibilityRule( identity, funcIdentity );

                auto locVarsIdentity = AppendToVectorTerm( funcIdentity, TSID( 0_locvars ) );
                c.env()->addVisibilityRule( funcIdentity, locVarsIdentity );

                auto func = BuildFunc( localC, *ftype, identity, funcIdentity, params, nullptr, c );
                const auto& pFuncCIR = func.cir();

                auto cfg = Compiler::LoadAndParseFile( pEnv.lock(), filename, locVarsIdentity,
                    ftype->returnType(), defReturnValue );

                if( !cfg )
                    return PoisonValue();

                if( cfg->isPoisoned() )
                    return PoisonValue();

                pFuncCIR->body() = cfg;
                return ToValue( func );
            } );

        RegisterBuiltinFunc< void( bool ) >( e, "Exit"_sid,
            [pEnv]( bool success )
            {
                exit( success ? EXIT_SUCCESS : EXIT_FAILURE );
            } );
    }
}








|
>












|
>












|
>




|
|





|
|
















|
|
|
<
<
<
|






|
|





|
|







|









|










|




|
|




|
|


|







|
|















>
|
|
|
<
<
<
|


|
|















>
|
>
|
>





|
|








|
|








|
|















|
|








|
|












<
<
|
<

<
>
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
282
283
284
285
286
287
288


289

290

291
                        "this doesn't evaluate to a constant." );
                    return;
                }

                verify::Func::SetEnableVerification( *FromValue< bool >( enable ) );
            } );

        RegisterBuiltinFunc< Intrinsic< void ( bool ) > >( e,
        "#VerificationDumpSolverOnFailure"_sid,
            []( auto&& c, const Value& enable )
            {
                if( !enable.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( enable.locationId(),
                        "this doesn't evaluate to a constant." );
                    return;
                }

                verify::Func::SetDumpSolverOnFailure( *FromValue< bool >( enable ) );
            } );

        RegisterBuiltinFunc< Intrinsic< void ( bool ) > >( e,
        "#VerificationDumpSolverOnSuccess"_sid,
            []( auto&& c, const Value& enable )
            {
                if( !enable.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( enable.locationId(),
                        "this doesn't evaluate to a constant." );
                    return;
                }

                verify::Func::SetDumpSolverOnSuccess( *FromValue< bool >( enable ) );
            } );*/

        RegisterBuiltinFunc< Intrinsic< void( CustomPattern< Value, FuncPattern >, string ) > >( e,
            "#DumpFunctionCFG"_sid,
            []( auto&& c, const Value& funcVal, const Value& filename )
            {
                if( !funcVal.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        funcVal.locationId(), "this doesn't evaluate to a constant." );
                    return;
                }

                if( !filename.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        filename.locationId(), "this doesn't evaluate to a constant." );
                    return;
                }

                auto name = *FromValue< string >( filename );
                auto func = CompileFunc( c, funcVal );
                if( func.isPoison() || DiagnosticsManager::GetInstance().errorsWereEmitted() )
                    return;

                auto f = *FromValue< builtins::Func >( func );
                if( !f.cir() )
                    return;

                ofstream out( name.c_str() );
                out << *f.cir();
            } );

        RegisterBuiltinFunc< Eager< void >( CustomPattern< Value, ValuePatternT > ) >(
            e, "#DumpValue"_sid, []( const auto& v ) { G_TRACE_VAL( v ); } );




        RegisterBuiltinFunc< Intrinsic< void( uint32_t ) > >( e, "#SetExecutionBudget"_sid,
            []( auto&& c, const Value& budget )
            {
                static bool used = false;

                if( !budget.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        budget.locationId(), "this doesn't evaluate to a constant." );
                    return;
                }

                if( used )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        0, "#SetExecutionBudget can only be used once." );
                    return;
                }

                used = true;
                execute::VM::SetExecutionBudget( *FromValue< uint32_t >( budget ) );
            } );

        RegisterBuiltinFunc< Eager< Value >( Value, string ) >( e, "ExternalFunction"_sid,
            []( const Value& f, const string& symbol )
            {
                auto ft = FromValue< FuncType >( f );
                if( !ft )
                    return PoisonValue();

                return ToValue( BuildExternalFunc( *ft, symbol ) );
            } );

        RegisterBuiltinFunc< Eager< Value >( Value, string ) >( e, "ExternalVarArgFunction"_sid,
            []( const Value& f, const string& symbol )
            {
                auto ft = FromValue< FuncType >( f );
                if( !ft )
                    return PoisonValue();

                return ToValue( BuildExternalFunc( *ft, symbol, true ) );
            } );

        // TODO: Clean: this is no longer needed now that we have an api to access the Env
        RegisterBuiltinFunc< void( string, Value ) >( e, "CreateConstant"_sid,
            [pEnv]( const string& name, const Value& v )
            {
                if( !v.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        v.locationId(), "this doesn't evaluate to a constant." );
                    return;
                }

                pEnv.lock()->storeValue(
                    AppendToVectorTerm( RootG0Identity(), StringId( name.c_str() ) ), ANYTERM( _ ),
                    ValueToEIR( v ) );
            } );

        RegisterBuiltinFunc< Intrinsic< uint32_t( string ) > >( e, "#Include"_sid,
            [pEnv]( auto&& c, const Value& fnameval ) -> Value
            {
                execute::VM vm;
                auto fname = Evaluate( fnameval, vm );
                auto filename = FromValue< string >( fname );
                if( !filename )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        fnameval.locationId(), "can't evaluate this to a string." );
                    return PoisonValue();
                }

                auto identity = TakeVectorTerm( c.identity(), VecSize( c.identity() ) - 1 );
                get< pvec >( identity )->terms().back() = TSID( 0_locvars );

                auto result = Compiler::LoadAndExecuteFile( pEnv.lock(), *filename, identity,
                    GetValueType< uint32_t >(), ToValue< uint32_t >( 0 ) );

                if( !result )
                    return ToValue< uint32_t >( 0 );

                return *result;
            } );

        RegisterBuiltinFunc< Eager< void >( ptr< OverloadSet >, bool ) >( e,
            "#SetVerboseResolution"_sid,
            [pEnv]( const ptr< OverloadSet >& pOvl, bool b ) { pOvl->setVerboseResolution( b ); } );




        RegisterBuiltinFunc< uint32_t( string ) >( e, "ExecuteFile"_sid,
            [pEnv]( const string& filename ) -> uint32_t
            {
                auto identity =
                    AppendToVectorTerm( RootG0Identity(), TERM( StringId( Env::NewUniqueId() ) ) );
                auto locVarsIdentity = AppendToVectorTerm( identity, TSID( 0_locvars ) );

                auto env = pEnv.lock();
                env->addVisibilityRule( builtins::RootG0Identity(), identity );
                env->addVisibilityRule( identity, locVarsIdentity );

                auto result = Compiler::LoadAndExecuteFile( env, filename, locVarsIdentity,
                    GetValueType< uint32_t >(), ToValue< uint32_t >( 0 ) );

                if( !result || result->isPoison() )
                    return 0;

                return *FromValue< uint32_t >( *result );
            } );

        RegisterBuiltinFunc<
            Intrinsic< Value( string, TypeWrapper< Term >, Value, Value, Value ) > >( e,
            "#CompileFileToFunction"_sid,
            [pEnv]( auto&& c, const Value& filenameVal, const Value& identityVal, const Value& rt,
                const Value& defReturnValue, const Value& params ) -> Value
            {
                ProfileZoneScopedN( "#CompileFileToFunction" );

                if( !filenameVal.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        filenameVal.locationId(), "this doesn't evaluate to a constant." );
                    return PoisonValue();
                }

                auto filename = *FromValue< string >( filenameVal );
                ProfileZoneName( filename.c_str(), filename.size() );

                if( !identityVal.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        identityVal.locationId(), "this doesn't evaluate to a constant." );
                    return PoisonValue();
                }

                auto identity = *FromValue< TypeWrapper< Term > >( identityVal );

                // Validate those generic value inputs
                if( !IsType( c, rt ) )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        rt.locationId(), "type expected.", 0 );
                    return PoisonValue();
                }

                if( CheckParamListKind( params ) != ParamListKind::Regular )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( params.locationId(),
                        "template parameter lists are not supported here.", 0 );
                    return PoisonValue();
                }

                sema::Context localC( pEnv.lock(), identity, ValueToEIR( rt ) );
                auto ftype = BuildFuncType( localC, rt, params );
                if( !ftype )
                    return PoisonValue();

                auto funcIdentity =
                    AppendToVectorTerm( identity, TERM( StringId( Env::NewUniqueId() ) ) );
                c.env()->addVisibilityRule( identity, funcIdentity );

                auto locVarsIdentity = AppendToVectorTerm( funcIdentity, TSID( 0_locvars ) );
                c.env()->addVisibilityRule( funcIdentity, locVarsIdentity );

                auto func = BuildFunc( localC, *ftype, identity, funcIdentity, params, nullptr, c );
                const auto& pFuncCIR = func.cir();

                auto cfg = Compiler::LoadAndParseFile(
                    pEnv.lock(), filename, locVarsIdentity, ftype->returnType(), defReturnValue );

                if( !cfg )
                    return PoisonValue();

                if( cfg->isPoisoned() )
                    return PoisonValue();

                pFuncCIR->body() = cfg;
                return ToValue( func );
            } );

        RegisterBuiltinFunc< void( bool ) >( e, "Exit"_sid,


            [pEnv]( bool success ) { exit( success ? EXIT_SUCCESS : EXIT_FAILURE ); } );

    }

} // namespace goose::g0api
Changes to bs/g0api/extensibility/cir.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
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace
{
    template< typename C, typename T, typename O, O opCode, typename... Params >
    void RegisterMkOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
    {
        RegisterBuiltinFunc< TypeWrapper< ptr< C > >
            ( ConstantParam< uint8_t, static_cast< uint8_t >( opCode ) >, Params... ) >( e, pOvlSet,
            []( uint8_t, const auto&... params ) -> TypeWrapper< ptr< C > >
            {
                return make_shared< C >( T( WrappedValueAccessor< Params >::Get( params )... ) );
            } );
    }

    template< typename C, typename T, typename O, O opCode, typename... Getters >
    void RegisterUnpackOverload( Env& e, const ptr< OverloadSet >& pOvlSet, Getters... getters )
    {
        using tup_type = tuple< WrapType<
            remove_cvref_t< decltype( ( static_cast< T* >( nullptr )->*getters )() ) > >... >;

        RegisterBuiltinFunc< bool ( ConstantParam< uint8_t, static_cast< uint8_t >( opCode ) >,
            TypeWrapper< ptr< C > >, TermRef< tup_type > ) >( e, pOvlSet,
            [&]( uint8_t, const auto& instr, auto& out )
            {
                const T* pt = get_if< T >( &instr->content() );

                if( !pt )
                    return false;













|
|

<
|
<








|







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 "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace
{
    template< typename C, typename T, typename O, O opCode, typename... Params >
    void RegisterMkOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
    {
        RegisterBuiltinFunc< TypeWrapper< ptr< C > >(
            ConstantParam< uint8_t, static_cast< uint8_t >( opCode ) >, Params... ) >( e, pOvlSet,
            []( uint8_t, const auto&... params ) -> TypeWrapper< ptr< C > >

            { return make_shared< C >( T( WrappedValueAccessor< Params >::Get( params )... ) ); } );

    }

    template< typename C, typename T, typename O, O opCode, typename... Getters >
    void RegisterUnpackOverload( Env& e, const ptr< OverloadSet >& pOvlSet, Getters... getters )
    {
        using tup_type = tuple< WrapType<
            remove_cvref_t< decltype( ( static_cast< T* >( nullptr )->*getters )() ) > >... >;

        RegisterBuiltinFunc< bool( ConstantParam< uint8_t, static_cast< uint8_t >( opCode ) >,
            TypeWrapper< ptr< C > >, TermRef< tup_type > ) >( e, pOvlSet,
            [&]( uint8_t, const auto& instr, auto& out )
            {
                const T* pt = get_if< T >( &instr->content() );

                if( !pt )
                    return false;
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
    template< typename T, InstrOpCode opCode, typename... Params >
    void RegisterMkInstrOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
    {
        RegisterMkOverload< Instruction, T, InstrOpCode, opCode, Params... >( e, pOvlSet );
    }

    template< typename T, InstrOpCode opCode, typename... Getters >

    void RegisterUnpackInstrOverload( Env& e, const ptr< OverloadSet >& pOvlSet, Getters... getters )
    {
        RegisterUnpackOverload< Instruction, T, InstrOpCode, opCode >( e, pOvlSet, getters... );
    }

    template< typename T, InstrOpCode opCode, typename... Getters >
    void RegisterInstrOverloads( Env& e,
        const ptr< OverloadSet >& MkInstr, const ptr< OverloadSet >& UnpackInstr, Getters... getters )
    {
        RegisterMkInstrOverload< T, opCode, WrapType<

            remove_cvref_t< decltype( ( static_cast< T* >( nullptr )->*getters )() ) > >... >( e, MkInstr );

        RegisterUnpackInstrOverload< T, opCode >( e, UnpackInstr );
    }

    template< typename T, InstrOpCode opCode >

    void RegisterBinOpInstrOverloads( Env& e, const ptr< OverloadSet >& MkInstr, const ptr< OverloadSet >& UnpackInstr )
    {
        RegisterInstrOverloads< T, opCode >( e, MkInstr, UnpackInstr, &T::locationId );
    }

    // This enum must match the order of the types in the Terminator variant.
    enum class TermiOpCode
    {







>
|





|
|

|
>
|
>




>
|







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
    template< typename T, InstrOpCode opCode, typename... Params >
    void RegisterMkInstrOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
    {
        RegisterMkOverload< Instruction, T, InstrOpCode, opCode, Params... >( e, pOvlSet );
    }

    template< typename T, InstrOpCode opCode, typename... Getters >
    void RegisterUnpackInstrOverload(
        Env& e, const ptr< OverloadSet >& pOvlSet, Getters... getters )
    {
        RegisterUnpackOverload< Instruction, T, InstrOpCode, opCode >( e, pOvlSet, getters... );
    }

    template< typename T, InstrOpCode opCode, typename... Getters >
    void RegisterInstrOverloads( Env& e, const ptr< OverloadSet >& MkInstr,
        const ptr< OverloadSet >& UnpackInstr, Getters... getters )
    {
        RegisterMkInstrOverload< T, opCode,
            WrapType<
                remove_cvref_t< decltype( ( static_cast< T* >( nullptr )->*getters )() ) > >... >(
            e, MkInstr );
        RegisterUnpackInstrOverload< T, opCode >( e, UnpackInstr );
    }

    template< typename T, InstrOpCode opCode >
    void RegisterBinOpInstrOverloads(
        Env& e, const ptr< OverloadSet >& MkInstr, const ptr< OverloadSet >& UnpackInstr )
    {
        RegisterInstrOverloads< T, opCode >( e, MkInstr, UnpackInstr, &T::locationId );
    }

    // This enum must match the order of the types in the Terminator variant.
    enum class TermiOpCode
    {
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
    template< typename T, TermiOpCode opCode, typename... Params >
    void RegisterMkTermiOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
    {
        RegisterMkOverload< Terminator, T, TermiOpCode, opCode, Params... >( e, pOvlSet );
    }

    template< typename T, TermiOpCode opCode, typename... Getters >

    void RegisterUnpackTermiOverload( Env& e, const ptr< OverloadSet >& pOvlSet, Getters... getters )
    {
        RegisterUnpackOverload< Terminator, T, TermiOpCode, opCode >( e, pOvlSet, getters... );
    }

    template< typename T, TermiOpCode opCode, typename... Getters >
    void RegisterTermiOverloads( Env& e,
        const ptr< OverloadSet >& MkTermi, const ptr< OverloadSet >& UnpackTermi, Getters... getters )
    {
        RegisterMkTermiOverload< T, opCode, WrapType<

            remove_cvref_t< decltype( ( static_cast< T* >( nullptr )->*getters )() ) > >... >( e, MkTermi );

        RegisterUnpackTermiOverload< T, opCode >( e, UnpackTermi );
    }
}

namespace goose::g0api
{
    void SetupCIRExtensibilityFuncs( Env& e )
    {
        ////////////////////////////
        // Instruction
        ////////////////////////////
        DefineConstant( e, "InstrOpCodeNone"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::None ) ) ) );

        DefineConstant( e, "InstrOpCodeCall"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Call ) ) ) );

        DefineConstant( e, "InstrOpCodeCallCheck"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::CallCheck ) ) ) );

        DefineConstant( e, "InstrOpCodeVarAddr"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::VarAddr ) ) ) );

        DefineConstant( e, "InstrOpCodeTempAddr"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::TempAddr ) ) ) );

        DefineConstant( e, "InstrOpCodeSelect"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Select ) ) ) );

        DefineConstant( e, "InstrOpCodePushConstant"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Constant ) ) ) );

        DefineConstant( e, "InstrOpCodeCreateTemporary"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::CreateTemporary ) ) ) );

        DefineConstant( e, "InstrOpCodeGetTemporary"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::GetTemporary ) ) ) );

        DefineConstant( e, "InstrOpCodeAllocVar"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::AllocVar ) ) ) );

        DefineConstant( e, "InstrOpCodeLoad"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Load ) ) ) );

        DefineConstant( e, "InstrOpCodeStore"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Store ) ) ) );

        DefineConstant( e, "InstrOpCodePhi"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Phi ) ) ) );

        DefineConstant( e, "InstrOpCodeNot"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Not ) ) ) );

        DefineConstant( e, "InstrOpCodeAnd"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::And ) ) ) );

        DefineConstant( e, "InstrOpCodeOr"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Or ) ) ) );

        DefineConstant( e, "InstrOpCodeXor"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Xor ) ) ) );

        DefineConstant( e, "InstrOpCodeShl"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Shl ) ) ) );

        DefineConstant( e, "InstrOpCodeLShr"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::LShr ) ) ) );

        DefineConstant( e, "InstrOpCodeAShr"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::AShr ) ) ) );

        DefineConstant( e, "InstrOpCodeAdd"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Add ) ) ) );

        DefineConstant( e, "InstrOpCodeSub"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Sub ) ) ) );

        DefineConstant( e, "InstrOpCodeMul"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Mul ) ) ) );


        DefineConstant( e, "InstrOpCodeUDiv"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::UDiv ) ) ) );
        DefineConstant( e, "InstrOpCodeSDiv"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SDiv ) ) ) );

        DefineConstant( e, "InstrOpCodeURem"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::URem ) ) ) );

        DefineConstant( e, "InstrOpCodeSRem"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SRem ) ) ) );

        DefineConstant( e, "InstrOpCodeEq"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Eq ) ) ) );

        DefineConstant( e, "InstrOpCodeNeq"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Neq ) ) ) );

        DefineConstant( e, "InstrOpCodeUGT"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::UGT ) ) ) );

        DefineConstant( e, "InstrOpCodeUGE"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::UGE ) ) ) );

        DefineConstant( e, "InstrOpCodeULT"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::ULT ) ) ) );

        DefineConstant( e, "InstrOpCodeULE"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::ULE ) ) ) );

        DefineConstant( e, "InstrOpCodeSGT"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SGT ) ) ) );

        DefineConstant( e, "InstrOpCodeSGE"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SGE ) ) ) );


        DefineConstant( e, "InstrOpCodeSLT"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SLT ) ) ) );
        DefineConstant( e, "InstrOpCodeSLE"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SLE ) ) ) );

        DefineConstant( e, "InstrOpCodeAssert"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Assert ) ) ) );

        DefineConstant( e, "InstrOpCodeGhostCall"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::GhostCall ) ) ) );

        DefineConstant( e, "InstrOpCodePHOverrideSet"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::PHOverrideSet ) ) ) );

        DefineConstant( e, "InstrOpCodePHOverrideClear"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::PHOverrideClear ) ) ) );

        DefineConstant( e, "InstrOpCodePlaceholder"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Placeholder ) ) ) );

        DefineConstant( e, "InstrOpCodeForAllSetup"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::ForAllSetup ) ) ) );

        DefineConstant( e, "InstrOpCodeForAll"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::ForAll ) ) ) );

        DefineConstant( e, "InstrOpCodeImplies"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Implies ) ) ) );

        DefineConstant( e, "InstrOpCodeIsPrefixOf"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::IsPrefixOf ) ) ) );


        RegisterBuiltinFunc< uint8_t ( TypeWrapper< ptr< Instruction > > ) >( e, "GetInstrOpCode"_sid,
            []( const auto& t )
            {
                return t->content().index();
            } );

        // MkInstr and UnpackInstr overloads
        auto MkInstr = CreateOverloadSet( e, "MkInstr"_sid );
        auto UnpackInstr = CreateOverloadSet( e, "UnpackInstr"_sid );

        RegisterInstrOverloads< Call, InstrOpCode::Call >( e, MkInstr, UnpackInstr, &Call::numArgs, &Call::locationId );

        RegisterInstrOverloads< CallCheck, InstrOpCode::CallCheck >( e, MkInstr, UnpackInstr, &CallCheck::numArgs, &CallCheck::locationId );

        RegisterInstrOverloads< VarAddr, InstrOpCode::VarAddr >( e, MkInstr, UnpackInstr, &VarAddr::varIndex, &VarAddr::locationId );

        RegisterInstrOverloads< TempAddr, InstrOpCode::TempAddr >( e, MkInstr, UnpackInstr, &TempAddr::tempIndex, &TempAddr::locationId );

        RegisterInstrOverloads< Select, InstrOpCode::Select >( e, MkInstr, UnpackInstr, &Select::memberIndex, &Select::locationId );

        RegisterInstrOverloads< CreateTemporary, InstrOpCode::CreateTemporary >( e, MkInstr, UnpackInstr, &CreateTemporary::index, &CreateTemporary::verif, &CreateTemporary::locationId );


        RegisterInstrOverloads< GetTemporary, InstrOpCode::GetTemporary >( e, MkInstr, UnpackInstr, &GetTemporary::type, &GetTemporary::index, &GetTemporary::locationId );

        RegisterInstrOverloads< AllocVar, InstrOpCode::AllocVar >( e, MkInstr, UnpackInstr, &AllocVar::type, &AllocVar::index, &AllocVar::locationId );

        RegisterInstrOverloads< Load, InstrOpCode::Load >( e, MkInstr, UnpackInstr, &Load::type, &Load::locationId );

        RegisterInstrOverloads< Store, InstrOpCode::Store >( e, MkInstr, UnpackInstr, &Store::type, &Store::srcLocId, &Store::destLocId );

        RegisterInstrOverloads< Phi, InstrOpCode::Phi >( e, MkInstr, UnpackInstr, &Phi::type, &Phi::numIncomings, &Phi::destIndex, &Phi::locationId );

        RegisterInstrOverloads< Not, InstrOpCode::Not >( e, MkInstr, UnpackInstr, &Not::locationId );


        RegisterBinOpInstrOverloads< And, InstrOpCode::And >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< Or, InstrOpCode::Or >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< Xor, InstrOpCode::Xor >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< Shl, InstrOpCode::Shl >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< LShr, InstrOpCode::LShr >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< AShr, InstrOpCode::AShr >( e, MkInstr, UnpackInstr );







>
|





|
|

|
>
|
>


|








|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
>
|
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
>
|
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>

|
<
<
|
<





|
>
|
>
|
>
|
>
|
>
|
>
>
|
>
|
>
|
>
|
>
|
>
|
>







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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
    template< typename T, TermiOpCode opCode, typename... Params >
    void RegisterMkTermiOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
    {
        RegisterMkOverload< Terminator, T, TermiOpCode, opCode, Params... >( e, pOvlSet );
    }

    template< typename T, TermiOpCode opCode, typename... Getters >
    void RegisterUnpackTermiOverload(
        Env& e, const ptr< OverloadSet >& pOvlSet, Getters... getters )
    {
        RegisterUnpackOverload< Terminator, T, TermiOpCode, opCode >( e, pOvlSet, getters... );
    }

    template< typename T, TermiOpCode opCode, typename... Getters >
    void RegisterTermiOverloads( Env& e, const ptr< OverloadSet >& MkTermi,
        const ptr< OverloadSet >& UnpackTermi, Getters... getters )
    {
        RegisterMkTermiOverload< T, opCode,
            WrapType<
                remove_cvref_t< decltype( ( static_cast< T* >( nullptr )->*getters )() ) > >... >(
            e, MkTermi );
        RegisterUnpackTermiOverload< T, opCode >( e, UnpackTermi );
    }
} // namespace

namespace goose::g0api
{
    void SetupCIRExtensibilityFuncs( Env& e )
    {
        ////////////////////////////
        // Instruction
        ////////////////////////////
        DefineConstant( e, "InstrOpCodeNone"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::None ) ) ) );
        DefineConstant( e, "InstrOpCodeCall"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Call ) ) ) );
        DefineConstant( e, "InstrOpCodeCallCheck"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::CallCheck ) ) ) );
        DefineConstant( e, "InstrOpCodeVarAddr"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::VarAddr ) ) ) );
        DefineConstant( e, "InstrOpCodeTempAddr"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::TempAddr ) ) ) );
        DefineConstant( e, "InstrOpCodeSelect"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Select ) ) ) );
        DefineConstant( e, "InstrOpCodePushConstant"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Constant ) ) ) );
        DefineConstant( e, "InstrOpCodeCreateTemporary"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::CreateTemporary ) ) ) );
        DefineConstant( e, "InstrOpCodeGetTemporary"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::GetTemporary ) ) ) );
        DefineConstant( e, "InstrOpCodeAllocVar"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::AllocVar ) ) ) );
        DefineConstant( e, "InstrOpCodeLoad"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Load ) ) ) );
        DefineConstant( e, "InstrOpCodeStore"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Store ) ) ) );
        DefineConstant( e, "InstrOpCodePhi"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Phi ) ) ) );
        DefineConstant( e, "InstrOpCodeNot"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Not ) ) ) );
        DefineConstant( e, "InstrOpCodeAnd"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::And ) ) ) );
        DefineConstant( e, "InstrOpCodeOr"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Or ) ) ) );
        DefineConstant( e, "InstrOpCodeXor"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Xor ) ) ) );
        DefineConstant( e, "InstrOpCodeShl"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Shl ) ) ) );
        DefineConstant( e, "InstrOpCodeLShr"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::LShr ) ) ) );
        DefineConstant( e, "InstrOpCodeAShr"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::AShr ) ) ) );
        DefineConstant( e, "InstrOpCodeAdd"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Add ) ) ) );
        DefineConstant( e, "InstrOpCodeSub"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Sub ) ) ) );
        DefineConstant( e, "InstrOpCodeMul"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Mul ) ) ) );
        DefineConstant( e, "InstrOpCodeUDiv"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::UDiv ) ) ) );
        DefineConstant( e, "InstrOpCodeSDiv"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SDiv ) ) ) );
        DefineConstant( e, "InstrOpCodeURem"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::URem ) ) ) );
        DefineConstant( e, "InstrOpCodeSRem"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SRem ) ) ) );
        DefineConstant( e, "InstrOpCodeEq"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Eq ) ) ) );
        DefineConstant( e, "InstrOpCodeNeq"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Neq ) ) ) );
        DefineConstant( e, "InstrOpCodeUGT"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::UGT ) ) ) );
        DefineConstant( e, "InstrOpCodeUGE"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::UGE ) ) ) );
        DefineConstant( e, "InstrOpCodeULT"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::ULT ) ) ) );
        DefineConstant( e, "InstrOpCodeULE"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::ULE ) ) ) );
        DefineConstant( e, "InstrOpCodeSGT"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SGT ) ) ) );
        DefineConstant( e, "InstrOpCodeSGE"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SGE ) ) ) );
        DefineConstant( e, "InstrOpCodeSLT"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SLT ) ) ) );
        DefineConstant( e, "InstrOpCodeSLE"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SLE ) ) ) );
        DefineConstant( e, "InstrOpCodeAssert"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Assert ) ) ) );
        DefineConstant( e, "InstrOpCodeGhostCall"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::GhostCall ) ) ) );
        DefineConstant( e, "InstrOpCodePHOverrideSet"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::PHOverrideSet ) ) ) );
        DefineConstant( e, "InstrOpCodePHOverrideClear"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::PHOverrideClear ) ) ) );
        DefineConstant( e, "InstrOpCodePlaceholder"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Placeholder ) ) ) );
        DefineConstant( e, "InstrOpCodeForAllSetup"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::ForAllSetup ) ) ) );
        DefineConstant( e, "InstrOpCodeForAll"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::ForAll ) ) ) );
        DefineConstant( e, "InstrOpCodeImplies"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Implies ) ) ) );
        DefineConstant( e, "InstrOpCodeIsPrefixOf"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::IsPrefixOf ) ) ) );

        RegisterBuiltinFunc< uint8_t( TypeWrapper< ptr< Instruction > > ) >(


            e, "GetInstrOpCode"_sid, []( const auto& t ) { return t->content().index(); } );


        // MkInstr and UnpackInstr overloads
        auto MkInstr = CreateOverloadSet( e, "MkInstr"_sid );
        auto UnpackInstr = CreateOverloadSet( e, "UnpackInstr"_sid );

        RegisterInstrOverloads< Call, InstrOpCode::Call >(
            e, MkInstr, UnpackInstr, &Call::numArgs, &Call::locationId );
        RegisterInstrOverloads< CallCheck, InstrOpCode::CallCheck >(
            e, MkInstr, UnpackInstr, &CallCheck::numArgs, &CallCheck::locationId );
        RegisterInstrOverloads< VarAddr, InstrOpCode::VarAddr >(
            e, MkInstr, UnpackInstr, &VarAddr::varIndex, &VarAddr::locationId );
        RegisterInstrOverloads< TempAddr, InstrOpCode::TempAddr >(
            e, MkInstr, UnpackInstr, &TempAddr::tempIndex, &TempAddr::locationId );
        RegisterInstrOverloads< Select, InstrOpCode::Select >(
            e, MkInstr, UnpackInstr, &Select::memberIndex, &Select::locationId );
        RegisterInstrOverloads< CreateTemporary, InstrOpCode::CreateTemporary >( e, MkInstr,
            UnpackInstr, &CreateTemporary::index, &CreateTemporary::verif,
            &CreateTemporary::locationId );
        RegisterInstrOverloads< GetTemporary, InstrOpCode::GetTemporary >( e, MkInstr, UnpackInstr,
            &GetTemporary::type, &GetTemporary::index, &GetTemporary::locationId );
        RegisterInstrOverloads< AllocVar, InstrOpCode::AllocVar >(
            e, MkInstr, UnpackInstr, &AllocVar::type, &AllocVar::index, &AllocVar::locationId );
        RegisterInstrOverloads< Load, InstrOpCode::Load >(
            e, MkInstr, UnpackInstr, &Load::type, &Load::locationId );
        RegisterInstrOverloads< Store, InstrOpCode::Store >(
            e, MkInstr, UnpackInstr, &Store::type, &Store::srcLocId, &Store::destLocId );
        RegisterInstrOverloads< Phi, InstrOpCode::Phi >( e, MkInstr, UnpackInstr, &Phi::type,
            &Phi::numIncomings, &Phi::destIndex, &Phi::locationId );
        RegisterInstrOverloads< Not, InstrOpCode::Not >(
            e, MkInstr, UnpackInstr, &Not::locationId );

        RegisterBinOpInstrOverloads< And, InstrOpCode::And >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< Or, InstrOpCode::Or >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< Xor, InstrOpCode::Xor >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< Shl, InstrOpCode::Shl >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< LShr, InstrOpCode::LShr >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< AShr, InstrOpCode::AShr >( e, MkInstr, UnpackInstr );
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

282

283

284

285

286

287
288
289
290
291
292

293

294

295

296

297

298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337

338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373

374
375
376
377
378
379
380

381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502

        RegisterBinOpInstrOverloads< ULT, InstrOpCode::ULT >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< ULE, InstrOpCode::ULE >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< SGT, InstrOpCode::SGT >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< SGE, InstrOpCode::SGE >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< SLT, InstrOpCode::SLT >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< SLE, InstrOpCode::SLE >( e, MkInstr, UnpackInstr );

        RegisterInstrOverloads< Assert, InstrOpCode::Assert >( e, MkInstr, UnpackInstr, &Assert::locationId );

        RegisterInstrOverloads< GhostCall, InstrOpCode::GhostCall >( e, MkInstr, UnpackInstr, &GhostCall::numArgs, &GhostCall::locationId );

        RegisterInstrOverloads< PHOverrideSet, InstrOpCode::PHOverrideSet >( e, MkInstr, UnpackInstr, &PHOverrideSet::name, &PHOverrideSet::locationId );

        RegisterInstrOverloads< PHOverrideClear, InstrOpCode::PHOverrideClear >( e, MkInstr, UnpackInstr, &PHOverrideClear::name, &PHOverrideClear::locationId );

        RegisterInstrOverloads< Placeholder, InstrOpCode::Placeholder >( e, MkInstr, UnpackInstr, &Placeholder::type, &Placeholder::name, &Placeholder::locationId );


        RegisterInstrOverloads< ForAllSetup, InstrOpCode::ForAllSetup >( e, MkInstr, UnpackInstr, &ForAllSetup::numArgs, &ForAllSetup::locationId );

        RegisterInstrOverloads< ForAll, InstrOpCode::ForAll >( e, MkInstr, UnpackInstr, &ForAll::locationId );


        RegisterBinOpInstrOverloads< Implies, InstrOpCode::Implies >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< IsPrefixOf, InstrOpCode::IsPrefixOf >( e, MkInstr, UnpackInstr );


        // TODO functions to manipulate Phi's incomings array

        ////////////////////////////
        // Terminator
        ////////////////////////////
        DefineConstant( e, "TermiOpCodeRetVoid"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::RetVoid ) ) ) );

        DefineConstant( e, "TermiOpCodeRet"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Ret ) ) ) );

        DefineConstant( e, "TermiOpCodeBranch"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Branch ) ) ) );

        DefineConstant( e, "TermiOpCodeCondBranch"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::CondBranch ) ) ) );

        DefineConstant( e, "TermiOpCodeBreak"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Break ) ) ) );

        DefineConstant( e, "TermiOpCodeContinue"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Continue ) ) ) );


        // MkTermi and UnpackTermi overloads
        auto MkTermi = CreateOverloadSet( e, "MkTermi"_sid );
        auto UnpackTermi = CreateOverloadSet( e, "UnpackTermi"_sid );

        RegisterTermiOverloads< RetVoid, TermiOpCode::RetVoid >( e, MkTermi, UnpackTermi, &RetVoid::locationId );

        RegisterTermiOverloads< Ret, TermiOpCode::Ret >( e, MkTermi, UnpackTermi, &Ret::locationId );

        RegisterTermiOverloads< Branch, TermiOpCode::Branch >( e, MkTermi, UnpackTermi, &Branch::dest );

        RegisterTermiOverloads< CondBranch, TermiOpCode::CondBranch >( e, MkTermi, UnpackTermi, &CondBranch::trueDest, &CondBranch::falseDest );

        RegisterTermiOverloads< Break, TermiOpCode::Break >( e, MkTermi, UnpackTermi, &Break::level );

        RegisterTermiOverloads< Continue, TermiOpCode::Continue >( e, MkTermi, UnpackTermi, &Continue::level );


        auto AppendInstr = CreateOverloadSet( e, "AppendInstr"_sid );

        ////////////////////////////
        // InstrSeq
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< ptr< InstrSeq > > () >( e, "MkInstrSeq"_sid,
            []()
            {
                return make_shared< InstrSeq >();
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< InstrSeq > >, TypeWrapper< ptr< Instruction > > ) >
            ( e, AppendInstr,
            []( const auto& pIS, auto& instr )
            {
                auto& is = *pIS.get();
                AppendToInstrSeq( is, *instr.get() );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< InstrSeq > >, TypeWrapper< ptr< InstrSeq > > ) >
            ( e, AppendInstr,
            []( const auto& pIS, auto& isToAppend )
            {
                auto& is = *pIS.get();
                AppendToInstrSeq( is, *isToAppend.get() );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< InstrSeq > >, TypeWrapper< Value > ) >
            ( e, AppendInstr,
            []( const auto& pIS, auto& val )
            {
                auto& is = *pIS.get();
                AppendToInstrSeq( is, val.get() );
            } );

        ////////////////////////////
        // BasicBlock
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< LocationId > ( TypeWrapper< ptr< BasicBlock > > ) >( e, "GetBasicBlockLocation"_sid,

            []( const auto& pBB ) -> TypeWrapper< LocationId >
            {
                return pBB->locationId();
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< LocationId > ) >( e, "SetBasicBlockLocation"_sid,
            []( const auto& pBB, const auto& loc )
            {
                pBB->setLocationId( loc );
            } );

        RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< BasicBlock > > ) >( e, "GetBasicBlockLoopId"_sid,
            []( const auto& pBB )
            {
                return pBB->loopId();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< BasicBlock > > ) >( e, "IsBasicBlockLoopHeader"_sid,
            []( const auto& pBB )
            {
                return pBB->isLoopHeader();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< BasicBlock > >, TermRef< TypeWrapper< ptr< Terminator > > > ) >
            ( e, "GetBasicBlockTerminator"_sid,
            []( const auto& pBB, auto& out )
            {
                const auto& bb = *pBB.get();

                if( !bb.terminator() )
                    return false;

                out = make_shared< Terminator >( *bb.terminator() );
                return true;
            } );


        RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< ptr< Terminator > > ) >( e, "SetBasicBlockTerminator"_sid,
            []( const auto& pBB, const auto& termi )
            {
                pBB->setTerminator( *termi.get() );
            } );

        // TODO InstrSeq, which will be annoying to deal with (we can't just iterate by indexing it so we'll need to figure something out for the api)


        RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< ptr< Instruction > > ) >
            ( e, AppendInstr,
            []( const auto& pBB, auto& instr )
            {
                auto& bb = *pBB.get();
                bb.append( *instr.get() );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< ptr< InstrSeq > > ) >
            ( e, AppendInstr,
            []( const auto& pBB, auto& is )
            {
                auto& bb = *pBB.get();
                bb.append( *is.get() );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< Value > ) >
            ( e, AppendInstr,
            []( const auto& pBB, auto& val )
            {
                auto& bb = *pBB.get();
                bb.append( val.get() );
            } );

        ////////////////////////////
        // CFG
        ////////////////////////////
        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > > ) >( e, "IsCFGPoisoned"_sid,
            []( const auto& pCFG )
            {
                return pCFG->isPoisoned();
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< CFG > > ) >( e, "PoisonCFG"_sid,
            []( const auto& pCFG )
            {
                pCFG->poison();
            } );

        RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< CFG > > ) >( e, "GetCFGBasicBlockCount"_sid,
            []( const auto& pCFG )
            {
                return static_cast< uint32_t >( pCFG->count() );
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > >, uint32_t, TermRef< TypeWrapper< ptr< BasicBlock > > > ) >
            ( e, "GetCFGBasicBlock"_sid,
            []( const auto& pCFG, uint32_t index, auto& out )
            {
                const auto& cfg = *pCFG.get();

                if( cfg.count() <= index )
                    return false;

                out = cfg.getBB( index );
                return true;
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > >, TermRef< TypeWrapper< ptr< BasicBlock > > > ) >
            ( e, "GetCFGEntryBasicBlock"_sid,
            []( const auto& pCFG, auto& out )
            {
                const auto& cfg = *pCFG.get();

                if( cfg.count() == 0 )
                    return false;

                out = cfg.entryBB();
                return true;
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > >, TermRef< TypeWrapper< ptr< BasicBlock > > > ) >
            ( e, "GetCFGLastBasicBlock"_sid,
            []( const auto& pCFG, auto& out )
            {
                const auto& cfg = *pCFG.get();

                if( cfg.count() == 0 )
                    return false;

                out = cfg.lastBB();
                return true;
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > >, TermRef< TypeWrapper< ptr< BasicBlock > > > ) >
            ( e, "GetCFGCurrentBasicBlock"_sid,
            []( const auto& pCFG, auto& out )
            {
                const auto& cfg = *pCFG.get();

                if( !cfg.currentBB() )
                    return false;

                out = cfg.currentBB();
                return true;
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< CFG > >, TypeWrapper< ptr< BasicBlock > > ) >
            ( e, "SetCFGCurrentBasicBlock"_sid,
            []( const auto& pCFG, auto& pBB )
            {
                auto& cfg = *pCFG.get();
                cfg.setCurrentBB( pBB.get() );
            } );

        RegisterBuiltinFunc< TypeWrapper< ptr< BasicBlock > > ( TypeWrapper< ptr< CFG > > ) >
            ( e, "CFGCreateBasicBlock"_sid,
            []( const auto& pCFG ) -> TypeWrapper< ptr< BasicBlock > >
            {
                auto& cfg = *pCFG.get();
                return cfg.createBB();
            } );

        RegisterBuiltinFunc< uint32_t () >
            ( e, "GenerateNewUID"_sid,
            []()
            {
                return util::GenerateNewUID();
            } );
    }
}








|
>
|
>
|
>
|
>
|
>

|
>
|
>


|
>






|
>
|
>
|
>
|
>
|
>
|
>





|
>
|
>
|
>
|
>
|
>
|
>






|
<
<
|
<

|
|






|
|






|
|









|
>
|
|
<
<
|
|
|
|
<
<
<
|
|
|
<
<
<
|
<
<
|
<

|
|











>
|
|
|
<
<
<
|
>

|
|






|
|






|
|









|
<
<
|
<

|
|
|
<
<
|
|
<
<
|
<

|
|











|
|











|
|











|
|











|
|






|
|






|
<
<
<
|
<

<
>
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385


386

387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418


419
420
421
422



423
424
425



426


427

428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445



446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476


477

478
479
480
481


482
483


484

485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554



555

556

557
        RegisterBinOpInstrOverloads< ULT, InstrOpCode::ULT >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< ULE, InstrOpCode::ULE >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< SGT, InstrOpCode::SGT >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< SGE, InstrOpCode::SGE >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< SLT, InstrOpCode::SLT >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< SLE, InstrOpCode::SLE >( e, MkInstr, UnpackInstr );

        RegisterInstrOverloads< Assert, InstrOpCode::Assert >(
            e, MkInstr, UnpackInstr, &Assert::locationId );
        RegisterInstrOverloads< GhostCall, InstrOpCode::GhostCall >(
            e, MkInstr, UnpackInstr, &GhostCall::numArgs, &GhostCall::locationId );
        RegisterInstrOverloads< PHOverrideSet, InstrOpCode::PHOverrideSet >(
            e, MkInstr, UnpackInstr, &PHOverrideSet::name, &PHOverrideSet::locationId );
        RegisterInstrOverloads< PHOverrideClear, InstrOpCode::PHOverrideClear >(
            e, MkInstr, UnpackInstr, &PHOverrideClear::name, &PHOverrideClear::locationId );
        RegisterInstrOverloads< Placeholder, InstrOpCode::Placeholder >( e, MkInstr, UnpackInstr,
            &Placeholder::type, &Placeholder::name, &Placeholder::locationId );

        RegisterInstrOverloads< ForAllSetup, InstrOpCode::ForAllSetup >(
            e, MkInstr, UnpackInstr, &ForAllSetup::numArgs, &ForAllSetup::locationId );
        RegisterInstrOverloads< ForAll, InstrOpCode::ForAll >(
            e, MkInstr, UnpackInstr, &ForAll::locationId );

        RegisterBinOpInstrOverloads< Implies, InstrOpCode::Implies >( e, MkInstr, UnpackInstr );
        RegisterBinOpInstrOverloads< IsPrefixOf, InstrOpCode::IsPrefixOf >(
            e, MkInstr, UnpackInstr );

        // TODO functions to manipulate Phi's incomings array

        ////////////////////////////
        // Terminator
        ////////////////////////////
        DefineConstant( e, "TermiOpCodeRetVoid"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::RetVoid ) ) ) );
        DefineConstant( e, "TermiOpCodeRet"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Ret ) ) ) );
        DefineConstant( e, "TermiOpCodeBranch"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Branch ) ) ) );
        DefineConstant( e, "TermiOpCodeCondBranch"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::CondBranch ) ) ) );
        DefineConstant( e, "TermiOpCodeBreak"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Break ) ) ) );
        DefineConstant( e, "TermiOpCodeContinue"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Continue ) ) ) );

        // MkTermi and UnpackTermi overloads
        auto MkTermi = CreateOverloadSet( e, "MkTermi"_sid );
        auto UnpackTermi = CreateOverloadSet( e, "UnpackTermi"_sid );

        RegisterTermiOverloads< RetVoid, TermiOpCode::RetVoid >(
            e, MkTermi, UnpackTermi, &RetVoid::locationId );
        RegisterTermiOverloads< Ret, TermiOpCode::Ret >(
            e, MkTermi, UnpackTermi, &Ret::locationId );
        RegisterTermiOverloads< Branch, TermiOpCode::Branch >(
            e, MkTermi, UnpackTermi, &Branch::dest );
        RegisterTermiOverloads< CondBranch, TermiOpCode::CondBranch >(
            e, MkTermi, UnpackTermi, &CondBranch::trueDest, &CondBranch::falseDest );
        RegisterTermiOverloads< Break, TermiOpCode::Break >(
            e, MkTermi, UnpackTermi, &Break::level );
        RegisterTermiOverloads< Continue, TermiOpCode::Continue >(
            e, MkTermi, UnpackTermi, &Continue::level );

        auto AppendInstr = CreateOverloadSet( e, "AppendInstr"_sid );

        ////////////////////////////
        // InstrSeq
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< ptr< InstrSeq > >() >(


            e, "MkInstrSeq"_sid, []() { return make_shared< InstrSeq >(); } );


        RegisterBuiltinFunc< void(
            TypeWrapper< ptr< InstrSeq > >, TypeWrapper< ptr< Instruction > > ) >( e, AppendInstr,
            []( const auto& pIS, auto& instr )
            {
                auto& is = *pIS.get();
                AppendToInstrSeq( is, *instr.get() );
            } );

        RegisterBuiltinFunc< void(
            TypeWrapper< ptr< InstrSeq > >, TypeWrapper< ptr< InstrSeq > > ) >( e, AppendInstr,
            []( const auto& pIS, auto& isToAppend )
            {
                auto& is = *pIS.get();
                AppendToInstrSeq( is, *isToAppend.get() );
            } );

        RegisterBuiltinFunc< void( TypeWrapper< ptr< InstrSeq > >, TypeWrapper< Value > ) >( e,
            AppendInstr,
            []( const auto& pIS, auto& val )
            {
                auto& is = *pIS.get();
                AppendToInstrSeq( is, val.get() );
            } );

        ////////////////////////////
        // BasicBlock
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< LocationId >( TypeWrapper< ptr< BasicBlock > > ) >( e,
            "GetBasicBlockLocation"_sid,
            []( const auto& pBB ) -> TypeWrapper< LocationId > { return pBB->locationId(); } );



        RegisterBuiltinFunc< void( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< LocationId > ) >(
            e, "SetBasicBlockLocation"_sid,
            []( const auto& pBB, const auto& loc ) { pBB->setLocationId( loc ); } );




        RegisterBuiltinFunc< uint32_t( TypeWrapper< ptr< BasicBlock > > ) >(
            e, "GetBasicBlockLoopId"_sid, []( const auto& pBB ) { return pBB->loopId(); } );




        RegisterBuiltinFunc< bool( TypeWrapper< ptr< BasicBlock > > ) >( e,


            "IsBasicBlockLoopHeader"_sid, []( const auto& pBB ) { return pBB->isLoopHeader(); } );


        RegisterBuiltinFunc< bool( TypeWrapper< ptr< BasicBlock > >,
            TermRef< TypeWrapper< ptr< Terminator > > > ) >( e, "GetBasicBlockTerminator"_sid,
            []( const auto& pBB, auto& out )
            {
                const auto& bb = *pBB.get();

                if( !bb.terminator() )
                    return false;

                out = make_shared< Terminator >( *bb.terminator() );
                return true;
            } );

        RegisterBuiltinFunc< void( TypeWrapper< ptr< BasicBlock > >,
            TypeWrapper< ptr< Terminator > > ) >( e, "SetBasicBlockTerminator"_sid,
            []( const auto& pBB, const auto& termi ) { pBB->setTerminator( *termi.get() ); } );




        // TODO InstrSeq, which will be annoying to deal with (we can't just iterate by indexing it
        // so we'll need to figure something out for the api)

        RegisterBuiltinFunc< void(
            TypeWrapper< ptr< BasicBlock > >, TypeWrapper< ptr< Instruction > > ) >( e, AppendInstr,
            []( const auto& pBB, auto& instr )
            {
                auto& bb = *pBB.get();
                bb.append( *instr.get() );
            } );

        RegisterBuiltinFunc< void(
            TypeWrapper< ptr< BasicBlock > >, TypeWrapper< ptr< InstrSeq > > ) >( e, AppendInstr,
            []( const auto& pBB, auto& is )
            {
                auto& bb = *pBB.get();
                bb.append( *is.get() );
            } );

        RegisterBuiltinFunc< void( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< Value > ) >( e,
            AppendInstr,
            []( const auto& pBB, auto& val )
            {
                auto& bb = *pBB.get();
                bb.append( val.get() );
            } );

        ////////////////////////////
        // CFG
        ////////////////////////////
        RegisterBuiltinFunc< bool( TypeWrapper< ptr< CFG > > ) >(


            e, "IsCFGPoisoned"_sid, []( const auto& pCFG ) { return pCFG->isPoisoned(); } );


        RegisterBuiltinFunc< void( TypeWrapper< ptr< CFG > > ) >(
            e, "PoisonCFG"_sid, []( const auto& pCFG ) { pCFG->poison(); } );



        RegisterBuiltinFunc< uint32_t( TypeWrapper< ptr< CFG > > ) >( e,
            "GetCFGBasicBlockCount"_sid,


            []( const auto& pCFG ) { return static_cast< uint32_t >( pCFG->count() ); } );


        RegisterBuiltinFunc< bool( TypeWrapper< ptr< CFG > >, uint32_t,
            TermRef< TypeWrapper< ptr< BasicBlock > > > ) >( e, "GetCFGBasicBlock"_sid,
            []( const auto& pCFG, uint32_t index, auto& out )
            {
                const auto& cfg = *pCFG.get();

                if( cfg.count() <= index )
                    return false;

                out = cfg.getBB( index );
                return true;
            } );

        RegisterBuiltinFunc< bool( TypeWrapper< ptr< CFG > >,
            TermRef< TypeWrapper< ptr< BasicBlock > > > ) >( e, "GetCFGEntryBasicBlock"_sid,
            []( const auto& pCFG, auto& out )
            {
                const auto& cfg = *pCFG.get();

                if( cfg.count() == 0 )
                    return false;

                out = cfg.entryBB();
                return true;
            } );

        RegisterBuiltinFunc< bool( TypeWrapper< ptr< CFG > >,
            TermRef< TypeWrapper< ptr< BasicBlock > > > ) >( e, "GetCFGLastBasicBlock"_sid,
            []( const auto& pCFG, auto& out )
            {
                const auto& cfg = *pCFG.get();

                if( cfg.count() == 0 )
                    return false;

                out = cfg.lastBB();
                return true;
            } );

        RegisterBuiltinFunc< bool( TypeWrapper< ptr< CFG > >,
            TermRef< TypeWrapper< ptr< BasicBlock > > > ) >( e, "GetCFGCurrentBasicBlock"_sid,
            []( const auto& pCFG, auto& out )
            {
                const auto& cfg = *pCFG.get();

                if( !cfg.currentBB() )
                    return false;

                out = cfg.currentBB();
                return true;
            } );

        RegisterBuiltinFunc< void( TypeWrapper< ptr< CFG > >, TypeWrapper< ptr< BasicBlock > > ) >(
            e, "SetCFGCurrentBasicBlock"_sid,
            []( const auto& pCFG, auto& pBB )
            {
                auto& cfg = *pCFG.get();
                cfg.setCurrentBB( pBB.get() );
            } );

        RegisterBuiltinFunc< TypeWrapper< ptr< BasicBlock > >( TypeWrapper< ptr< CFG > > ) >( e,
            "CFGCreateBasicBlock"_sid,
            []( const auto& pCFG ) -> TypeWrapper< ptr< BasicBlock > >
            {
                auto& cfg = *pCFG.get();
                return cfg.createBB();
            } );

        RegisterBuiltinFunc< uint32_t() >(



            e, "GenerateNewUID"_sid, []() { return util::GenerateNewUID(); } );

    }

} // namespace goose::g0api
Changes to bs/g0api/extensibility/context.cpp.
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


namespace goose::g0api
{
    void SetupContextExtensibilityFuncs( Env& e )
    {
        wptr< Env > pEnv = e.shared_from_this();

        RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< ptr< Context > > ) >( e, "ContextGetIdentity"_sid,
            []( const auto& c )
            {
                return c->identity();
            } );

        ////////////////////////////
        // Scopes
        ////////////////////////////
        RegisterBuiltinFunc< void ( TypeWrapper< ptr< sema::Context > > ) >( e, "PushLifetimeScope"_sid,
            []( const auto& c )
            {
                BeginLifetimeScope( *c.get() );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< sema::Context > > ) >( e, "PopLifetimeScope"_sid,
            []( const auto& c )
            {
                EndLifetimeScope( *c.get() );
            } );

        RegisterBuiltinFunc< TypeWrapper< ptr< VisibilityScope > > ( TypeWrapper< ptr< sema::Context > > ) >( e, "PushVisibilityScope"_sid,

            []( const auto& c ) -> TypeWrapper< ptr< VisibilityScope > >
            {
                return make_shared< VisibilityScope >( *c.get() );
            } );

        RegisterBuiltinFunc< void( TermRef< TypeWrapper< ptr< VisibilityScope > > > ) >( e, "PopVisibilityScope"_sid,
            []( auto& vs )
            {
                vs = nullptr;
            } );

        RegisterBuiltinFunc< TypeWrapper< ptr< Scope > > ( TypeWrapper< ptr< sema::Context > > ) >( e, "PushScope"_sid,
            []( const auto& c ) -> TypeWrapper< ptr< Scope > >
            {
                return make_shared< Scope >( *c.get() );
            } );

        RegisterBuiltinFunc< void( TermRef< TypeWrapper< ptr< Scope > > > ) >( e, "PopScope"_sid,
            []( auto& vs )
            {
                vs = nullptr;
            } );
    }
}








|
<
<
|
<




|
<
<
|
<

|
<
<
|
<

|
>

<
|
<

|
|
|
<
<
<
|
|
<
|
<

|
|
<
<
<

<
>
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

namespace goose::g0api
{
    void SetupContextExtensibilityFuncs( Env& e )
    {
        wptr< Env > pEnv = e.shared_from_this();

        RegisterBuiltinFunc< TypeWrapper< Term >( TypeWrapper< ptr< Context > > ) >(


            e, "ContextGetIdentity"_sid, []( const auto& c ) { return c->identity(); } );


        ////////////////////////////
        // Scopes
        ////////////////////////////
        RegisterBuiltinFunc< void( TypeWrapper< ptr< sema::Context > > ) >(


            e, "PushLifetimeScope"_sid, []( const auto& c ) { BeginLifetimeScope( *c.get() ); } );


        RegisterBuiltinFunc< void( TypeWrapper< ptr< sema::Context > > ) >(


            e, "PopLifetimeScope"_sid, []( const auto& c ) { EndLifetimeScope( *c.get() ); } );


        RegisterBuiltinFunc< TypeWrapper< ptr< VisibilityScope > >(
            TypeWrapper< ptr< sema::Context > > ) >( e, "PushVisibilityScope"_sid,
            []( const auto& c ) -> TypeWrapper< ptr< VisibilityScope > >

            { return make_shared< VisibilityScope >( *c.get() ); } );


        RegisterBuiltinFunc< void( TermRef< TypeWrapper< ptr< VisibilityScope > > > ) >(
            e, "PopVisibilityScope"_sid, []( auto& vs ) { vs = nullptr; } );




        RegisterBuiltinFunc< TypeWrapper< ptr< Scope > >( TypeWrapper< ptr< sema::Context > > ) >(
            e, "PushScope"_sid, []( const auto& c ) -> TypeWrapper< ptr< Scope > >

            { return make_shared< Scope >( *c.get() ); } );


        RegisterBuiltinFunc< void( TermRef< TypeWrapper< ptr< Scope > > > ) >(
            e, "PopScope"_sid, []( auto& vs ) { vs = nullptr; } );



    }

} // namespace goose::g0api
Changes to bs/g0api/extensibility/diagnostics.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

#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace goose::g0api
{
    void SetupDiagnosticsExtensibilityFuncs( Env& e )
    {
        RegisterBuiltinFunc< Intrinsic< void ( bool ) > >( e, "#DiagnosticsEnableTraces"_sid,
            []( auto&& c, const Value& enable )
            {
                if( !enable.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( enable.locationId(),
                        "this doesn't evaluate to a constant." );
                    return;
                }

                DiagnosticsManager::GetInstance().setTraceMode( *FromValue< bool >( enable ) );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< LocationId >, string ) >
            ( e, "PushDiagnosticsContext"_sid,
            []( const TypeWrapper< LocationId >& locationId, const string& desc )
            {
                DiagnosticsManager::GetInstance().pushDiagnosticsContext(
                    make_shared< ContextInfos >( desc, locationId, false ) );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< LocationId >, string, bool ) >
            ( e, "PushDiagnosticsContext"_sid,
            []( const TypeWrapper< LocationId >& locationId, const string& desc, bool isRootContext )

            {
                DiagnosticsManager::GetInstance().pushDiagnosticsContext(
                    make_shared< ContextInfos >( desc, locationId, isRootContext ) );
            } );

        RegisterBuiltinFunc< void () >( e, "PopDiagnosticsContext"_sid,
            []()
            {
                DiagnosticsManager::GetInstance().popDiagnosticsContext();
            } );

        RegisterBuiltinFunc< void ( string ) >( e, "EmitErrorMessage"_sid,
            []( const string& message )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage( 0, message );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< LocationId >, string ) >( e, "EmitErrorMessage"_sid,
            []( const TypeWrapper< LocationId >& locationId, const string& message )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage( locationId, message );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< LocationId >, string, uint32_t ) >( e, "EmitErrorMessage"_sid,

            []( const TypeWrapper< LocationId >& locationId, const string& message, uint32_t contextLevels )
            {

                DiagnosticsManager::GetInstance().emitErrorMessage( locationId, message, contextLevels );

            } );

        RegisterBuiltinFunc< void ( TypeWrapper< LocationId >, TypeWrapper< StringId >, string ) >( e, "EmitErrorMessage"_sid,

            []( const TypeWrapper< LocationId >& locationId, const TypeWrapper< StringId >& errId, const string& message )
            {

                DiagnosticsManager::GetInstance().emitErrorMessage( locationId, errId, message );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< LocationId >, TypeWrapper< StringId >, string, uint32_t ) >( e, "EmitErrorMessage"_sid,

            []( const TypeWrapper< LocationId >& locationId, const TypeWrapper< StringId >& errId, const string& message, uint32_t contextLevels )

            {
                DiagnosticsManager::GetInstance().emitErrorMessage( locationId, errId, message, contextLevels );

            } );

        RegisterBuiltinFunc< void ( TypeWrapper< LocationId >, string ) >( e, "EmitSyntaxErrorMessage"_sid,

            []( const TypeWrapper< LocationId >& locationId, const string& message )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId, message );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< LocationId >, string, uint32_t ) >( e, "EmitSyntaxErrorMessage"_sid,

            []( const TypeWrapper< LocationId >& locationId, const string& message, uint32_t contextLevels )

            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId, message, contextLevels );

            } );

        RegisterBuiltinFunc< void ( TypeWrapper< LocationId >, string ) >( e, "EmitLexerErrorMessage"_sid,

            []( const TypeWrapper< LocationId >& locationId, const string& message )
            {
                DiagnosticsManager::GetInstance().emitLexerErrorMessage( locationId, message );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< LocationId >, string ) >( e, "EmitTraceMessage"_sid,
            []( const TypeWrapper< LocationId >& locationId, const string& message )
            {
                DiagnosticsManager::GetInstance().emitTraceMessage( locationId, message );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< LocationId >, TypeWrapper< StringId >, string ) >( e, "DefineCustomDiagnostic"_sid,

            []( const TypeWrapper< LocationId >& locationId, const TypeWrapper< StringId >& errId, const string& message )
            {

                DiagnosticsManager::GetInstance().defineCustomDiagnostic( locationId, errId, message );

            } );

        RegisterBuiltinFunc< void ( TypeWrapper< LocationId >, TypeWrapper< StringId > ) >( e, "DefineCustomDiagnostic"_sid,

            []( const TypeWrapper< LocationId >& locationId, const TypeWrapper< StringId >& errId )
            {
                DiagnosticsManager::GetInstance().defineCustomDiagnostic( locationId, errId );
            } );

        RegisterBuiltinFunc< bool () >( e, "WereErrorsEmitted"_sid,
            []()
            {
                return DiagnosticsManager::GetInstance().errorsWereEmitted();
            } );
    }
}













|




|
|






|
|






|
|
|
>





|
<
<
|
<

|

<
|
<

|

<
|
<

|
>
|
<
>
|
>


|
>
|
<
>
|
<

|
>
|
>

|
>


|
>

<
|
<

|
>
|
>

|
>


|
>

<
|
<

|

<
|
<

|
>
|
<
>
|
>


|
>

<
|
<

|
<
<
|
<

<
>
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
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace goose::g0api
{
    void SetupDiagnosticsExtensibilityFuncs( Env& e )
    {
        RegisterBuiltinFunc< Intrinsic< void( bool ) > >( e, "#DiagnosticsEnableTraces"_sid,
            []( auto&& c, const Value& enable )
            {
                if( !enable.isConstant() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        enable.locationId(), "this doesn't evaluate to a constant." );
                    return;
                }

                DiagnosticsManager::GetInstance().setTraceMode( *FromValue< bool >( enable ) );
            } );

        RegisterBuiltinFunc< void( TypeWrapper< LocationId >, string ) >( e,
            "PushDiagnosticsContext"_sid,
            []( const TypeWrapper< LocationId >& locationId, const string& desc )
            {
                DiagnosticsManager::GetInstance().pushDiagnosticsContext(
                    make_shared< ContextInfos >( desc, locationId, false ) );
            } );

        RegisterBuiltinFunc< void( TypeWrapper< LocationId >, string, bool ) >( e,
            "PushDiagnosticsContext"_sid,
            []( const TypeWrapper< LocationId >& locationId, const string& desc,
                bool isRootContext )
            {
                DiagnosticsManager::GetInstance().pushDiagnosticsContext(
                    make_shared< ContextInfos >( desc, locationId, isRootContext ) );
            } );

        RegisterBuiltinFunc< void() >( e, "PopDiagnosticsContext"_sid,


            []() { DiagnosticsManager::GetInstance().popDiagnosticsContext(); } );


        RegisterBuiltinFunc< void( string ) >( e, "EmitErrorMessage"_sid,
            []( const string& message )

            { DiagnosticsManager::GetInstance().emitErrorMessage( 0, message ); } );


        RegisterBuiltinFunc< void( TypeWrapper< LocationId >, string ) >( e, "EmitErrorMessage"_sid,
            []( const TypeWrapper< LocationId >& locationId, const string& message )

            { DiagnosticsManager::GetInstance().emitErrorMessage( locationId, message ); } );


        RegisterBuiltinFunc< void( TypeWrapper< LocationId >, string, uint32_t ) >( e,
            "EmitErrorMessage"_sid,
            []( const TypeWrapper< LocationId >& locationId, const string& message,

                uint32_t contextLevels ) {
                DiagnosticsManager::GetInstance().emitErrorMessage(
                    locationId, message, contextLevels );
            } );

        RegisterBuiltinFunc< void( TypeWrapper< LocationId >, TypeWrapper< StringId >, string ) >(
            e, "EmitErrorMessage"_sid,
            []( const TypeWrapper< LocationId >& locationId, const TypeWrapper< StringId >& errId,

                const string& message )
            { DiagnosticsManager::GetInstance().emitErrorMessage( locationId, errId, message ); } );


        RegisterBuiltinFunc< void( TypeWrapper< LocationId >, TypeWrapper< StringId >, string,
            uint32_t ) >( e, "EmitErrorMessage"_sid,
            []( const TypeWrapper< LocationId >& locationId, const TypeWrapper< StringId >& errId,
                const string& message, uint32_t contextLevels )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage(
                    locationId, errId, message, contextLevels );
            } );

        RegisterBuiltinFunc< void( TypeWrapper< LocationId >, string ) >( e,
            "EmitSyntaxErrorMessage"_sid,
            []( const TypeWrapper< LocationId >& locationId, const string& message )

            { DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId, message ); } );


        RegisterBuiltinFunc< void( TypeWrapper< LocationId >, string, uint32_t ) >( e,
            "EmitSyntaxErrorMessage"_sid,
            []( const TypeWrapper< LocationId >& locationId, const string& message,
                uint32_t contextLevels )
            {
                DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                    locationId, message, contextLevels );
            } );

        RegisterBuiltinFunc< void( TypeWrapper< LocationId >, string ) >( e,
            "EmitLexerErrorMessage"_sid,
            []( const TypeWrapper< LocationId >& locationId, const string& message )

            { DiagnosticsManager::GetInstance().emitLexerErrorMessage( locationId, message ); } );


        RegisterBuiltinFunc< void( TypeWrapper< LocationId >, string ) >( e, "EmitTraceMessage"_sid,
            []( const TypeWrapper< LocationId >& locationId, const string& message )

            { DiagnosticsManager::GetInstance().emitTraceMessage( locationId, message ); } );


        RegisterBuiltinFunc< void( TypeWrapper< LocationId >, TypeWrapper< StringId >, string ) >(
            e, "DefineCustomDiagnostic"_sid,
            []( const TypeWrapper< LocationId >& locationId, const TypeWrapper< StringId >& errId,

                const string& message ) {
                DiagnosticsManager::GetInstance().defineCustomDiagnostic(
                    locationId, errId, message );
            } );

        RegisterBuiltinFunc< void( TypeWrapper< LocationId >, TypeWrapper< StringId > ) >( e,
            "DefineCustomDiagnostic"_sid,
            []( const TypeWrapper< LocationId >& locationId, const TypeWrapper< StringId >& errId )

            { DiagnosticsManager::GetInstance().defineCustomDiagnostic( locationId, errId ); } );


        RegisterBuiltinFunc< bool() >( e, "WereErrorsEmitted"_sid,


            []() { return DiagnosticsManager::GetInstance().errorsWereEmitted(); } );

    }

} // namespace goose::g0api
Changes to bs/g0api/extensibility/eir.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
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
282
283
284
285
286
287
288
289
290
291


292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317

318
319
320
321
322
323

324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378

379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413

414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace
{
    template< typename T >
    void RegisterMkTermOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
    {
        if constexpr( IsTypeWrapper< T >::value )
        {
            RegisterBuiltinFunc< TypeWrapper< Term > ( T ) >( e, pOvlSet,
                []( const T& v ) -> TypeWrapper< Term >
                {
                    return TERM( v.get() );
                } );
        }
        else
        {
            RegisterBuiltinFunc< TypeWrapper< Term > ( T ) >( e, pOvlSet,
                []( const T& v ) -> TypeWrapper< Term >
                {
                    return TERM( v );
                } );
        }
    }

    template< typename T >
    void RegisterGetTermValueOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
    {
        if constexpr( IsTypeWrapper< T >::value )
        {
            RegisterBuiltinFunc< bool ( TypeWrapper< Term >, TermRef< T > ) >( e, pOvlSet,
                []( const TypeWrapper< Term >& t, TermRef< T >& tref )
                {
                    const auto* pVal = get_if< typename T::type >( &t.get() );
                    if( !pVal )
                        return false;

                    tref = *pVal;
                    return true;
                } );
        }
        else
        {
            RegisterBuiltinFunc< bool ( TypeWrapper< Term >, TermRef< T > ) >( e, pOvlSet,
                []( const TypeWrapper< Term >& t, TermRef< T >& tref )
                {
                    const auto* pVal = get_if< T >( &t.get() );
                    if( !pVal )
                        return false;

                    tref = *pVal;
                    return true;
                } );
        }
    }
}

namespace goose::g0api
{
    void SetupEIRExtensibilityFuncs( Env& e )
    {
        // Constants.
        DefineConstant( e, "DelimiterOpenParen"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( Delimiter::OpenParen ) ) ) );

        DefineConstant( e, "DelimiterOpenBrace"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( Delimiter::OpenBrace ) ) ) );

        DefineConstant( e, "DelimiterOpenBracket"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( Delimiter::OpenBracket ) ) ) );

        DefineConstant( e, "DelimiterCloseParen"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( Delimiter::CloseParen ) ) ) );

        DefineConstant( e, "DelimiterCloseBrace"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( Delimiter::CloseBrace ) ) ) );

        DefineConstant( e, "DelimiterCloseBracket"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( Delimiter::CloseBracket ) ) ) );


        // This enum must match the order of the types in the Term variant.
        enum class TermType
        {
            UInt64,
            LocationId,
            String,
            StringId,
            Delimiter,
            Hole,
            AnyTerm,
            VecOfLength,
            Vec,
            BigInt,
            FixedInt,
            Internal
        };

        DefineConstant( e, "TermTypeUInt64"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::UInt64 ) ) ) );

        DefineConstant( e, "TermTypeLocationId"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::LocationId ) ) ) );

        DefineConstant( e, "TermTypeString"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::String ) ) ) );

        DefineConstant( e, "TermTypeStringId"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::StringId ) ) ) );

        DefineConstant( e, "TermTypeDelimiter"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Delimiter ) ) ) );

        DefineConstant( e, "TermTypeHole"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Hole ) ) ) );

        DefineConstant( e, "TermTypeAnyTerm"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::AnyTerm ) ) ) );

        DefineConstant( e, "TermTypeVecOfLength"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::VecOfLength ) ) ) );


        DefineConstant( e, "TermTypeVec"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Vec ) ) ) );
        DefineConstant( e, "TermTypeBigInt"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::BigInt ) ) ) );

        DefineConstant( e, "TermTypeFixedInt"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::FixedInt ) ) ) );

        DefineConstant( e, "TermTypeInternal"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Internal ) ) ) );



        DefineConstant( e, "HoleBhvStandard"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( Hole::Behavior::Standard ) ) ) );

        DefineConstant( e, "HoleBhvPack"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( Hole::Behavior::Pack ) ) ) );

        DefineConstant( e, "HoleBhvAny"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( Hole::Behavior::Any ) ) ) );

        // Functions
        RegisterBuiltinFunc< BigInt ( TypeWrapper< Term > ) >( e, "GetTermType"_sid,
            []( const auto& t )
            {
                return BigInt::FromU32( min< uint8_t >( t.get().index(), static_cast< uint8_t >( TermType::Internal ) ) );

            } );

        ////////////////////////////
        // MkTerm overloads
        ////////////////////////////
        auto MkTerm = CreateOverloadSet( e, "MkTerm"_sid );

        RegisterMkTermOverload< uint64_t >( e, MkTerm );
        RegisterMkTermOverload< TypeWrapper< LocationId > >( e, MkTerm );
        RegisterMkTermOverload< string >( e, MkTerm );
        RegisterMkTermOverload< TypeWrapper< StringId > >( e, MkTerm );

        RegisterBuiltinFunc< TypeWrapper< Term > ( uint8_t ) >( e, "MkDelimiterTerm"_sid,
            []( uint8_t d ) -> TypeWrapper< Term >
            {
                return TERM( static_cast< Delimiter >( d ) );
            } );

        RegisterMkTermOverload< TypeWrapper< Hole > >( e, MkTerm );
        RegisterMkTermOverload< TypeWrapper< AnyTerm > >( e, MkTerm );
        RegisterMkTermOverload< TypeWrapper< VecOfLength > >( e, MkTerm );
        RegisterMkTermOverload< TypeWrapper< pvec > >( e, MkTerm );
        RegisterMkTermOverload< BigInt >( e, MkTerm );
        RegisterMkTermOverload< TypeWrapper< APSInt > >( e, MkTerm );

        ////////////////////////////
        // GetTermValue overloads
        ////////////////////////////
        auto GetTermValue = CreateOverloadSet( e, "GetTermValue"_sid );
        RegisterGetTermValueOverload< uint64_t >( e, GetTermValue );
        RegisterGetTermValueOverload< TypeWrapper< LocationId > >( e, GetTermValue );
        RegisterGetTermValueOverload< string >( e, GetTermValue );
        RegisterGetTermValueOverload< TypeWrapper< StringId > >( e, GetTermValue );

        RegisterBuiltinFunc< bool ( TypeWrapper< Term >, TermRef< uint8_t > ) >( e, "GetDelimiterTermValue"_sid,

            []( const auto& t, auto& tref )
            {
                const auto* pVal = get_if< Delimiter >( &t.get() );
                if( !pVal )
                    return false;

                tref = static_cast< uint8_t >( *pVal );
                return true;
            } );

        RegisterGetTermValueOverload< TypeWrapper< Hole > >( e, GetTermValue );
        RegisterGetTermValueOverload< TypeWrapper< AnyTerm > >( e, GetTermValue );
        RegisterGetTermValueOverload< TypeWrapper< VecOfLength > >( e, GetTermValue );
        RegisterGetTermValueOverload< TypeWrapper< pvec > >( e, GetTermValue );
        RegisterGetTermValueOverload< BigInt >( e, GetTermValue );
        RegisterGetTermValueOverload< TypeWrapper< APSInt > >( e, GetTermValue );

        ////////////////////////////
        // LocationId
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< LocationId > ( TypeWrapper< LocationId >, TypeWrapper< LocationId > ) >( e,
            "MkSpanningLocation"_sid,
            []( const auto& loc1, const auto& loc2 ) -> TypeWrapper< LocationId >
            {

                return static_cast< LocationId >( Location::CreateSpanningLocation( loc1.get(), loc2.get() ) );
            } );

        ////////////////////////////
        // StringId
        ////////////////////////////
        RegisterBuiltinFunc< Eager< TypeWrapper< StringId > > ( string ) >( e, "MkStringId"_sid,
            []( string s ) -> TypeWrapper< StringId >
            {
                return s;
            } );

        RegisterBuiltinFunc< Eager< TypeWrapper< StringId > > ( uint32_t ) >( e, "MkStringId"_sid,
            []( uint32_t id ) -> TypeWrapper< StringId >
            {
                return id;
            } );

        ////////////////////////////
        // Hole
        ////////////////////////////
        RegisterBuiltinFunc< Intrinsic< TypeWrapper< Hole > ( TypeWrapper< StringId >, TypeWrapper< StringId >, uint8_t ) > >( e, "MkHole"_sid,

            []( const auto& c, const Value& nameVal, const Value& kindVal, const Value& bhvVal ) -> Value

            {
                auto name = *FromValue< TypeWrapper< StringId > >( nameVal );
                auto kind = *FromValue< TypeWrapper< StringId > >( kindVal );
                auto bhv = *FromValue< uint8_t >( bhvVal );

                if( bhv > static_cast< uint8_t >( Hole::Behavior::Any ) )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( bhvVal.locationId(),
                        "invalid hole behavior." );
                    return PoisonValue();
                }

                return ToValue( TypeWrapper< Hole >( Hole( name, kind, static_cast< Hole::Behavior >( bhv ) ) ) );

            } );

        RegisterBuiltinFunc< TypeWrapper< Hole > ( TypeWrapper< StringId >, TypeWrapper< StringId > ) >( e, "MkHole"_sid,

            []( const auto& name, const auto& kind ) -> TypeWrapper< Hole >
            {
                return Hole( name, kind );
            } );

        RegisterBuiltinFunc< TypeWrapper< StringId > ( TypeWrapper< Hole > ) >( e, "GetHoleName"_sid,
            []( const auto& h ) -> TypeWrapper< StringId >
            {
                return h.get().name();
            } );

        RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< Hole > ) >( e, "GetHoleKind"_sid,
            []( const auto& h ) -> TypeWrapper< Term >
            {
                return h.get().kind();
            } );

        RegisterBuiltinFunc< uint8_t ( TypeWrapper< Hole > ) >( e, "GetHoleBehavior"_sid,
            []( const auto& h )
            {
                return static_cast< uint8_t>( h.get().behavior() );
            } );

        ////////////////////////////
        // AnyTerm
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< StringId > ( TypeWrapper< AnyTerm > ) >( e, "GetAnyTermVarName"_sid,

            []( const auto& at ) -> TypeWrapper< StringId >
            {
                return at.get().varName();
            } );

        RegisterBuiltinFunc< TypeWrapper< AnyTerm > ( TypeWrapper< StringId > ) >( e, "MkAnyTerm"_sid,

            []( const auto& name ) -> TypeWrapper< AnyTerm >
            {
                return AnyTerm( name );
            } );

        ////////////////////////////
        // VecOfLength
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< StringId > ( TypeWrapper< VecOfLength > ) >( e, "GetVecOfLengthVarName"_sid,

            []( const auto& at ) -> TypeWrapper< StringId >
            {
                return at.get().varName();
            } );

        RegisterBuiltinFunc< TypeWrapper< VecOfLength > ( TypeWrapper< StringId > ) >( e, "MkVecOfLength"_sid,

            []( const auto& name ) -> TypeWrapper< VecOfLength >
            {
                return VecOfLength( name );
            } );

        ////////////////////////////
        // Vector
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< pvec > () >( e, "MkVec"_sid,
            []() -> TypeWrapper< pvec >
            {
                return make_shared< Vector >();
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, TypeWrapper< pvec > ) >( e, "MkVecConcat"_sid,
            []( const auto& vec1, const auto& vec2 )
            {
                return make_shared< Vector >( Vector::MakeConcat( *vec1.get(), *vec2.get() ) );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, uint32_t len ) >( e, "VecReserve"_sid,
            []( const auto& vec, uint32_t len )
            {
                vec->reserve( len );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, uint32_t, TypeWrapper< Term > ) >( e, "SetTerm"_sid,
            []( const auto& vec, uint32_t index, const auto& t )
            {
                vec->terms()[index] = t.get();
            } );



        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, int32_t ) >( e, "SetVecWeight"_sid,
            []( const auto& vec, int32_t w )
            {
                vec->setWeight( w );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, int32_t ) >( e, "SetVecWeightOverride"_sid,
            []( const auto& vec, int32_t w )
            {
                vec->setWeightOverride( w );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, TypeWrapper< Term > ) >( e, "VecAppend"_sid,
            []( const auto& vec, const auto& t )
            {
                vec->append( t.get() );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< pvec >, TypeWrapper< Term > ) >( e, "VecSetRepetition"_sid,
            []( const auto& vec, const auto& t )
            {
                vec->setRepetitionTerm( t.get() );
            } );

        RegisterBuiltinFunc< tuple< uint32_t, bool > ( TypeWrapper< pvec > ) >( e, "GetVecLength"_sid,

            []( const auto& vec )
            {
                auto vl = vec->length();
                return make_tuple( static_cast< uint32_t >( vl.minLength() ), vl.isVariable() );
            } );


        RegisterBuiltinFunc< bool ( TypeWrapper< pvec >, uint32_t, TermRef< TypeWrapper< Term > > ) >( e, "GetVecTerm"_sid,
            []( const auto& vec, uint32_t index, auto& out )
            {
                const auto& v = *vec.get();

                if( v.length().minLength() <= index )
                    return false;

                out = v.terms()[index];
                return true;
            } );

        RegisterBuiltinFunc< int32_t ( TypeWrapper< pvec > ) >( e, "GetVecWeight"_sid,
            []( const auto& vec )
            {
                return vec->weight();
            } );

        RegisterBuiltinFunc< int32_t ( TypeWrapper< pvec > ) >( e, "GetVecWeightOverride"_sid,
            []( const auto& vec )
            {
                return vec->weightOverride();
            } );

        RegisterBuiltinFunc< int32_t ( TypeWrapper< pvec >, TermRef< TypeWrapper< Term > > ) >( e, "GetVecRepetitionTerm"_sid,
            []( const auto& vec, auto& out )
            {
                const auto& rt = vec->repetitionTerm();

                if( !rt )
                    return false;

                out = *rt;
                return true;
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< pvec > ) >( e, "IsVecEmpty"_sid,
            []( const auto& vec )
            {
                return vec->empty();
            } );

        ////////////////////////////
        // Helpers
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< Term >, TypeWrapper< Term > ) >( e, "AppendToVectorTerm"_sid,
            []( const auto& vec, const auto& t ) -> TypeWrapper< Term >
            {
                return AppendToVectorTerm( vec, t );
            } );

        ////////////////////////////
        // Integers
        ////////////////////////////
        RegisterBuiltinFunc< Intrinsic< TypeWrapper< APSInt > ( BigInt, bool ) > >( e, "ToFixedInt"_sid,

            []( auto&& c, const Value& biVal, const Value& sVal )
            {
                auto bi = *FromValue< BigInt >( biVal );
                auto s = *FromValue< bool >( sVal );

                if( !s && bi.isNegative() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( biVal.locationId(),
                        "this is negative and can't be converted to an unsigned int." );
                    return PoisonValue();
                }

                auto ai = bi.getAPSInt();
                if( !s )
                    ai.setIsSigned( false );

                return ToValue( TypeWrapper< APSInt >( s ) );
            } );

        auto ToBigInt = CreateOverloadSet( e, "ToBigInt"_sid );
        RegisterBuiltinFunc< Eager< BigInt > ( TypeWrapper< APSInt > ) >( e, ToBigInt,
            []( const auto& fInt )
            {
                return fInt;
            } );

        RegisterBuiltinFunc< Eager< BigInt > ( char32_t ) >( e, ToBigInt,
            []( char32_t c )
            {
                return BigInt::FromU32( c );
            } );

        ////////////////////////////
        // Propositions
        ////////////////////////////

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< Context > >, TypeWrapper< Value >, TermRef< TypeWrapper< ptr< Propositions > > > ) >( e, "GetTypePredicates"_sid,
            []( const auto& c, const auto& type, auto& out ) -> bool
            {
                if( !ParseTypePredicates( *c.get(), type.get() ) )
                    return false;

                auto ppPreds = GetTypePredicates( type );

                if( !ppPreds || !( *ppPreds ) )
                    return false;

                out = *ppPreds;
                return true;
            } );

        RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< Propositions > > ) >( e, "GetPropositionsCount"_sid,
            []( const auto& preds )
            {
                return preds->props().size();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< Propositions > >, uint32_t, TermRef< TypeWrapper< Value > > ) >( e, "GetProposition"_sid,
            []( const auto& preds, uint32_t index, auto& out )
            {
                if( preds->props().size() <= index )
                    return false;

                out = preds->props()[index];
                return true;
            } );
    }
}










<
|



|
|
<
<
<



|
|
<
<
<








|












|











|






|
>
|
>
|
>
|
>
|
>
|
>


















|
>
|
>
|
>
|
>
|
>
|
>
|
>
|
>
>
|
|
>
|
>
|
>

>
|
>
|
>
|


|


|
>












|

<
|
<

















|
>




















|
|


>
|





|
|
|
<
<
<
|
|
<
<
<




|
>
|
>







|
|



|
>


|
>

<
|
<

|
|
|
<
<
<
|
|
|
<
<
<
|
|
|
<
<
<



|
>
|
|
<
<
<
|
>
|
<
<
<




|
>
|
|
<
<
<
|
>
|
<
<
<




|
<
<
|
<

|
|
<
|
<

|
|
|
<
<
<
|
|
<
|
|
>
>

<
<
<
<
<
<
|
|
|
<
<
<
|
|
|
<
<
|
|
|
|
<
<
<
|
>






>
|











|
|
|
<
<
<
|
|
|
<
<
|
|











|
|
<
<
<




|
|
<
|
<




|
>




















|
|
|
<
<
<
|
<
<
|
<




>
|














|
|
|
<
<
|
|









|
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
282



283
284
285


286
287
288
289



290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313



314
315
316


317
318
319
320
321
322
323
324
325
326
327
328
329
330
331



332
333
334
335
336
337

338

339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367



368


369

370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392


393
394
395
396
397
398
399
400
401
402
403
404
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace
{

    template< typename T > void RegisterMkTermOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
    {
        if constexpr( IsTypeWrapper< T >::value )
        {
            RegisterBuiltinFunc< TypeWrapper< Term >( T ) >(
                e, pOvlSet, []( const T& v ) -> TypeWrapper< Term > { return TERM( v.get() ); } );



        }
        else
        {
            RegisterBuiltinFunc< TypeWrapper< Term >( T ) >(
                e, pOvlSet, []( const T& v ) -> TypeWrapper< Term > { return TERM( v ); } );



        }
    }

    template< typename T >
    void RegisterGetTermValueOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
    {
        if constexpr( IsTypeWrapper< T >::value )
        {
            RegisterBuiltinFunc< bool( TypeWrapper< Term >, TermRef< T > ) >( e, pOvlSet,
                []( const TypeWrapper< Term >& t, TermRef< T >& tref )
                {
                    const auto* pVal = get_if< typename T::type >( &t.get() );
                    if( !pVal )
                        return false;

                    tref = *pVal;
                    return true;
                } );
        }
        else
        {
            RegisterBuiltinFunc< bool( TypeWrapper< Term >, TermRef< T > ) >( e, pOvlSet,
                []( const TypeWrapper< Term >& t, TermRef< T >& tref )
                {
                    const auto* pVal = get_if< T >( &t.get() );
                    if( !pVal )
                        return false;

                    tref = *pVal;
                    return true;
                } );
        }
    }
} // namespace

namespace goose::g0api
{
    void SetupEIRExtensibilityFuncs( Env& e )
    {
        // Constants.
        DefineConstant( e, "DelimiterOpenParen"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( Delimiter::OpenParen ) ) ) );
        DefineConstant( e, "DelimiterOpenBrace"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( Delimiter::OpenBrace ) ) ) );
        DefineConstant( e, "DelimiterOpenBracket"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( Delimiter::OpenBracket ) ) ) );
        DefineConstant( e, "DelimiterCloseParen"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( Delimiter::CloseParen ) ) ) );
        DefineConstant( e, "DelimiterCloseBrace"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( Delimiter::CloseBrace ) ) ) );
        DefineConstant( e, "DelimiterCloseBracket"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( Delimiter::CloseBracket ) ) ) );

        // This enum must match the order of the types in the Term variant.
        enum class TermType
        {
            UInt64,
            LocationId,
            String,
            StringId,
            Delimiter,
            Hole,
            AnyTerm,
            VecOfLength,
            Vec,
            BigInt,
            FixedInt,
            Internal
        };

        DefineConstant( e, "TermTypeUInt64"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( TermType::UInt64 ) ) ) );
        DefineConstant( e, "TermTypeLocationId"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( TermType::LocationId ) ) ) );
        DefineConstant( e, "TermTypeString"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( TermType::String ) ) ) );
        DefineConstant( e, "TermTypeStringId"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( TermType::StringId ) ) ) );
        DefineConstant( e, "TermTypeDelimiter"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Delimiter ) ) ) );
        DefineConstant( e, "TermTypeHole"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Hole ) ) ) );
        DefineConstant( e, "TermTypeAnyTerm"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( TermType::AnyTerm ) ) ) );
        DefineConstant( e, "TermTypeVecOfLength"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( TermType::VecOfLength ) ) ) );
        DefineConstant( e, "TermTypeVec"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Vec ) ) ) );
        DefineConstant( e, "TermTypeBigInt"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( TermType::BigInt ) ) ) );
        DefineConstant( e, "TermTypeFixedInt"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( TermType::FixedInt ) ) ) );
        DefineConstant( e, "TermTypeInternal"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Internal ) ) ) );

        DefineConstant( e, "HoleBhvStandard"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( Hole::Behavior::Standard ) ) ) );
        DefineConstant( e, "HoleBhvPack"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( Hole::Behavior::Pack ) ) ) );
        DefineConstant( e, "HoleBhvAny"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( Hole::Behavior::Any ) ) ) );

        // Functions
        RegisterBuiltinFunc< BigInt( TypeWrapper< Term > ) >( e, "GetTermType"_sid,
            []( const auto& t )
            {
                return BigInt::FromU32( min< uint8_t >(
                    t.get().index(), static_cast< uint8_t >( TermType::Internal ) ) );
            } );

        ////////////////////////////
        // MkTerm overloads
        ////////////////////////////
        auto MkTerm = CreateOverloadSet( e, "MkTerm"_sid );

        RegisterMkTermOverload< uint64_t >( e, MkTerm );
        RegisterMkTermOverload< TypeWrapper< LocationId > >( e, MkTerm );
        RegisterMkTermOverload< string >( e, MkTerm );
        RegisterMkTermOverload< TypeWrapper< StringId > >( e, MkTerm );

        RegisterBuiltinFunc< TypeWrapper< Term >( uint8_t ) >( e, "MkDelimiterTerm"_sid,
            []( uint8_t d ) -> TypeWrapper< Term >

            { return TERM( static_cast< Delimiter >( d ) ); } );


        RegisterMkTermOverload< TypeWrapper< Hole > >( e, MkTerm );
        RegisterMkTermOverload< TypeWrapper< AnyTerm > >( e, MkTerm );
        RegisterMkTermOverload< TypeWrapper< VecOfLength > >( e, MkTerm );
        RegisterMkTermOverload< TypeWrapper< pvec > >( e, MkTerm );
        RegisterMkTermOverload< BigInt >( e, MkTerm );
        RegisterMkTermOverload< TypeWrapper< APSInt > >( e, MkTerm );

        ////////////////////////////
        // GetTermValue overloads
        ////////////////////////////
        auto GetTermValue = CreateOverloadSet( e, "GetTermValue"_sid );
        RegisterGetTermValueOverload< uint64_t >( e, GetTermValue );
        RegisterGetTermValueOverload< TypeWrapper< LocationId > >( e, GetTermValue );
        RegisterGetTermValueOverload< string >( e, GetTermValue );
        RegisterGetTermValueOverload< TypeWrapper< StringId > >( e, GetTermValue );

        RegisterBuiltinFunc< bool( TypeWrapper< Term >, TermRef< uint8_t > ) >( e,
            "GetDelimiterTermValue"_sid,
            []( const auto& t, auto& tref )
            {
                const auto* pVal = get_if< Delimiter >( &t.get() );
                if( !pVal )
                    return false;

                tref = static_cast< uint8_t >( *pVal );
                return true;
            } );

        RegisterGetTermValueOverload< TypeWrapper< Hole > >( e, GetTermValue );
        RegisterGetTermValueOverload< TypeWrapper< AnyTerm > >( e, GetTermValue );
        RegisterGetTermValueOverload< TypeWrapper< VecOfLength > >( e, GetTermValue );
        RegisterGetTermValueOverload< TypeWrapper< pvec > >( e, GetTermValue );
        RegisterGetTermValueOverload< BigInt >( e, GetTermValue );
        RegisterGetTermValueOverload< TypeWrapper< APSInt > >( e, GetTermValue );

        ////////////////////////////
        // LocationId
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< LocationId >(
            TypeWrapper< LocationId >, TypeWrapper< LocationId > ) >( e, "MkSpanningLocation"_sid,
            []( const auto& loc1, const auto& loc2 ) -> TypeWrapper< LocationId >
            {
                return static_cast< LocationId >(
                    Location::CreateSpanningLocation( loc1.get(), loc2.get() ) );
            } );

        ////////////////////////////
        // StringId
        ////////////////////////////
        RegisterBuiltinFunc< Eager< TypeWrapper< StringId > >( string ) >(
            e, "MkStringId"_sid, []( string s ) -> TypeWrapper< StringId > { return s; } );




        RegisterBuiltinFunc< Eager< TypeWrapper< StringId > >( uint32_t ) >(
            e, "MkStringId"_sid, []( uint32_t id ) -> TypeWrapper< StringId > { return id; } );




        ////////////////////////////
        // Hole
        ////////////////////////////
        RegisterBuiltinFunc< Intrinsic< TypeWrapper< Hole >(
            TypeWrapper< StringId >, TypeWrapper< StringId >, uint8_t ) > >( e, "MkHole"_sid,
            []( const auto& c, const Value& nameVal, const Value& kindVal,
                const Value& bhvVal ) -> Value
            {
                auto name = *FromValue< TypeWrapper< StringId > >( nameVal );
                auto kind = *FromValue< TypeWrapper< StringId > >( kindVal );
                auto bhv = *FromValue< uint8_t >( bhvVal );

                if( bhv > static_cast< uint8_t >( Hole::Behavior::Any ) )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        bhvVal.locationId(), "invalid hole behavior." );
                    return PoisonValue();
                }

                return ToValue( TypeWrapper< Hole >(
                    Hole( name, kind, static_cast< Hole::Behavior >( bhv ) ) ) );
            } );

        RegisterBuiltinFunc< TypeWrapper< Hole >(
            TypeWrapper< StringId >, TypeWrapper< StringId > ) >( e, "MkHole"_sid,
            []( const auto& name, const auto& kind ) -> TypeWrapper< Hole >

            { return Hole( name, kind ); } );


        RegisterBuiltinFunc< TypeWrapper< StringId >( TypeWrapper< Hole > ) >( e, "GetHoleName"_sid,
            []( const auto& h ) -> TypeWrapper< StringId > { return h.get().name(); } );




        RegisterBuiltinFunc< TypeWrapper< Term >( TypeWrapper< Hole > ) >( e, "GetHoleKind"_sid,
            []( const auto& h ) -> TypeWrapper< Term > { return h.get().kind(); } );




        RegisterBuiltinFunc< uint8_t( TypeWrapper< Hole > ) >( e, "GetHoleBehavior"_sid,
            []( const auto& h ) { return static_cast< uint8_t >( h.get().behavior() ); } );




        ////////////////////////////
        // AnyTerm
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< StringId >( TypeWrapper< AnyTerm > ) >( e,
            "GetAnyTermVarName"_sid,
            []( const auto& at ) -> TypeWrapper< StringId > { return at.get().varName(); } );




        RegisterBuiltinFunc< TypeWrapper< AnyTerm >( TypeWrapper< StringId > ) >( e,
            "MkAnyTerm"_sid,
            []( const auto& name ) -> TypeWrapper< AnyTerm > { return AnyTerm( name ); } );




        ////////////////////////////
        // VecOfLength
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< StringId >( TypeWrapper< VecOfLength > ) >( e,
            "GetVecOfLengthVarName"_sid,
            []( const auto& at ) -> TypeWrapper< StringId > { return at.get().varName(); } );




        RegisterBuiltinFunc< TypeWrapper< VecOfLength >( TypeWrapper< StringId > ) >( e,
            "MkVecOfLength"_sid,
            []( const auto& name ) -> TypeWrapper< VecOfLength > { return VecOfLength( name ); } );




        ////////////////////////////
        // Vector
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< pvec >() >(


            e, "MkVec"_sid, []() -> TypeWrapper< pvec > { return make_shared< Vector >(); } );


        RegisterBuiltinFunc< void( TypeWrapper< pvec >, TypeWrapper< pvec > ) >( e,
            "MkVecConcat"_sid, []( const auto& vec1, const auto& vec2 )

            { return make_shared< Vector >( Vector::MakeConcat( *vec1.get(), *vec2.get() ) ); } );


        RegisterBuiltinFunc< void( TypeWrapper< pvec >, uint32_t len ) >(
            e, "VecReserve"_sid, []( const auto& vec, uint32_t len ) { vec->reserve( len ); } );




        RegisterBuiltinFunc< void( TypeWrapper< pvec >, uint32_t, TypeWrapper< Term > ) >( e,
            "SetTerm"_sid, []( const auto& vec, uint32_t index, const auto& t )

            { vec->terms()[index] = t.get(); } );

        RegisterBuiltinFunc< void( TypeWrapper< pvec >, int32_t ) >(
            e, "SetVecWeight"_sid, []( const auto& vec, int32_t w ) { vec->setWeight( w ); } );







        RegisterBuiltinFunc< void( TypeWrapper< pvec >, int32_t ) >( e, "SetVecWeightOverride"_sid,
            []( const auto& vec, int32_t w ) { vec->setWeightOverride( w ); } );




        RegisterBuiltinFunc< void( TypeWrapper< pvec >, TypeWrapper< Term > ) >(
            e, "VecAppend"_sid, []( const auto& vec, const auto& t ) { vec->append( t.get() ); } );



        RegisterBuiltinFunc< void( TypeWrapper< pvec >, TypeWrapper< Term > ) >( e,
            "VecSetRepetition"_sid,
            []( const auto& vec, const auto& t ) { vec->setRepetitionTerm( t.get() ); } );




        RegisterBuiltinFunc< tuple< uint32_t, bool >( TypeWrapper< pvec > ) >( e,
            "GetVecLength"_sid,
            []( const auto& vec )
            {
                auto vl = vec->length();
                return make_tuple( static_cast< uint32_t >( vl.minLength() ), vl.isVariable() );
            } );

        RegisterBuiltinFunc< bool(
            TypeWrapper< pvec >, uint32_t, TermRef< TypeWrapper< Term > > ) >( e, "GetVecTerm"_sid,
            []( const auto& vec, uint32_t index, auto& out )
            {
                const auto& v = *vec.get();

                if( v.length().minLength() <= index )
                    return false;

                out = v.terms()[index];
                return true;
            } );

        RegisterBuiltinFunc< int32_t( TypeWrapper< pvec > ) >(
            e, "GetVecWeight"_sid, []( const auto& vec ) { return vec->weight(); } );




        RegisterBuiltinFunc< int32_t( TypeWrapper< pvec > ) >( e, "GetVecWeightOverride"_sid,
            []( const auto& vec ) { return vec->weightOverride(); } );



        RegisterBuiltinFunc< int32_t( TypeWrapper< pvec >, TermRef< TypeWrapper< Term > > ) >( e,
            "GetVecRepetitionTerm"_sid,
            []( const auto& vec, auto& out )
            {
                const auto& rt = vec->repetitionTerm();

                if( !rt )
                    return false;

                out = *rt;
                return true;
            } );

        RegisterBuiltinFunc< bool( TypeWrapper< pvec > ) >(
            e, "IsVecEmpty"_sid, []( const auto& vec ) { return vec->empty(); } );




        ////////////////////////////
        // Helpers
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< Term >( TypeWrapper< Term >, TypeWrapper< Term > ) >( e,
            "AppendToVectorTerm"_sid, []( const auto& vec, const auto& t ) -> TypeWrapper< Term >

            { return AppendToVectorTerm( vec, t ); } );


        ////////////////////////////
        // Integers
        ////////////////////////////
        RegisterBuiltinFunc< Intrinsic< TypeWrapper< APSInt >( BigInt, bool ) > >( e,
            "ToFixedInt"_sid,
            []( auto&& c, const Value& biVal, const Value& sVal )
            {
                auto bi = *FromValue< BigInt >( biVal );
                auto s = *FromValue< bool >( sVal );

                if( !s && bi.isNegative() )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( biVal.locationId(),
                        "this is negative and can't be converted to an unsigned int." );
                    return PoisonValue();
                }

                auto ai = bi.getAPSInt();
                if( !s )
                    ai.setIsSigned( false );

                return ToValue( TypeWrapper< APSInt >( s ) );
            } );

        auto ToBigInt = CreateOverloadSet( e, "ToBigInt"_sid );
        RegisterBuiltinFunc< Eager< BigInt >( TypeWrapper< APSInt > ) >(
            e, ToBigInt, []( const auto& fInt ) { return fInt; } );




        RegisterBuiltinFunc< Eager< BigInt >( char32_t ) >(


            e, ToBigInt, []( char32_t c ) { return BigInt::FromU32( c ); } );


        ////////////////////////////
        // Propositions
        ////////////////////////////
        RegisterBuiltinFunc< bool( TypeWrapper< ptr< Context > >, TypeWrapper< Value >,
            TermRef< TypeWrapper< ptr< Propositions > > > ) >( e, "GetTypePredicates"_sid,
            []( const auto& c, const auto& type, auto& out ) -> bool
            {
                if( !ParseTypePredicates( *c.get(), type.get() ) )
                    return false;

                auto ppPreds = GetTypePredicates( type );

                if( !ppPreds || !( *ppPreds ) )
                    return false;

                out = *ppPreds;
                return true;
            } );

        RegisterBuiltinFunc< uint32_t( TypeWrapper< ptr< Propositions > > ) >( e,
            "GetPropositionsCount"_sid, []( const auto& preds ) { return preds->props().size(); } );



        RegisterBuiltinFunc< bool( TypeWrapper< ptr< Propositions > >, uint32_t,
            TermRef< TypeWrapper< Value > > ) >( e, "GetProposition"_sid,
            []( const auto& preds, uint32_t index, auto& out )
            {
                if( preds->props().size() <= index )
                    return false;

                out = preds->props()[index];
                return true;
            } );
    }
} // namespace goose::g0api
Changes to bs/g0api/extensibility/env.cpp.
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


namespace goose::g0api
{
    void SetupEnvExtensibilityFuncs( Env& e )
    {
        wptr< Env > pEnv = e.shared_from_this();

        DefineConstant( e, "RetrieveValueStatusSuccess"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( Env::Status::Success ) ) ) );

        DefineConstant( e, "RetrieveValueStatusNoMatch"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( Env::Status::NoMatch ) ) ) );

        DefineConstant( e, "RetrieveValueStatusAmbiguousMatch"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( Env::Status::AmbiguousMatch ) ) ) );


        // We could reconstruct this easily on the goose side but I prefer it to be defined in a single place

        DefineConstant( e, "RootG0Identity"_sid, ValueToEIR( ToValue( TypeWrapper< Term >( RootG0Identity() ) ) ) );



        RegisterBuiltinFunc< void ( TypeWrapper< Term >, TypeWrapper< Term >, TypeWrapper< Term > ) >( e, "EnvStoreValue"_sid,
            [pEnv]( const TypeWrapper< Term >& idPat, const TypeWrapper< Term >& contextIdPat, const TypeWrapper< Term >& val )

            {
                if( auto e = pEnv.lock() )
                    e->storeValue( idPat, contextIdPat, val );
            } );

        // TODO ValueProvider overload for EnvStoreValue (once we have wrapping of goose functions as C++ functions)



        RegisterBuiltinFunc< uint8_t ( TypeWrapper< Term >, TypeWrapper< Term >, TermRef< TypeWrapper< Term > > ) >( e, "EnvRetrieveValue"_sid,
            [pEnv]( const TypeWrapper< Term >& id, const TypeWrapper< Term >& contextId, TermRef< TypeWrapper< Term > >& out )

            {
                auto e = pEnv.lock();
                if( !e )
                    return static_cast< uint8_t >( Env::Status::NoMatch );

                Term t;
                auto result = e->retrieveValue( id, contextId, t );
                out = t;
                return static_cast< uint8_t >( result );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< Term >, TypeWrapper< Term >, bool ) >( e, "EnvAddVisibilityRule"_sid,

            [pEnv]( const TypeWrapper< Term >& toImport, const TypeWrapper< Term >& destination, bool transitive )

            {
                if( auto e = pEnv.lock() )
                    e->addVisibilityRule( toImport, destination, transitive );
            } );

        RegisterBuiltinFunc< uint32_t () >( e, "EnvNewUniqueId"_sid,
            []()
            {
                return Env::NewUniqueId();
            } );
    }
}








|
>
|
>
|
>

|
>
|
>

>
|
|
>





|
>

>
|
|
>











|
>
|
>





|
<
<
|
<

<
>
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

namespace goose::g0api
{
    void SetupEnvExtensibilityFuncs( Env& e )
    {
        wptr< Env > pEnv = e.shared_from_this();

        DefineConstant( e, "RetrieveValueStatusSuccess"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( Env::Status::Success ) ) ) );
        DefineConstant( e, "RetrieveValueStatusNoMatch"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( Env::Status::NoMatch ) ) ) );
        DefineConstant( e, "RetrieveValueStatusAmbiguousMatch"_sid,
            ValueToEIR( ToValue( static_cast< uint8_t >( Env::Status::AmbiguousMatch ) ) ) );

        // We could reconstruct this easily on the goose side but I prefer it to be defined in a
        // single place
        DefineConstant( e, "RootG0Identity"_sid,
            ValueToEIR( ToValue( TypeWrapper< Term >( RootG0Identity() ) ) ) );

        RegisterBuiltinFunc< void( TypeWrapper< Term >, TypeWrapper< Term >,
            TypeWrapper< Term > ) >( e, "EnvStoreValue"_sid,
            [pEnv]( const TypeWrapper< Term >& idPat, const TypeWrapper< Term >& contextIdPat,
                const TypeWrapper< Term >& val )
            {
                if( auto e = pEnv.lock() )
                    e->storeValue( idPat, contextIdPat, val );
            } );

        // TODO ValueProvider overload for EnvStoreValue (once we have wrapping of goose functions
        // as C++ functions)

        RegisterBuiltinFunc< uint8_t( TypeWrapper< Term >, TypeWrapper< Term >,
            TermRef< TypeWrapper< Term > > ) >( e, "EnvRetrieveValue"_sid,
            [pEnv]( const TypeWrapper< Term >& id, const TypeWrapper< Term >& contextId,
                TermRef< TypeWrapper< Term > >& out )
            {
                auto e = pEnv.lock();
                if( !e )
                    return static_cast< uint8_t >( Env::Status::NoMatch );

                Term t;
                auto result = e->retrieveValue( id, contextId, t );
                out = t;
                return static_cast< uint8_t >( result );
            } );

        RegisterBuiltinFunc< void( TypeWrapper< Term >, TypeWrapper< Term >, bool ) >( e,
            "EnvAddVisibilityRule"_sid,
            [pEnv]( const TypeWrapper< Term >& toImport, const TypeWrapper< Term >& destination,
                bool transitive )
            {
                if( auto e = pEnv.lock() )
                    e->addVisibilityRule( toImport, destination, transitive );
            } );

        RegisterBuiltinFunc< uint32_t() >(


            e, "EnvNewUniqueId"_sid, []() { return Env::NewUniqueId(); } );

    }

} // namespace goose::g0api
Changes to bs/g0api/extensibility/extensibility.h.
22
23
24
25
26
27
28
29
30
31
        SetupValueExtensibilityFuncs( e );
        SetupContextExtensibilityFuncs( e );
        SetupEnvExtensibilityFuncs( e );
        SetupDiagnosticsExtensibilityFuncs( e );
        SetupMiscExtensibilityFuncs( e );
        SetupParserExtensibilityFuncs( e );
    }
}

#endif







|


22
23
24
25
26
27
28
29
30
31
        SetupValueExtensibilityFuncs( e );
        SetupContextExtensibilityFuncs( e );
        SetupEnvExtensibilityFuncs( e );
        SetupDiagnosticsExtensibilityFuncs( e );
        SetupMiscExtensibilityFuncs( e );
        SetupParserExtensibilityFuncs( e );
    }
} // namespace goose::g0api

#endif
Changes to bs/g0api/extensibility/misc.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
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
    void SetupMiscExtensibilityFuncs( Env& e )
    {
        ////////////////////////////
        // Local variables
        ////////////////////////////
        auto declLocVar = CreateOverloadSet( e, "DeclareLocalVar"_sid );

        RegisterBuiltinFunc< TypeWrapper< Value > (
            TypeWrapper< ptr< sema::Context > >, TypeWrapper< Term >, TypeWrapper< StringId >, TypeWrapper< LocationId > ) >( e, declLocVar,

            []( const auto& c, const auto& type, const auto& name, const auto& locId ) -> TypeWrapper< Value >
            {

                return builtins::DeclareLocalVar( *c.get(), type, name, nullopt, locId );
            } );

        RegisterBuiltinFunc< TypeWrapper< Value > (
            TypeWrapper< ptr< sema::Context > >, TypeWrapper< Term >, TypeWrapper< StringId >, TypeWrapper< Value >, TypeWrapper< LocationId > ) >( e, declLocVar,

            []( const auto& c, const auto& type, const auto& name, const auto& initializer, const auto& locId ) -> TypeWrapper< Value >
            {

                return builtins::DeclareLocalVar( *c.get(), type, name, initializer, locId );
            } );

        RegisterBuiltinFunc< TypeWrapper< Value > (

            TypeWrapper< ptr< sema::Context > >, TypeWrapper< Term >, TypeWrapper< StringId >, TypeWrapper< Value >, TypeWrapper< LocationId > ) >( e, "DeclareLocalVarWithTypeInference"_sid,
            []( const auto& c, const auto& typeTExpr, const auto& name, const auto& initializer, const auto& locId ) -> TypeWrapper< Value >

            {
                return builtins::DeclareLocalVarWithTypeInference( *c.get(), typeTExpr, name, initializer, locId );

            } );

        ////////////////////////////
        // Tuples
        ////////////////////////////
        RegisterBuiltinFunc< uint32_t ( CustomPattern< Value, TuplePattern > ) >( e, "GetTupleSize"_sid,
            []( const auto& tup )
            {
                return builtins::TupleSize( tup );
            } );

        RegisterBuiltinFunc< uint32_t ( TypeWrapper< Value > ) >( e, "GetTupleSize"_sid,
            []( const auto& tup ) -> uint32_t
            {
                if( !IsTuple( tup.get() ) )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( tup.get().locationId(),
                        "this wrapped value is not a tuple." );
                    return 0;
                }

                return builtins::TupleSize( tup.get() );
            } );


        RegisterBuiltinFunc< TypeWrapper< Value > ( CustomPattern< Value, TuplePattern >, uint32_t, AccessSpecifier ) >( e, "GetComputedTupleElement"_sid,
            []( const auto& tup, uint32_t index, AccessSpecifier as )
            {
                if( index >= TupleSize( tup ) )
                    return  PoisonValue();
                return builtins::GetComputedTupleElement( tup, index, ValueToEIR( ToValue( as ) ) );
            } );


        RegisterBuiltinFunc< TypeWrapper< Value > ( TypeWrapper< Value >, uint32_t, AccessSpecifier ) >( e, "GetComputedTupleElement"_sid,
            []( const auto& tup, uint32_t index, AccessSpecifier as )
            {
                if( !IsTuple( tup.get() ) )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( tup.get().locationId(),
                        "this wrapped value is not a tuple." );
                    return PoisonValue();
                }

                if( index >= TupleSize( tup ) )
                    return  PoisonValue();
                return builtins::GetComputedTupleElement( tup, index, ValueToEIR( ToValue( as ) ) );
            } );

        ////////////////////////////
        // Decls
        ////////////////////////////
        using decl_tup_type = tuple< TypeWrapper< StringId >, TypeWrapper< Term > >;
        RegisterBuiltinFunc< decl_tup_type ( TypeWrapper< ptr< Decl > > ) >( e, "UnpackDecl"_sid,
            []( const auto& decl ) -> decl_tup_type
            {
                return { decl->name(), decl->type() };
            } );

        RegisterBuiltinFunc< decl_tup_type ( TypeWrapper< ptr< TDecl > > ) >( e, "UnpackDecl"_sid,
            []( const auto& decl ) -> decl_tup_type
            {
                return { decl->name(), decl->type() };
            } );

        RegisterBuiltinFunc< decl_tup_type ( TypeWrapper< ptr< TNamedDecl > > ) >( e, "UnpackDecl"_sid,
            []( const auto& decl ) -> decl_tup_type
            {
                return { decl->name(), decl->type() };
            } );

        RegisterBuiltinFunc< TypeWrapper< Value > ( TypeWrapper< ptr< Context > >, TypeWrapper< Value >, TypeWrapper< pvec > ) >( e, "Invoke"_sid,
            []( const auto& c, const auto& callee, const auto& args )
            {
                auto pInvRule = GetInvocationRule( *c->env(), callee );
                if( !pInvRule )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( callee.get().locationId(),
                        "this value can't be invoked." );
                    return PoisonValue();
                }

                pvec argVec = make_shared< Vector >();
                argVec->reserve( args->terms().size() );

                // Unwrap the args if needed
                for( auto&& t : args->terms() )
                {
                    if( auto unwrapped = FromValue< TypeWrapper< Value > >( *EIRToValue( t ) ) )
                        argVec->append( ValueToEIR( *unwrapped ) );
                    else
                        argVec->append( t );
                }

                return ResolveInvocation( *c.get(), pInvRule, callee, VectorToTuple( TSID( open ), argVec ) );
            } );

        ////////////////////////////
        // Propositions
        ////////////////////////////
        RegisterBuiltinFunc< void ( TypeWrapper< ptr< Propositions > >, TypeWrapper< Term > ) >( e, "PropositionsSetIdentity"_sid,

            []( const TypeWrapper< ptr< Propositions > >& props, const TypeWrapper< Term >& identity )
            {
                props->setIdentity( identity );
            } );


        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< Propositions > >, TypeWrapper< ptr< Context > > ) >( e, "PropositionsParse"_sid,
            []( const TypeWrapper< ptr< Propositions > >& props, const TypeWrapper< ptr< Context > >& c )

            {
                if( !props.get() || !c.get() )
                    return false;

                return props->parse( *c.get() );
            } );

        RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< Propositions > > ) >( e, "PropositionsGetCount"_sid,

            []( const TypeWrapper< ptr< Propositions > >& props ) -> uint32_t
            {
                if( !props.get() )
                    return 0;

                return props->props().size();
            } );

        RegisterBuiltinFunc< Value ( TypeWrapper< ptr< Propositions > >, uint32_t ) >( e, "PropositionsGet"_sid,

            []( const TypeWrapper< ptr< Propositions > >& props, uint32_t i )
            {
                if( !props.get() || i >= props->props().size() )
                    return PoisonValue();

                return props->props()[i];
            } );
    }
}







|
|
>
|
<
>
|
<

|
|
>
|
<
>
|
<

|
>
|
|
>

|
>





|
<
<
|
<

|




|
|






>
|



|



>
|




|
|




|







|
|
|
<
<
<
|
|
|
<
<
|
|
|
|
<
<
|
|





|
|








<




|
|
|





|
>
|
<
|
<

>
|
|
>







|
>








|
>








|
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
    void SetupMiscExtensibilityFuncs( Env& e )
    {
        ////////////////////////////
        // Local variables
        ////////////////////////////
        auto declLocVar = CreateOverloadSet( e, "DeclareLocalVar"_sid );

        RegisterBuiltinFunc< TypeWrapper< Value >( TypeWrapper< ptr< sema::Context > >,
            TypeWrapper< Term >, TypeWrapper< StringId >, TypeWrapper< LocationId > ) >( e,
            declLocVar,
            []( const auto& c, const auto& type, const auto& name,

                const auto& locId ) -> TypeWrapper< Value >
            { return builtins::DeclareLocalVar( *c.get(), type, name, nullopt, locId ); } );


        RegisterBuiltinFunc< TypeWrapper< Value >( TypeWrapper< ptr< sema::Context > >,
            TypeWrapper< Term >, TypeWrapper< StringId >, TypeWrapper< Value >,
            TypeWrapper< LocationId > ) >( e, declLocVar,
            []( const auto& c, const auto& type, const auto& name, const auto& initializer,

                const auto& locId ) -> TypeWrapper< Value >
            { return builtins::DeclareLocalVar( *c.get(), type, name, initializer, locId ); } );


        RegisterBuiltinFunc< TypeWrapper< Value >( TypeWrapper< ptr< sema::Context > >,
            TypeWrapper< Term >, TypeWrapper< StringId >, TypeWrapper< Value >,
            TypeWrapper< LocationId > ) >( e, "DeclareLocalVarWithTypeInference"_sid,
            []( const auto& c, const auto& typeTExpr, const auto& name, const auto& initializer,
                const auto& locId ) -> TypeWrapper< Value >
            {
                return builtins::DeclareLocalVarWithTypeInference(
                    *c.get(), typeTExpr, name, initializer, locId );
            } );

        ////////////////////////////
        // Tuples
        ////////////////////////////
        RegisterBuiltinFunc< uint32_t( CustomPattern< Value, TuplePattern > ) >(


            e, "GetTupleSize"_sid, []( const auto& tup ) { return builtins::TupleSize( tup ); } );


        RegisterBuiltinFunc< uint32_t( TypeWrapper< Value > ) >( e, "GetTupleSize"_sid,
            []( const auto& tup ) -> uint32_t
            {
                if( !IsTuple( tup.get() ) )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        tup.get().locationId(), "this wrapped value is not a tuple." );
                    return 0;
                }

                return builtins::TupleSize( tup.get() );
            } );

        RegisterBuiltinFunc< TypeWrapper< Value >( CustomPattern< Value, TuplePattern >, uint32_t,
            AccessSpecifier ) >( e, "GetComputedTupleElement"_sid,
            []( const auto& tup, uint32_t index, AccessSpecifier as )
            {
                if( index >= TupleSize( tup ) )
                    return PoisonValue();
                return builtins::GetComputedTupleElement( tup, index, ValueToEIR( ToValue( as ) ) );
            } );

        RegisterBuiltinFunc< TypeWrapper< Value >(
            TypeWrapper< Value >, uint32_t, AccessSpecifier ) >( e, "GetComputedTupleElement"_sid,
            []( const auto& tup, uint32_t index, AccessSpecifier as )
            {
                if( !IsTuple( tup.get() ) )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        tup.get().locationId(), "this wrapped value is not a tuple." );
                    return PoisonValue();
                }

                if( index >= TupleSize( tup ) )
                    return PoisonValue();
                return builtins::GetComputedTupleElement( tup, index, ValueToEIR( ToValue( as ) ) );
            } );

        ////////////////////////////
        // Decls
        ////////////////////////////
        using decl_tup_type = tuple< TypeWrapper< StringId >, TypeWrapper< Term > >;
        RegisterBuiltinFunc< decl_tup_type( TypeWrapper< ptr< Decl > > ) >( e, "UnpackDecl"_sid,
            []( const auto& decl ) -> decl_tup_type { return { decl->name(), decl->type() }; } );




        RegisterBuiltinFunc< decl_tup_type( TypeWrapper< ptr< TDecl > > ) >( e, "UnpackDecl"_sid,
            []( const auto& decl ) -> decl_tup_type { return { decl->name(), decl->type() }; } );



        RegisterBuiltinFunc< decl_tup_type( TypeWrapper< ptr< TNamedDecl > > ) >( e,
            "UnpackDecl"_sid,
            []( const auto& decl ) -> decl_tup_type { return { decl->name(), decl->type() }; } );



        RegisterBuiltinFunc< TypeWrapper< Value >( TypeWrapper< ptr< Context > >,
            TypeWrapper< Value >, TypeWrapper< pvec > ) >( e, "Invoke"_sid,
            []( const auto& c, const auto& callee, const auto& args )
            {
                auto pInvRule = GetInvocationRule( *c->env(), callee );
                if( !pInvRule )
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage(
                        callee.get().locationId(), "this value can't be invoked." );
                    return PoisonValue();
                }

                pvec argVec = make_shared< Vector >();
                argVec->reserve( args->terms().size() );

                // Unwrap the args if needed
                for( auto&& t : args->terms() )

                    if( auto unwrapped = FromValue< TypeWrapper< Value > >( *EIRToValue( t ) ) )
                        argVec->append( ValueToEIR( *unwrapped ) );
                    else
                        argVec->append( t );

                return ResolveInvocation(
                    *c.get(), pInvRule, callee, VectorToTuple( TSID( open ), argVec ) );
            } );

        ////////////////////////////
        // Propositions
        ////////////////////////////
        RegisterBuiltinFunc< void( TypeWrapper< ptr< Propositions > >, TypeWrapper< Term > ) >( e,
            "PropositionsSetIdentity"_sid,
            []( const TypeWrapper< ptr< Propositions > >& props,

                const TypeWrapper< Term >& identity ) { props->setIdentity( identity ); } );


        RegisterBuiltinFunc< bool( TypeWrapper< ptr< Propositions > >,
            TypeWrapper< ptr< Context > > ) >( e, "PropositionsParse"_sid,
            []( const TypeWrapper< ptr< Propositions > >& props,
                const TypeWrapper< ptr< Context > >& c )
            {
                if( !props.get() || !c.get() )
                    return false;

                return props->parse( *c.get() );
            } );

        RegisterBuiltinFunc< uint32_t( TypeWrapper< ptr< Propositions > > ) >( e,
            "PropositionsGetCount"_sid,
            []( const TypeWrapper< ptr< Propositions > >& props ) -> uint32_t
            {
                if( !props.get() )
                    return 0;

                return props->props().size();
            } );

        RegisterBuiltinFunc< Value( TypeWrapper< ptr< Propositions > >, uint32_t ) >( e,
            "PropositionsGet"_sid,
            []( const TypeWrapper< ptr< Propositions > >& props, uint32_t i )
            {
                if( !props.get() || i >= props->props().size() )
                    return PoisonValue();

                return props->props()[i];
            } );
    }
} // namespace goose::g0api
Changes to bs/g0api/extensibility/parser.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

#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace goose::g0api
{
    void SetupParserExtensibilityFuncs( Env& e )
    {
        RegisterBuiltinFunc< TypeWrapper< ptr< parse::Resolver > >
            ( TypeWrapper< ptr< sema::Context > >, TypeWrapper< ptr< vector< TermLoc > > > ) >( e, "ResolverCreate"_sid,

            []( const auto& c, const auto& toks ) -> TypeWrapper< ptr< parse::Resolver > >
            {
                auto tokProvider = lex::MakeVectorAdapter( *toks.get() );
                return make_shared< parse::Resolver >( tokProvider, *c.get() );
            } );

        RegisterBuiltinFunc< TypeWrapper< ptr< parse::Parser > >
            ( TypeWrapper< ptr< parse::Resolver > > ) >( e, "ParserCreate"_sid,
            []( const auto& r ) -> TypeWrapper< ptr< parse::Parser > >
            {
                return make_shared< parse::Parser >( r.get() );
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< parse::Parser > > ) >( e, "ParserParseSequence"_sid,
            []( const auto& p )
            {
                p.get()->parseSequence();
            } );

        RegisterBuiltinFunc< TypeWrapper< Value > ( TypeWrapper< ptr< parse::Parser > > ) >( e, "ParserParseParenBlock"_sid,
            []( const auto& p )
            {
                return p.get()->parseParenBlock();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< parse::Parser > > ) >( e, "ParserParseBraceBlock"_sid,
            []( const auto& p )
            {
                return p.get()->parseBraceBlock();
            } );

        RegisterBuiltinFunc< void ( TypeWrapper< ptr< parse::Parser > > ) >( e, "ParserFlushValue"_sid,
            []( const auto& p )
            {
                p.get()->flushValue();
            } );
    }
}













|
|
>






|
|

<
|
<

|
|
|
<
<
|
|
|
|
<
<
|
|
<
<
|
<

|
<
<
|
<

<
>
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
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace goose::g0api
{
    void SetupParserExtensibilityFuncs( Env& e )
    {
        RegisterBuiltinFunc< TypeWrapper< ptr< parse::Resolver > >(
            TypeWrapper< ptr< sema::Context > >, TypeWrapper< ptr< vector< TermLoc > > > ) >( e,
            "ResolverCreate"_sid,
            []( const auto& c, const auto& toks ) -> TypeWrapper< ptr< parse::Resolver > >
            {
                auto tokProvider = lex::MakeVectorAdapter( *toks.get() );
                return make_shared< parse::Resolver >( tokProvider, *c.get() );
            } );

        RegisterBuiltinFunc< TypeWrapper< ptr< parse::Parser > >(
            TypeWrapper< ptr< parse::Resolver > > ) >( e, "ParserCreate"_sid,
            []( const auto& r ) -> TypeWrapper< ptr< parse::Parser > >

            { return make_shared< parse::Parser >( r.get() ); } );


        RegisterBuiltinFunc< void( TypeWrapper< ptr< parse::Parser > > ) >(
            e, "ParserParseSequence"_sid, []( const auto& p ) { p.get()->parseSequence(); } );



        RegisterBuiltinFunc< TypeWrapper< Value >( TypeWrapper< ptr< parse::Parser > > ) >( e,
            "ParserParseParenBlock"_sid,
            []( const auto& p ) { return p.get()->parseParenBlock(); } );



        RegisterBuiltinFunc< bool( TypeWrapper< ptr< parse::Parser > > ) >( e,
            "ParserParseBraceBlock"_sid,


            []( const auto& p ) { return p.get()->parseBraceBlock(); } );


        RegisterBuiltinFunc< void( TypeWrapper< ptr< parse::Parser > > ) >(


            e, "ParserFlushValue"_sid, []( const auto& p ) { p.get()->flushValue(); } );

    }

} // namespace goose::g0api
Changes to bs/g0api/extensibility/termref.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
#ifndef GOOSE_G0API_EXTENSIBILITY_TERMREF_H
#define GOOSE_G0API_EXTENSIBILITY_TERMREF_H

#include "execute/termaddr.h"

namespace goose::g0api
{
    template< typename T >
    class TermRef
    {
        public:
            TermRef( Term* pTerm ) :
                m_pTerm( pTerm )
            {}

            auto* get() const

            {
                return m_pTerm;
            }

            template< typename TT >
            TermRef& operator=( TT&& rhs )
            {
                if( m_pTerm )
                    *m_pTerm = ValueToEIR( ToValue< T >( forward< TT >( rhs ) ) );
                return *this;
            }

        private:
            Term* m_pTerm = nullptr;
    };

}

namespace goose::eir
{
    template< typename T >
    struct Bridge< g0api::TermRef< T > >
    {
        static const Term& Type()
        {
            static auto type = ValueToEIR( ToValue(
                builtins::ReferenceType( GetValueType< T >(), builtins::MutAccessSpecifier() ) ) );
            return type;
        }

        static optional< g0api::TermRef< T > > FromValue( const Value& v )
        {
            auto pTerm = FromValue< Term* >( v );
            return g0api::TermRef< T >( pTerm );
        }
    };
}

#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
#ifndef GOOSE_G0API_EXTENSIBILITY_TERMREF_H
#define GOOSE_G0API_EXTENSIBILITY_TERMREF_H

#include "execute/termaddr.h"

namespace goose::g0api
{
    template< typename T > class TermRef

    {
      public:
        TermRef( Term* pTerm ) :
            m_pTerm( pTerm )

        {

        }

        auto* get() const { return m_pTerm; }


        template< typename TT > TermRef& operator=( TT&& rhs )

        {
            if( m_pTerm )
                *m_pTerm = ValueToEIR( ToValue< T >( forward< TT >( rhs ) ) );
            return *this;
        }

      private:
        Term* m_pTerm = nullptr;
    };
} // namespace goose::g0api


namespace goose::eir
{
    template< typename T > struct Bridge< g0api::TermRef< T > >

    {
        static const Term& Type()
        {
            static auto type = ValueToEIR( ToValue(
                builtins::ReferenceType( GetValueType< T >(), builtins::MutAccessSpecifier() ) ) );
            return type;
        }

        static optional< g0api::TermRef< T > > FromValue( const Value& v )
        {
            auto pTerm = FromValue< Term* >( v );
            return g0api::TermRef< T >( pTerm );
        }
    };
} // namespace goose::eir

#endif
Changes to bs/g0api/extensibility/value.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

#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace goose::g0api
{
    void SetupValueExtensibilityFuncs( Env& e )
    {

        DefineConstant( e, "PoisonValue"_sid, ValueToEIR( ToValue( TypeWrapper< Value >( PoisonValue() ) ) ) );

        ////////////////////////////
        // MkValue overloads
        ////////////////////////////
        auto MkValue = CreateOverloadSet( e, "MkValue"_sid );

        RegisterBuiltinFunc< TypeWrapper< Value > ( TypeWrapper< Term >, TypeWrapper< Term > ) >( e, MkValue,
            []( const auto& type, const auto& val ) -> TypeWrapper< Value >
            {
                return Value( type.get(), val.get() );
            } );

            RegisterBuiltinFunc< TypeWrapper< Value > ( TypeWrapper< Term >, TypeWrapper< ptr< InstrSeq > > ) >( e, MkValue,

            []( const auto& type, const auto& is ) -> TypeWrapper< Value >
            {
                return Value( type.get(), is.get() );
            } );

        RegisterBuiltinFunc< TypeWrapper< Value > ( TypeWrapper< Term > ) >( e, MkValue,
            []( const auto& type ) -> TypeWrapper< Value >
            {
                return Value( type.get(), make_shared< InstrSeq >() );
            } );

        RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< Value > ) >( e, "GetValueType"_sid,
            []( const auto& val ) -> TypeWrapper< Term >
            {
                return val.get().type();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Value >, TermRef< TypeWrapper< Term > > ) >( e, "GetValueVal"_sid,

            []( const auto& val, auto& out )
            {
                if( !val.get().isConstant() )
                    return false;

                out = val.get().val();
                return true;
            } );


        RegisterBuiltinFunc< bool ( TypeWrapper< Value >, TermRef< TypeWrapper< ptr< InstrSeq > > > ) >( e, "GetValueCIR"_sid,
            []( const auto& val, auto& out )
            {
                if( val.get().isConstant() )
                    return false;

                out = val.get().cir();
                return true;
            } );

        RegisterBuiltinFunc< TypeWrapper< LocationId > ( TypeWrapper< Value > ) >( e, "GetValueLocation"_sid,

            []( const auto& val ) -> TypeWrapper< LocationId >
            {
                return val.get().locationId();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Value > ) >( e, "IsPoisonValue"_sid,
            []( const auto& val )
            {
                return val.get().isPoison();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Value > ) >( e, "IsConstantValue"_sid,
            []( const auto& val )
            {
                return val.get().isConstant();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Value > ) >( e, "IsTypeValue"_sid,
            []( const auto& val )
            {
                return val.get().isType();
            } );

        RegisterBuiltinFunc< TypeWrapper< Value > ( TypeWrapper< Value >, TypeWrapper< LocationId > ) >( e, "SetValueLocation"_sid,
            []( const auto& v, const auto& loc ) -> TypeWrapper< Value >
            {
                Value val( v );
                return val.setLocationId( loc );
            } );

        RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< Value > ) >( e, "ValueToEIR"_sid,
            []( const auto& val ) -> TypeWrapper< Term >
            {
                return ValueToEIR( val.get() );
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< Term >, TermRef< TypeWrapper< Value > > ) >( e, "EIRToValue"_sid,

            []( const auto& t, auto& out )
            {
                auto val = EIRToValue( t.get() );

                if( !val )
                    return false;

                out = move( *val );
                return true;
            } );

        RegisterBuiltinFunc< Eager< TypeWrapper< Value > > ( CustomPattern< Value, ValuePatternT > ) >( e, "WrapValue"_sid,

            []( const auto& v ) -> TypeWrapper< Value >
            {
                return v;
            } );
    }
}













>
|






|
|
<
|
<

|
>

<
|
<

|

<
|
<

|
|
|
<
<
<
|
>









>
|









|
>
|
|
<
<
<
|
<
<
|
<

|
<
<
|
<

|
|
|
<
<
|
|






|
|
|
<
<
<
|
>











|
>
|
<
<
<

<
>
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
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;

namespace goose::g0api
{
    void SetupValueExtensibilityFuncs( Env& e )
    {
        DefineConstant(
            e, "PoisonValue"_sid, ValueToEIR( ToValue( TypeWrapper< Value >( PoisonValue() ) ) ) );

        ////////////////////////////
        // MkValue overloads
        ////////////////////////////
        auto MkValue = CreateOverloadSet( e, "MkValue"_sid );

        RegisterBuiltinFunc< TypeWrapper< Value >( TypeWrapper< Term >, TypeWrapper< Term > ) >( e,
            MkValue, []( const auto& type, const auto& val ) -> TypeWrapper< Value >

            { return Value( type.get(), val.get() ); } );


        RegisterBuiltinFunc< TypeWrapper< Value >(
            TypeWrapper< Term >, TypeWrapper< ptr< InstrSeq > > ) >( e, MkValue,
            []( const auto& type, const auto& is ) -> TypeWrapper< Value >

            { return Value( type.get(), is.get() ); } );


        RegisterBuiltinFunc< TypeWrapper< Value >( TypeWrapper< Term > ) >( e, MkValue,
            []( const auto& type ) -> TypeWrapper< Value >

            { return Value( type.get(), make_shared< InstrSeq >() ); } );


        RegisterBuiltinFunc< TypeWrapper< Term >( TypeWrapper< Value > ) >( e, "GetValueType"_sid,
            []( const auto& val ) -> TypeWrapper< Term > { return val.get().type(); } );




        RegisterBuiltinFunc< bool( TypeWrapper< Value >, TermRef< TypeWrapper< Term > > ) >( e,
            "GetValueVal"_sid,
            []( const auto& val, auto& out )
            {
                if( !val.get().isConstant() )
                    return false;

                out = val.get().val();
                return true;
            } );

        RegisterBuiltinFunc< bool( TypeWrapper< Value >,
            TermRef< TypeWrapper< ptr< InstrSeq > > > ) >( e, "GetValueCIR"_sid,
            []( const auto& val, auto& out )
            {
                if( val.get().isConstant() )
                    return false;

                out = val.get().cir();
                return true;
            } );

        RegisterBuiltinFunc< TypeWrapper< LocationId >( TypeWrapper< Value > ) >( e,
            "GetValueLocation"_sid,
            []( const auto& val ) -> TypeWrapper< LocationId > { return val.get().locationId(); } );




        RegisterBuiltinFunc< bool( TypeWrapper< Value > ) >(


            e, "IsPoisonValue"_sid, []( const auto& val ) { return val.get().isPoison(); } );


        RegisterBuiltinFunc< bool( TypeWrapper< Value > ) >(


            e, "IsConstantValue"_sid, []( const auto& val ) { return val.get().isConstant(); } );


        RegisterBuiltinFunc< bool( TypeWrapper< Value > ) >(
            e, "IsTypeValue"_sid, []( const auto& val ) { return val.get().isType(); } );



        RegisterBuiltinFunc< TypeWrapper< Value >(
            TypeWrapper< Value >, TypeWrapper< LocationId > ) >( e, "SetValueLocation"_sid,
            []( const auto& v, const auto& loc ) -> TypeWrapper< Value >
            {
                Value val( v );
                return val.setLocationId( loc );
            } );

        RegisterBuiltinFunc< TypeWrapper< Term >( TypeWrapper< Value > ) >( e, "ValueToEIR"_sid,
            []( const auto& val ) -> TypeWrapper< Term > { return ValueToEIR( val.get() ); } );




        RegisterBuiltinFunc< bool( TypeWrapper< Term >, TermRef< TypeWrapper< Value > > ) >( e,
            "EIRToValue"_sid,
            []( const auto& t, auto& out )
            {
                auto val = EIRToValue( t.get() );

                if( !val )
                    return false;

                out = move( *val );
                return true;
            } );

        RegisterBuiltinFunc< Eager< TypeWrapper< Value > >(
            CustomPattern< Value, ValuePatternT > ) >(
            e, "WrapValue"_sid, []( const auto& v ) -> TypeWrapper< Value > { return v; } );



    }

} // namespace goose::g0api
Changes to bs/g0api/g0api.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
#ifndef GOOSE_G0_API_H
#define GOOSE_G0_API_H

#include "sema/sema.h"
#include "builtins/builtins.h"

namespace goose::g0api
{
    using namespace sema;
    using namespace cir;
    using namespace builtins;
}

#include "types.h"

// TODO_SSA reenable
//#include "cgapi/cgapi.h"
//#include "extensibility/extensibility.h"

namespace goose::g0api
{
    extern void SetupApiString( Env& e );
    extern void SetupApiCompiler( Env& e );

    static inline void SetupG0Api( Env& e )
    {
        SetupApiString( e );
        SetupApiCompiler( e );
        // TODO_SSA reenable
        //SetupApiCodeGen( e );
        //SetupApiExtensibility( e );
    }
}

#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
#ifndef GOOSE_G0_API_H
#define GOOSE_G0_API_H

#include "sema/sema.h"
#include "builtins/builtins.h"

namespace goose::g0api
{
    using namespace sema;
    using namespace cir;
    using namespace builtins;
} // namespace goose::g0api

#include "types.h"

// TODO_SSA reenable
// #include "cgapi/cgapi.h"
// #include "extensibility/extensibility.h"

namespace goose::g0api
{
    extern void SetupApiString( Env& e );
    extern void SetupApiCompiler( Env& e );

    static inline void SetupG0Api( Env& e )
    {
        SetupApiString( e );
        SetupApiCompiler( e );
        // TODO_SSA reenable
        // SetupApiCodeGen( e );
        // SetupApiExtensibility( e );
    }
} // namespace goose::g0api

#endif
Changes to bs/g0api/string.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
#include "g0api/g0api.h"

using namespace goose;

namespace goose::g0api
{
    void SetupApiString( Env& e )
    {
        // TODO: these doesn't really belongs here, to be moved the day I deal with
        // the builtin io api
        RegisterBuiltinFunc< void ( uint8_t ) >( e, "PrintAsciiChar"_sid,
            []( const uint8_t c )
            {
                putchar( c );
            } );

        RegisterBuiltinFunc< void () >( e, "FlushOutput"_sid,
            []()
            {
                fflush( nullptr );
            } );

        RegisterBuiltinFunc< Eager< string > ( string, string ) >( e, "strcat"_sid,
            []( const string& str1, const string& str2 )
            {
                return str1 + str2;
            } );

        // Strip space characters from the beginning of the first string, if any,
        // then checks whether the first provided string begins with the second one.
        // This is needed for the proto-front end, as a way to check the commandline
        // for the direct execution escape marker without using the standard library
        // (which isn't setup at that early stage).
        RegisterBuiltinFunc< Eager< bool > ( string, string ) >( e, "StrStartsWith"_sid,
            []( const string& str1, const string& str2 )
            {
                string_view sv( str1 );
                sv.remove_prefix( min( sv.find_first_not_of(" "), sv.size() ) );
                return sv.starts_with( str2 );
            } );

        // Checks whether the first provided string ends with the second one.
        // Needed for early front end code.
        RegisterBuiltinFunc< Eager< bool > ( string, string ) >( e, "StrEndsWith"_sid,
            []( const string& str1, const string& str2 )
            {
                string_view sv( str1 );
                return sv.ends_with( str2 );
            } );

        // Strip space characters from the beginning of the string, then
        // remove everything from the provided string until the first space,
        // then trim all the spaces until the next non-space character.
        // This is needed for the proto-front end, as a way to strip the direct
        // execution escape marker without using the standard library
        // (which isn't setup at that early stage).
        RegisterBuiltinFunc< Eager< string > ( string ) >( e, "StrStripFirstSubStr"_sid,
            []( const string& str )
            {
                string_view sv( str );
                sv.remove_prefix( min( sv.find_first_not_of( " " ), sv.size() ) );
                sv.remove_prefix( min( sv.find_first_of( " " ), sv.size() ) );
                sv.remove_prefix( min( sv.find_first_not_of( " " ), sv.size() ) );
                return string( sv.begin(), sv.end() );
            } );
    }
}










|
|
|
<
<
<
|
<
|
<
<
<
|
|
|
<
<
<
|
|
|
<
|
|



|





|






|
<
|
|
|
<
|









|
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
#include "g0api/g0api.h"

using namespace goose;

namespace goose::g0api
{
    void SetupApiString( Env& e )
    {
        // TODO: these doesn't really belongs here, to be moved the day I deal with
        // the builtin io api
        RegisterBuiltinFunc< void( uint8_t ) >(
            e, "PrintAsciiChar"_sid, []( const uint8_t c ) { putchar( c ); } );




        RegisterBuiltinFunc< void() >( e, "FlushOutput"_sid, []() { fflush( nullptr ); } );





        RegisterBuiltinFunc< Eager< string >( string, string ) >(
            e, "strcat"_sid, []( const string& str1, const string& str2 ) { return str1 + str2; } );




        // Strip space characters from the beginning of the first string, if any, then checks
        // whether the first provided string begins with the second one. This is needed for the
        // proto-front end, as a way to check the commandline for the direct execution escape marker

        // without using the standard library (which isn't setup at that early stage).
        RegisterBuiltinFunc< Eager< bool >( string, string ) >( e, "StrStartsWith"_sid,
            []( const string& str1, const string& str2 )
            {
                string_view sv( str1 );
                sv.remove_prefix( min( sv.find_first_not_of( " " ), sv.size() ) );
                return sv.starts_with( str2 );
            } );

        // Checks whether the first provided string ends with the second one.
        // Needed for early front end code.
        RegisterBuiltinFunc< Eager< bool >( string, string ) >( e, "StrEndsWith"_sid,
            []( const string& str1, const string& str2 )
            {
                string_view sv( str1 );
                return sv.ends_with( str2 );
            } );

        // Strip space characters from the beginning of the string, then remove everything from the

        // provided string until the first space, then trim all the spaces until the next non-space
        // character. This is needed for the proto-front end, as a way to strip the direct execution
        // escape marker without using the standard library (which isn't setup at that early stage).

        RegisterBuiltinFunc< Eager< string >( string ) >( e, "StrStripFirstSubStr"_sid,
            []( const string& str )
            {
                string_view sv( str );
                sv.remove_prefix( min( sv.find_first_not_of( " " ), sv.size() ) );
                sv.remove_prefix( min( sv.find_first_of( " " ), sv.size() ) );
                sv.remove_prefix( min( sv.find_first_not_of( " " ), sv.size() ) );
                return string( sv.begin(), sv.end() );
            } );
    }
} // namespace goose::g0api
Changes to bs/g0api/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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include "g0api/g0api.h"
#include "eir/eir.h"

using namespace goose;
using namespace goose::g0api;

namespace
{
    template< typename T >
    void SetupWrapperForType( Env& e, StringId name, const ptr< OverloadSet >& pEquals, const ptr< OverloadSet >& pNotEquals )

    {
        DefineConstant( e, name, GetValueType< TypeWrapper< T > >() );


        using reftype = CustomPattern< Value, ReferenceType::PatternMutableOfType< TypeWrapper< T > > >;

        auto type = ValueToEIR( ToValue( builtins::ReferenceType( GetValueType< TypeWrapper< T > >(), MutAccessSpecifier() ) ) );
        RegisterBuiltinFunc< Intrinsic< Value ( reftype ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r )
            {
                // Do nothing: the types we're wrapping are default initialized anyway.
                // The only purpose of this Initialize overload is to let goose know that
                // the type has a default initialization.
                return Value( GetValueType< void >(), 0U );
            } );

        using constreftype = CustomPattern<
            TypeWrapper< T >, ReferenceType::PatternConstOfType< TypeWrapper< T > > >;
        RegisterBuiltinFunc< bool ( constreftype, constreftype ) >( e, pEquals,
            []( const TypeWrapper< T >& lhs, const TypeWrapper< T >& rhs )
            {
                return lhs.get() == rhs.get();
            } );

        RegisterBuiltinFunc< bool ( constreftype, constreftype ) >( e, pNotEquals,
            []( const TypeWrapper< T >& lhs, const TypeWrapper< T >& rhs )
            {
                return lhs.get() != rhs.get();
            } );

        RegisterBuiltinFunc< string ( TypeWrapper< T > ) >( e, "ToString"_sid,
            []( const TypeWrapper< T >& t )
            {
                stringstream sstr;
                sstr << t.get();
                return sstr.str();
            } );
    }
}

namespace goose::g0api
{
    void SetupTypeWrappers( Env& e )
    {
        auto pEquals = GetOverloadSet( e, "operator_equals"_sid );
        auto pNotEquals = GetOverloadSet( e, "operator_not_equals"_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
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
#include "g0api/g0api.h"
#include "eir/eir.h"

using namespace goose;
using namespace goose::g0api;

namespace
{
    template< typename T >
    void SetupWrapperForType( Env& e, StringId name, const ptr< OverloadSet >& pEquals,
        const ptr< OverloadSet >& pNotEquals )
    {
        DefineConstant( e, name, GetValueType< TypeWrapper< T > >() );

        using reftype =
            CustomPattern< Value, ReferenceType::PatternMutableOfType< TypeWrapper< T > > >;
        auto type = ValueToEIR( ToValue(
            builtins::ReferenceType( GetValueType< TypeWrapper< T > >(), MutAccessSpecifier() ) ) );
        RegisterBuiltinFunc< Intrinsic< Value( reftype ) > >( e, e.extInitialize(),
            []( auto&& c, const Value& r )
            {
                // Do nothing: the types we're wrapping are default initialized anyway.
                // The only purpose of this Initialize overload is to let goose know that
                // the type has a default initialization.
                return Value( GetValueType< void >(), 0U );
            } );

        using constreftype = CustomPattern< TypeWrapper< T >,
            ReferenceType::PatternConstOfType< TypeWrapper< T > > >;
        RegisterBuiltinFunc< bool( constreftype, constreftype ) >( e, pEquals,
            []( const TypeWrapper< T >& lhs, const TypeWrapper< T >& rhs )

            { return lhs.get() == rhs.get(); } );


        RegisterBuiltinFunc< bool( constreftype, constreftype ) >( e, pNotEquals,
            []( const TypeWrapper< T >& lhs, const TypeWrapper< T >& rhs )

            { return lhs.get() != rhs.get(); } );


        RegisterBuiltinFunc< string( TypeWrapper< T > ) >( e, "ToString"_sid,
            []( const TypeWrapper< T >& t )
            {
                stringstream sstr;
                sstr << t.get();
                return sstr.str();
            } );
    }
} // namespace

namespace goose::g0api
{
    void SetupTypeWrappers( Env& e )
    {
        auto pEquals = GetOverloadSet( e, "operator_equals"_sid );
        auto pNotEquals = GetOverloadSet( e, "operator_not_equals"_sid );
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
        SetupWrapperForType< ptr< Terminator > >( e, "Terminator"_sid, pEquals, pNotEquals );

        ////////////////////////////
        // Context
        ////////////////////////////
        SetupWrapperForType< ptr< Context > >( e, "Context"_sid, pEquals, pNotEquals );

        RegisterBuiltinFunc< TypeWrapper< Value > ( TypeWrapper< ptr< Context > > ) >( e, "ContextGetBuilder"_sid,

            []( const TypeWrapper< ptr< Context > >& c )
            {
                return c->builder();
            } );

        RegisterBuiltinFunc< bool ( TypeWrapper< ptr< Context > >, TermRef< TypeWrapper< ptr< CFG > > > ) >( e, "ContextGetCFG"_sid,
            []( const TypeWrapper< ptr< Context > >& c, TermRef< TypeWrapper< ptr< CFG > > >& out )
            {
                auto cfg = GetCFG( *c.get() );
                if( !cfg )
                    return false;

                out = cfg;
                return true;
            } );

        SetupWrapperForType< ptr< VisibilityScope > >( e, "VisibilityScope"_sid, pEquals, pNotEquals );

        SetupWrapperForType< ptr< Scope > >( e, "Scope"_sid, pEquals, pNotEquals );

        ////////////////////////////
        // ReferenceType
        ////////////////////////////
        SetupWrapperForType< ptr< ReferenceType > >( e, "RefTypeDesc"_sid, pEquals, pNotEquals );


        RegisterBuiltinFunc< bool ( TypeWrapper< Term >, TermRef< TypeWrapper< ptr< ReferenceType > > > ) >( e, "EIRToRefTypeDesc"_sid,
            []( const TypeWrapper< Term >& t, TermRef< TypeWrapper< ptr< ReferenceType > > >& out )
            {
                auto val = EIRToValue( t.get() );
                if( !val )
                    return false;

                auto refType = FromValue< ReferenceType >( *val );
                if( !refType )
                    return false;

                out = make_shared< ReferenceType >( move( *refType ) );
                return true;
            } );

        RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< ptr< ReferenceType > > ) >( e, "RefTypeDescToEIR"_sid,

            []( const TypeWrapper< ptr< ReferenceType > >& rt ) -> TypeWrapper< Term >
            {
                return ValueToEIR( ToValue( *rt.get() ) );
            } );

        RegisterBuiltinFunc< TypeWrapper< ptr< ReferenceType > > ( TypeWrapper< Term >, TypeWrapper< Term > ) >( e, "MkRefTypeDesc"_sid,


            []( const TypeWrapper< Term >& type, const TypeWrapper< Term >& bhv ) -> TypeWrapper< ptr< ReferenceType > >
            {
                return make_shared< ReferenceType >( type, bhv );
            } );

        RegisterBuiltinFunc< tuple< TypeWrapper< Term >, TypeWrapper< Term > > ( TypeWrapper< ptr< ReferenceType > > ) >( e, "UnpackRefTypeDesc"_sid,

            []( const TypeWrapper< ptr< ReferenceType > >& refType ) -> tuple< TypeWrapper< Term >, TypeWrapper< Term > >
            {

                return { refType->type(), refType->accessSpec() };
            } );


        RegisterBuiltinFunc< Intrinsic< Address ( CustomPattern< Value, ReferenceType::PatternAny > ) > >( e, "RefToAddr"_sid,

            []( auto&& c, Value ref )
            {
                ref.type() = GetValueType< Address >();
                return ref;
            } );

        ////////////////////////////
        // AccessSpecifier
        ////////////////////////////
        RegisterBuiltinFunc< bool ( TypeWrapper< Term >, TermRef< AccessSpecifier > ) >( e, "EIRToAccessSpecifier"_sid,

            []( const TypeWrapper< Term >& t, TermRef< AccessSpecifier >& out )
            {
                auto val = EIRToValue( t.get() );
                if( !val )
                    return false;

                auto as = FromValue< AccessSpecifier >( *val );
                if( !as )
                    return false;

                out = move( *as );
                return true;
            } );

        RegisterBuiltinFunc< TypeWrapper< Term > ( AccessSpecifier ) >( e, "AccessSpecifierToEIR"_sid,
            []( const AccessSpecifier& as ) -> TypeWrapper< Term >
            {
                return ValueToEIR( ToValue( as ) );
            } );

        RegisterBuiltinFunc< AccessSpecifier ( TypeWrapper< Term >, TypeWrapper< Term > ) >( e, "MkAccessSpecifier"_sid,

            []( const TypeWrapper< Term >& mode, const TypeWrapper< Term >& lifetime )
            {
                return AccessSpecifier( mode, lifetime );
            } );

        RegisterBuiltinFunc< tuple< TypeWrapper< Term >, TypeWrapper< Term > > ( AccessSpecifier ) >( e, "UnpackAccessSpecifier"_sid,

            []( const AccessSpecifier& as ) -> tuple< TypeWrapper< Term >, TypeWrapper< Term > >
            {
                return { as.mode(), as.lifetime() };
            } );

        ////////////////////////////
        // Lifetime
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< StringId > ( Lifetime ) >( e, "LifetimeToStringId"_sid,
            []( const Lifetime& lt ) -> TypeWrapper< StringId >
            {
                return lt;
            } );

        RegisterBuiltinFunc< Lifetime ( TypeWrapper< StringId > ) >( e, "StringIdToLifetime"_sid,
            []( const TypeWrapper< StringId >& sid ) -> TypeWrapper< StringId >
            {
                return sid;
            } );

        ////////////////////////////
        // Tuple
        ////////////////////////////
        RegisterBuiltinFunc< BigInt ( CustomPattern< Value, TuplePattern > ) >( e, "GetTupleSize"_sid,

            []( const Value& tup )
            {
                return BigInt::FromU32( TupleSize( tup ) );
            } );

        RegisterBuiltinFunc< TypeWrapper< Value > ( CustomPattern< Value, TuplePattern >, BigInt ) >( e, "GetConstantTupleElement"_sid,
            []( const auto& tup, BigInt index )
            {
                auto result = EIRToValue( builtins::GetConstantTupleElement( tup, index.toU64() ) );
                return result ? *result : PoisonValue();
            } );

        ////////////////////////////
        // Decl stuff
        ////////////////////////////
        SetupWrapperForType< ptr< Decl > >( e, "WrappedDecl"_sid, pEquals, pNotEquals );
        SetupWrapperForType< ptr< TDecl > >( e, "WrappedTDecl"_sid, pEquals, pNotEquals );
        SetupWrapperForType< ptr< TNamedDecl > >( e, "WrappedTNamedDecl"_sid, pEquals, pNotEquals );

        ////////////////////////////
        // Parsing stuff
        ////////////////////////////
        SetupWrapperForType< ptr< vector< TermLoc > > >( e, "Tokens"_sid, pEquals, pNotEquals );
        SetupWrapperForType< ptr< parse::Resolver > >( e, "Resolver"_sid, pEquals, pNotEquals );
        SetupWrapperForType< ptr< parse::Parser > >( e, "Parser"_sid, pEquals, pNotEquals );
    }
}







|
>
|
|
<
<
|
|










|
>







>
|














|
>

<
|
<

|
>
>
|
<
|
<

|
>
|
<
>
|
<

>
|
>









|
>














|
|
<
|
<

|
>

<
|
<

|
>

<
|
<




|
|
|
<
<
<
|
|
<
<
<




|
>
|
|
<
<
|
|




















|
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
        SetupWrapperForType< ptr< Terminator > >( e, "Terminator"_sid, pEquals, pNotEquals );

        ////////////////////////////
        // Context
        ////////////////////////////
        SetupWrapperForType< ptr< Context > >( e, "Context"_sid, pEquals, pNotEquals );

        RegisterBuiltinFunc< TypeWrapper< Value >( TypeWrapper< ptr< Context > > ) >( e,
            "ContextGetBuilder"_sid,
            []( const TypeWrapper< ptr< Context > >& c ) { return c->builder(); } );



        RegisterBuiltinFunc< bool( TypeWrapper< ptr< Context > >,
            TermRef< TypeWrapper< ptr< CFG > > > ) >( e, "ContextGetCFG"_sid,
            []( const TypeWrapper< ptr< Context > >& c, TermRef< TypeWrapper< ptr< CFG > > >& out )
            {
                auto cfg = GetCFG( *c.get() );
                if( !cfg )
                    return false;

                out = cfg;
                return true;
            } );

        SetupWrapperForType< ptr< VisibilityScope > >(
            e, "VisibilityScope"_sid, pEquals, pNotEquals );
        SetupWrapperForType< ptr< Scope > >( e, "Scope"_sid, pEquals, pNotEquals );

        ////////////////////////////
        // ReferenceType
        ////////////////////////////
        SetupWrapperForType< ptr< ReferenceType > >( e, "RefTypeDesc"_sid, pEquals, pNotEquals );

        RegisterBuiltinFunc< bool( TypeWrapper< Term >,
            TermRef< TypeWrapper< ptr< ReferenceType > > > ) >( e, "EIRToRefTypeDesc"_sid,
            []( const TypeWrapper< Term >& t, TermRef< TypeWrapper< ptr< ReferenceType > > >& out )
            {
                auto val = EIRToValue( t.get() );
                if( !val )
                    return false;

                auto refType = FromValue< ReferenceType >( *val );
                if( !refType )
                    return false;

                out = make_shared< ReferenceType >( move( *refType ) );
                return true;
            } );

        RegisterBuiltinFunc< TypeWrapper< Term >( TypeWrapper< ptr< ReferenceType > > ) >( e,
            "RefTypeDescToEIR"_sid,
            []( const TypeWrapper< ptr< ReferenceType > >& rt ) -> TypeWrapper< Term >

            { return ValueToEIR( ToValue( *rt.get() ) ); } );


        RegisterBuiltinFunc< TypeWrapper< ptr< ReferenceType > >(
            TypeWrapper< Term >, TypeWrapper< Term > ) >( e, "MkRefTypeDesc"_sid,
            []( const TypeWrapper< Term >& type,
                const TypeWrapper< Term >& bhv ) -> TypeWrapper< ptr< ReferenceType > >

            { return make_shared< ReferenceType >( type, bhv ); } );


        RegisterBuiltinFunc< tuple< TypeWrapper< Term >, TypeWrapper< Term > >(
            TypeWrapper< ptr< ReferenceType > > ) >( e, "UnpackRefTypeDesc"_sid,
            []( const TypeWrapper< ptr< ReferenceType > >& refType )

                -> tuple< TypeWrapper< Term >, TypeWrapper< Term > >
            { return { refType->type(), refType->accessSpec() }; } );


        RegisterBuiltinFunc<
            Intrinsic< Address( CustomPattern< Value, ReferenceType::PatternAny > ) > >( e,
            "RefToAddr"_sid,
            []( auto&& c, Value ref )
            {
                ref.type() = GetValueType< Address >();
                return ref;
            } );

        ////////////////////////////
        // AccessSpecifier
        ////////////////////////////
        RegisterBuiltinFunc< bool( TypeWrapper< Term >, TermRef< AccessSpecifier > ) >( e,
            "EIRToAccessSpecifier"_sid,
            []( const TypeWrapper< Term >& t, TermRef< AccessSpecifier >& out )
            {
                auto val = EIRToValue( t.get() );
                if( !val )
                    return false;

                auto as = FromValue< AccessSpecifier >( *val );
                if( !as )
                    return false;

                out = move( *as );
                return true;
            } );

        RegisterBuiltinFunc< TypeWrapper< Term >( AccessSpecifier ) >( e,
            "AccessSpecifierToEIR"_sid, []( const AccessSpecifier& as ) -> TypeWrapper< Term >

            { return ValueToEIR( ToValue( as ) ); } );


        RegisterBuiltinFunc< AccessSpecifier( TypeWrapper< Term >, TypeWrapper< Term > ) >( e,
            "MkAccessSpecifier"_sid,
            []( const TypeWrapper< Term >& mode, const TypeWrapper< Term >& lifetime )

            { return AccessSpecifier( mode, lifetime ); } );


        RegisterBuiltinFunc< tuple< TypeWrapper< Term >, TypeWrapper< Term > >( AccessSpecifier ) >(
            e, "UnpackAccessSpecifier"_sid,
            []( const AccessSpecifier& as ) -> tuple< TypeWrapper< Term >, TypeWrapper< Term > >

            { return { as.mode(), as.lifetime() }; } );


        ////////////////////////////
        // Lifetime
        ////////////////////////////
        RegisterBuiltinFunc< TypeWrapper< StringId >( Lifetime ) >( e, "LifetimeToStringId"_sid,
            []( const Lifetime& lt ) -> TypeWrapper< StringId > { return lt; } );




        RegisterBuiltinFunc< Lifetime( TypeWrapper< StringId > ) >( e, "StringIdToLifetime"_sid,
            []( const TypeWrapper< StringId >& sid ) -> TypeWrapper< StringId > { return sid; } );




        ////////////////////////////
        // Tuple
        ////////////////////////////
        RegisterBuiltinFunc< BigInt( CustomPattern< Value, TuplePattern > ) >( e,
            "GetTupleSize"_sid,
            []( const Value& tup ) { return BigInt::FromU32( TupleSize( tup ) ); } );



        RegisterBuiltinFunc< TypeWrapper< Value >( CustomPattern< Value, TuplePattern >, BigInt ) >(
            e, "GetConstantTupleElement"_sid,
            []( const auto& tup, BigInt index )
            {
                auto result = EIRToValue( builtins::GetConstantTupleElement( tup, index.toU64() ) );
                return result ? *result : PoisonValue();
            } );

        ////////////////////////////
        // Decl stuff
        ////////////////////////////
        SetupWrapperForType< ptr< Decl > >( e, "WrappedDecl"_sid, pEquals, pNotEquals );
        SetupWrapperForType< ptr< TDecl > >( e, "WrappedTDecl"_sid, pEquals, pNotEquals );
        SetupWrapperForType< ptr< TNamedDecl > >( e, "WrappedTNamedDecl"_sid, pEquals, pNotEquals );

        ////////////////////////////
        // Parsing stuff
        ////////////////////////////
        SetupWrapperForType< ptr< vector< TermLoc > > >( e, "Tokens"_sid, pEquals, pNotEquals );
        SetupWrapperForType< ptr< parse::Resolver > >( e, "Resolver"_sid, pEquals, pNotEquals );
        SetupWrapperForType< ptr< parse::Parser > >( e, "Parser"_sid, pEquals, pNotEquals );
    }
} // namespace goose::g0api
Changes to bs/g0api/types.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
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
    // TODO_SSA reenable
    /*template<>
    struct TypeWrapperTraits< ptr< codegen::Module > >
    {
        static auto typeId() { return "CGModule"_sid; }
    };*/

    template<>
    struct TypeWrapperTraits< LocationId >
    {
        static auto typeId() { return "LocationId"_sid; }
    };

    template<>
    struct TypeWrapperTraits< StringId >
    {
        static auto typeId() { return "StringId"_sid; }
    };

    template<>
    struct TypeWrapperTraits< Hole >
    {
        static auto typeId() { return "Hole"_sid; }
    };

    template<>
    struct TypeWrapperTraits< AnyTerm >
    {
        static auto typeId() { return "AnyTerm"_sid; }
    };

    template<>
    struct TypeWrapperTraits< VecOfLength >
    {
        static auto typeId() { return "VecOfLength"_sid; }
    };

    template<>
    struct TypeWrapperTraits< APSInt >
    {
        static auto typeId() { return "FixedInt"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< cir::BasicBlock > >
    {
        static auto typeId() { return "BasicBlock"_sid; }
    };

    template<>
    struct TypeWrapperTraits< wptr< cir::BasicBlock > >
    {
        static auto typeId() { return "BasicBlock"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< cir::Instruction > >
    {
        static auto typeId() { return "Instruction"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< cir::InstrSeq > >
    {
        static auto typeId() { return "InstrSeq"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< cir::Terminator > >
    {
        static auto typeId() { return "Terminator"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< ReferenceType > >
    {
        static auto typeId() { return "ReferenceType"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< VisibilityScope > >
    {
        static auto typeId() { return "VisibilityScope"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< Scope > >
    {
        static auto typeId() { return "Scope"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< parse::Resolver > >
    {
        static auto typeId() { return "Resolver"_sid; }
    };

    template<>
    struct TypeWrapperTraits< ptr< parse::Parser > >
    {
        static auto typeId() { return "Parser"_sid; }
    };
}


#endif







<
|




<
|




<
|




<
|




<
|




<
|




<
|




<
|




<
|




<
|




<
|




<
|




<
|




<
|




<
|




<
|



<
>


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
    // TODO_SSA reenable
    /*template<>
    struct TypeWrapperTraits< ptr< codegen::Module > >
    {
        static auto typeId() { return "CGModule"_sid; }
    };*/


    template<> struct TypeWrapperTraits< LocationId >
    {
        static auto typeId() { return "LocationId"_sid; }
    };


    template<> struct TypeWrapperTraits< StringId >
    {
        static auto typeId() { return "StringId"_sid; }
    };


    template<> struct TypeWrapperTraits< Hole >
    {
        static auto typeId() { return "Hole"_sid; }
    };


    template<> struct TypeWrapperTraits< AnyTerm >
    {
        static auto typeId() { return "AnyTerm"_sid; }
    };


    template<> struct TypeWrapperTraits< VecOfLength >
    {
        static auto typeId() { return "VecOfLength"_sid; }
    };


    template<> struct TypeWrapperTraits< APSInt >
    {
        static auto typeId() { return "FixedInt"_sid; }
    };


    template<> struct TypeWrapperTraits< ptr< cir::BasicBlock > >
    {
        static auto typeId() { return "BasicBlock"_sid; }
    };


    template<> struct TypeWrapperTraits< wptr< cir::BasicBlock > >
    {
        static auto typeId() { return "BasicBlock"_sid; }
    };


    template<> struct TypeWrapperTraits< ptr< cir::Instruction > >
    {
        static auto typeId() { return "Instruction"_sid; }
    };


    template<> struct TypeWrapperTraits< ptr< cir::InstrSeq > >
    {
        static auto typeId() { return "InstrSeq"_sid; }
    };


    template<> struct TypeWrapperTraits< ptr< cir::Terminator > >
    {
        static auto typeId() { return "Terminator"_sid; }
    };


    template<> struct TypeWrapperTraits< ptr< ReferenceType > >
    {
        static auto typeId() { return "ReferenceType"_sid; }
    };


    template<> struct TypeWrapperTraits< ptr< VisibilityScope > >
    {
        static auto typeId() { return "VisibilityScope"_sid; }
    };


    template<> struct TypeWrapperTraits< ptr< Scope > >
    {
        static auto typeId() { return "Scope"_sid; }
    };


    template<> struct TypeWrapperTraits< ptr< parse::Resolver > >
    {
        static auto typeId() { return "Resolver"_sid; }
    };


    template<> struct TypeWrapperTraits< ptr< parse::Parser > >
    {
        static auto typeId() { return "Parser"_sid; }
    };

} // namespace goose::builtins

#endif
Changes to bs/lex/comment.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 "lex.h"

using namespace goose;
using namespace goose::lex;

// TODO handle strings nested inside of comments,
// so that we ignore any comment marker appearing as part
// of a commented out string literal.

// Skip the current comment. We expect that the first / of the introducer
// was already consumed. This skips any combination of nested/overlapping block and line
// comments.
// In particular, comment blocks that start or end in the middle of a line comment
// are handled properly.
// Line comments aren't interrupted by the presence of an end of comment block later
// on the same line either.
void Lexer::skipComment()
{
    uint32_t blockCommentDepth = 0;
    uint32_t lastCommentLine = 0;

    auto c = m_input.get();
    if( c == '*' )
        ++blockCommentDepth;
    else
        lastCommentLine = m_lineNum;

    auto isInComment = [&]()
    {
        return blockCommentDepth || lastCommentLine == m_lineNum;
    };

    while( m_input.good() && isInComment() )
    {
        auto c = m_input.get();

        if( c == '\n' )
        {





|
<
|

|
|
<
|
<
|
<











<
<
|
<







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
#include "lex.h"

using namespace goose;
using namespace goose::lex;

// TODO handle strings nested inside of comments, so that we ignore any comment marker appearing as

// part of a commented out string literal.

// Skip the current comment. We expect that the first / of the introducer was already consumed. This
// skips any combination of nested/overlapping block and line comments. In particular, comment

// blocks that start or end in the middle of a line comment are handled properly. Line comments

// aren't interrupted by the presence of an end of comment block later on the same line either.

void Lexer::skipComment()
{
    uint32_t blockCommentDepth = 0;
    uint32_t lastCommentLine = 0;

    auto c = m_input.get();
    if( c == '*' )
        ++blockCommentDepth;
    else
        lastCommentLine = m_lineNum;



    auto isInComment = [&]() { return blockCommentDepth || lastCommentLine == m_lineNum; };


    while( m_input.good() && isInComment() )
    {
        auto c = m_input.get();

        if( c == '\n' )
        {
Changes to bs/lex/intlit.cpp.
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

TermLoc Lexer::readIntegerLiteralBin( const LocationPoint& ls )
{
    // skip leading zeros
    while( m_input.good() )
    {
        auto c = m_input.peek();
        if( c!= '0' )
            break;

        m_input.get();
    }

    string str;
    while( m_input.good() )







|







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

TermLoc Lexer::readIntegerLiteralBin( const LocationPoint& ls )
{
    // skip leading zeros
    while( m_input.good() )
    {
        auto c = m_input.peek();
        if( c != '0' )
            break;

        m_input.get();
    }

    string str;
    while( m_input.good() )
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
{
    uint32_t consumedLen = 2;

    // skip leading zeros
    while( m_input.good() )
    {
        auto c = m_input.peek();
        if( c!= '0' )
            break;

        m_input.get();
        ++consumedLen;
    }

    string str;







|







89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
{
    uint32_t consumedLen = 2;

    // skip leading zeros
    while( m_input.good() )
    {
        auto c = m_input.peek();
        if( c != '0' )
            break;

        m_input.get();
        ++consumedLen;
    }

    string str;
Changes to bs/lex/lex.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef GOOSE_LEX_H
#define GOOSE_LEX_H

#include "util/util.h"
#include "eir/eir.h"
#include "diagnostics/diagnostics.h"

namespace goose::lex
{
    using namespace util;
    using namespace eir;
    using namespace diagnostics;
}

#include "tokenprovider.h"
#include "lexer.h"
#include "vectoradapter.h"

#include "vectoradapter.inl"













|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef GOOSE_LEX_H
#define GOOSE_LEX_H

#include "util/util.h"
#include "eir/eir.h"
#include "diagnostics/diagnostics.h"

namespace goose::lex
{
    using namespace util;
    using namespace eir;
    using namespace diagnostics;
} // namespace goose::lex

#include "tokenprovider.h"
#include "lexer.h"
#include "vectoradapter.h"

#include "vectoradapter.inl"

Changes to bs/lex/lexer.cpp.
1
2
3
4
5
6
7
8
9


10
11
12
13
14
15
16
#include "lex.h"

using namespace goose;
using namespace goose::lex;

Lexer::Lexer( istream& input, const string& filename ) :
    m_filename( Location::GetCachedFilename( filename ) ),
    m_input( input )
{}



void Lexer::newLine()
{
    ++m_lineNum;
    m_lastLineBreakOffset = getCurrentPos();
}









<
>
>







1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
16
17
#include "lex.h"

using namespace goose;
using namespace goose::lex;

Lexer::Lexer( istream& input, const string& filename ) :
    m_filename( Location::GetCachedFilename( filename ) ),
    m_input( input )

{
}

void Lexer::newLine()
{
    ++m_lineNum;
    m_lastLineBreakOffset = getCurrentPos();
}

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
        str += c;
    }

    return TermLoc( StringId( str ), ls.toLoc( str.size() ) );
}

// TODO this is inefficient, put that stuff into a lookup table.
// Also maybe instead of being hardcoded in the lexer it could be
// configurable, so that when a new operator is defined it can reserve
// its characters here (either as a first char only, or not)

// Currently, - $ ~ and ! can only be used as the first character of an operator identifier,
// so that they can be used as the first character of a prefix operator (we don't want to make it mandatory to insert
// a space between an infix and a prefix operator).
bool Lexer::isOpFirstChar( char c )
{
    static string charTable = "+*/%&|^<>=?:.,-$!~";
    return charTable.find_first_of( c ) != string::npos;
}

bool Lexer::isOpChar( char c, char firstChar )
{
    // $ is accepted if the identifier started with a $,
    // because we have a $$ operator. Bit hackish, bleh
    if( firstChar == '$' && c == '$' )
        return true;

    static string charTable = "+*/%&|^<>=?:.,";
    return charTable.find_first_of( c ) != string::npos;
}

TermLoc Lexer::readOperatorIdentifier( const LocationPoint& ls )
{
    // TODO this will need to be reworked some day to handle unicode
    // identifiers.

    string str;

    auto firstChar = m_input.get();
    str += firstChar;

    while( m_input.good() )







|
<
|

|
|
|








|
|









|
<







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
        str += c;
    }

    return TermLoc( StringId( str ), ls.toLoc( str.size() ) );
}

// TODO this is inefficient, put that stuff into a lookup table.
// Also maybe instead of being hardcoded in the lexer it could be configurable, so that when a new

// operator is defined it can reserve its characters here (either as a first char only, or not)

// Currently, - $ ~ and ! can only be used as the first character of an operator identifier, so that
// they can be used as the first character of a prefix operator (we don't want to make it mandatory
// to insert a space between an infix and a prefix operator).
bool Lexer::isOpFirstChar( char c )
{
    static string charTable = "+*/%&|^<>=?:.,-$!~";
    return charTable.find_first_of( c ) != string::npos;
}

bool Lexer::isOpChar( char c, char firstChar )
{
    // $ is accepted if the identifier started with a $, because we have a $$ operator. Bit hackish,
    // bleh
    if( firstChar == '$' && c == '$' )
        return true;

    static string charTable = "+*/%&|^<>=?:.,";
    return charTable.find_first_of( c ) != string::npos;
}

TermLoc Lexer::readOperatorIdentifier( const LocationPoint& ls )
{
    // TODO this will need to be reworked some day to handle unicode identifiers.


    string str;

    auto firstChar = m_input.get();
    str += firstChar;

    while( m_input.good() )
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
    return TermLoc( StringId( str ), ls.toLoc( str.size() ) );
}

TermLoc Lexer::readSemicolon( const LocationPoint& ls )
{
    auto offset = getCurrentPos();

    // The semicolon is just an identifier, but with a special lexing rule:
    // consecutive semicolons are collapsed into one.
    while( m_input.good() )
    {
        skipSpacingIfAny();
        auto c = m_input.peek();
        if( c != ';' )
            break;








|
|







119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
    return TermLoc( StringId( str ), ls.toLoc( str.size() ) );
}

TermLoc Lexer::readSemicolon( const LocationPoint& ls )
{
    auto offset = getCurrentPos();

    // The semicolon is just an identifier, but with a special lexing rule: consecutive semicolons
    // are collapsed into one.
    while( m_input.good() )
    {
        skipSpacingIfAny();
        auto c = m_input.peek();
        if( c != ';' )
            break;

194
195
196
197
198
199
200
201
202
203
204
        case ';':
            return readSemicolon( ls );
    }

    if( isOpFirstChar( c ) )
        return readOperatorIdentifier( ls );

    DiagnosticsManager::GetInstance().emitLexerErrorMessage(
        ls.toLoc(), "unrecognized token." );
    return nullopt;
}







|
<


193
194
195
196
197
198
199
200

201
202
        case ';':
            return readSemicolon( ls );
    }

    if( isOpFirstChar( c ) )
        return readOperatorIdentifier( ls );

    DiagnosticsManager::GetInstance().emitLexerErrorMessage( ls.toLoc(), "unrecognized token." );

    return nullopt;
}
Changes to bs/lex/lexer.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
#ifndef GOOSE_LEX_LEXER_H
#define GOOSE_LEX_LEXER_H

namespace goose::lex
{
    class Lexer : public TokenProvider
    {
        public:
            Lexer( istream& input, const string& filename );

            virtual bool eos() const override final;

            virtual optional< TermLoc > consume() override final;
            virtual optional< TermLoc > lookAhead( size_t distance = 0 ) override final;

            virtual LocationId currentLocation() override final;

            virtual pos_type position() const override final;
            virtual void setPosition( pos_type position ) override final;

        private:
            LocationPoint currentLocationPoint() const;

            auto getCurrentPos() const
            {
                // Use this instead of tellg because tellg refuses
                // to give a correct position once we have reached
                // eof (or merely gazed upon it through peek())
                // which is some fucking bullshit imo
                return m_input.rdbuf()->pubseekoff( 0, ios::cur );
            }

            void newLine();

            void skipComment();
            void skipSpacingIfAny();

            optional< char32_t > readCodePoint();
            TermLoc readCharacterLiteral( const LocationPoint& ls );
            TermLoc readStringLiteral( const LocationPoint& ls );

            TermLoc readIntegerLiteral( const LocationPoint& ls );
            TermLoc readIntegerLiteralBin( const LocationPoint& ls );
            TermLoc readIntegerLiteralDec( const LocationPoint& ls, uint32_t consumedLen );
            TermLoc readIntegerLiteralHex( const LocationPoint& ls );
            TermLoc readAlphanumericIdentifier( const LocationPoint& ls );

            static bool isOpFirstChar( char c );
            static bool isOpChar( char c, char firstChar );
            TermLoc readOperatorIdentifier( const LocationPoint& ls );

            TermLoc readSemicolon( const LocationPoint& ls );

            optional< TermLoc > readToken();

            const string& m_filename;
            istream& m_input;

            using lookahead_cache_entry = tuple< TermLoc, istream::pos_type, uint32_t >;
            deque< lookahead_cache_entry > m_lookAheadCache;

            istream::pos_type m_lastLineBreakOffset = 0;
            istream::pos_type m_commitedLastLineBreakOffset = 0;

            uint32_t m_lineNum = 1;
            uint32_t m_commitedLineNum = 1;
    };
}

#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
64
65
66
67
68
69
#ifndef GOOSE_LEX_LEXER_H
#define GOOSE_LEX_LEXER_H

namespace goose::lex
{
    class Lexer : public TokenProvider
    {
      public:
        Lexer( istream& input, const string& filename );

        virtual bool eos() const override final;

        virtual optional< TermLoc > consume() override final;
        virtual optional< TermLoc > lookAhead( size_t distance = 0 ) override final;

        virtual LocationId currentLocation() override final;

        virtual pos_type position() const override final;
        virtual void setPosition( pos_type position ) override final;

      private:
        LocationPoint currentLocationPoint() const;

        auto getCurrentPos() const
        {
            // Use this instead of tellg because tellg refuses to give a correct position once

            // we have reached eof (or merely gazed upon it through peek()) which is some
            // fucking bullshit imo
            return m_input.rdbuf()->pubseekoff( 0, ios::cur );
        }

        void newLine();

        void skipComment();
        void skipSpacingIfAny();

        optional< char32_t > readCodePoint();
        TermLoc readCharacterLiteral( const LocationPoint& ls );
        TermLoc readStringLiteral( const LocationPoint& ls );

        TermLoc readIntegerLiteral( const LocationPoint& ls );
        TermLoc readIntegerLiteralBin( const LocationPoint& ls );
        TermLoc readIntegerLiteralDec( const LocationPoint& ls, uint32_t consumedLen );
        TermLoc readIntegerLiteralHex( const LocationPoint& ls );
        TermLoc readAlphanumericIdentifier( const LocationPoint& ls );

        static bool isOpFirstChar( char c );
        static bool isOpChar( char c, char firstChar );
        TermLoc readOperatorIdentifier( const LocationPoint& ls );

        TermLoc readSemicolon( const LocationPoint& ls );

        optional< TermLoc > readToken();

        const string& m_filename;
        istream& m_input;

        using lookahead_cache_entry = tuple< TermLoc, istream::pos_type, uint32_t >;
        deque< lookahead_cache_entry > m_lookAheadCache;

        istream::pos_type m_lastLineBreakOffset = 0;
        istream::pos_type m_commitedLastLineBreakOffset = 0;

        uint32_t m_lineNum = 1;
        uint32_t m_commitedLineNum = 1;
    };
} // namespace goose::lex

#endif
Changes to bs/lex/lookahead.cpp.
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
    return get< 0 >( m_lookAheadCache[distance] );
}

LocationPoint Lexer::currentLocationPoint() const
{
    auto offset = getCurrentPos();

    return LocationPoint(
        m_filename,
        offset,
        m_lineNum,
        offset - m_lastLineBreakOffset + 1
    );
}

LocationId Lexer::currentLocation()
{
    if( auto la = lookAhead() )
        return la->second;

    auto offset = getCurrentPos();

    return Location::Create(
        m_filename,
        offset, 1,
        m_lineNum,
        offset - m_lastLineBreakOffset + 1
    );
}

TokenProvider::pos_type Lexer::position() const
{
    return { getCurrentPos(), static_cast< size_t >( m_commitedLastLineBreakOffset ), m_commitedLineNum };

}

void Lexer::setPosition( pos_type position )
{
    m_lookAheadCache.clear();

    auto&& [offst, lbOffst, lnum] = position;







|
<
<
<
<
<









|
<
<
<
<
<




|
>







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
    return get< 0 >( m_lookAheadCache[distance] );
}

LocationPoint Lexer::currentLocationPoint() const
{
    auto offset = getCurrentPos();

    return LocationPoint( m_filename, offset, m_lineNum, offset - m_lastLineBreakOffset + 1 );





}

LocationId Lexer::currentLocation()
{
    if( auto la = lookAhead() )
        return la->second;

    auto offset = getCurrentPos();

    return Location::Create( m_filename, offset, 1, m_lineNum, offset - m_lastLineBreakOffset + 1 );





}

TokenProvider::pos_type Lexer::position() const
{
    return { getCurrentPos(), static_cast< size_t >( m_commitedLastLineBreakOffset ),
        m_commitedLineNum };
}

void Lexer::setPosition( pos_type position )
{
    m_lookAheadCache.clear();

    auto&& [offst, lbOffst, lnum] = position;
Changes to bs/lex/strlit.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include "lex.h"

using namespace goose;
using namespace goose::lex;

namespace
{
    optional< char32_t > ReadOctalCodePoint( istream& in )
    {
        char32_t codePoint = 0;

        for( int i = 0 ; i < 3; ++i )
        {
            if( in.eof() )
                return nullopt;

            auto c = in.get();
            if( c < '0' || c > '7' )
                return nullopt;

            codePoint <<= 3;
            codePoint |= c - '0';
        }

        return codePoint;
    }

    optional< char32_t > ReadHexCodePoint( istream& in, int len )
    {
        char32_t codePoint = 0;

        for( int i = 0 ; i < len; ++i )
        {
            if( in.eof() )
                return nullopt;

            auto c = in.get();

            if( isdigit( c ) )











|



















|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include "lex.h"

using namespace goose;
using namespace goose::lex;

namespace
{
    optional< char32_t > ReadOctalCodePoint( istream& in )
    {
        char32_t codePoint = 0;

        for( int i = 0; i < 3; ++i )
        {
            if( in.eof() )
                return nullopt;

            auto c = in.get();
            if( c < '0' || c > '7' )
                return nullopt;

            codePoint <<= 3;
            codePoint |= c - '0';
        }

        return codePoint;
    }

    optional< char32_t > ReadHexCodePoint( istream& in, int len )
    {
        char32_t codePoint = 0;

        for( int i = 0; i < len; ++i )
        {
            if( in.eof() )
                return nullopt;

            auto c = in.get();

            if( isdigit( c ) )
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
        }
        else if( codePoint <= 0xffff )
        {
            str += static_cast< uint8_t >( ( ( codePoint >> 12 ) & 0xf ) | 0xe0 );
            codePoint <<= ( 32 - 12 );
            trailingCharsCount = 2;
        }
        else if( codePoint <= 0x10ffff )
        {
            str += static_cast< uint8_t >( ( ( codePoint >> 18 ) & 7 ) | 0xf0 );
            codePoint <<= ( 32 - 18 );
            trailingCharsCount = 3;
        }
        else
            return false;

        while( trailingCharsCount-- )
        {
            str += static_cast< uint8_t >( ( ( codePoint >> ( 32 - 6 ) ) & 0x3f ) | 0x80 );
            codePoint <<= 6;
        }

        return true;
    }
}

TermLoc Lexer::readCharacterLiteral( const LocationPoint& ls )
{
    m_input.get();

    u32string str;
    bool complete = false;







|
















|







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
        }
        else if( codePoint <= 0xffff )
        {
            str += static_cast< uint8_t >( ( ( codePoint >> 12 ) & 0xf ) | 0xe0 );
            codePoint <<= ( 32 - 12 );
            trailingCharsCount = 2;
        }
        else if( codePoint <= 0x10'ffff )
        {
            str += static_cast< uint8_t >( ( ( codePoint >> 18 ) & 7 ) | 0xf0 );
            codePoint <<= ( 32 - 18 );
            trailingCharsCount = 3;
        }
        else
            return false;

        while( trailingCharsCount-- )
        {
            str += static_cast< uint8_t >( ( ( codePoint >> ( 32 - 6 ) ) & 0x3f ) | 0x80 );
            codePoint <<= 6;
        }

        return true;
    }
} // namespace

TermLoc Lexer::readCharacterLiteral( const LocationPoint& ls )
{
    m_input.get();

    u32string str;
    bool complete = false;
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
            m_input.get();

            // Read an utf-8 character and decode it.
            int len = 0;
            if( c >= 0xf8 )
            {
                DiagnosticsManager::GetInstance().emitLexerErrorMessage(
                    currentLocationPoint().toLoc( 1 ),
                    "invalid utf-8 code unit." );
                error = true;
            }
            else if( c >= 0xf0 )
            {
                len = 3;
                codePoint = c & 7;
            }







|
<







132
133
134
135
136
137
138
139

140
141
142
143
144
145
146
            m_input.get();

            // Read an utf-8 character and decode it.
            int len = 0;
            if( c >= 0xf8 )
            {
                DiagnosticsManager::GetInstance().emitLexerErrorMessage(
                    currentLocationPoint().toLoc( 1 ), "invalid utf-8 code unit." );

                error = true;
            }
            else if( c >= 0xf0 )
            {
                len = 3;
                codePoint = c & 7;
            }
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
                    newLine();
                    break;
                }

                if( c >= 0xc0 && !error )
                {
                    DiagnosticsManager::GetInstance().emitLexerErrorMessage(
                        currentLocationPoint().toLoc( 1 ),
                        "invalid utf-8 code unit." );
                    error = true;
                }

                codePoint <<= 6;
                codePoint |= c & 0x3f;


            }

            if( m_input.fail() )
                break;

            str += codePoint;
        }
    }

    if( !complete )
    {
        auto loc = ls.toLoc( str.size() + 1 );
        DiagnosticsManager::GetInstance().emitLexerErrorMessage( loc,
            "unterminated char literal." );

        return TermLoc( U' ', loc );
    }

    auto loc = ls.toLoc( str.size() + 2 );

    // TODO currently we define ct_char as being a u32 containing one unicode codepoint.
    // Maybe it would be more intuitive to instead have it be a grapheme literal that contains
    // one unicode grapheme cluster, the idea being that a single thing displayed in the source should
    // be able to be represented by this literal. But that would involve reading up on unicode and
    // grapheme clusters and shit and implement it so let's say fuck it for the bs compiler
    if( str.size() > 1 && !error )
    {
        auto loc = ls.toLoc( str.size() + 1 );
        DiagnosticsManager::GetInstance().emitLexerErrorMessage( loc,
            "a char literal can contain only one unicode codepoint." );
    }

    return TermLoc( str.front(), loc );
}

TermLoc Lexer::readStringLiteral( const LocationPoint& ls )
{







|
<





<
<












|
|







|
|





|
|







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
                    newLine();
                    break;
                }

                if( c >= 0xc0 && !error )
                {
                    DiagnosticsManager::GetInstance().emitLexerErrorMessage(
                        currentLocationPoint().toLoc( 1 ), "invalid utf-8 code unit." );

                    error = true;
                }

                codePoint <<= 6;
                codePoint |= c & 0x3f;


            }

            if( m_input.fail() )
                break;

            str += codePoint;
        }
    }

    if( !complete )
    {
        auto loc = ls.toLoc( str.size() + 1 );
        DiagnosticsManager::GetInstance().emitLexerErrorMessage(
            loc, "unterminated char literal." );

        return TermLoc( U' ', loc );
    }

    auto loc = ls.toLoc( str.size() + 2 );

    // TODO currently we define ct_char as being a u32 containing one unicode codepoint.
    // Maybe it would be more intuitive to instead have it be a grapheme literal that contains one
    // unicode grapheme cluster, the idea being that a single thing displayed in the source should
    // be able to be represented by this literal. But that would involve reading up on unicode and
    // grapheme clusters and shit and implement it so let's say fuck it for the bs compiler
    if( str.size() > 1 && !error )
    {
        auto loc = ls.toLoc( str.size() + 1 );
        DiagnosticsManager::GetInstance().emitLexerErrorMessage(
            loc, "a char literal can contain only one unicode codepoint." );
    }

    return TermLoc( str.front(), loc );
}

TermLoc Lexer::readStringLiteral( const LocationPoint& ls )
{
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
282
283
284
285
286
287
288
289
        if( m_input.fail() )
            break;
    }

    if( !complete )
    {
        auto loc = ls.toLoc( str.size() + 1 );
        DiagnosticsManager::GetInstance().emitLexerErrorMessage( loc,
            "unterminated string literal." );

        return TermLoc( str, loc );
    }

    return TermLoc( str, ls.toLoc( str.size() + 2 ) );
}

optional< char32_t > Lexer::readCodePoint()
{
    auto escLS = currentLocationPoint();
    auto c = m_input.get();

    // Just handle the same set of escape sequences as C++
    // for now, I cba having a profound and detailed reflection
    // about fucking escape characters right now.
    if( c != '\\')
        return c;

    c = m_input.peek();

    if( isdigit( c ) )
    {
        auto codePoint = ReadOctalCodePoint( m_input );
        if( !codePoint )
        {
            DiagnosticsManager::GetInstance().emitLexerErrorMessage(
                escLS.toLoc( 4 ),
                "invalid octal character code." );
        }

        return codePoint;
    }
    else
    {
        m_input.get();







|
|












|
<
|
|










<
|







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
282
283
        if( m_input.fail() )
            break;
    }

    if( !complete )
    {
        auto loc = ls.toLoc( str.size() + 1 );
        DiagnosticsManager::GetInstance().emitLexerErrorMessage(
            loc, "unterminated string literal." );

        return TermLoc( str, loc );
    }

    return TermLoc( str, ls.toLoc( str.size() + 2 ) );
}

optional< char32_t > Lexer::readCodePoint()
{
    auto escLS = currentLocationPoint();
    auto c = m_input.get();

    // Just handle the same set of escape sequences as C++ for now, I cba having a profound and

    // detailed reflection about fucking escape characters right now.
    if( c != '\\' )
        return c;

    c = m_input.peek();

    if( isdigit( c ) )
    {
        auto codePoint = ReadOctalCodePoint( m_input );
        if( !codePoint )
        {
            DiagnosticsManager::GetInstance().emitLexerErrorMessage(

                escLS.toLoc( 4 ), "invalid octal character code." );
        }

        return codePoint;
    }
    else
    {
        m_input.get();
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378

            case 'x':
            {
                auto codePoint = ReadHexCodePoint( m_input, 2 );
                if( !codePoint )
                {
                    DiagnosticsManager::GetInstance().emitLexerErrorMessage(
                        escLS.toLoc( 4 ),
                        "invalid hex character code." );
                }

                return codePoint;
            }
            break;

            case 'u':
            {
                auto codePoint = ReadHexCodePoint( m_input, 4 );
                if( !codePoint )
                {
                    DiagnosticsManager::GetInstance().emitLexerErrorMessage(
                        escLS.toLoc( 6 ),
                        "invalid hex character code." );
                }

                return codePoint;
            }
            break;

            case 'U':
            {
                auto codePoint = ReadHexCodePoint( m_input, 8 );
                if( !codePoint )
                {
                    DiagnosticsManager::GetInstance().emitLexerErrorMessage(
                        escLS.toLoc( 10 ),
                        "invalid hex character code." );
                }

                if( codePoint > 0x10ffff  )
                {
                    DiagnosticsManager::GetInstance().emitLexerErrorMessage(
                        escLS.toLoc( 10 ),
                        "invalid code point value." );
                }

                return codePoint;
            }
            break;

            default:
                DiagnosticsManager::GetInstance().emitLexerErrorMessage(
                    escLS.toLoc( 2 ),
                    "invalid escape sequence." );
                break;
        }
    }

    return nullopt;
}







<
|












<
|












|
<


|


|
<








|
<






313
314
315
316
317
318
319

320
321
322
323
324
325
326
327
328
329
330
331
332

333
334
335
336
337
338
339
340
341
342
343
344
345
346

347
348
349
350
351
352

353
354
355
356
357
358
359
360
361

362
363
364
365
366
367

            case 'x':
            {
                auto codePoint = ReadHexCodePoint( m_input, 2 );
                if( !codePoint )
                {
                    DiagnosticsManager::GetInstance().emitLexerErrorMessage(

                        escLS.toLoc( 4 ), "invalid hex character code." );
                }

                return codePoint;
            }
            break;

            case 'u':
            {
                auto codePoint = ReadHexCodePoint( m_input, 4 );
                if( !codePoint )
                {
                    DiagnosticsManager::GetInstance().emitLexerErrorMessage(

                        escLS.toLoc( 6 ), "invalid hex character code." );
                }

                return codePoint;
            }
            break;

            case 'U':
            {
                auto codePoint = ReadHexCodePoint( m_input, 8 );
                if( !codePoint )
                {
                    DiagnosticsManager::GetInstance().emitLexerErrorMessage(
                        escLS.toLoc( 10 ), "invalid hex character code." );

                }

                if( codePoint > 0x10'ffff )
                {
                    DiagnosticsManager::GetInstance().emitLexerErrorMessage(
                        escLS.toLoc( 10 ), "invalid code point value." );

                }

                return codePoint;
            }
            break;

            default:
                DiagnosticsManager::GetInstance().emitLexerErrorMessage(
                    escLS.toLoc( 2 ), "invalid escape sequence." );

                break;
        }
    }

    return nullopt;
}
Changes to bs/lex/tokenprovider.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_LEX_TOKENPROVIDER_H
#define GOOSE_LEX_TOKENPROVIDER_H

namespace goose::lex
{
    class TokenProvider
    {
        public:
            virtual ~TokenProvider() {}

            virtual bool eos() const = 0;

            virtual optional< TermLoc > consume() = 0;
            virtual optional< TermLoc > lookAhead( size_t distance = 0 ) = 0;

            virtual LocationId currentLocation() = 0;

            // For backtracking
            using pos_type = tuple< size_t, size_t, uint32_t >;
            virtual pos_type position() const = 0;
            virtual void setPosition( pos_type position ) = 0;
    };
}

#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
#ifndef GOOSE_LEX_TOKENPROVIDER_H
#define GOOSE_LEX_TOKENPROVIDER_H

namespace goose::lex
{
    class TokenProvider
    {
      public:
        virtual ~TokenProvider() {}

        virtual bool eos() const = 0;

        virtual optional< TermLoc > consume() = 0;
        virtual optional< TermLoc > lookAhead( size_t distance = 0 ) = 0;

        virtual LocationId currentLocation() = 0;

        // For backtracking
        using pos_type = tuple< size_t, size_t, uint32_t >;
        virtual pos_type position() const = 0;
        virtual void setPosition( pos_type position ) = 0;
    };
} // namespace goose::lex

#endif
Changes to bs/lex/vectoradapter.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
#ifndef GOOSE_LEX_VECTORADAPTER_H
#define GOOSE_LEX_VECTORADAPTER_H

namespace goose::lex
{
    template< typename V >
    class VectorAdapter : public TokenProvider
    {
        public:
            VectorAdapter( const V& v ) :
                m_vector( v )
            {}



            virtual bool eos() const override final;

            virtual optional< TermLoc > consume() override final;
            virtual optional< TermLoc > lookAhead( size_t distance = 0 ) override final;

            virtual LocationId currentLocation() override final;

            virtual pos_type position() const override final;
            virtual void setPosition( pos_type position ) override final;

        private:
            const V& m_vector;

            size_t m_index = 0;
    };

    template< typename V >
    auto MakeVectorAdapter( V&& vec )
    {
        return make_shared< VectorAdapter< V > >( forward< V >( vec ) );
    }
}

#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
#ifndef GOOSE_LEX_VECTORADAPTER_H
#define GOOSE_LEX_VECTORADAPTER_H

namespace goose::lex
{

    template< typename V > class VectorAdapter : public TokenProvider
    {
      public:
        VectorAdapter( const V& v ) :
            m_vector( v )

        {
        }

        virtual bool eos() const override final;

        virtual optional< TermLoc > consume() override final;
        virtual optional< TermLoc > lookAhead( size_t distance = 0 ) override final;

        virtual LocationId currentLocation() override final;

        virtual pos_type position() const override final;
        virtual void setPosition( pos_type position ) override final;

      private:
        const V& m_vector;

        size_t m_index = 0;
    };

    template< typename V > auto MakeVectorAdapter( V&& vec )

    {
        return make_shared< VectorAdapter< V > >( forward< V >( vec ) );
    }
} // namespace goose::lex

#endif
Changes to bs/lex/vectoradapter.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
#ifndef GOOSE_LEX_VECTORADAPTER_INL
#define GOOSE_LEX_VECTORADAPTER_INL

namespace goose::lex
{
    template< typename V >
    bool VectorAdapter< V >::eos() const
    {
        return m_index >= m_vector.size();
    }

    template< typename V >
    optional< TermLoc > VectorAdapter< V >::consume()
    {
        if( eos() )
            return nullopt;

        return m_vector[m_index++];
    }

    template< typename V >
    optional< TermLoc > VectorAdapter< V >::lookAhead( size_t distance )
    {
        if( ( m_index + distance ) >= m_vector.size() )
            return nullopt;

        return m_vector[m_index + distance];
    }

    // This is a bit approximative, might have to figure out a way to improve this.
    // Especially since almost all non top-level code is parsed through a vector adapter.
    template< typename V >
    LocationId VectorAdapter< V >::currentLocation()
    {
        if( m_vector.empty() )
            return {};

        if( eos() )
            return m_vector.back().second;

        return lookAhead()->second;
    }

    template< typename V >
    TokenProvider::pos_type VectorAdapter< V >::position() const
    {
        return { m_index, 0, 0 };
    }

    template< typename V >
    void VectorAdapter< V >::setPosition( pos_type position )
    {
        m_index = get< 0 >( position );
    }
}

#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
#ifndef GOOSE_LEX_VECTORADAPTER_INL
#define GOOSE_LEX_VECTORADAPTER_INL

namespace goose::lex
{
    template< typename V > bool VectorAdapter< V >::eos() const

    {
        return m_index >= m_vector.size();
    }


    template< typename V > optional< TermLoc > VectorAdapter< V >::consume()
    {
        if( eos() )
            return nullopt;

        return m_vector[m_index++];
    }


    template< typename V > optional< TermLoc > VectorAdapter< V >::lookAhead( size_t distance )
    {
        if( ( m_index + distance ) >= m_vector.size() )
            return nullopt;

        return m_vector[m_index + distance];
    }

    // This is a bit approximative, might have to figure out a way to improve this.
    // Especially since almost all non top-level code is parsed through a vector adapter.

    template< typename V > LocationId VectorAdapter< V >::currentLocation()
    {
        if( m_vector.empty() )
            return {};

        if( eos() )
            return m_vector.back().second;

        return lookAhead()->second;
    }


    template< typename V > TokenProvider::pos_type VectorAdapter< V >::position() const
    {
        return { m_index, 0, 0 };
    }


    template< typename V > void VectorAdapter< V >::setPosition( pos_type position )
    {
        m_index = get< 0 >( position );
    }
} // namespace goose::lex

#endif
Changes to bs/parse/bracketblock.cpp.
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
{
    if( m_lastValue )
    {
        // Maybe a bit hackish: if there's currently a left value,
        // this bracket block is part of an expression (ie "requires" and such)
        // and should be parsed as a proposition list.
        auto [props, loc] = tokenizeBracketBlock();
        return ToValue( TypeWrapper< ptr< Propositions > >( props ) )
            .setLocationId( loc );
    }

    auto cfg = GetCFG( context() );
    if( !cfg )
    {
        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
            resolver()->currentLocation(), "ghost code blocks aren't allowed here.", 0 );







|
<







36
37
38
39
40
41
42
43

44
45
46
47
48
49
50
{
    if( m_lastValue )
    {
        // Maybe a bit hackish: if there's currently a left value,
        // this bracket block is part of an expression (ie "requires" and such)
        // and should be parsed as a proposition list.
        auto [props, loc] = tokenizeBracketBlock();
        return ToValue( TypeWrapper< ptr< Propositions > >( props ) ).setLocationId( loc );

    }

    auto cfg = GetCFG( context() );
    if( !cfg )
    {
        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
            resolver()->currentLocation(), "ghost code blocks aren't allowed here.", 0 );
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
    if( preds )
        preds = make_shared< Propositions >( *preds );
    else
    {
        preds = make_shared< Propositions >();
        auto& c = context();

        // Create a new identity for the predicates, which imports everything from the parent identity
        // and also makes @val visible as a placeholder for the type's value.
        preds->setIdentity( VEC( Env::NewUniqueId() ) );
        c.env()->addVisibilityRule( c.identity(), preds->identity() );

        auto name = "@val"_sid;
        auto typeTerm = ValueToEIR( type );

        auto phVal = ValueToEIR( BuildComputedValue( typeTerm, cir::Placeholder( typeTerm, name, {} ) ) );

        auto valuePlaceholderIdentity = AppendToVectorTerm( preds->identity(), TERM( name ) );
        c.env()->storeValue( valuePlaceholderIdentity, ANYTERM( _ ), phVal );

        // The placeholder is also available as the default lhs value, so "@val" can be omitted in many cases

        auto defLhsIdentity = AppendToVectorTerm( preds->identity(), TSID( 0_def_lhs ) );
        c.env()->storeValue( defLhsIdentity, ANYTERM( _ ), move( phVal ) );
    }

    auto [props, loc] = tokenizeBracketBlock();
    preds->append( move( *props ) );

    loc = Location::CreateSpanningLocation( type.locationId(), loc );

    pushValue( Value( type.type(), InjectPredicatesIntoStdType( type.val(), preds ) )
        .setLocationId( loc ) );
    return true;
}







|
|





>
|




|
>










|


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
    if( preds )
        preds = make_shared< Propositions >( *preds );
    else
    {
        preds = make_shared< Propositions >();
        auto& c = context();

        // Create a new identity for the predicates, which imports everything from the parent
        // identity and also makes @val visible as a placeholder for the type's value.
        preds->setIdentity( VEC( Env::NewUniqueId() ) );
        c.env()->addVisibilityRule( c.identity(), preds->identity() );

        auto name = "@val"_sid;
        auto typeTerm = ValueToEIR( type );
        auto phVal =
            ValueToEIR( BuildComputedValue( typeTerm, cir::Placeholder( typeTerm, name, {} ) ) );

        auto valuePlaceholderIdentity = AppendToVectorTerm( preds->identity(), TERM( name ) );
        c.env()->storeValue( valuePlaceholderIdentity, ANYTERM( _ ), phVal );

        // The placeholder is also available as the default lhs value, so "@val" can be omitted in
        // many cases
        auto defLhsIdentity = AppendToVectorTerm( preds->identity(), TSID( 0_def_lhs ) );
        c.env()->storeValue( defLhsIdentity, ANYTERM( _ ), move( phVal ) );
    }

    auto [props, loc] = tokenizeBracketBlock();
    preds->append( move( *props ) );

    loc = Location::CreateSpanningLocation( type.locationId(), loc );

    pushValue( Value( type.type(), InjectPredicatesIntoStdType( type.val(), preds ) )
                   .setLocationId( loc ) );
    return true;
}
Changes to bs/parse/func.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
{
    if( !parseFuncType( returnType, paramsDecl ) )
        return false;

    if( IsTExpr( *peekLastValue() ) )
        return parseTemplateFunctionExpression( paramsDecl );

    // Check if a brace block follows, in which case this is a function declaration. Otherwise, it's just a function type.

    auto next = m_resolver->lookAheadUnresolved();
    if( !next )
    {
        // Just leave the type as is, nothing to do.
        return true;
    }

    auto decomp = Decompose( next->first, Val< Delimiter >() );
    if( !decomp || *decomp != Delimiter::OpenBrace )
    {
        // Just leave the type as is, nothing to do.
        return true;
    }

    return parseFunctionDeclExpr( paramsDecl );
}

bool Parser::parseFunctionDeclExpr( const Value& paramsDecl )
{

    auto loc = Location::CreateSpanningLocation( peekLastValue()->locationId(), paramsDecl.locationId() );

    auto funcType = *FromValue< FuncType >( *popValue() );

    auto pBody = getFuncBody();
    if( !pBody )
        return false;

    const auto& c = context();

    // TODO: see if we can replace this unique id with something that is just unique within the current
    // function. This would avoid wild changes in the mangled names based on unrelated definitions being added
    // elsewhere.
    auto identity = DuplicateVectorTerm( c.identity() );
    get< pvec >( identity )->terms().back() = TERM( StringId( Env::NewUniqueId() ) );

    auto bodyContext = context();

    auto func = ToValue( BuildFunc( context(), funcType, c.identity(),
        identity, paramsDecl, move( pBody ), bodyContext ) ).setLocationId( loc );


    DiagnosticsContext dc( 0, true );
    VerbosityContext( Verbosity::Normal, true );

    auto newFunc = CompileFunc( c, func );
    pushValue( move( newFunc ) );
    return true;







|
>



















>
|









|
|
|





|
|
>







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
{
    if( !parseFuncType( returnType, paramsDecl ) )
        return false;

    if( IsTExpr( *peekLastValue() ) )
        return parseTemplateFunctionExpression( paramsDecl );

    // Check if a brace block follows, in which case this is a function declaration. Otherwise, it's
    // just a function type.
    auto next = m_resolver->lookAheadUnresolved();
    if( !next )
    {
        // Just leave the type as is, nothing to do.
        return true;
    }

    auto decomp = Decompose( next->first, Val< Delimiter >() );
    if( !decomp || *decomp != Delimiter::OpenBrace )
    {
        // Just leave the type as is, nothing to do.
        return true;
    }

    return parseFunctionDeclExpr( paramsDecl );
}

bool Parser::parseFunctionDeclExpr( const Value& paramsDecl )
{
    auto loc =
        Location::CreateSpanningLocation( peekLastValue()->locationId(), paramsDecl.locationId() );

    auto funcType = *FromValue< FuncType >( *popValue() );

    auto pBody = getFuncBody();
    if( !pBody )
        return false;

    const auto& c = context();

    // TODO: see if we can replace this unique id with something that is just unique within the
    // current function. This would avoid wild changes in the mangled names based on unrelated
    // definitions being added elsewhere.
    auto identity = DuplicateVectorTerm( c.identity() );
    get< pvec >( identity )->terms().back() = TERM( StringId( Env::NewUniqueId() ) );

    auto bodyContext = context();

    auto func = ToValue( BuildFunc( context(), funcType, c.identity(), identity, paramsDecl,
                             move( pBody ), bodyContext ) )
                    .setLocationId( loc );

    DiagnosticsContext dc( 0, true );
    VerbosityContext( Verbosity::Normal, true );

    auto newFunc = CompileFunc( c, func );
    pushValue( move( newFunc ) );
    return true;
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
    get< pvec >( funcIdentity )->terms().back() = TERM( d->name() );

    // If we're here, this is not an overload, but the first time we come accross this function.
    // So create a new overload set. The case of actually overloading an existing function is parsed
    // elsewhere.
    auto pOvlSet = make_shared< OverloadSet >( funcIdentity );

    c.env()->storeValue( funcIdentity, ANYTERM( _ ),
        ValueToEIR( ToValue( pOvlSet ) ) );

    auto func = parseFunctionDeclaration( funcIdentity, paramsDecl, pOvlSet );
    if( !func )
        return false;

    pushValue( move( *func ) );
    return true;
}

optional< Value > Parser::parseFunctionDeclaration( const Term& identity, const Value& paramsDecl, const ptr< sema::OverloadSet >& pOvlSet )

{
    if( !peekLastValue() )
        return nullopt;

    auto& c = context();

    if( IsTExpr( *peekLastValue() ) )
        return parseTemplateFunctionDeclaration( c.identity(), identity, paramsDecl, pOvlSet );


    auto loc = Location::CreateSpanningLocation( peekLastValue()->locationId(), paramsDecl.locationId() );

    auto funcType = *FromValue< FuncType >( *popValue() );
    auto bodyContext = c;
    auto func = BuildFunc( c, funcType, c.identity(), identity, paramsDecl, nullptr, bodyContext );

    return ToValue( FuncDecl( func, pOvlSet ) ).setLocationId( loc );
}







|
<









|
>









>
|







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
    get< pvec >( funcIdentity )->terms().back() = TERM( d->name() );

    // If we're here, this is not an overload, but the first time we come accross this function.
    // So create a new overload set. The case of actually overloading an existing function is parsed
    // elsewhere.
    auto pOvlSet = make_shared< OverloadSet >( funcIdentity );

    c.env()->storeValue( funcIdentity, ANYTERM( _ ), ValueToEIR( ToValue( pOvlSet ) ) );


    auto func = parseFunctionDeclaration( funcIdentity, paramsDecl, pOvlSet );
    if( !func )
        return false;

    pushValue( move( *func ) );
    return true;
}

optional< Value > Parser::parseFunctionDeclaration(
    const Term& identity, const Value& paramsDecl, const ptr< sema::OverloadSet >& pOvlSet )
{
    if( !peekLastValue() )
        return nullopt;

    auto& c = context();

    if( IsTExpr( *peekLastValue() ) )
        return parseTemplateFunctionDeclaration( c.identity(), identity, paramsDecl, pOvlSet );

    auto loc =
        Location::CreateSpanningLocation( peekLastValue()->locationId(), paramsDecl.locationId() );

    auto funcType = *FromValue< FuncType >( *popValue() );
    auto bodyContext = c;
    auto func = BuildFunc( c, funcType, c.identity(), identity, paramsDecl, nullptr, bodyContext );

    return ToValue( FuncDecl( func, pOvlSet ) ).setLocationId( loc );
}
Changes to bs/parse/funcdecl.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
#include "parse.h"
#include "builtins/builtins.h"
#include "precedence.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::builtins;

void parse::SetupFuncDeclDropValue( Env& e )
{
    RegisterBuiltinFunc< Intrinsic< void ( Value, FuncDecl ) > >( e, e.extDropValue(),
        []( const Context& c, const Value& b, const Value& v )
        {
            auto fd = *FromValue< FuncDecl >( v );
            if( fd.func().type().kind() == FuncType::Kind::Ghost )
            {
                // Complain if the overload set isn't empty and isn't ghost
                if( fd.ovlSet()->empty() )
                    fd.ovlSet()->setGhost( true );
                else
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( v.locationId(),
                        "a function can't have both ghost and non-ghost overloads." );
                    return;
                }
            }
            else if( !fd.func().tokens() )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage( v.locationId(),
                     "expected body after function declaration." );
                return;
            }


            if( !fd.ovlSet()->add( *c.env(), ToValue( fd.func() ).setLocationId( v.locationId() ) ) )
            {
                // TODO display details
                DiagnosticsManager::GetInstance().emitErrorMessage( v.locationId(),
                    "Duplicate function overload." );
            }
        } );
}

namespace goose::eir
{
    const Term& Bridge< parse::FuncDecl >::Type()
    {

        static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( ct_type ), TSID( func_decl ) ) ) );
        return type;
    }

    Value Bridge< parse::FuncDecl >::ToValue( const parse::FuncDecl& fd )
    {
        return Value( Type(), VEC(
            TERM( static_pointer_cast< void >( fd.ovlSet() ) ),
            ValueToEIR( ::ToValue( fd.func() ) ) ) );
    }

    optional< parse::FuncDecl > Bridge< parse::FuncDecl >::FromValue( const Value& v )
    {
        if( v.type() != Type() )
            return nullopt;

        auto decomp = Decompose( v.val(),
            Vec(
                Val< ptr< void > >(),
                SubTerm()
            )
        );
        assert( decomp );
        auto&& [pOvlSet, func] = *decomp;

        return FuncDecl{ *FromValue< Func >( *EIRToValue( func ) ),
            static_pointer_cast< sema::OverloadSet >( pOvlSet ) };
    }
}










|

















|
|



>
|


|
|








>
|





|
|
|







|
<
<
<
<
<






|
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
#include "parse.h"
#include "builtins/builtins.h"
#include "precedence.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::builtins;

void parse::SetupFuncDeclDropValue( Env& e )
{
    RegisterBuiltinFunc< Intrinsic< void( Value, FuncDecl ) > >( e, e.extDropValue(),
        []( const Context& c, const Value& b, const Value& v )
        {
            auto fd = *FromValue< FuncDecl >( v );
            if( fd.func().type().kind() == FuncType::Kind::Ghost )
            {
                // Complain if the overload set isn't empty and isn't ghost
                if( fd.ovlSet()->empty() )
                    fd.ovlSet()->setGhost( true );
                else
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( v.locationId(),
                        "a function can't have both ghost and non-ghost overloads." );
                    return;
                }
            }
            else if( !fd.func().tokens() )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage(
                    v.locationId(), "expected body after function declaration." );
                return;
            }

            if( !fd.ovlSet()->add(
                    *c.env(), ToValue( fd.func() ).setLocationId( v.locationId() ) ) )
            {
                // TODO display details
                DiagnosticsManager::GetInstance().emitErrorMessage(
                    v.locationId(), "Duplicate function overload." );
            }
        } );
}

namespace goose::eir
{
    const Term& Bridge< parse::FuncDecl >::Type()
    {
        static auto type =
            ValueToEIR( Value( TypeType(), VEC( TSID( ct_type ), TSID( func_decl ) ) ) );
        return type;
    }

    Value Bridge< parse::FuncDecl >::ToValue( const parse::FuncDecl& fd )
    {
        return Value( Type(),
            VEC( TERM( static_pointer_cast< void >( fd.ovlSet() ) ),
                ValueToEIR( ::ToValue( fd.func() ) ) ) );
    }

    optional< parse::FuncDecl > Bridge< parse::FuncDecl >::FromValue( const Value& v )
    {
        if( v.type() != Type() )
            return nullopt;

        auto decomp = Decompose( v.val(), Vec( Val< ptr< void > >(), SubTerm() ) );





        assert( decomp );
        auto&& [pOvlSet, func] = *decomp;

        return FuncDecl{ *FromValue< Func >( *EIRToValue( func ) ),
            static_pointer_cast< sema::OverloadSet >( pOvlSet ) };
    }
} // namespace goose::eir
Changes to bs/parse/funcdecl.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_PARSE_FUNCDECL_H
#define GOOSE_PARSE_FUNCDECL_H

// TODO: make a TFuncDecl version of this

#include "parse.h"

namespace goose::parse
{
    extern void SetupFuncDeclDropValue( sema::Env& e );

    // Describes a function declaration. It can be augmented with a function body
    // if it expects one (ie its not a ghost function) and we encounter an infix brace block,
    // with a func decl as the current left value.
    // This allows function qualifier operators (such as "ghost") to modify the decl before
    // we deal with affixing a body (which can be influenced by said operator)
    class FuncDecl
    {
        public:
            template< typename F, typename O >
            FuncDecl( F&& func, O&& pOvlSet ) :
                m_func( forward< F >( func ) ),
                m_pOvlSet( forward< O >( pOvlSet ) )
            {}



            const auto& func() const { return m_func; }

            auto& func() { return m_func; }

            const auto& ovlSet() const { return m_pOvlSet; }

        private:
            builtins::Func m_func;
            ptr< sema::OverloadSet > m_pOvlSet;
    };

}

namespace goose::eir
{
    template<>
    struct Bridge< parse::FuncDecl >
    {
        static const Term& Type();
        static Value ToValue( const parse::FuncDecl& fd );
        static optional< parse::FuncDecl > FromValue( const Value& v );
    };
}

#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
#ifndef GOOSE_PARSE_FUNCDECL_H
#define GOOSE_PARSE_FUNCDECL_H

// TODO: make a TFuncDecl version of this

#include "parse.h"

namespace goose::parse
{
    extern void SetupFuncDeclDropValue( sema::Env& e );

    // Describes a function declaration. It can be augmented with a function body if it expects one
    // (ie its not a ghost function) and we encounter an infix brace block, with a func decl as the

    // current left value. This allows function qualifier operators (such as "ghost") to modify the
    // decl before we deal with affixing a body (which can be influenced by said operator)
    class FuncDecl
    {
      public:
        template< typename F, typename O >
        FuncDecl( F&& func, O&& pOvlSet ) :
            m_func( forward< F >( func ) ),
            m_pOvlSet( forward< O >( pOvlSet ) )

        {
        }

        const auto& func() const { return m_func; }

        auto& func() { return m_func; }

        const auto& ovlSet() const { return m_pOvlSet; }

      private:
        builtins::Func m_func;
        ptr< sema::OverloadSet > m_pOvlSet;
    };
} // namespace goose::parse


namespace goose::eir
{

    template<> struct Bridge< parse::FuncDecl >
    {
        static const Term& Type();
        static Value ToValue( const parse::FuncDecl& fd );
        static optional< parse::FuncDecl > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/parse/functype.cpp.
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

    optional< Term > verificationIdentity;

    // Check if the params are valid, and whether they might actually be template params.
    switch( CheckParamListKind( paramsDecl ) )
    {
        case ParamListKind::Invalid:
            DiagnosticsManager::GetInstance().emitErrorMessage( paramsDecl.locationId(),
                "invalid parameter list." );
            return false;

        case ParamListKind::Template:
            pushValue( ToValue( BuildTFuncType( returnType, paramsDecl ) ).setLocationId( loc ) );
            break;

        case ParamListKind::Regular:







|
|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

    optional< Term > verificationIdentity;

    // Check if the params are valid, and whether they might actually be template params.
    switch( CheckParamListKind( paramsDecl ) )
    {
        case ParamListKind::Invalid:
            DiagnosticsManager::GetInstance().emitErrorMessage(
                paramsDecl.locationId(), "invalid parameter list." );
            return false;

        case ParamListKind::Template:
            pushValue( ToValue( BuildTFuncType( returnType, paramsDecl ) ).setLocationId( loc ) );
            break;

        case ParamListKind::Regular:
Changes to bs/parse/overload.cpp.
32
33
34
35
36
37
38
39

40
41
42
43
44
45
46
        return false;

    auto& c = context();

    optional< Value > func;

    if( IsTExpr( *peekLastValue() ) )
        func = parseTemplateFunctionDeclaration( c.identity(), pOvlSet->identity(), paramsDecl, pOvlSet );

    else
        func = parseFunctionDeclaration( pOvlSet->identity(), paramsDecl, pOvlSet );

    if( !func )
        return false;

    pushValue( *func );







|
>







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
        return false;

    auto& c = context();

    optional< Value > func;

    if( IsTExpr( *peekLastValue() ) )
        func = parseTemplateFunctionDeclaration(
            c.identity(), pOvlSet->identity(), paramsDecl, pOvlSet );
    else
        func = parseFunctionDeclaration( pOvlSet->identity(), paramsDecl, pOvlSet );

    if( !func )
        return false;

    pushValue( *func );
Changes to bs/parse/parenblock.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include "parse.h"
#include "builtins/builtins.h"
#include "precedence.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::builtins;

Value Parser::parseParenBlock()
{
    // Create a nested verbosity context to restrict the scope of
    // error silencing caused by syntax errors to the block.
    VerbosityContext vc( Verbosity::Normal );

    auto tok = m_resolver->consume();

    auto p = makeNestedParser( Delimiter::OpenParen );
    p.parseExpression();
    auto content = p.popValue();










|
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include "parse.h"
#include "builtins/builtins.h"
#include "precedence.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::builtins;

Value Parser::parseParenBlock()
{
    // Create a nested verbosity context to restrict the scope of error silencing caused by syntax
    // errors to the block.
    VerbosityContext vc( Verbosity::Normal );

    auto tok = m_resolver->consume();

    auto p = makeNestedParser( Delimiter::OpenParen );
    p.parseExpression();
    auto content = p.popValue();
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
    if( !content )
        return Value( EmptyClosedTuple() ).setLocationId( loc );

    // If the content is an open tuple, turn it into a closed (parenthesized) tuple.
    if( IsOpenTuple( *content ) )
        return CloseTuple( *content ).setLocationId( loc );

    // Otherwise wrap the content into a tuple: we prefer to treat single elements enclosed in parentheses
    // as tuples for consistency. A type checking rule takes care of peeling off single element tuples
    // where applicable.
    auto tup = EmptyClosedTuple();
    return AppendToTuple( tup, *content ).setLocationId( loc );
}

// If the left value is invocable or is a type, the postfix paren block is a call or
// a function type expression and have application precedence.
// Otherwise, it is an implicit separator and have separator precedence.







|
|
|







40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
    if( !content )
        return Value( EmptyClosedTuple() ).setLocationId( loc );

    // If the content is an open tuple, turn it into a closed (parenthesized) tuple.
    if( IsOpenTuple( *content ) )
        return CloseTuple( *content ).setLocationId( loc );

    // Otherwise wrap the content into a tuple: we prefer to treat single elements enclosed in
    // parentheses as tuples for consistency. A type checking rule takes care of peeling off single
    // element tuples where applicable.
    auto tup = EmptyClosedTuple();
    return AppendToTuple( tup, *content ).setLocationId( loc );
}

// If the left value is invocable or is a type, the postfix paren block is a call or
// a function type expression and have application precedence.
// Otherwise, it is an implicit separator and have separator precedence.
Changes to bs/parse/parse.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
#ifndef GOOSE_PARSE_H
#define GOOSE_PARSE_H

#include "cir/cir.h"
#include "lex/lex.h"
#include "sema/sema.h"
// TODO_SSA reenable
//#include "verify/verify.h"
#include "execute/execute.h"
#include "diagnostics/diagnostics.h"
#include "builtins/builtins.h"
#include "precedence.h"

namespace goose::parse
{
    using namespace util;
    using namespace eir;
    using namespace diagnostics;
}

#include "resolver.h"
#include "parser.h"
#include "rule.h"
#include "rule-helpers.h"
#include "funcdecl.h"
#include "tfuncdecl.h"

#include "parser.inl"
#include "rule-helpers.inl"

namespace goose::parse
{
    static inline void SetupParsingBuiltins( sema::Env& e )
    {
        SetupFuncDeclDropValue( e );
        SetupTFuncDeclDropValue( e );
    }
}

#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
#ifndef GOOSE_PARSE_H
#define GOOSE_PARSE_H

#include "cir/cir.h"
#include "lex/lex.h"
#include "sema/sema.h"
// TODO_SSA reenable
// #include "verify/verify.h"
#include "execute/execute.h"
#include "diagnostics/diagnostics.h"
#include "builtins/builtins.h"
#include "precedence.h"

namespace goose::parse
{
    using namespace util;
    using namespace eir;
    using namespace diagnostics;
} // namespace goose::parse

#include "resolver.h"
#include "parser.h"
#include "rule.h"
#include "rule-helpers.h"
#include "funcdecl.h"
#include "tfuncdecl.h"

#include "parser.inl"
#include "rule-helpers.inl"

namespace goose::parse
{
    static inline void SetupParsingBuiltins( sema::Env& e )
    {
        SetupFuncDeclDropValue( e );
        SetupTFuncDeclDropValue( e );
    }
} // namespace goose::parse

#endif
Changes to bs/parse/parser.cpp.
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    return p;
}

// Parse a sequence of expression. Each expression is
// a statement.
void Parser::parseSequence()
{
    for(;;)
    {
        LifetimeScopeGuard lsg( context() );

        {
            // Each statement gets its own visibility scope,
            // so that vars defined inside of the statement are only
            // visible from within it.







|







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    return p;
}

// Parse a sequence of expression. Each expression is
// a statement.
void Parser::parseSequence()
{
    for( ;; )
    {
        LifetimeScopeGuard lsg( context() );

        {
            // Each statement gets its own visibility scope,
            // so that vars defined inside of the statement are only
            // visible from within it.
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
            break;
    }
}

void Parser::flushValue()
{
    auto cfg = GetCFG( context() );
    if( m_lastValue && cfg && ( !cfg->currentBB() || cfg->currentBB()->terminator() )  )
    {
        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
            m_lastValue->locationId(), "unreachable code.", 0 );
    }

    // Flush the pending value, by invoking the DropValue
    // extension point, where an overload will decide







|







75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
            break;
    }
}

void Parser::flushValue()
{
    auto cfg = GetCFG( context() );
    if( m_lastValue && cfg && ( !cfg->currentBB() || cfg->currentBB()->terminator() ) )
    {
        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
            m_lastValue->locationId(), "unreachable code.", 0 );
    }

    // Flush the pending value, by invoking the DropValue
    // extension point, where an overload will decide
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
        InvokeOverloadSet( context(), context().env()->extDropValue(),
            MakeClosedTuple( context().builder(), val ), val.locationId() );
    }
}

optional< uint32_t > Parser::getPrecedence( const Term& t )
{
    return visit( [&]( auto&& content )
    {
        return getPrecedence( t, content );
    }, t );
}

bool Parser::parsePrefix( const Term& t, uint32_t precedence )
{
    return visit( [&]( auto&& content )
    {
        return parsePrefix( content, precedence );
    }, t );
}

bool Parser::parseInfix( const Term& t, uint32_t precedence )
{
    return visit( [&]( auto&& content )
    {
        return parseInfix( content, precedence );
    }, t );
}

bool Parser::parsePrefix( const BigInt& intlit, uint32_t )
{
    auto tok = m_resolver->consume();
    pushValue( ToValue( intlit ).setLocationId( tok->second ) );
    return true;







|
<
<
<




|
<
<
<




|
<
<
<







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
        InvokeOverloadSet( context(), context().env()->extDropValue(),
            MakeClosedTuple( context().builder(), val ), val.locationId() );
    }
}

optional< uint32_t > Parser::getPrecedence( const Term& t )
{
    return visit( [&]( auto&& content ) { return getPrecedence( t, content ); }, t );



}

bool Parser::parsePrefix( const Term& t, uint32_t precedence )
{
    return visit( [&]( auto&& content ) { return parsePrefix( content, precedence ); }, t );



}

bool Parser::parseInfix( const Term& t, uint32_t precedence )
{
    return visit( [&]( auto&& content ) { return parseInfix( content, precedence ); }, t );



}

bool Parser::parsePrefix( const BigInt& intlit, uint32_t )
{
    auto tok = m_resolver->consume();
    pushValue( ToValue( intlit ).setLocationId( tok->second ) );
    return true;
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
bool Parser::parsePrefix( const string& strlit, uint32_t )
{
    auto tok = m_resolver->consume();
    pushValue( ToValue( strlit ).setLocationId( tok->second ) );
    return true;
}

// Standalone (ie not part of a grammar construct that expects them such as a decl), unresolved identifiers
// end up here.
bool Parser::parsePrefix( StringId strid, uint32_t )
{
    auto tok = m_resolver->consume();
    pushValue( ToValue( strid ).setLocationId( tok->second ) );
    return true;
}

// Infix identifiers: if the left value is a type, then we have a decl.
optional< uint32_t > Parser::getPrecedence( const Term&, StringId strid )
{
    const auto& leftVal = peekLastValue();
    if( !leftVal )
        return nullopt;

    // If leftVal is a type, this is a decl, so set the precedence to Application.
    if( IsTExpr( *leftVal ) || IsType( context(), *leftVal ) )
    {
        // If it is a regular decl (such as function parameters and variable declarations),
        // it have the "Decl" precedence.
        // However, if the identifier is followed by a parent block,
        // this is a function declaration, and it gets the "FuncDecl" precedence, which
        // is set to be lower than the comma operator.
        //
        // The reason for this is that while we want to construct tuples of about anything just
        // by separating things with commas (not only to build function parameters and function arguments
        // but also for multiple return values and multiple variable declarations), we also
        // want to be able to use a tuple of those things as the return type for a function,
        // so the construction of the tuple (the comma operator) needs a higher precedence than
        // the function declaration. In other words, we don't want the last type of a tuple of type to
        // become the return type of a function subsequently added to the tuple.

        auto next = m_resolver->lookAheadUnresolved( 1 );
        if( !next )
            return precedence::Decl;

        auto decomp = Decompose( next->first, Val< Delimiter >() );







|
|



















|
|
<


|
|
|
|
|







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
bool Parser::parsePrefix( const string& strlit, uint32_t )
{
    auto tok = m_resolver->consume();
    pushValue( ToValue( strlit ).setLocationId( tok->second ) );
    return true;
}

// Standalone (ie not part of a grammar construct that expects them such as a decl), unresolved
// identifiers end up here.
bool Parser::parsePrefix( StringId strid, uint32_t )
{
    auto tok = m_resolver->consume();
    pushValue( ToValue( strid ).setLocationId( tok->second ) );
    return true;
}

// Infix identifiers: if the left value is a type, then we have a decl.
optional< uint32_t > Parser::getPrecedence( const Term&, StringId strid )
{
    const auto& leftVal = peekLastValue();
    if( !leftVal )
        return nullopt;

    // If leftVal is a type, this is a decl, so set the precedence to Application.
    if( IsTExpr( *leftVal ) || IsType( context(), *leftVal ) )
    {
        // If it is a regular decl (such as function parameters and variable declarations),
        // it have the "Decl" precedence.
        // However, if the identifier is followed by a parent block, this is a function declaration,
        // and it gets the "FuncDecl" precedence, which is set to be lower than the comma operator.

        //
        // The reason for this is that while we want to construct tuples of about anything just
        // by separating things with commas (not only to build function parameters and function
        // arguments but also for multiple return values and multiple variable declarations), we
        // also want to be able to use a tuple of those things as the return type for a function, so
        // the construction of the tuple (the comma operator) needs a higher precedence than the
        // function declaration. In other words, we don't want the last type of a tuple of type to
        // become the return type of a function subsequently added to the tuple.

        auto next = m_resolver->lookAheadUnresolved( 1 );
        if( !next )
            return precedence::Decl;

        auto decomp = Decompose( next->first, Val< Delimiter >() );
266
267
268
269
270
271
272
273

274
275
276
277
278
279
280
        return nullopt;

    if( val->type() == GetValueType< ptr< OverloadSet > >() )
    {
        if( !peekLastValue() )
            return nullopt;

        if( ( !IsTExpr( *peekLastValue() ) || IsTFunc( *peekLastValue() ) ) && !IsType( context(), *peekLastValue() ) )

            return nullopt;

        return getInfixOverloadSetPrecedence();
    }

    // If the term is an infix rule value, invoke its getPrecedence() function.
    auto rule = FromValue< Rule >( *val );







|
>







256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
        return nullopt;

    if( val->type() == GetValueType< ptr< OverloadSet > >() )
    {
        if( !peekLastValue() )
            return nullopt;

        if( ( !IsTExpr( *peekLastValue() ) || IsTFunc( *peekLastValue() ) )
            && !IsType( context(), *peekLastValue() ) )
            return nullopt;

        return getInfixOverloadSetPrecedence();
    }

    // If the term is an infix rule value, invoke its getPrecedence() function.
    auto rule = FromValue< Rule >( *val );
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
    auto rule = FromValue< Rule >( *val );
    if( !rule )
        return false;

    if( !( *rule )->isInfix() )
        return false;

    // If an infix parsing rule fails to parse, attempt to backtrack,
    // so we can try to parse it again as a prefix rule if it supports it
    // (case of infix operators that may act as prefix operators using a default lhs
    // value in some contexts)
    auto savedPosition = m_resolver->position();
    auto savedLastVal = m_lastValue;

    m_resolver->consume();

    if( ( *rule )->parseInfix( *this, t.second, prec ) )
        return true;







|
<
|
|







300
301
302
303
304
305
306
307

308
309
310
311
312
313
314
315
316
    auto rule = FromValue< Rule >( *val );
    if( !rule )
        return false;

    if( !( *rule )->isInfix() )
        return false;

    // If an infix parsing rule fails to parse, attempt to backtrack, so we can try to parse it

    // again as a prefix rule if it supports it (case of infix operators that may act as prefix
    // operators using a default lhs value in some contexts)
    auto savedPosition = m_resolver->position();
    auto savedLastVal = m_lastValue;

    m_resolver->consume();

    if( ( *rule )->parseInfix( *this, t.second, prec ) )
        return true;
335
336
337
338
339
340
341
342
343
344
345
346
347
348
    if( !m_lastValue )
        return nullopt;

    auto typeVal = *popValue();

    if( !typeVal.isConstant() )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( typeVal.locationId(),
            "this type expression can't be evaluated here.", 0 );
        return PoisonType();
    }

    return typeVal;
}







|
|





325
326
327
328
329
330
331
332
333
334
335
336
337
338
    if( !m_lastValue )
        return nullopt;

    auto typeVal = *popValue();

    if( !typeVal.isConstant() )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage(
            typeVal.locationId(), "this type expression can't be evaluated here.", 0 );
        return PoisonType();
    }

    return typeVal;
}
Changes to bs/parse/parser.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19


20
21
22
23
24
25

26
27
28
29
30
31
32
33
34
35

36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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
#ifndef GOOSE_PARSE_PARSER_H
#define GOOSE_PARSE_PARSER_H

namespace goose::parse
{
    // This is a modified Pratt parser. Instead of directly operating on the input
    // token stream, we first attempt to resolve identifiers according to the current
    // semantic context. Keywords and operator names are resolved to pratt parsing rules,
    // which are then applied directly.
    // This provide a powerful way to externally extend the syntax (for instance to implement
    // custom statements) and to decide which keywords and operators should be in scope
    // depending on which feature sets of the language we want to use.
    class Parser
    {
        public:
            Parser( const ptr< Resolver >& r ) :
                m_resolver( r )
            {}



            Parser( const ptr< Resolver >& r, Delimiter introDelimiter ) :
                m_resolver( r ),
                m_introDelimiter( introDelimiter )
            {}

            ~Parser()

            {
                flushValue();
            }

            // Build a parser for a nested parsing context that needs to share everything
            // except the current sequence and value.
            Parser makeNestedParser();
            Parser makeNestedParser( Delimiter introDelimiter );

            const auto& resolver() const { return m_resolver; }

            auto& resolver() { return m_resolver; }

            const auto& context() const { return resolver()->context(); }

            auto& context() { return resolver()->context(); }

            void parseSequence();
            bool parseExpression( uint32_t precedence = 0 );
            void parsePostfixExpression( uint32_t precedence );

            Value parseParenBlock();
            bool parseBraceBlock();

            template< typename V >
            void pushValue( V&& val );

            optional< Value > peekLastValue() const
            {
                return m_lastValue;
            }

            optional< Value > popValue()
            {
                optional< Value > result;
                swap( result, m_lastValue );
                return result;
            }

            optional< Value > popType();

            void flushValue();

            bool isInParenExpr() const
            {
                return m_introDelimiter && *m_introDelimiter == Delimiter::OpenParen;
            }

            ptr< void > getFuncBody();

            tuple< ptr< builtins::Propositions >, LocationId > tokenizeBracketBlock();

        private:
            optional< uint32_t > getPrecedence( const Term& t );
            bool parsePrefix( const Term&, uint32_t precedence );
            bool parseInfix( const Term&, uint32_t precedence );

            // Default getPrecedence overload
            template< typename T >
            optional< uint32_t > getPrecedence( const Term&, const T& );

            // Default parsePrefix overload
            template< typename T >
            bool parsePrefix( const T& t, uint32_t );

            // Default parseInfix overload
            template< typename T >
            bool parseInfix( const T& t, uint32_t );

            // Literals
            bool parsePrefix( const BigInt& intlit, uint32_t );
            bool parsePrefix( uint64_t charlit, uint32_t );
            bool parsePrefix( const string& strlit, uint32_t );

            // Unresolved identifiers
            bool parsePrefix( StringId strid, uint32_t );

            optional< uint32_t > getPrecedence( const Term&, StringId strid );
            bool parseInfix( StringId strid, uint32_t );

            // Vector terms (values)
            optional< uint32_t > getPrecedence( const Term&, const pvec& vec );
            bool parsePrefix( const pvec& vec, uint32_t prec );
            bool parseInfix( const pvec& vec, uint32_t prec );

            // Blocks
            optional< uint32_t > getPrecedence( const Term&, const Delimiter& d );
            bool parsePrefix( const Delimiter& d, uint32_t prec );
            bool parseInfix( const Delimiter& d, uint32_t prec );

            optional< uint32_t > getPostfixParenBlockPrecedence();
            bool parsePostfixParenBlock( uint32_t prec );
            Value parseParamList();

            bool parseNestedBraceBlock();
            optional< uint32_t > getPostfixBraceBlockPrecedence();
            bool parsePostfixBraceBlock( uint32_t prec );

            Value parseBracketBlock();
            optional< uint32_t > getPostfixBracketBlockPrecedence();
            bool parsePostfixBracketBlock( uint32_t prec );

            // Function types
            bool parseFuncType( const Value& returnType, const Value& paramsDecl );
            bool parseTFuncType( const Value& returnType, const Value& paramsDecl );
            void parseFuncTypeVerificationStatements();

            // Functions
            bool parseFunctionExpression( const Value& returnType, const Value& paramsDecl );
            bool parseFunctionDeclExpr( const Value& paramsDecl );
            bool parseFunctionDeclaration( const Value& decl, const Value& paramsDecl );
            optional< Value > parseFunctionDeclaration( const Term& identity, const Value& paramsDecl, const ptr< sema::OverloadSet >& pOvlSet );


            // Template functions
            bool parseTemplateFunctionExpression( const Value& returnType, const Value& paramsDecl );
            bool parseTemplateFunctionExpression( const Value& paramsDecl );
            bool parseTemplateFunctionTNamedDecl( const Value& tnamedDecl, const Value& paramsDecl );
            Value parseTemplateFunctionDeclaration( const Term& parentIdentity, const Term& identity, const Value& paramsDecl, const ptr< sema::OverloadSet >& pOvlSet );


            // Overloading
            optional< uint32_t > getInfixOverloadSetPrecedence();
            bool parseInfixOverloadSet( const ptr< sema::OverloadSet >& pOvlSet, uint32_t prec );

            // Resolver to fetch input terms from.
            ptr< Resolver > m_resolver;

            // The last value that we have built,
            // not yet pushed onto the output sequence
            // since it can be transformed by the next infix
            // or postfix rule.
            optional< Value > m_lastValue;

            // The delimiter that caused the creation of this nested parser, if any.
            // Used to diagnose misplaced delimiters.
            optional< Delimiter > m_introDelimiter;
    };
}

#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
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
#ifndef GOOSE_PARSE_PARSER_H
#define GOOSE_PARSE_PARSER_H

namespace goose::parse
{
    // This is a modified Pratt parser. Instead of directly operating on the input token stream, we
    // first attempt to resolve identifiers according to the current semantic context. Keywords and
    // operator names are resolved to pratt parsing rules, which are then applied directly. This

    // provide a powerful way to externally extend the syntax (for instance to implement custom
    // statements) and to decide which keywords and operators should be in scope depending on which
    // feature sets of the language we want to use.
    class Parser
    {
      public:
        Parser( const ptr< Resolver >& r ) :
            m_resolver( r )

        {
        }

        Parser( const ptr< Resolver >& r, Delimiter introDelimiter ) :
            m_resolver( r ),
            m_introDelimiter( introDelimiter )

        {

        }

        ~Parser() { flushValue(); }


        // Build a parser for a nested parsing context that needs to share everything
        // except the current sequence and value.
        Parser makeNestedParser();
        Parser makeNestedParser( Delimiter introDelimiter );

        const auto& resolver() const { return m_resolver; }

        auto& resolver() { return m_resolver; }

        const auto& context() const { return resolver()->context(); }

        auto& context() { return resolver()->context(); }

        void parseSequence();
        bool parseExpression( uint32_t precedence = 0 );
        void parsePostfixExpression( uint32_t precedence );

        Value parseParenBlock();
        bool parseBraceBlock();

        template< typename V > void pushValue( V&& val );


        optional< Value > peekLastValue() const { return m_lastValue; }




        optional< Value > popValue()
        {
            optional< Value > result;
            swap( result, m_lastValue );
            return result;
        }

        optional< Value > popType();

        void flushValue();

        bool isInParenExpr() const
        {
            return m_introDelimiter && *m_introDelimiter == Delimiter::OpenParen;
        }

        ptr< void > getFuncBody();

        tuple< ptr< builtins::Propositions >, LocationId > tokenizeBracketBlock();

      private:
        optional< uint32_t > getPrecedence( const Term& t );
        bool parsePrefix( const Term&, uint32_t precedence );
        bool parseInfix( const Term&, uint32_t precedence );

        // Default getPrecedence overload

        template< typename T > optional< uint32_t > getPrecedence( const Term&, const T& );

        // Default parsePrefix overload

        template< typename T > bool parsePrefix( const T& t, uint32_t );

        // Default parseInfix overload
        template< typename T > bool parseInfix( const T& t, uint32_t );


        // Literals
        bool parsePrefix( const BigInt& intlit, uint32_t );
        bool parsePrefix( uint64_t charlit, uint32_t );
        bool parsePrefix( const string& strlit, uint32_t );

        // Unresolved identifiers
        bool parsePrefix( StringId strid, uint32_t );

        optional< uint32_t > getPrecedence( const Term&, StringId strid );
        bool parseInfix( StringId strid, uint32_t );

        // Vector terms (values)
        optional< uint32_t > getPrecedence( const Term&, const pvec& vec );
        bool parsePrefix( const pvec& vec, uint32_t prec );
        bool parseInfix( const pvec& vec, uint32_t prec );

        // Blocks
        optional< uint32_t > getPrecedence( const Term&, const Delimiter& d );
        bool parsePrefix( const Delimiter& d, uint32_t prec );
        bool parseInfix( const Delimiter& d, uint32_t prec );

        optional< uint32_t > getPostfixParenBlockPrecedence();
        bool parsePostfixParenBlock( uint32_t prec );
        Value parseParamList();

        bool parseNestedBraceBlock();
        optional< uint32_t > getPostfixBraceBlockPrecedence();
        bool parsePostfixBraceBlock( uint32_t prec );

        Value parseBracketBlock();
        optional< uint32_t > getPostfixBracketBlockPrecedence();
        bool parsePostfixBracketBlock( uint32_t prec );

        // Function types
        bool parseFuncType( const Value& returnType, const Value& paramsDecl );
        bool parseTFuncType( const Value& returnType, const Value& paramsDecl );
        void parseFuncTypeVerificationStatements();

        // Functions
        bool parseFunctionExpression( const Value& returnType, const Value& paramsDecl );
        bool parseFunctionDeclExpr( const Value& paramsDecl );
        bool parseFunctionDeclaration( const Value& decl, const Value& paramsDecl );
        optional< Value > parseFunctionDeclaration( const Term& identity, const Value& paramsDecl,
            const ptr< sema::OverloadSet >& pOvlSet );

        // Template functions
        bool parseTemplateFunctionExpression( const Value& returnType, const Value& paramsDecl );
        bool parseTemplateFunctionExpression( const Value& paramsDecl );
        bool parseTemplateFunctionTNamedDecl( const Value& tnamedDecl, const Value& paramsDecl );
        Value parseTemplateFunctionDeclaration( const Term& parentIdentity, const Term& identity,
            const Value& paramsDecl, const ptr< sema::OverloadSet >& pOvlSet );

        // Overloading
        optional< uint32_t > getInfixOverloadSetPrecedence();
        bool parseInfixOverloadSet( const ptr< sema::OverloadSet >& pOvlSet, uint32_t prec );

        // Resolver to fetch input terms from.
        ptr< Resolver > m_resolver;

        // The last value that we have built, not yet pushed onto the output sequence since it

        // can be transformed by the next infix or postfix rule.

        optional< Value > m_lastValue;

        // The delimiter that caused the creation of this nested parser, if any.
        // Used to diagnose misplaced delimiters.
        optional< Delimiter > m_introDelimiter;
    };
} // namespace goose::parse

#endif
Changes to bs/parse/parser.inl.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
#ifndef GOOSE_PARSE_PARSER_INL
#define GOOSE_PARSE_PARSER_INL

namespace goose::parse
{
    template< typename V >
    void Parser::pushValue( V&& val )
    {
        if( val.isPoison() )
        {
            if( !DiagnosticsManager::GetInstance().errorsWereEmitted() )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage( val.locationId(),
                    "internal: pushing an undiagnosed poison value" );
            }

            DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );
            builtins::PoisonBuilder( context() );
        }

        flushValue();

        m_lastValue = forward< V >( val );

        if( !context().locationId().invalid() )
            m_lastValue->setLocationId( context().locationId() );

        // If the value we just pushed is of void type, it was pushed only for its side effects
        // (for instance a SetVar instruction), so it should be flushed immediately.
        if( m_lastValue->type() == GetValueType< void >() )
            flushValue();
    }

    template< typename T >
    optional< uint32_t > Parser::getPrecedence( const Term&, const T& )
    {
        return nullopt;
    }

    template< typename T >
    bool Parser::parsePrefix( const T&, uint32_t )
    {
        return false;
    }

    template< typename T >
    bool Parser::parseInfix( const T&, uint32_t prec )
    {
        return false;
    }
}

#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
#ifndef GOOSE_PARSE_PARSER_INL
#define GOOSE_PARSE_PARSER_INL

namespace goose::parse
{
    template< typename V > void Parser::pushValue( V&& val )

    {
        if( val.isPoison() )
        {
            if( !DiagnosticsManager::GetInstance().errorsWereEmitted() )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage(
                    val.locationId(), "internal: pushing an undiagnosed poison value" );
            }

            DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );
            builtins::PoisonBuilder( context() );
        }

        flushValue();

        m_lastValue = forward< V >( val );

        if( !context().locationId().invalid() )
            m_lastValue->setLocationId( context().locationId() );

        // If the value we just pushed is of void type, it was pushed only for its side effects (for
        // instance a SetVar instruction), so it should be flushed immediately.
        if( m_lastValue->type() == GetValueType< void >() )
            flushValue();
    }


    template< typename T > optional< uint32_t > Parser::getPrecedence( const Term&, const T& )
    {
        return nullopt;
    }


    template< typename T > bool Parser::parsePrefix( const T&, uint32_t )
    {
        return false;
    }


    template< typename T > bool Parser::parseInfix( const T&, uint32_t prec )
    {
        return false;
    }
} // namespace goose::parse

#endif
Changes to bs/parse/resolver.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "parse.h"

using namespace goose;
using namespace goose::parse;

void Resolver::init()
{
    m_context.onContextRestoredSignal().subscribe( [&]
    {
        clearLookAheadCache();
    } );
}

bool Resolver::eos() const
{
    clearCacheIfNeeded();
    return m_tokProvider->eos() && m_lookAheadCache.empty();
}







|
<
<
<







1
2
3
4
5
6
7
8



9
10
11
12
13
14
15
#include "parse.h"

using namespace goose;
using namespace goose::parse;

void Resolver::init()
{
    m_context.onContextRestoredSignal().subscribe( [&] { clearLookAheadCache(); } );



}

bool Resolver::eos() const
{
    clearCacheIfNeeded();
    return m_tokProvider->eos() && m_lookAheadCache.empty();
}
193
194
195
196
197
198
199
200

201
202
203
204

205
206
207
208

209
210
211
212
213
214
215

        co_yield consumeUnit();
    }

    switch( end )
    {
        case Delimiter::CloseParen:
            DiagnosticsManager::GetInstance().emitLexerErrorMessage( currentLocation(), "missing ')'." );

            break;

        case Delimiter::CloseBrace:
            DiagnosticsManager::GetInstance().emitLexerErrorMessage( currentLocation(), "missing '}'." );

            break;

        case Delimiter::CloseBracket:
            DiagnosticsManager::GetInstance().emitLexerErrorMessage( currentLocation(), "missing ']'." );

            break;

        default:
            break;
    }
}








|
>



|
>



|
>







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

        co_yield consumeUnit();
    }

    switch( end )
    {
        case Delimiter::CloseParen:
            DiagnosticsManager::GetInstance().emitLexerErrorMessage(
                currentLocation(), "missing ')'." );
            break;

        case Delimiter::CloseBrace:
            DiagnosticsManager::GetInstance().emitLexerErrorMessage(
                currentLocation(), "missing '}'." );
            break;

        case Delimiter::CloseBracket:
            DiagnosticsManager::GetInstance().emitLexerErrorMessage(
                currentLocation(), "missing ']'." );
            break;

        default:
            break;
    }
}

Changes to bs/parse/resolver.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#ifndef GOOSE_PARSE_RESOLVER_H
#define GOOSE_PARSE_RESOLVER_H

namespace goose::parse
{
    class Resolver
    {
        public:
            Resolver( const ptr< lex::TokenProvider >& tokProv, const sema::Context& c ) :
                m_tokProvider( tokProv ),
                m_context( c ),
                m_valueStoreVersion( c.env()->valueStoreVersion() )
            {
                init();
            }

            Resolver( const Resolver& r, const sema::Context& c ) :
                m_tokProvider( r.m_tokProvider ),
                m_context( c ),
                m_valueStoreVersion( c.env()->valueStoreVersion() )
            {
                init();
            }

            const auto& context() const { return m_context; }

            auto& context() { return m_context; }

            bool eos() const;

            auto currentLocation() { return m_tokProvider->currentLocation(); }

            // Consume the next token. Bound identifiers are resolved.
            optional< TermLoc > consume();

            // Consume the next token. No resolution is performed on identifiers.
            optional< TermLoc > consumeUnresolved();

            optional< TermLoc > lookAhead( size_t distance = 0 );
            optional< TermLoc > lookAheadUnresolved( size_t distance = 0 );

            // Consume the next unit from the provider and yield each token that it contains.
            // An unit is:
            //   - any brace, paren or bracket block and every unit they contain
            //   - any other token
            Generator< TermLoc > consumeUnit();

            void clearLookAheadCache() const;

            lex::TokenProvider::pos_type position() const;
            void setPosition( lex::TokenProvider::pos_type position );

        private:
            void init();
            TermLoc resolve( const TermLoc& t ) const;

            void clearCacheIfNeeded() const
            {
                if( m_context.env()->valueStoreVersion() == m_valueStoreVersion )
                    return;

                clearLookAheadCache();
            }

            Generator< TermLoc > consumeBlock( Delimiter end );

            ptr< lex::TokenProvider > m_tokProvider;
            sema::Context m_context;
            mutable deque< TermLoc > m_lookAheadCache;

            mutable uint64_t m_valueStoreVersion = 0;
    };
}

#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
64
65
66
67
68
69
70
71
72
73
74
75
#ifndef GOOSE_PARSE_RESOLVER_H
#define GOOSE_PARSE_RESOLVER_H

namespace goose::parse
{
    class Resolver
    {
      public:
        Resolver( const ptr< lex::TokenProvider >& tokProv, const sema::Context& c ) :
            m_tokProvider( tokProv ),
            m_context( c ),
            m_valueStoreVersion( c.env()->valueStoreVersion() )
        {
            init();
        }

        Resolver( const Resolver& r, const sema::Context& c ) :
            m_tokProvider( r.m_tokProvider ),
            m_context( c ),
            m_valueStoreVersion( c.env()->valueStoreVersion() )
        {
            init();
        }

        const auto& context() const { return m_context; }

        auto& context() { return m_context; }

        bool eos() const;

        auto currentLocation() { return m_tokProvider->currentLocation(); }

        // Consume the next token. Bound identifiers are resolved.
        optional< TermLoc > consume();

        // Consume the next token. No resolution is performed on identifiers.
        optional< TermLoc > consumeUnresolved();

        optional< TermLoc > lookAhead( size_t distance = 0 );
        optional< TermLoc > lookAheadUnresolved( size_t distance = 0 );

        // Consume the next unit from the provider and yield each token that it contains.
        // An unit is:
        //   - any brace, paren or bracket block and every unit they contain
        //   - any other token
        Generator< TermLoc > consumeUnit();

        void clearLookAheadCache() const;

        lex::TokenProvider::pos_type position() const;
        void setPosition( lex::TokenProvider::pos_type position );

      private:
        void init();
        TermLoc resolve( const TermLoc& t ) const;

        void clearCacheIfNeeded() const
        {
            if( m_context.env()->valueStoreVersion() == m_valueStoreVersion )
                return;

            clearLookAheadCache();
        }

        Generator< TermLoc > consumeBlock( Delimiter end );

        ptr< lex::TokenProvider > m_tokProvider;
        sema::Context m_context;
        mutable deque< TermLoc > m_lookAheadCache;

        mutable uint64_t m_valueStoreVersion = 0;
    };
} // namespace goose::parse

#endif
Changes to bs/parse/rule-helpers.cpp.
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    {
        if( v.type() != Type() )
            return nullopt;

        const auto& p = get< ptr< void > >( v.val() );
        return static_pointer_cast< parse::Rule >( p );
    }
}

namespace goose::parse
{
    void RegisterRule( sema::Env& env, const Term& identity, Rule&& rule )
    {
        auto ruleVal = ToValue( move( rule ) );
        auto ruleTerm = ValueToEIR( ruleVal );
        env.storeValue( identity, ANYTERM( _ ), ruleTerm );
    }
}







|









|
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    {
        if( v.type() != Type() )
            return nullopt;

        const auto& p = get< ptr< void > >( v.val() );
        return static_pointer_cast< parse::Rule >( p );
    }
} // namespace goose::eir

namespace goose::parse
{
    void RegisterRule( sema::Env& env, const Term& identity, Rule&& rule )
    {
        auto ruleVal = ToValue( move( rule ) );
        auto ruleTerm = ValueToEIR( ruleVal );
        env.storeValue( identity, ANYTERM( _ ), ruleTerm );
    }
} // namespace goose::parse
Changes to bs/parse/rule-helpers.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
#ifndef GOOSE_PARSE_RULE_HELPERS_H
#define GOOSE_PARSE_RULE_HELPERS_H

namespace goose::eir
{
    template<>
    struct Bridge< parse::Rule >
    {
        static const Term& Type();
        static Value ToValue( parse::Rule&& r );
        static optional< ptr< parse::Rule > > FromValue( const Value& v );
    };
}

namespace goose::parse
{
    void RegisterRule( sema::Env& env, const Term& identity, Rule&& rule );

    template< typename... R >
    void BuildParseRule( sema::Env& env, StringId name, const Term& identity, R&&... ruleBuilders )
    {
        Rule r;
        ( ( ruleBuilders( env, r, name ) ), ... );
        RegisterRule( env, identity, move( r ) );
    }

    template< typename F >
    void MakePrefixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func );

    template< typename F >
    void MakePostfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func );

    template< typename F >

    void MakeLeftAssInfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func );

    template< typename F >

    void MakeRightAssInfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func );

}

#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
#ifndef GOOSE_PARSE_RULE_HELPERS_H
#define GOOSE_PARSE_RULE_HELPERS_H

namespace goose::eir
{

    template<> struct Bridge< parse::Rule >
    {
        static const Term& Type();
        static Value ToValue( parse::Rule&& r );
        static optional< ptr< parse::Rule > > FromValue( const Value& v );
    };
} // namespace goose::eir

namespace goose::parse
{
    void RegisterRule( sema::Env& env, const Term& identity, Rule&& rule );

    template< typename... R >
    void BuildParseRule( sema::Env& env, StringId name, const Term& identity, R&&... ruleBuilders )
    {
        Rule r;
        ( ( ruleBuilders( env, r, name ) ), ... );
        RegisterRule( env, identity, move( r ) );
    }

    template< typename F >
    void MakePrefixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func );

    template< typename F >
    void MakePostfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func );

    template< typename F >
    void MakeLeftAssInfixOp(
        sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func );

    template< typename F >
    void MakeRightAssInfixOp(
        sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func );
} // namespace goose::parse


#endif
Changes to bs/parse/rule-helpers.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
#ifndef GOOSE_PARSE_RULE_HELPERS_INL
#define GOOSE_PARSE_RULE_HELPERS_INL

// TODO do something about the horrific code duplication in there someday

namespace goose::parse
{
    template< typename F >
    void MakePrefixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
    {

        r.setPrefixFunc( [=]( Parser& p, LocationId locationId, uint32_t prec )
        {
            optional< Value > rightVal;

            {
                auto np = p.makeNestedParser();
                if( !np.parseExpression( precedence + 1 ) )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                        "expected an expression.", 0 );
                    return false;
                }

                rightVal = np.popValue();
                if( !rightVal )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                        "expected an expression.", 0 );
                    return false;
                }
            }

            if( rightVal->isPoison() )
            {
                p.pushValue( PoisonValue() );
                return true;
            }

            DiagnosticsContext dc( locationId, false );

            auto loc = Location::CreateSpanningLocation( locationId, rightVal->locationId() );
            sema::Context::CurrentLocationGuard clg( p.context(), loc );

            auto result = func( p, move( *rightVal ) );
            p.pushValue( move( result ) );
            return true;
        } );
    }

    template< typename F >
    void MakePostfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
    {
        r.setInfixFunc(
            [=]( const Parser& p ) { return precedence; },
            [=]( Parser& p, LocationId locationId, uint32_t prec )
            {
                auto leftVal = p.popValue();
                if( !leftVal )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                        "expected a value operand.", 0 );
                    return false;
                }

                if( leftVal->isPoison() )
                {
                    p.pushValue( PoisonValue() );
                    return true;










>
|
|
|

|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|

|
|
|
|
|

|

|
|

|
|
|
|





<
|





|
|







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
#ifndef GOOSE_PARSE_RULE_HELPERS_INL
#define GOOSE_PARSE_RULE_HELPERS_INL

// TODO do something about the horrific code duplication in there someday

namespace goose::parse
{
    template< typename F >
    void MakePrefixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
    {
        r.setPrefixFunc(
            [=]( Parser& p, LocationId locationId, uint32_t prec )
            {
                optional< Value > rightVal;

                {
                    auto np = p.makeNestedParser();
                    if( !np.parseExpression( precedence + 1 ) )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                            locationId, "expected an expression.", 0 );
                        return false;
                    }

                    rightVal = np.popValue();
                    if( !rightVal )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                            locationId, "expected an expression.", 0 );
                        return false;
                    }
                }

                if( rightVal->isPoison() )
                {
                    p.pushValue( PoisonValue() );
                    return true;
                }

                DiagnosticsContext dc( locationId, false );

                auto loc = Location::CreateSpanningLocation( locationId, rightVal->locationId() );
                sema::Context::CurrentLocationGuard clg( p.context(), loc );

                auto result = func( p, move( *rightVal ) );
                p.pushValue( move( result ) );
                return true;
            } );
    }

    template< typename F >
    void MakePostfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
    {

        r.setInfixFunc( [=]( const Parser& p ) { return precedence; },
            [=]( Parser& p, LocationId locationId, uint32_t prec )
            {
                auto leftVal = p.popValue();
                if( !leftVal )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                        locationId, "expected a value operand.", 0 );
                    return false;
                }

                if( leftVal->isPoison() )
                {
                    p.pushValue( PoisonValue() );
                    return true;
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
                return true;
            } );
    }

    template< typename F >
    void MakeLeftAssInfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
    {
        // If there's a contextually defined default lhs value, we can parse infix operators as if they were
        // prefix operators.
        //
        // This is so @val can be omitted in type predicates to write super terse conditions like int(32)[>5 <10] which is
        // a delightful combination of neat and fucked up that's never going to bite me in the ass

        if( !r.isPrefix() )
        {

            r.setPrefixFunc( [=]( Parser& p, LocationId locationId, uint32_t prec )
            {

                auto defLhsVal = p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
                if( !defLhsVal )
                    return false;

                optional< Value > rightVal;
                {
                    auto np = p.makeNestedParser();
                    if( !np.parseExpression( precedence + 1 ) )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                            "expected an expression.", 0 );
                        return false;
                    }

                    rightVal = np.popValue();
                    if( !rightVal )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                            "expected an expression.", 0 );
                        return false;
                    }
                }


                auto loc = Location::CreateSpanningLocation( locationId, rightVal->locationId() );
                sema::Context::CurrentLocationGuard clg( p.context(), loc );

                auto result = func( p, *EIRToValue( *defLhsVal ), move( *rightVal ) );
                p.pushValue( move( result ) );
                return true;
            } );
        }

        r.setInfixFunc(
            [=]( const Parser& p ) { return precedence; },
            [=]( Parser& p, LocationId locationId, uint32_t prec )
            {
                auto leftVal = p.popValue();
                if( !leftVal )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                        "expected a value operand.", 0 );
                    return false;
                }

                optional< Value > rightVal;
                {
                    auto np = p.makeNestedParser();
                    if( !np.parseExpression( precedence + 1 ) )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                            "expected an expression.", 0 );
                        return false;
                    }

                    rightVal = np.popValue();
                    if( !rightVal )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                            "expected an expression.", 0 );
                        return false;
                    }
                }

                if( leftVal->isPoison() || rightVal->isPoison() )
                {
                    p.pushValue( PoisonValue() );
                    return true;
                }

                DiagnosticsContext dcl( leftVal->locationId(), false );
                DiagnosticsContext dcr( rightVal->locationId(), false );

                DiagnosticsContext dc( locationId, false );

                auto loc = Location::CreateSpanningLocation( leftVal->locationId(), rightVal->locationId() );

                sema::Context::CurrentLocationGuard clg( p.context(), loc );

                // If there is a default lhs value available in our current context, fail silently
                // and put back the leftVal. The parsing will then end and drop the current expression (leftVal), and

                // try parsing this again as a prefix operator with the default lhs value. That's not hackish at all, honest

                auto defLhsVal = p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
                if( defLhsVal )
                {
                    VerbosityContext vc( Verbosity::Silent );

                    auto result = func( p, *leftVal, move( *rightVal ) );
                    if( result.isPoison() )
                        return false;

                    p.pushValue( move( result ) );
                    return true;
                }

                auto result = func( p, move( *leftVal ), move( *rightVal ) );
                p.pushValue( move( result ) );
                return true;
            } );
    }

    template< typename F >

    void MakeRightAssInfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
    {
        // See the comment in MakeLeftAssInfixOp for an explanation of this fantastic
        // "hey lets pretend that this infix operator is a prefix operator with a default lhs value" trick
        if( !r.isPrefix() )
        {

            r.setPrefixFunc( [=]( Parser& p, LocationId locationId, uint32_t prec )
            {

                auto defLhsVal = p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
                if( !defLhsVal )
                    return false;

                optional< Value > rightVal;
                {
                    auto np = p.makeNestedParser();
                    if( !np.parseExpression( precedence ) )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                            "expected an expression.", 0 );
                        return false;
                    }

                    rightVal = np.popValue();
                    if( !rightVal )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                            "expected an expression.", 0 );
                        return false;
                    }
                }


                auto loc = Location::CreateSpanningLocation( locationId, rightVal->locationId() );
                sema::Context::CurrentLocationGuard clg( p.context(), loc );

                auto result = func( p, *EIRToValue( *defLhsVal ), move( *rightVal ) );
                p.pushValue( move( result ) );
                return true;
            } );
        }

        r.setInfixFunc(
            [=]( const Parser& p ) { return precedence; },
            [=]( Parser& p, LocationId locationId, uint32_t prec )
            {
                auto leftVal = p.popValue();
                if( !leftVal )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                        "expected a value operand.", 0 );
                    return false;
                }

                optional< Value > rightVal;

                {
                    auto np = p.makeNestedParser();
                    if( !np.parseExpression( precedence ) )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                            "expected an expression.", 0 );
                        return false;
                    }

                    rightVal = np.popValue();
                    if( !rightVal )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
                            "expected an expression.", 0 );
                        return false;
                    }
                }

                if( leftVal->isPoison() || rightVal->isPoison() )
                {
                    p.pushValue( PoisonValue() );
                    return true;
                }

                DiagnosticsContext dcl( leftVal->locationId(), false );
                DiagnosticsContext dcr( rightVal->locationId(), false );

                DiagnosticsContext dc( locationId, false );

                auto loc = Location::CreateSpanningLocation( leftVal->locationId(), rightVal->locationId() );

                sema::Context::CurrentLocationGuard clg( p.context(), loc );


                auto defLhsVal = p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
                if( defLhsVal )
                {
                    VerbosityContext vc( Verbosity::Silent );

                    auto result = func( p, move( *leftVal ), move( *rightVal ) );
                    if( result.isPoison() )
                        return false;

                    p.pushValue( move( result ) );
                    return true;
                }

                auto result = func( p, move( *leftVal ), move( *rightVal ) );
                p.pushValue( move( result ) );
                return true;
            } );
    }
}

#endif







|
|

|
|
>


>
|
|
>
|
|
|

|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|

>
|
|

|
|
|
|


<
|





|
|








|
|






|
|















|
>



|
>
|
>
|



















>
|

|
|


>
|
|
>
|
|
|

|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|

>
|
|

|
|
|
|


<
|





|
|









|
|






|
|















|
>


>
|

















|


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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
                return true;
            } );
    }

    template< typename F >
    void MakeLeftAssInfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
    {
        // If there's a contextually defined default lhs value, we can parse infix operators as if
        // they were prefix operators.
        //
        // This is so @val can be omitted in type predicates to write super terse conditions like
        // int(32)[>5 <10] which is a delightful combination of neat and fucked up that's never
        // going to bite me in the ass
        if( !r.isPrefix() )
        {
            r.setPrefixFunc(
                [=]( Parser& p, LocationId locationId, uint32_t prec )
                {
                    auto defLhsVal =
                        p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
                    if( !defLhsVal )
                        return false;

                    optional< Value > rightVal;
                    {
                        auto np = p.makeNestedParser();
                        if( !np.parseExpression( precedence + 1 ) )
                        {
                            DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                                locationId, "expected an expression.", 0 );
                            return false;
                        }

                        rightVal = np.popValue();
                        if( !rightVal )
                        {
                            DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                                locationId, "expected an expression.", 0 );
                            return false;
                        }
                    }

                    auto loc =
                        Location::CreateSpanningLocation( locationId, rightVal->locationId() );
                    sema::Context::CurrentLocationGuard clg( p.context(), loc );

                    auto result = func( p, *EIRToValue( *defLhsVal ), move( *rightVal ) );
                    p.pushValue( move( result ) );
                    return true;
                } );
        }


        r.setInfixFunc( [=]( const Parser& p ) { return precedence; },
            [=]( Parser& p, LocationId locationId, uint32_t prec )
            {
                auto leftVal = p.popValue();
                if( !leftVal )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                        locationId, "expected a value operand.", 0 );
                    return false;
                }

                optional< Value > rightVal;
                {
                    auto np = p.makeNestedParser();
                    if( !np.parseExpression( precedence + 1 ) )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                            locationId, "expected an expression.", 0 );
                        return false;
                    }

                    rightVal = np.popValue();
                    if( !rightVal )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                            locationId, "expected an expression.", 0 );
                        return false;
                    }
                }

                if( leftVal->isPoison() || rightVal->isPoison() )
                {
                    p.pushValue( PoisonValue() );
                    return true;
                }

                DiagnosticsContext dcl( leftVal->locationId(), false );
                DiagnosticsContext dcr( rightVal->locationId(), false );

                DiagnosticsContext dc( locationId, false );

                auto loc = Location::CreateSpanningLocation(
                    leftVal->locationId(), rightVal->locationId() );
                sema::Context::CurrentLocationGuard clg( p.context(), loc );

                // If there is a default lhs value available in our current context, fail silently
                // and put back the leftVal. The parsing will then end and drop the current
                // expression (leftVal), and try parsing this again as a prefix operator with the
                // default lhs value. That's not hackish at all, honest
                auto defLhsVal =
                    p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
                if( defLhsVal )
                {
                    VerbosityContext vc( Verbosity::Silent );

                    auto result = func( p, *leftVal, move( *rightVal ) );
                    if( result.isPoison() )
                        return false;

                    p.pushValue( move( result ) );
                    return true;
                }

                auto result = func( p, move( *leftVal ), move( *rightVal ) );
                p.pushValue( move( result ) );
                return true;
            } );
    }

    template< typename F >
    void MakeRightAssInfixOp(
        sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
    {
        // See the comment in MakeLeftAssInfixOp for an explanation of this fantastic "hey lets
        // pretend that this infix operator is a prefix operator with a default lhs value" trick
        if( !r.isPrefix() )
        {
            r.setPrefixFunc(
                [=]( Parser& p, LocationId locationId, uint32_t prec )
                {
                    auto defLhsVal =
                        p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
                    if( !defLhsVal )
                        return false;

                    optional< Value > rightVal;
                    {
                        auto np = p.makeNestedParser();
                        if( !np.parseExpression( precedence ) )
                        {
                            DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                                locationId, "expected an expression.", 0 );
                            return false;
                        }

                        rightVal = np.popValue();
                        if( !rightVal )
                        {
                            DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                                locationId, "expected an expression.", 0 );
                            return false;
                        }
                    }

                    auto loc =
                        Location::CreateSpanningLocation( locationId, rightVal->locationId() );
                    sema::Context::CurrentLocationGuard clg( p.context(), loc );

                    auto result = func( p, *EIRToValue( *defLhsVal ), move( *rightVal ) );
                    p.pushValue( move( result ) );
                    return true;
                } );
        }


        r.setInfixFunc( [=]( const Parser& p ) { return precedence; },
            [=]( Parser& p, LocationId locationId, uint32_t prec )
            {
                auto leftVal = p.popValue();
                if( !leftVal )
                {
                    DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                        locationId, "expected a value operand.", 0 );
                    return false;
                }

                optional< Value > rightVal;

                {
                    auto np = p.makeNestedParser();
                    if( !np.parseExpression( precedence ) )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                            locationId, "expected an expression.", 0 );
                        return false;
                    }

                    rightVal = np.popValue();
                    if( !rightVal )
                    {
                        DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
                            locationId, "expected an expression.", 0 );
                        return false;
                    }
                }

                if( leftVal->isPoison() || rightVal->isPoison() )
                {
                    p.pushValue( PoisonValue() );
                    return true;
                }

                DiagnosticsContext dcl( leftVal->locationId(), false );
                DiagnosticsContext dcr( rightVal->locationId(), false );

                DiagnosticsContext dc( locationId, false );

                auto loc = Location::CreateSpanningLocation(
                    leftVal->locationId(), rightVal->locationId() );
                sema::Context::CurrentLocationGuard clg( p.context(), loc );

                auto defLhsVal =
                    p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
                if( defLhsVal )
                {
                    VerbosityContext vc( Verbosity::Silent );

                    auto result = func( p, move( *leftVal ), move( *rightVal ) );
                    if( result.isPoison() )
                        return false;

                    p.pushValue( move( result ) );
                    return true;
                }

                auto result = func( p, move( *leftVal ), move( *rightVal ) );
                p.pushValue( move( result ) );
                return true;
            } );
    }
} // namespace goose::parse

#endif
Changes to bs/parse/rule.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
#ifndef GOOSE_PARSE_RULE_H
#define GOOSE_PARSE_RULE_H

#include "parse.h"

namespace goose::parse
{
    class Rule
    {
        public:
            using parse_func = function< bool ( Parser& p, LocationId location, uint32_t precedence ) >;
            using precedence_func = function< optional< uint32_t > ( const Parser& p ) >;

            Rule() {}

            Rule( parse_func&& prefixParseFunc ) :
                m_prefixParseFunc( move( prefixParseFunc ) )
            {}



            Rule( precedence_func&& precFunc, parse_func&& infixParseFunc ) :
                m_infixParseFunc( move( infixParseFunc ) ),
                m_precFunc( move( precFunc ) )
            {}



            Rule( parse_func&& prefixParseFunc, precedence_func&& precFunc, parse_func&& infixParseFunc ) :

                m_prefixParseFunc( move( prefixParseFunc ) ),
                m_infixParseFunc( move( infixParseFunc ) ),
                m_precFunc( move( precFunc ) )
            {}



            bool isPrefix() const { return !!m_prefixParseFunc; }

            bool isInfix() const { return !!m_infixParseFunc && !!m_precFunc; }

            void setPrefixFunc( parse_func&& prefixParseFunc )
            {
                m_prefixParseFunc = move( prefixParseFunc );
            }

            void setInfixFunc( precedence_func&& precFunc, parse_func&& infixParseFunc  )
            {
                m_precFunc = move( precFunc );
                m_infixParseFunc = move( infixParseFunc );
            }

            optional< uint32_t > getPrecedence( const Parser& p )
            {
                return m_precFunc( p );
            }

            bool parsePrefix( Parser& p, LocationId locationId, uint32_t precedence )
            {
                return m_prefixParseFunc( p, locationId, precedence );
            }

            bool parseInfix( Parser& p, LocationId locationId, uint32_t precedence )
            {
                return m_infixParseFunc( p, locationId, precedence );
            }

        private:
            parse_func m_prefixParseFunc;
            parse_func m_infixParseFunc;
            precedence_func m_precFunc;
    };
}

#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
64
65
66
67
68
69
#ifndef GOOSE_PARSE_RULE_H
#define GOOSE_PARSE_RULE_H

#include "parse.h"

namespace goose::parse
{
    class Rule
    {
      public:
        using parse_func = function< bool( Parser& p, LocationId location, uint32_t precedence ) >;
        using precedence_func = function< optional< uint32_t >( const Parser& p ) >;

        Rule() {}

        Rule( parse_func&& prefixParseFunc ) :
            m_prefixParseFunc( move( prefixParseFunc ) )

        {
        }

        Rule( precedence_func&& precFunc, parse_func&& infixParseFunc ) :
            m_infixParseFunc( move( infixParseFunc ) ),
            m_precFunc( move( precFunc ) )

        {
        }

        Rule( parse_func&& prefixParseFunc, precedence_func&& precFunc,
            parse_func&& infixParseFunc ) :
            m_prefixParseFunc( move( prefixParseFunc ) ),
            m_infixParseFunc( move( infixParseFunc ) ),
            m_precFunc( move( precFunc ) )

        {
        }

        bool isPrefix() const { return !!m_prefixParseFunc; }

        bool isInfix() const { return !!m_infixParseFunc && !!m_precFunc; }

        void setPrefixFunc( parse_func&& prefixParseFunc )
        {
            m_prefixParseFunc = move( prefixParseFunc );
        }

        void setInfixFunc( precedence_func&& precFunc, parse_func&& infixParseFunc )
        {
            m_precFunc = move( precFunc );
            m_infixParseFunc = move( infixParseFunc );
        }

        optional< uint32_t > getPrecedence( const Parser& p ) { return m_precFunc( p ); }




        bool parsePrefix( Parser& p, LocationId locationId, uint32_t precedence )
        {
            return m_prefixParseFunc( p, locationId, precedence );
        }

        bool parseInfix( Parser& p, LocationId locationId, uint32_t precedence )
        {
            return m_infixParseFunc( p, locationId, precedence );
        }

      private:
        parse_func m_prefixParseFunc;
        parse_func m_infixParseFunc;
        precedence_func m_precFunc;
    };
} // namespace goose::parse

#endif
Changes to bs/parse/tfunc.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
        return false;

    return parseTemplateFunctionExpression( paramsDecl );
}

bool Parser::parseTemplateFunctionExpression( const Value& paramsDecl )
{
    // Check if a brace block follows, in which case this is a function declaration. Otherwise, it's just a function type.

    auto next = m_resolver->lookAheadUnresolved();
    if( !next )
    {
        // Just leave the type as is, nothing to do.
        return true;
    }

    auto decomp = Decompose( next->first, Val< Delimiter >() );
    if( !decomp || *decomp != Delimiter::OpenBrace )
    {
        // Just leave the type as is, nothing to do.
        return true;
    }

    // TODO: see if we can replace this unique id with something that is just unique within the current
    // function. This would avoid wild changes in the mangled names based on unrelated definitions being added
    // elsewhere.
    const auto& c = context();
    auto tfuncIdentity = AppendToVectorTerm( c.identity(),
        TERM( StringId( Env::NewUniqueId() ) ) );


    auto loc = Location::CreateSpanningLocation( peekLastValue()->locationId(), paramsDecl.locationId() );

    auto tFuncType = *FromValue< TFuncType >( *popValue() );

    ptr< void > pBody;
    if( tFuncType.kind() != FuncType::Kind::Ghost )
    {
        pBody = getFuncBody();







|
>














|
|
|

|
<

>
|







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
        return false;

    return parseTemplateFunctionExpression( paramsDecl );
}

bool Parser::parseTemplateFunctionExpression( const Value& paramsDecl )
{
    // Check if a brace block follows, in which case this is a function declaration. Otherwise, it's
    // just a function type.
    auto next = m_resolver->lookAheadUnresolved();
    if( !next )
    {
        // Just leave the type as is, nothing to do.
        return true;
    }

    auto decomp = Decompose( next->first, Val< Delimiter >() );
    if( !decomp || *decomp != Delimiter::OpenBrace )
    {
        // Just leave the type as is, nothing to do.
        return true;
    }

    // TODO: see if we can replace this unique id with something that is just unique within the
    // current function. This would avoid wild changes in the mangled names based on unrelated
    // definitions being added elsewhere.
    const auto& c = context();
    auto tfuncIdentity = AppendToVectorTerm( c.identity(), TERM( StringId( Env::NewUniqueId() ) ) );


    auto loc =
        Location::CreateSpanningLocation( peekLastValue()->locationId(), paramsDecl.locationId() );

    auto tFuncType = *FromValue< TFuncType >( *popValue() );

    ptr< void > pBody;
    if( tFuncType.kind() != FuncType::Kind::Ghost )
    {
        pBody = getFuncBody();
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
    get< pvec >( tfuncIdentity )->terms().back() = TERM( d->name() );

    // If we're here, this is not an overload, but the first time we come accross this function.
    // So create a new overload set. The case of actually overloading an existing function is parsed
    // elsewhere.
    auto pOvlSet = make_shared< OverloadSet >( tfuncIdentity );

    c.env()->storeValue( tfuncIdentity, ANYTERM( _ ),
        ValueToEIR( ToValue( pOvlSet ) ) );


    auto tfunc = parseTemplateFunctionDeclaration( c.identity(), tfuncIdentity, paramsDecl, pOvlSet );
    if( tfunc.isPoison() )
        return true;

    pushValue( move( tfunc ) );
    return true;
}

Value Parser::parseTemplateFunctionDeclaration( const Term& parentIdentity, const Term& identity, const Value& paramsDecl, const ptr< sema::OverloadSet >& pOvlSet )

{

    auto loc = Location::CreateSpanningLocation( peekLastValue()->locationId(), paramsDecl.locationId() );

    auto& c = context();
    auto tFuncType = *FromValue< TFuncType >( *popValue() );

    auto tfunc = BuildTFunc( c, tFuncType, parentIdentity, identity, paramsDecl, nullptr );
    if( !tfunc )
        return PoisonValue();

    return ToValue( TFuncDecl( *tfunc, pOvlSet ) ).setLocationId( loc );
}







|
<

>
|







|
>

>
|










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
    get< pvec >( tfuncIdentity )->terms().back() = TERM( d->name() );

    // If we're here, this is not an overload, but the first time we come accross this function.
    // So create a new overload set. The case of actually overloading an existing function is parsed
    // elsewhere.
    auto pOvlSet = make_shared< OverloadSet >( tfuncIdentity );

    c.env()->storeValue( tfuncIdentity, ANYTERM( _ ), ValueToEIR( ToValue( pOvlSet ) ) );


    auto tfunc =
        parseTemplateFunctionDeclaration( c.identity(), tfuncIdentity, paramsDecl, pOvlSet );
    if( tfunc.isPoison() )
        return true;

    pushValue( move( tfunc ) );
    return true;
}

Value Parser::parseTemplateFunctionDeclaration( const Term& parentIdentity, const Term& identity,
    const Value& paramsDecl, const ptr< sema::OverloadSet >& pOvlSet )
{
    auto loc =
        Location::CreateSpanningLocation( peekLastValue()->locationId(), paramsDecl.locationId() );

    auto& c = context();
    auto tFuncType = *FromValue< TFuncType >( *popValue() );

    auto tfunc = BuildTFunc( c, tFuncType, parentIdentity, identity, paramsDecl, nullptr );
    if( !tfunc )
        return PoisonValue();

    return ToValue( TFuncDecl( *tfunc, pOvlSet ) ).setLocationId( loc );
}
Changes to bs/parse/tfuncdecl.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
#include "parse.h"
#include "builtins/builtins.h"
#include "precedence.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::builtins;

void parse::SetupTFuncDeclDropValue( Env& e )
{
    RegisterBuiltinFunc< Intrinsic< void ( Value, TFuncDecl ) > >( e, e.extDropValue(),
        []( const Context& c, const Value& b, const Value& v )
        {
            auto tfd = *FromValue< TFuncDecl >( v );
            if( tfd.tFunc().type().kind() == FuncType::Kind::Ghost )
            {
                // Complain if the overload set isn't empty and isn't ghost
                if( tfd.ovlSet()->empty() )
                    tfd.ovlSet()->setGhost( true );
                else
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( v.locationId(),
                        "a function can't have both ghost and non-ghost overloads." );
                    return;
                }
            }
            else if( !tfd.tFunc().tokens() )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage( v.locationId(),
                     "expected body after function declaration." );
                return;
            }


            if( !tfd.ovlSet()->add( *c.env(), ToValue( tfd.tFunc() ).setLocationId( v.locationId() ) ) )
            {
                // TODO display details
                DiagnosticsManager::GetInstance().emitErrorMessage( v.locationId(),
                    "Duplicate function overload." );
            }
        } );
}

namespace goose::eir
{
    const Term& Bridge< parse::TFuncDecl >::Type()
    {

        static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( ct_type ), TSID( tfunc_decl ) ) ) );
        return type;
    }

    Value Bridge< parse::TFuncDecl >::ToValue( const parse::TFuncDecl& tfd )
    {
        return Value( Type(), VEC(
            TERM( static_pointer_cast< void >( tfd.ovlSet() ) ),
            ValueToEIR( ::ToValue( tfd.tFunc() ) ) ) );
    }

    optional< parse::TFuncDecl > Bridge< parse::TFuncDecl >::FromValue( const Value& v )
    {
        if( v.type() != Type() )
            return nullopt;

        auto decomp = Decompose( v.val(),
            Vec(
                Val< ptr< void > >(),
                SubTerm()
            )
        );
        assert( decomp );
        auto&& [pOvlSet, tfunc] = *decomp;

        return TFuncDecl{ *FromValue< TFunc >( *EIRToValue( tfunc ) ),
            static_pointer_cast< sema::OverloadSet >( pOvlSet ) };
    }
}










|

















|
|



>
|


|
|








>
|





|
|
|







|
<
<
<
<
<






|
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
#include "parse.h"
#include "builtins/builtins.h"
#include "precedence.h"

using namespace goose;
using namespace goose::parse;
using namespace goose::builtins;

void parse::SetupTFuncDeclDropValue( Env& e )
{
    RegisterBuiltinFunc< Intrinsic< void( Value, TFuncDecl ) > >( e, e.extDropValue(),
        []( const Context& c, const Value& b, const Value& v )
        {
            auto tfd = *FromValue< TFuncDecl >( v );
            if( tfd.tFunc().type().kind() == FuncType::Kind::Ghost )
            {
                // Complain if the overload set isn't empty and isn't ghost
                if( tfd.ovlSet()->empty() )
                    tfd.ovlSet()->setGhost( true );
                else
                {
                    DiagnosticsManager::GetInstance().emitErrorMessage( v.locationId(),
                        "a function can't have both ghost and non-ghost overloads." );
                    return;
                }
            }
            else if( !tfd.tFunc().tokens() )
            {
                DiagnosticsManager::GetInstance().emitErrorMessage(
                    v.locationId(), "expected body after function declaration." );
                return;
            }

            if( !tfd.ovlSet()->add(
                    *c.env(), ToValue( tfd.tFunc() ).setLocationId( v.locationId() ) ) )
            {
                // TODO display details
                DiagnosticsManager::GetInstance().emitErrorMessage(
                    v.locationId(), "Duplicate function overload." );
            }
        } );
}

namespace goose::eir
{
    const Term& Bridge< parse::TFuncDecl >::Type()
    {
        static auto type =
            ValueToEIR( Value( TypeType(), VEC( TSID( ct_type ), TSID( tfunc_decl ) ) ) );
        return type;
    }

    Value Bridge< parse::TFuncDecl >::ToValue( const parse::TFuncDecl& tfd )
    {
        return Value( Type(),
            VEC( TERM( static_pointer_cast< void >( tfd.ovlSet() ) ),
                ValueToEIR( ::ToValue( tfd.tFunc() ) ) ) );
    }

    optional< parse::TFuncDecl > Bridge< parse::TFuncDecl >::FromValue( const Value& v )
    {
        if( v.type() != Type() )
            return nullopt;

        auto decomp = Decompose( v.val(), Vec( Val< ptr< void > >(), SubTerm() ) );





        assert( decomp );
        auto&& [pOvlSet, tfunc] = *decomp;

        return TFuncDecl{ *FromValue< TFunc >( *EIRToValue( tfunc ) ),
            static_pointer_cast< sema::OverloadSet >( pOvlSet ) };
    }
} // namespace goose::eir
Changes to bs/parse/tfuncdecl.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
#ifndef GOOSE_PARSE_TFUNCDECL_H
#define GOOSE_PARSE_TFUNCDECL_H

#include "parse.h"

namespace goose::parse
{
    extern void SetupTFuncDeclDropValue( sema::Env& e );

    // Describes a template function declaration. It can be augmented with a function body
    // if it expects one (ie its not a ghost function) and we encounter an infix brace block,
    // with a func decl as the current left value.
    // This allows function qualifier operators (such as "ghost") to modify the decl before
    // we deal with affixing a body (which can be influenced by said operator)

    class TFuncDecl
    {
        public:
            template< typename F, typename O >
            TFuncDecl( F&& tFunc, O&& pOvlSet ) :
                m_tFunc( forward< F >( tFunc ) ),
                m_pOvlSet( forward< O >( pOvlSet ) )
            {}



            const auto& tFunc() const { return m_tFunc; }

            auto& tFunc() { return m_tFunc; }

            const auto& ovlSet() const { return m_pOvlSet; }

        private:
            builtins::TFunc m_tFunc;
            ptr< sema::OverloadSet > m_pOvlSet;
    };

}

namespace goose::eir
{
    template<>
    struct Bridge< parse::TFuncDecl >
    {
        static const Term& Type();
        static Value ToValue( const parse::TFuncDecl& tfd );
        static optional< parse::TFuncDecl > FromValue( const Value& v );
    };
}

#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
#ifndef GOOSE_PARSE_TFUNCDECL_H
#define GOOSE_PARSE_TFUNCDECL_H

#include "parse.h"

namespace goose::parse
{
    extern void SetupTFuncDeclDropValue( sema::Env& e );

    // Describes a template function declaration. It can be augmented with a function body if it
    // expects one (ie its not a ghost function) and we encounter an infix brace block, with a func

    // decl as the current left value. This allows function qualifier operators (such as "ghost") to
    // modify the decl before we deal with affixing a body (which can be influenced by said
    // operator)
    class TFuncDecl
    {
      public:
        template< typename F, typename O >
        TFuncDecl( F&& tFunc, O&& pOvlSet ) :
            m_tFunc( forward< F >( tFunc ) ),
            m_pOvlSet( forward< O >( pOvlSet ) )

        {
        }

        const auto& tFunc() const { return m_tFunc; }

        auto& tFunc() { return m_tFunc; }

        const auto& ovlSet() const { return m_pOvlSet; }

      private:
        builtins::TFunc m_tFunc;
        ptr< sema::OverloadSet > m_pOvlSet;
    };
} // namespace goose::parse


namespace goose::eir
{

    template<> struct Bridge< parse::TFuncDecl >
    {
        static const Term& Type();
        static Value ToValue( const parse::TFuncDecl& tfd );
        static optional< parse::TFuncDecl > FromValue( const Value& v );
    };
} // namespace goose::eir

#endif
Changes to bs/precedence.h.
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
    constexpr uint32_t FuncQualifier = 600;

    // A funcdecl is a decl followed by a paren block.
    // This needs a lower precedence than the comma operator so that
    // we can use a tuple of types as the return type of a function.
    constexpr uint32_t FuncDecl = 700;

    // Comma should have a lower precedence than most other operators: we want to be able to construct
    // tuples of types simply by separating type expressions with commas, and we want to be able to use any operator
    // in a type expression.
    // It needs to be higher than assignment however, as putting assignment into a tuple makes no sense, but we
    // want to be able to assign something to a tuple.
    constexpr uint32_t CommaOp = 800;

    constexpr uint32_t EllipsisOp = 850;

    constexpr uint32_t Decl = 900;

    constexpr uint32_t AccessQualifier = 950;







|
|
|
|
|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
    constexpr uint32_t FuncQualifier = 600;

    // A funcdecl is a decl followed by a paren block.
    // This needs a lower precedence than the comma operator so that
    // we can use a tuple of types as the return type of a function.
    constexpr uint32_t FuncDecl = 700;

    // Comma should have a lower precedence than most other operators: we want to be able to
    // construct tuples of types simply by separating type expressions with commas, and we want to
    // be able to use any operator in a type expression. It needs to be higher than assignment
    // however, as putting assignment into a tuple makes no sense, but we want to be able to assign
    // something to a tuple.
    constexpr uint32_t CommaOp = 800;

    constexpr uint32_t EllipsisOp = 850;

    constexpr uint32_t Decl = 900;

    constexpr uint32_t AccessQualifier = 950;
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
    constexpr uint32_t EqualityOp = 1500;
    constexpr uint32_t GreaterLesserOp = 1600;
    constexpr uint32_t BitShiftOp = 1700;
    constexpr uint32_t AddSubOp = 1800;
    constexpr uint32_t MulDivOp = 1900;
    constexpr uint32_t UnaryOps = 2000;

    // Applications are all cases where a tuple is applied to something that immediately precedes it:
    // ie function calls and function type expressions.
    constexpr uint32_t Application = 3000;

    constexpr uint32_t DotOp = 4000;

    // ', used to attach a lifetime to an access specifier, may have a tvar on the rhs
    // (for overload resolution depending on whether some references are aliasing),
    // so it has a priority a smidge below that of the dollar operator.
    constexpr uint32_t ApostropheOp = 5000;

    // The dollar operator, used to build a template named capture texpr, is never expected to have a sub-expression to its right
    // so it have the max priority.
    constexpr uint32_t Dollar = 6000;
}

#endif







|
|









|
|

|


47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
    constexpr uint32_t EqualityOp = 1500;
    constexpr uint32_t GreaterLesserOp = 1600;
    constexpr uint32_t BitShiftOp = 1700;
    constexpr uint32_t AddSubOp = 1800;
    constexpr uint32_t MulDivOp = 1900;
    constexpr uint32_t UnaryOps = 2000;

    // Applications are all cases where a tuple is applied to something that immediately precedes
    // it: ie function calls and function type expressions.
    constexpr uint32_t Application = 3000;

    constexpr uint32_t DotOp = 4000;

    // ', used to attach a lifetime to an access specifier, may have a tvar on the rhs
    // (for overload resolution depending on whether some references are aliasing),
    // so it has a priority a smidge below that of the dollar operator.
    constexpr uint32_t ApostropheOp = 5000;

    // The dollar operator, used to build a template named capture texpr, is never expected to have
    // a sub-expression to its right so it have the max priority.
    constexpr uint32_t Dollar = 6000;
} // namespace goose::precedence

#endif
Changes to bs/sema/context.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
{
    auto parentBaseIdentity = identity();
    parentBaseIdentity = TakeVectorTerm( parentBaseIdentity, VecSize( parentBaseIdentity ) - 1 );

    // We construct an identity to hold all the compilation time constants for this scope,
    // that will be visible in every nested scopes and functions.

    // We create a separate identity for the local variables, from which both all inherited constants
    // and local variables from the parent scope will be visible. That one will be made current,
    // and can be truncated to find the "compmilation constant" identity where we need to see only the
    // compilation constants (for instance, in parameter lists).

    // TODO: see if we can replace this unique id with something that is just unique within the current
    // function. This would avoid wild changes in the mangled names based on unrelated definitions being added
    // elsewhere.
    auto localIdentity = AppendToVectorTerm( parentBaseIdentity,
        TERM( StringId( Env::NewUniqueId() ) ) );
    auto localVarsIdentity = AppendToVectorTerm( localIdentity, TSID( 0_locvars ) );

    env()->addVisibilityRule( parentBaseIdentity, localIdentity );
    env()->addVisibilityRule( identity(), localVarsIdentity );

    setIdentity( localVarsIdentity );
}







|
|
|
|

|
|
|
|
|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
    auto parentBaseIdentity = identity();
    parentBaseIdentity = TakeVectorTerm( parentBaseIdentity, VecSize( parentBaseIdentity ) - 1 );

    // We construct an identity to hold all the compilation time constants for this scope,
    // that will be visible in every nested scopes and functions.

    // We create a separate identity for the local variables, from which both all inherited
    // constants and local variables from the parent scope will be visible. That one will be made
    // current, and can be truncated to find the "compmilation constant" identity where we need to
    // see only the compilation constants (for instance, in parameter lists).

    // TODO: see if we can replace this unique id with something that is just unique within the
    // current function. This would avoid wild changes in the mangled names based on unrelated
    // definitions being added elsewhere.
    auto localIdentity =
        AppendToVectorTerm( parentBaseIdentity, TERM( StringId( Env::NewUniqueId() ) ) );
    auto localVarsIdentity = AppendToVectorTerm( localIdentity, TSID( 0_locvars ) );

    env()->addVisibilityRule( parentBaseIdentity, localIdentity );
    env()->addVisibilityRule( identity(), localVarsIdentity );

    setIdentity( localVarsIdentity );
}
Changes to bs/sema/context.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15


16
17
18
19
20
21
22
23
24
25

26
27
28
29
30

31

32

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
#ifndef GOOSE_SEMA_CONTEXT_H
#define GOOSE_SEMA_CONTEXT_H

namespace goose::sema
{
    class Env;

    class Context
    {
        public:
            template< typename E, typename I >
            Context( E&& pEnv, I&& identity ) :
                m_pEnv( forward< E >( pEnv ) ),
                m_identity( forward< I >( identity ) )
            {}



            template< typename E, typename I, typename R >
            Context( E&& pEnv, I&& identity, R&& returnType ) :
                m_pEnv( forward< E >( pEnv ) ),
                m_identity( forward< I >( identity ) ),
                m_returnType( forward< R >( returnType ) )
            {}

            template< typename B >
            void setBuilder( B&& b )

            {
                m_builder = forward< B >( b );
            }

            const auto& env() const { return m_pEnv; }

            const auto& identity() const { return m_identity; }

            const auto& returnType() const { return m_returnType; }

            const auto& builder() const { return m_builder; }

            auto& onContextRestoredSignal() { return m_onContextRestored; }

            template< typename T >
            T builder() const { return FromValue< T >( m_builder ); }

            template< typename T >
            void setEnv( T&& pEnv )
            {
                m_pEnv = forward< T >( pEnv );
            }

            void setIdentity( const Term& identity )
            {
                m_identity = identity;
            }

            auto locationId() const { return m_currentLoc; }

            void beginVisibilityScope();

            class CurrentLocationGuard
            {
                public:
                    CurrentLocationGuard( Context& c, LocationId loc ) :
                        m_context( c ),
                        m_previousLoc( c.m_currentLoc )
                    {
                        c.m_currentLoc = loc;
                    }

                    ~CurrentLocationGuard()
                    {
                        m_context.m_currentLoc = m_previousLoc;
                    }

                private:
                    Context& m_context;
                    LocationId m_previousLoc;
            };

            class CurrentContextGuard
            {
                public:
                    CurrentContextGuard( Context& c ) :
                        m_context( c ),
                        m_savedBuilder( c.builder() ),
                        m_savedIdentity( c.identity() )
                    {}



                    ~CurrentContextGuard()
                    {
                        m_context.setIdentity( m_savedIdentity );
                        m_context.setBuilder( m_savedBuilder );
                        m_context.m_onContextRestored();
                    }

                private:
                    Context& m_context;
                    Value m_savedBuilder;
                    Term m_savedIdentity;
            };

            // Crappy solution for LowerableType/LowerableValue for the BS compiler
            static inline const auto* DefaultContext()
            {
                return m_defaultContext;
            }

            struct DefaultContextGuard
            {
                DefaultContextGuard( const Context& newContext )
                {
                    m_previousContext = Context::DefaultContext();
                    Context::m_defaultContext = &newContext;
                }

                ~DefaultContextGuard()
                {
                    Context::m_defaultContext = m_previousContext;
                }

                const Context* m_previousContext = nullptr;
            };

        private:
            Signal<>            m_onContextRestored;
            ptr< Env >          m_pEnv;
            Term                m_identity;
            optional< Term >    m_returnType;

            Value               m_builder;

            LocationId          m_currentLoc;

            static const Context*   m_defaultContext;
    };
}


#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
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
#ifndef GOOSE_SEMA_CONTEXT_H
#define GOOSE_SEMA_CONTEXT_H

namespace goose::sema
{
    class Env;

    class Context
    {
      public:
        template< typename E, typename I >
        Context( E&& pEnv, I&& identity ) :
            m_pEnv( forward< E >( pEnv ) ),
            m_identity( forward< I >( identity ) )

        {
        }

        template< typename E, typename I, typename R >
        Context( E&& pEnv, I&& identity, R&& returnType ) :
            m_pEnv( forward< E >( pEnv ) ),
            m_identity( forward< I >( identity ) ),
            m_returnType( forward< R >( returnType ) )

        {


        }

        template< typename B > void setBuilder( B&& b ) { m_builder = forward< B >( b ); }


        const auto& env() const { return m_pEnv; }

        const auto& identity() const { return m_identity; }

        const auto& returnType() const { return m_returnType; }

        const auto& builder() const { return m_builder; }

        auto& onContextRestoredSignal() { return m_onContextRestored; }


        template< typename T > T builder() const { return FromValue< T >( m_builder ); }

        template< typename T > void setEnv( T&& pEnv ) { m_pEnv = forward< T >( pEnv ); }





        void setIdentity( const Term& identity ) { m_identity = identity; }




        auto locationId() const { return m_currentLoc; }

        void beginVisibilityScope();

        class CurrentLocationGuard
        {
          public:
            CurrentLocationGuard( Context& c, LocationId loc ) :
                m_context( c ),
                m_previousLoc( c.m_currentLoc )
            {
                c.m_currentLoc = loc;
            }

            ~CurrentLocationGuard() { m_context.m_currentLoc = m_previousLoc; }




          private:
            Context& m_context;
            LocationId m_previousLoc;
        };

        class CurrentContextGuard
        {
          public:
            CurrentContextGuard( Context& c ) :
                m_context( c ),
                m_savedBuilder( c.builder() ),
                m_savedIdentity( c.identity() )

            {
            }

            ~CurrentContextGuard()
            {
                m_context.setIdentity( m_savedIdentity );
                m_context.setBuilder( m_savedBuilder );
                m_context.m_onContextRestored();
            }

          private:
            Context& m_context;
            Value m_savedBuilder;
            Term m_savedIdentity;
        };

        // Crappy solution for LowerableType/LowerableValue for the BS compiler
        static inline const auto* DefaultContext() { return m_defaultContext; }




        struct DefaultContextGuard
        {
            DefaultContextGuard( const Context& newContext )
            {
                m_previousContext = Context::DefaultContext();
                Context::m_defaultContext = &newContext;
            }



            ~DefaultContextGuard() { Context::m_defaultContext = m_previousContext; }


            const Context* m_previousContext = nullptr;
        };

      private:
        Signal<> m_onContextRestored;
        ptr< Env > m_pEnv;
        Term m_identity;
        optional< Term > m_returnType;

        Value m_builder;

        LocationId m_currentLoc;

        static const Context* m_defaultContext;
    };

} // namespace goose::sema

#endif
Changes to bs/sema/ctmm.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#ifndef GOOSE_SEMA_CTMM_H
#define GOOSE_SEMA_CTMM_H

namespace goose::sema
{
    // This implements a virtual memory address space for compilation time execution.
    class CTMemoryManager
    {
        public:
            static constexpr uint64_t PageSizeBits = 12;
            static constexpr uint64_t PageSize = 1 << PageSizeBits;
            static constexpr uint64_t PageSizeMask = PageSize - 1;

            using VirtualPointer = uint64_t;
            using PageIndex = uint64_t;

            bool createPage( PageIndex pi, bool readOnly = false )
            {
                auto&& [it, success] = m_pages.emplace( pi, readOnly );
                return success;
            }

            void destroyPage( PageIndex pi, bool readOnly = false )
            {
                m_pages.erase( pi );
            }

            template< typename T >
            optional< T > load( VirtualPointer vp ) const
            {
                if( !IsPointerAlignmentValid( vp, sizeof( T ) ) )
                    return nullopt;

                auto page = getPage( GetPageIndex( vp ) );
                if( !page )
                    return nullopt;

                return *reinterpret_cast< const T* >( page->m_pData->data() + GetOffsetInPage( vp ) );
            }

            template< typename T >
            bool store( VirtualPointer vp, const T& val )
            {
                if( !IsPointerAlignmentValid( vp, sizeof( T ) ) )
                    return false;

                auto page = getPage( GetPageIndex( vp ) );
                if( !page || page->readOnly )
                    return false;

                *reinterpret_cast< const T* >( page->m_pData->data() + GetOffsetInPage( vp ) ) = val;
                return true;
            }

            static PageIndex GetPageIndex( VirtualPointer vp )
            {
                return vp >> PageSizeBits;
            }

            static PageIndex GetOffsetInPage( VirtualPointer vp )
            {
                return vp & PageSizeMask;
            }

        private:
            struct Page
            {
                Page( bool ro ) : readOnly( ro ) {}




                unique_ptr< array< uint8_t, PageSize > > m_pData;
                bool readOnly = false;
            };

            const Page* getPage( PageIndex pi ) const
            {
                auto it = m_pages.find( pi );
                if( it == m_pages.end() )
                    return nullptr;

                return &it->second;
            }

            // Checks that the given virtual pointer has correct
            // alignment for the size of a type (which must be a power of 2)
            static bool IsPointerAlignmentValid( VirtualPointer vp, size_t typeSize )
            {
                return ( vp & ( typeSize - 1 ) ) == 0;
            }

            unordered_map< PageIndex, Page > m_pages;
    };
}

#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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#ifndef GOOSE_SEMA_CTMM_H
#define GOOSE_SEMA_CTMM_H

namespace goose::sema
{
    // This implements a virtual memory address space for compilation time execution.
    class CTMemoryManager
    {
      public:
        static constexpr uint64_t PageSizeBits = 12;
        static constexpr uint64_t PageSize = 1 << PageSizeBits;
        static constexpr uint64_t PageSizeMask = PageSize - 1;

        using VirtualPointer = uint64_t;
        using PageIndex = uint64_t;

        bool createPage( PageIndex pi, bool readOnly = false )
        {
            auto&& [it, success] = m_pages.emplace( pi, readOnly );
            return success;
        }

        void destroyPage( PageIndex pi, bool readOnly = false ) { m_pages.erase( pi ); }





        template< typename T > optional< T > load( VirtualPointer vp ) const
        {
            if( !IsPointerAlignmentValid( vp, sizeof( T ) ) )
                return nullopt;

            auto page = getPage( GetPageIndex( vp ) );
            if( !page )
                return nullopt;

            return *reinterpret_cast< const T* >( page->m_pData->data() + GetOffsetInPage( vp ) );
        }


        template< typename T > bool store( VirtualPointer vp, const T& val )
        {
            if( !IsPointerAlignmentValid( vp, sizeof( T ) ) )
                return false;

            auto page = getPage( GetPageIndex( vp ) );
            if( !page || page->readOnly )
                return false;

            *reinterpret_cast< const T* >( page->m_pData->data() + GetOffsetInPage( vp ) ) = val;
            return true;
        }

        static PageIndex GetPageIndex( VirtualPointer vp ) { return vp >> PageSizeBits; }




        static PageIndex GetOffsetInPage( VirtualPointer vp ) { return vp & PageSizeMask; }




      private:
        struct Page
        {
            Page( bool ro ) :
                readOnly( ro )
            {
            }

            unique_ptr< array< uint8_t, PageSize > > m_pData;
            bool readOnly = false;
        };

        const Page* getPage( PageIndex pi ) const
        {
            auto it = m_pages.find( pi );
            if( it == m_pages.end() )
                return nullptr;

            return &it->second;
        }

        // Checks that the given virtual pointer has correct
        // alignment for the size of a type (which must be a power of 2)
        static bool IsPointerAlignmentValid( VirtualPointer vp, size_t typeSize )
        {
            return ( vp & ( typeSize - 1 ) ) == 0;
        }

        unordered_map< PageIndex, Page > m_pages;
    };
} // namespace goose::sema

#endif
Changes to bs/sema/env.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
#include "sema.h"

using namespace goose;
using namespace goose::cir;
using namespace goose::sema;

vector< ptr< cir::Func > >   Env::ms_cirFuncs;
DependencyGraph Env::ms_depsGraph;
uint32_t Env::ms_nextUniqueId = 0;

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 )
{
    storeValue( idPat, contextIdPat, make_shared< ValueProvider >(

        [val]( const Env& e, const Term& identity, const Term& contextId, Term& result )
        {
            result = val;
            return Status::Success;
        } ) );
}


void Env::storeValue( const Term& idPat, const Term& contextIdPat, const ptr< ValueProvider >& valp )
{
    m_valueStore = Merge( m_valueStore, VEC( idPat, contextIdPat ),
        [valp]( auto&& oldValp )
        {
            if( !oldValp )
                return valp;

            // If another value provider was already registered with the same pattern,
            // combine them through a lambda that will call them both and check for ambiguousness.

            return make_shared< ValueProvider >( [oldValp, valp]( Env& e, const Term& identity, const Term& contextId, Term& result )
            {
                auto s1 = ( *oldValp )( e, identity, contextId, result );
                if( s1 == Status::AmbiguousMatch )
                    return s1;

                auto s2 = ( *valp )( e, identity, contextId, result );
                if( s2 == Status::AmbiguousMatch )
                    return s2;

                if( s1 == s2 )
                {
                    if( s1 == Status::NoMatch )
                        return Status::NoMatch;
                    else
                        return Status::AmbiguousMatch;
                }

                return Status::Success;
            } );
        } );

    ++m_valueStoreVersion;
}

Env::Status Env::retrieveValue( const Term& id, const Term& contextId, Term& result )
{






|








<
>
>





|
>
|
|
|
|
|


>
|









>
|
|
|
|
|

|
|
|

|
|
|
|
|
|
|

|
|







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
#include "sema.h"

using namespace goose;
using namespace goose::cir;
using namespace goose::sema;

vector< ptr< cir::Func > > Env::ms_cirFuncs;
DependencyGraph Env::ms_depsGraph;
uint32_t Env::ms_nextUniqueId = 0;

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 )
{
    storeValue( idPat, contextIdPat,
        make_shared< ValueProvider >(
            [val]( const Env& e, const Term& identity, const Term& contextId, Term& result )
            {
                result = val;
                return Status::Success;
            } ) );
}

void Env::storeValue(
    const Term& idPat, const Term& contextIdPat, const ptr< ValueProvider >& valp )
{
    m_valueStore = Merge( m_valueStore, VEC( idPat, contextIdPat ),
        [valp]( auto&& oldValp )
        {
            if( !oldValp )
                return valp;

            // If another value provider was already registered with the same pattern,
            // combine them through a lambda that will call them both and check for ambiguousness.
            return make_shared< ValueProvider >(
                [oldValp, valp]( Env& e, const Term& identity, const Term& contextId, Term& result )
                {
                    auto s1 = ( *oldValp )( e, identity, contextId, result );
                    if( s1 == Status::AmbiguousMatch )
                        return s1;

                    auto s2 = ( *valp )( e, identity, contextId, result );
                    if( s2 == Status::AmbiguousMatch )
                        return s2;

                    if( s1 == s2 )
                    {
                        if( s1 == Status::NoMatch )
                            return Status::NoMatch;
                        else
                            return Status::AmbiguousMatch;
                    }

                    return Status::Success;
                } );
        } );

    ++m_valueStoreVersion;
}

Env::Status Env::retrieveValue( const Term& id, const Term& contextId, Term& result )
{
119
120
121
122
123
124
125
126


127
128

129
130
131
132
    // Create the pattern: it is the destination identity,
    // followed by one or more elements.
    auto vecPat = Vector::MakeAppend( vec, ANYTERM( _ ) );
    vecPat.setRepetitionTerm( ANYTERM( _ ) );

    auto pat = TERM( make_shared< Vector >( move( vecPat ) ) );

    storeValue( pat, transitive ? ANYTERM( _ ) : destination, make_shared< ValueProvider >(


        [toImport, prefixLength]( Env& e, const Term& identity, const Term& contextId, Term& result )
        {

            auto newId = ConcatenateVectorTerms( toImport, DropVectorTerm( identity, prefixLength ) );
            return e.retrieveValue( newId, contextId, result );
        } ) );
}







|
>
>
|
|
>
|
|
|

123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
    // Create the pattern: it is the destination identity,
    // followed by one or more elements.
    auto vecPat = Vector::MakeAppend( vec, ANYTERM( _ ) );
    vecPat.setRepetitionTerm( ANYTERM( _ ) );

    auto pat = TERM( make_shared< Vector >( move( vecPat ) ) );

    storeValue( pat, transitive ? ANYTERM( _ ) : destination,
        make_shared< ValueProvider >(
            [toImport, prefixLength](
                Env& e, const Term& identity, const Term& contextId, Term& result )
            {
                auto newId =
                    ConcatenateVectorTerms( toImport, DropVectorTerm( identity, prefixLength ) );
                return e.retrieveValue( newId, contextId, result );
            } ) );
}
Changes to bs/sema/env.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
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
    class TemplateRuleSet;
    class InvocationRuleSet;
    class TypeCheckingRuleSet;
    class OverloadSet;

    class Env : public enable_shared_from_this< Env >
    {
        public:
            Env();
            ~Env();

            enum class Status
            {
                Success,
                NoMatch,
                AmbiguousMatch
            };


            using ValueProvider = function< Status ( Env& e, const Term& identity, const Term& contextId, Term& result ) >;

            void storeValue( const Term& idPat, const Term& contextIdPat, const Term& val );

            void storeValue( const Term& idPat, const Term& contextIdPat, const ptr< ValueProvider >& valp );
            Status retrieveValue( const Term& id, const Term& contextId, Term& result );
            optional< Term > retrieveValue( const Term& id, const Term& contextId );

            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& extIsType() { return m_extIsType; }

            const auto& extIsType() const { return m_extIsType; }

            auto& extDropValue() { return m_extDropValue; }

            const auto& extDropValue() const { return m_extDropValue; }

            auto& extDestroyValue() { return m_extDestroyValue; }

            const auto& extDestroyValue() const { return m_extDestroyValue; }

            auto& extInitialize() { return m_extInitialize; }

            const auto& extInitialize() const { return m_extInitialize; }

            auto& extIsTrivialInitialization() { return m_extIsTrivialInitialization; }

            const auto& extIsTrivialInitialization() const { return m_extIsTrivialInitialization; }

            auto& extIsTrivialAssignment() { return m_extIsTrivialAssignment; }

            const auto& extIsTrivialAssignment() const { return m_extIsTrivialAssignment; }

            auto& extGetUnderlyingType() { return m_extGetUnderlyingType; }

            const auto& extGetUnderlyingType() const { return m_extGetUnderlyingType; }

            auto& extLowerType() { return m_extLowerType; }

            const auto& extLowerType() const { return m_extLowerType; }

            auto& extLowerValue() { return m_extLowerValue; }

            const auto& extLowerValue() const { return m_extLowerValue; }



            auto& extConvertFuncParam() { return m_extConvertFuncParam; }
            const auto& extConvertFuncParam() const { return m_extConvertFuncParam; }

            auto& extConvertFuncArgs() { return m_extConvertFuncArgs; }

            const auto& extConvertFuncArgs() const { return m_extConvertFuncArgs; }

            auto& extPoisonBuilder() { return m_extPoisonBuilder; }

            const auto& extPoisonBuilder() const { return m_extPoisonBuilder; }

            auto& extBuilderAllowsOverloading() { return m_extBuilderAllowsOverloading; }

            const auto& extBuilderAllowsOverloading() const { return m_extBuilderAllowsOverloading; }

            auto& extGetCFG() { return m_extGetCFG; }

            const auto& extGetCFG() const { return m_extGetCFG; }

            auto& extFinalize() { return m_extFinalize; }

            const auto& extFinalize() const { return m_extFinalize; }

            auto& extGetBreakableScopeLevels() { return m_extGetBreakableScopeLevels; }

            const auto& extGetBreakableScopeLevels() const { return m_extGetBreakableScopeLevels; }

            auto& extGetContinuableScopeLevels() { return m_extGetContinuableScopeLevels; }

            const auto& extGetContinuableScopeLevels() const { return m_extGetContinuableScopeLevels; }







            auto& extBeginVisibilityScope() { return m_extBeginVisibilityScope; }
            const auto& extBeginVisibilityScope() const { return m_extBeginVisibilityScope; }

            auto& extBeginLifetimeScope() { return m_extBeginLifetimeScope; }
            const auto& extBeginLifetimeScope() const { return m_extBeginLifetimeScope; }

            auto& extEndLifetimeScope() { return m_extEndLifetimeScope; }

            const auto& extEndLifetimeScope() const { return m_extEndLifetimeScope; }

            auto& extBeginBreakableScope() { return m_extBeginBreakableScope; }

            const auto& extBeginBreakableScope() const { return m_extBeginBreakableScope; }

            auto& extEndBreakableScope() { return m_extEndBreakableScope; }

            const auto& extEndBreakableScope() const { return m_extEndBreakableScope; }

            auto& extBeginContinuableScope() { return m_extBeginContinuableScope; }

            const auto& extBeginContinuableScope() const { return m_extBeginContinuableScope; }

            auto& extEndContinuableScope() { return m_extEndContinuableScope; }

            const auto& extEndContinuableScope() const { return m_extEndContinuableScope; }

            auto& extDeclareValue() { return m_extDeclareValue; }

            const auto& extDeclareValue() const { return m_extDeclareValue; }











            auto& extOnValueDeclared() { return m_extOnValueDeclared; }
            const auto& extOnValueDeclared() const { return m_extOnValueDeclared; }

            auto& extDestroyLiveValue() { return m_extDestroyLiveValue; }

            const auto& extDestroyLiveValue() const { return m_extDestroyLiveValue; }

            auto& extDestroyAllLiveValues() { return m_extDestroyAllLiveValues; }
            const auto& extDestroyAllLiveValues() const { return m_extDestroyAllLiveValues; }

            auto& extDestroyAllLiveValuesFromBreakScope() { return m_extDestroyAllLiveValuesFromBreakScope; }


            const auto& extDestroyAllLiveValuesFromBreakScope() const { return m_extDestroyAllLiveValuesFromBreakScope; }

            auto& extDestroyAllLiveValuesFromContinueScope() { return m_extDestroyAllLiveValuesFromContinueScope; }


            const auto& extDestroyAllLiveValuesFromContinueScope() const { return m_extDestroyAllLiveValuesFromContinueScope; }




            auto& extInVerificationCode() { return m_extInVerificationCode; }

            const auto& extInVerificationCode() const { return m_extInVerificationCode; }

            auto& extIsBranchingAllowed() { return m_extIsBranchingAllowed; }

            const auto& extIsBranchingAllowed() const { return m_extIsBranchingAllowed; }

            auto& extCTForEach() { return m_extCTForEach; }

            const auto& extCTForEach() const { return m_extCTForEach; }

            template< typename... T >
            static auto CreateCIRFunc( T&&... args )
            {
                ms_cirFuncs.emplace_back( make_shared< cir::Func >( forward< T >( args )... ) );
                return ms_cirFuncs.back();
            }

            static inline auto& DepsGraph() { return ms_depsGraph; }

        private:
            Trie< ptr< ValueProvider > >        m_valueStore;
            ptr< TemplateRuleSet >              m_templateRuleSet;
            ptr< InvocationRuleSet >            m_invocationRuleSet;
            ptr< TypeCheckingRuleSet >          m_unificationRuleSet;

            ptr< OverloadSet >                  m_extIsType;

            ptr< OverloadSet >                  m_extDropValue;
            ptr< OverloadSet >                  m_extDestroyValue;
            ptr< OverloadSet >                  m_extInitialize;

            ptr< OverloadSet >                  m_extIsTrivialInitialization;
            ptr< OverloadSet >                  m_extIsTrivialAssignment;

            ptr< OverloadSet >                  m_extGetUnderlyingType;
            ptr< OverloadSet >                  m_extLowerType;
            ptr< OverloadSet >                  m_extLowerValue;

            ptr< OverloadSet >                  m_extConvertFuncParam;
            ptr< OverloadSet >                  m_extConvertFuncArgs;

            ptr< OverloadSet >                  m_extPoisonBuilder;

            ptr< OverloadSet >                  m_extBuilderAllowsOverloading;
            ptr< OverloadSet >                  m_extGetCFG;
            ptr< OverloadSet >                  m_extFinalize;

            ptr< OverloadSet >                  m_extGetBreakableScopeLevels;
            ptr< OverloadSet >                  m_extGetContinuableScopeLevels;

            ptr< OverloadSet >                  m_extBeginVisibilityScope;

            ptr< OverloadSet >                  m_extBeginLifetimeScope;
            ptr< OverloadSet >                  m_extEndLifetimeScope;

            ptr< OverloadSet >                  m_extBeginBreakableScope;
            ptr< OverloadSet >                  m_extEndBreakableScope;
            ptr< OverloadSet >                  m_extBeginContinuableScope;
            ptr< OverloadSet >                  m_extEndContinuableScope;

            ptr< OverloadSet >                  m_extDeclareValue;
            ptr< OverloadSet >                  m_extOnValueDeclared;

            ptr< OverloadSet >                  m_extDestroyLiveValue;
            ptr< OverloadSet >                  m_extDestroyAllLiveValues;
            ptr< OverloadSet >                  m_extDestroyAllLiveValuesFromBreakScope;
            ptr< OverloadSet >                  m_extDestroyAllLiveValuesFromContinueScope;

            ptr< OverloadSet >                  m_extInVerificationCode;
            ptr< OverloadSet >                  m_extIsBranchingAllowed;

            ptr< OverloadSet >                  m_extCTForEach;

            uint64_t                            m_valueStoreVersion = 0;

            ptr< CTMemoryManager >              m_memManager;

            // CIR funcs form a cyclic graph, since functions can
            // be recursive or mutually recursive.
            // Since they end up being stored in cir::Call in the form of values,
            // it means that func values have to keep a weak_ptr to their cir func.
            // Therefore we have Env take care of keeping the cir func ownership.
            static vector< ptr< cir::Func > >   ms_cirFuncs;

            static DependencyGraph ms_depsGraph;

            static uint32_t ms_nextUniqueId;
    };
}

#endif







|
|
|

|
|
|
|
|
|

>
|

|
>
|
|
|

|

>
|

|
>
|
>
|

|
>
|
>
|

|

|
>
|

|
>
|

|
>
|

|
>
|

|
>
|

|
>
|

|
>
|

|
>
|

|
>
|
>
>

<
|

|
>
|

|
>
|

|
>
|

|
>
|

|
>
|

|
>
|

|
>
|
>
>
>
>
>
>

<
<
<
<
|

|
>
|

|
>
|

|
>
|

|
>
|

|
>
|

|
>
|
>
>
>
>
>
>
>
>
>
>

<
|

|
>
|
|
|
|
|
|
>
>
|
|
|
>
>
|
|
>
>
>
|
>
|

|
>
|

|
>
|

|
<
|
|
|
|

|

|
|
|
|
|

|

|
|
|

|
|

|
|
|

|
|

|

|
|
|

|
|

|

|
|

|
|
|
|

|
|

|
|
|
|

|
|

|

|

|

|
|
|
|
|
|

|

|

|


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
    class TemplateRuleSet;
    class InvocationRuleSet;
    class TypeCheckingRuleSet;
    class OverloadSet;

    class Env : public enable_shared_from_this< Env >
    {
      public:
        Env();
        ~Env();

        enum class Status
        {
            Success,
            NoMatch,
            AmbiguousMatch
        };

        using ValueProvider =
            function< Status( Env& e, const Term& identity, const Term& contextId, Term& result ) >;

        void storeValue( const Term& idPat, const Term& contextIdPat, const Term& val );
        void storeValue(
            const Term& idPat, const Term& contextIdPat, const ptr< ValueProvider >& valp );
        Status retrieveValue( const Term& id, const Term& contextId, Term& result );
        optional< Term > retrieveValue( const Term& id, const Term& contextId );

        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& extIsType() { return m_extIsType; }

        const auto& extIsType() const { return m_extIsType; }

        auto& extDropValue() { return m_extDropValue; }

        const auto& extDropValue() const { return m_extDropValue; }

        auto& extDestroyValue() { return m_extDestroyValue; }

        const auto& extDestroyValue() const { return m_extDestroyValue; }

        auto& extInitialize() { return m_extInitialize; }

        const auto& extInitialize() const { return m_extInitialize; }

        auto& extIsTrivialInitialization() { return m_extIsTrivialInitialization; }

        const auto& extIsTrivialInitialization() const { return m_extIsTrivialInitialization; }

        auto& extIsTrivialAssignment() { return m_extIsTrivialAssignment; }

        const auto& extIsTrivialAssignment() const { return m_extIsTrivialAssignment; }

        auto& extGetUnderlyingType() { return m_extGetUnderlyingType; }

        const auto& extGetUnderlyingType() const { return m_extGetUnderlyingType; }

        auto& extLowerType() { return m_extLowerType; }

        const auto& extLowerType() const { return m_extLowerType; }

        auto& extLowerValue() { return m_extLowerValue; }

        const auto& extLowerValue() const { return m_extLowerValue; }

        auto& extConvertFuncParam() { return m_extConvertFuncParam; }


        const auto& extConvertFuncParam() const { return m_extConvertFuncParam; }

        auto& extConvertFuncArgs() { return m_extConvertFuncArgs; }

        const auto& extConvertFuncArgs() const { return m_extConvertFuncArgs; }

        auto& extPoisonBuilder() { return m_extPoisonBuilder; }

        const auto& extPoisonBuilder() const { return m_extPoisonBuilder; }

        auto& extBuilderAllowsOverloading() { return m_extBuilderAllowsOverloading; }

        const auto& extBuilderAllowsOverloading() const { return m_extBuilderAllowsOverloading; }

        auto& extGetCFG() { return m_extGetCFG; }

        const auto& extGetCFG() const { return m_extGetCFG; }

        auto& extFinalize() { return m_extFinalize; }

        const auto& extFinalize() const { return m_extFinalize; }

        auto& extGetBreakableScopeLevels() { return m_extGetBreakableScopeLevels; }

        const auto& extGetBreakableScopeLevels() const { return m_extGetBreakableScopeLevels; }

        auto& extGetContinuableScopeLevels() { return m_extGetContinuableScopeLevels; }

        const auto& extGetContinuableScopeLevels() const { return m_extGetContinuableScopeLevels; }

        auto& extBeginVisibilityScope() { return m_extBeginVisibilityScope; }

        const auto& extBeginVisibilityScope() const { return m_extBeginVisibilityScope; }

        auto& extBeginLifetimeScope() { return m_extBeginLifetimeScope; }





        const auto& extBeginLifetimeScope() const { return m_extBeginLifetimeScope; }

        auto& extEndLifetimeScope() { return m_extEndLifetimeScope; }

        const auto& extEndLifetimeScope() const { return m_extEndLifetimeScope; }

        auto& extBeginBreakableScope() { return m_extBeginBreakableScope; }

        const auto& extBeginBreakableScope() const { return m_extBeginBreakableScope; }

        auto& extEndBreakableScope() { return m_extEndBreakableScope; }

        const auto& extEndBreakableScope() const { return m_extEndBreakableScope; }

        auto& extBeginContinuableScope() { return m_extBeginContinuableScope; }

        const auto& extBeginContinuableScope() const { return m_extBeginContinuableScope; }

        auto& extEndContinuableScope() { return m_extEndContinuableScope; }

        const auto& extEndContinuableScope() const { return m_extEndContinuableScope; }

        auto& extDeclareValue() { return m_extDeclareValue; }

        const auto& extDeclareValue() const { return m_extDeclareValue; }

        auto& extOnValueDeclared() { return m_extOnValueDeclared; }

        const auto& extOnValueDeclared() const { return m_extOnValueDeclared; }

        auto& extDestroyLiveValue() { return m_extDestroyLiveValue; }

        const auto& extDestroyLiveValue() const { return m_extDestroyLiveValue; }

        auto& extDestroyAllLiveValues() { return m_extDestroyAllLiveValues; }


        const auto& extDestroyAllLiveValues() const { return m_extDestroyAllLiveValues; }

        auto& extDestroyAllLiveValuesFromBreakScope()
        {
            return m_extDestroyAllLiveValuesFromBreakScope;
        }

        const auto& extDestroyAllLiveValuesFromBreakScope() const
        {
            return m_extDestroyAllLiveValuesFromBreakScope;
        }

        auto& extDestroyAllLiveValuesFromContinueScope()
        {
            return m_extDestroyAllLiveValuesFromContinueScope;
        }

        const auto& extDestroyAllLiveValuesFromContinueScope() const
        {
            return m_extDestroyAllLiveValuesFromContinueScope;
        }

        auto& extInVerificationCode() { return m_extInVerificationCode; }

        const auto& extInVerificationCode() const { return m_extInVerificationCode; }

        auto& extIsBranchingAllowed() { return m_extIsBranchingAllowed; }

        const auto& extIsBranchingAllowed() const { return m_extIsBranchingAllowed; }

        auto& extCTForEach() { return m_extCTForEach; }

        const auto& extCTForEach() const { return m_extCTForEach; }

        template< typename... T > static auto CreateCIRFunc( T&&... args )

        {
            ms_cirFuncs.emplace_back( make_shared< cir::Func >( forward< T >( args )... ) );
            return ms_cirFuncs.back();
        }

        static inline auto& DepsGraph() { return ms_depsGraph; }

      private:
        Trie< ptr< ValueProvider > > m_valueStore;
        ptr< TemplateRuleSet > m_templateRuleSet;
        ptr< InvocationRuleSet > m_invocationRuleSet;
        ptr< TypeCheckingRuleSet > m_unificationRuleSet;

        ptr< OverloadSet > m_extIsType;

        ptr< OverloadSet > m_extDropValue;
        ptr< OverloadSet > m_extDestroyValue;
        ptr< OverloadSet > m_extInitialize;

        ptr< OverloadSet > m_extIsTrivialInitialization;
        ptr< OverloadSet > m_extIsTrivialAssignment;

        ptr< OverloadSet > m_extGetUnderlyingType;
        ptr< OverloadSet > m_extLowerType;
        ptr< OverloadSet > m_extLowerValue;

        ptr< OverloadSet > m_extConvertFuncParam;
        ptr< OverloadSet > m_extConvertFuncArgs;

        ptr< OverloadSet > m_extPoisonBuilder;

        ptr< OverloadSet > m_extBuilderAllowsOverloading;
        ptr< OverloadSet > m_extGetCFG;
        ptr< OverloadSet > m_extFinalize;

        ptr< OverloadSet > m_extGetBreakableScopeLevels;
        ptr< OverloadSet > m_extGetContinuableScopeLevels;

        ptr< OverloadSet > m_extBeginVisibilityScope;

        ptr< OverloadSet > m_extBeginLifetimeScope;
        ptr< OverloadSet > m_extEndLifetimeScope;

        ptr< OverloadSet > m_extBeginBreakableScope;
        ptr< OverloadSet > m_extEndBreakableScope;
        ptr< OverloadSet > m_extBeginContinuableScope;
        ptr< OverloadSet > m_extEndContinuableScope;

        ptr< OverloadSet > m_extDeclareValue;
        ptr< OverloadSet > m_extOnValueDeclared;

        ptr< OverloadSet > m_extDestroyLiveValue;
        ptr< OverloadSet > m_extDestroyAllLiveValues;
        ptr< OverloadSet > m_extDestroyAllLiveValuesFromBreakScope;
        ptr< OverloadSet > m_extDestroyAllLiveValuesFromContinueScope;

        ptr< OverloadSet > m_extInVerificationCode;
        ptr< OverloadSet > m_extIsBranchingAllowed;

        ptr< OverloadSet > m_extCTForEach;

        uint64_t m_valueStoreVersion = 0;

        ptr< CTMemoryManager > m_memManager;

        // CIR funcs form a cyclic graph, since functions can
        // be recursive or mutually recursive.
        // Since they end up being stored in cir::Call in the form of values,
        // it means that func values have to keep a weak_ptr to their cir func.
        // Therefore we have Env take care of keeping the cir func ownership.
        static vector< ptr< cir::Func > > ms_cirFuncs;

        static DependencyGraph ms_depsGraph;

        static uint32_t ms_nextUniqueId;
    };
} // namespace goose::sema

#endif
Changes to bs/sema/hole.cpp.
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
    {
        static auto hp = HOLE( ""_sid, ""_sid, Hole::Behavior::Any );
        return hp;
    }

    optional< Hole > HoleFromIRExpr( const Term& t )
    {
        auto result = Decompose( t,
            Val< Hole >()
        );

        return result;
    }

    const Term& ForwardingHolePattern()
    {
        static auto hp = HOLE( ""_sid, "fwd"_sid, Hole::Behavior::Any );
        return hp;
    }

    optional< Hole > ForwardingHoleFromIRExpr( const Term& t )
    {
        auto result = Decompose( t,
            Val< Hole >()
        );

        return result;
    }
}







|
<
<












|
<
<



|
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
    {
        static auto hp = HOLE( ""_sid, ""_sid, Hole::Behavior::Any );
        return hp;
    }

    optional< Hole > HoleFromIRExpr( const Term& t )
    {
        auto result = Decompose( t, Val< Hole >() );



        return result;
    }

    const Term& ForwardingHolePattern()
    {
        static auto hp = HOLE( ""_sid, "fwd"_sid, Hole::Behavior::Any );
        return hp;
    }

    optional< Hole > ForwardingHoleFromIRExpr( const Term& t )
    {
        auto result = Decompose( t, Val< Hole >() );



        return result;
    }
} // namespace goose::sema
Changes to bs/sema/hole.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef GOOSE_SEMA_HOLE_H
#define GOOSE_SEMA_HOLE_H

namespace goose::sema
{
    extern StringId HoleBhvToSid( Hole::Behavior bhv );
    extern Hole::Behavior SidToHoleBhv( StringId sid );

    extern const Term& HolePattern();
    extern optional< Hole > HoleFromIRExpr( const Term& t );

    extern const Term& ForwardingHolePattern();
    extern optional< Hole > ForwardingHoleFromIRExpr( const Term& t );
}

#endif













|


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef GOOSE_SEMA_HOLE_H
#define GOOSE_SEMA_HOLE_H

namespace goose::sema
{
    extern StringId HoleBhvToSid( Hole::Behavior bhv );
    extern Hole::Behavior SidToHoleBhv( StringId sid );

    extern const Term& HolePattern();
    extern optional< Hole > HoleFromIRExpr( const Term& t );

    extern const Term& ForwardingHolePattern();
    extern optional< Hole > ForwardingHoleFromIRExpr( const Term& t );
} // namespace goose::sema

#endif
Changes to bs/sema/inv-ruleset.cpp.
1
2
3
4
5
6
7
8
9
#include "sema.h"

using namespace goose;
using namespace goose::sema;

void InvocationRuleSet::addRule( const Term& calleePat, const ptr< InvocationRule >& r )
{
    m_rules = Merge( m_rules, calleePat, [r]( auto&& ){ return r; } );
}







|

1
2
3
4
5
6
7
8
9
#include "sema.h"

using namespace goose;
using namespace goose::sema;

void InvocationRuleSet::addRule( const Term& calleePat, const ptr< InvocationRule >& r )
{
    m_rules = Merge( m_rules, calleePat, [r]( auto&& ) { return r; } );
}
Changes to bs/sema/inv-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
#ifndef GOOSE_SEMA_INV_RULESET_H
#define GOOSE_SEMA_INV_RULESET_H

namespace goose::sema
{
    class InvocationRule
    {
        public:
            virtual ~InvocationRule() {}

            virtual bool canBeInvoked( const Context& c, const Value& callee ) const
            {
                return true;
            }

            virtual Value resolveInvocation( Context& c, LocationId locationId, const Value& callee, const Term& args ) const = 0;

            virtual Value invoke( Context& c, LocationId locationId, const Value& callee, const Term& args, const Term& checkedCallPat, TypeCheckingContext& tcc ) const

            {
                return PoisonValue();
            }

            virtual optional< Term > getSignature( const Value& callee ) const
            {
                return nullopt;
            }

            virtual Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& checkedCallPat, TypeCheckingContext& tcc ) const
            {
                return PoisonValue();
            }
    };

    class InvocationRuleSet
    {
        public:
            InvocationRuleSet() {}

            void addRule( const Term& calleePat, const ptr< InvocationRule >& r );

            const auto& rules() const { return m_rules; }

        private:
            Trie< ptr< InvocationRule > > m_rules;
    };
}


#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
#ifndef GOOSE_SEMA_INV_RULESET_H
#define GOOSE_SEMA_INV_RULESET_H

namespace goose::sema
{
    class InvocationRule
    {
      public:
        virtual ~InvocationRule() {}

        virtual bool canBeInvoked( const Context& c, const Value& callee ) const { return true; }



        virtual Value resolveInvocation(
            Context& c, LocationId locationId, const Value& callee, const Term& args ) const = 0;

        virtual Value invoke( Context& c, LocationId locationId, const Value& callee,
            const Term& args, const Term& checkedCallPat, TypeCheckingContext& tcc ) const
        {
            return PoisonValue();
        }

        virtual optional< Term > getSignature( const Value& callee ) const { return nullopt; }



        virtual Value prepareFunc( const Context& c, LocationId funcValLocation,
            const Value& callee, const Term& checkedCallPat, TypeCheckingContext& tcc ) const
        {
            return PoisonValue();
        }
    };

    class InvocationRuleSet
    {
      public:
        InvocationRuleSet() {}

        void addRule( const Term& calleePat, const ptr< InvocationRule >& r );

        const auto& rules() const { return m_rules; }

      private:
        Trie< ptr< InvocationRule > > m_rules;
    };

} // namespace goose::sema

#endif
Changes to bs/sema/invocation.cpp.
39
40
41
42
43
44
45

46
47
48
49
50
51
52
53
54

    bool CanBeInvoked( const Context& c, const Value& callee )
    {
        auto pIR = GetInvocationRule( *c.env(), callee );
        return pIR && pIR->canBeInvoked( c, callee );
    }


    Value ResolveInvocation( Context& c, const ptr< InvocationRule >& pInvRule, const Value& callee, const Value& args )
    {
        if( callee.isPoison() || args.isPoison() )
            return PoisonValue();

        auto loc = Location::CreateSpanningLocation( callee.locationId(), args.locationId() );
        return pInvRule->resolveInvocation( c, loc, callee, args.val() ).setLocationId( loc );
    }
}







>
|







|
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

    bool CanBeInvoked( const Context& c, const Value& callee )
    {
        auto pIR = GetInvocationRule( *c.env(), callee );
        return pIR && pIR->canBeInvoked( c, callee );
    }

    Value ResolveInvocation(
        Context& c, const ptr< InvocationRule >& pInvRule, const Value& callee, const Value& args )
    {
        if( callee.isPoison() || args.isPoison() )
            return PoisonValue();

        auto loc = Location::CreateSpanningLocation( callee.locationId(), args.locationId() );
        return pInvRule->resolveInvocation( c, loc, callee, args.val() ).setLocationId( loc );
    }
} // namespace goose::sema
Changes to bs/sema/invocation.h.
1
2
3
4
5
6
7

8
9

10
11
#ifndef GOOSE_SEMA_INVOCATION_H
#define GOOSE_SEMA_INVOCATION_H

namespace goose::sema
{
    extern ptr< InvocationRule > GetInvocationRule( const Env& e, const Value& callee );
    extern bool CanBeInvoked( const Context& c, const Value& callee );

    extern Value ResolveInvocation( Context& c, const ptr< InvocationRule >& pInvRule, const Value& callee, const Value& args );
}


#endif







>
|
<
>


1
2
3
4
5
6
7
8
9

10
11
12
#ifndef GOOSE_SEMA_INVOCATION_H
#define GOOSE_SEMA_INVOCATION_H

namespace goose::sema
{
    extern ptr< InvocationRule > GetInvocationRule( const Env& e, const Value& callee );
    extern bool CanBeInvoked( const Context& c, const Value& callee );
    extern Value ResolveInvocation(
        Context& c, const ptr< InvocationRule >& pInvRule, const Value& callee, const Value& args );

} // namespace goose::sema

#endif
Changes to bs/sema/lower.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"

using namespace goose;
using namespace goose::eir;
using namespace goose::builtins;

namespace goose::sema
{
    optional< Value > GetUnderlyingType( const Context& c, const Value& type )
    {
        DiagnosticsContext dc( type.locationId(), "When invoking _GetUnderlyingType." );
        auto result = InvokeOverloadSet( c, c.env()->extGetUnderlyingType(), MakeOpenTuple( type ),
            type.locationId() );
        if( result.isPoison() )
            return nullopt;

        return result;
    }

    optional< Value > LowerType( const Context& c, const Value& type )
    {
        DiagnosticsContext dc( type.locationId(), "When invoking _LowerType." );
        auto result = InvokeOverloadSet( c, c.env()->extLowerType(), MakeOpenTuple( type ),
            type.locationId() );
        if( result.isPoison() )
            return nullopt;

        return result;
    }

    optional< Value > LowerValue( const Context& c, const Value& val )
    {
        DiagnosticsContext dc( val.locationId(), "When invoking _LowerValue." );
        auto result = InvokeOverloadSet( c, c.env()->extLowerValue(), MakeOpenTuple( val ),
            val.locationId() );
        if( result.isPoison() )
            return nullopt;

        return result;
    }
}












|
|









|
|









|
|





|
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"

using namespace goose;
using namespace goose::eir;
using namespace goose::builtins;

namespace goose::sema
{
    optional< Value > GetUnderlyingType( const Context& c, const Value& type )
    {
        DiagnosticsContext dc( type.locationId(), "When invoking _GetUnderlyingType." );
        auto result = InvokeOverloadSet(
            c, c.env()->extGetUnderlyingType(), MakeOpenTuple( type ), type.locationId() );
        if( result.isPoison() )
            return nullopt;

        return result;
    }

    optional< Value > LowerType( const Context& c, const Value& type )
    {
        DiagnosticsContext dc( type.locationId(), "When invoking _LowerType." );
        auto result = InvokeOverloadSet(
            c, c.env()->extLowerType(), MakeOpenTuple( type ), type.locationId() );
        if( result.isPoison() )
            return nullopt;

        return result;
    }

    optional< Value > LowerValue( const Context& c, const Value& val )
    {
        DiagnosticsContext dc( val.locationId(), "When invoking _LowerValue." );
        auto result = InvokeOverloadSet(
            c, c.env()->extLowerValue(), MakeOpenTuple( val ), val.locationId() );
        if( result.isPoison() )
            return nullopt;

        return result;
    }
} // namespace goose::sema
Changes to bs/sema/overloadset.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
}

bool OverloadSet::add( const Env& e, const Value& callee, const ptr< InvocationRule >& pInvRule )
{
    auto signature = pInvRule->getSignature( callee );
    assert( signature );

    auto sigDecomp = Decompose( *signature,
        Val< pvec >()
    );

    assert( sigDecomp );

    bool success = false;
    m_trie = m_trie->merge( *sigDecomp->get(), [&]( auto&& previous ) -> Overload

    {
        if( previous.callee )
            return move( previous );

        success = true;
        return { pInvRule, callee };
    } );

    m_resolutionCache.clear();
    return success;
}

OverloadSet::TCGen OverloadSet::typeCheck( const Term& callPat, const TypeCheckingContext& tcc ) const

{
    auto callDecomp = Decompose( callPat,
        Val< pvec >()
    );

    if( !callDecomp )
        co_return;

    for( auto&& [callVec,uniCallVec,ovl,tcc] : m_trie->typeCheck( *callDecomp->get(), tcc ) )
    {
        auto uniCall = TERM( make_shared< Vector >( uniCallVec ) );
        co_yield { move( uniCall ), ovl, tcc };
    }
}

optional< OverloadSet::Overload > OverloadSet::getResolutionFromCache( const Term& args ) const







|
<
<




|
>
|
|
|

|
|
|





|
>

|
<
<




|







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
}

bool OverloadSet::add( const Env& e, const Value& callee, const ptr< InvocationRule >& pInvRule )
{
    auto signature = pInvRule->getSignature( callee );
    assert( signature );

    auto sigDecomp = Decompose( *signature, Val< pvec >() );



    assert( sigDecomp );

    bool success = false;
    m_trie = m_trie->merge( *sigDecomp->get(),
        [&]( auto&& previous ) -> Overload
        {
            if( previous.callee )
                return move( previous );

            success = true;
            return { pInvRule, callee };
        } );

    m_resolutionCache.clear();
    return success;
}

OverloadSet::TCGen OverloadSet::typeCheck(
    const Term& callPat, const TypeCheckingContext& tcc ) const
{
    auto callDecomp = Decompose( callPat, Val< pvec >() );



    if( !callDecomp )
        co_return;

    for( auto&& [callVec, uniCallVec, ovl, tcc] : m_trie->typeCheck( *callDecomp->get(), tcc ) )
    {
        auto uniCall = TERM( make_shared< Vector >( uniCallVec ) );
        co_yield { move( uniCall ), ovl, tcc };
    }
}

optional< OverloadSet::Overload > OverloadSet::getResolutionFromCache( const Term& args ) const
Changes to bs/sema/overloadset.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
#ifndef GOOSE_SEMA_OVERLOADSET_H
#define GOOSE_SEMA_OVERLOADSET_H

namespace goose::sema
{
    class OverloadSet
    {
        public:
            struct Overload
            {
                ptr< InvocationRule > pInvRule;
                optional< Value > callee;
            };

            OverloadSet( const Term& id ) :
                m_identity( id )
            {}



            bool ghost() const { return m_ghost; }

            void setGhost( bool g ) { m_ghost = g; }

            bool verboseResolution() const { return m_verboseResolution; }

            void setVerboseResolution( bool vr ) { m_verboseResolution = vr; }

            bool empty() const { return m_trie->empty(); }

            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& callPat, const TypeCheckingContext& tcc ) const;

            optional< Overload > getResolutionFromCache( const Term& args ) const;
            void addResolutionToCache( const Term& args, const Overload& ovl );

            Generator< const Overload* > enumerate() const;

        private:
            Term m_identity;

            using trie_type = TCTrie< Overload >;
            ptr< trie_type > m_trie = make_shared< trie_type >();

            unordered_map< Term, Overload > m_resolutionCache;

            bool m_ghost = false;
            bool m_verboseResolution = false;
    };
}

#endif







|
|
|
|
|
|

|
|
<
|
>
>
|
>
|

|
>
|

|

|

|
|

|
<
<
<
<

|

|
|

|

|
|

|
|

|

|
|

|


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35




36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#ifndef GOOSE_SEMA_OVERLOADSET_H
#define GOOSE_SEMA_OVERLOADSET_H

namespace goose::sema
{
    class OverloadSet
    {
      public:
        struct Overload
        {
            ptr< InvocationRule > pInvRule;
            optional< Value > callee;
        };

        OverloadSet( const Term& id ) :
            m_identity( id )

        {
        }

        bool ghost() const { return m_ghost; }

        void setGhost( bool g ) { m_ghost = g; }

        bool verboseResolution() const { return m_verboseResolution; }

        void setVerboseResolution( bool vr ) { m_verboseResolution = vr; }

        bool empty() const { return m_trie->empty(); }

        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& callPat, const TypeCheckingContext& tcc ) const;

        optional< Overload > getResolutionFromCache( const Term& args ) const;
        void addResolutionToCache( const Term& args, const Overload& ovl );

        Generator< const Overload* > enumerate() const;

      private:
        Term m_identity;

        using trie_type = TCTrie< Overload >;
        ptr< trie_type > m_trie = make_shared< trie_type >();

        unordered_map< Term, Overload > m_resolutionCache;

        bool m_ghost = false;
        bool m_verboseResolution = false;
    };
} // namespace goose::sema

#endif
Changes to bs/sema/postprocess.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
#include "sema.h"

namespace goose::sema
{
    Term WrapWithPostprocFunc( const Term& t, const ptr< PostProcFunc >& pp )
    {
        return SetWeight( VEC( TSID( postproc ),
            TERM( static_pointer_cast< void >( pp ) ),
            t ), GetWeight( t ) );
    }

    Term WrapWithPostprocFunc( const Term& t, PostProcFunc&& pp )
    {
        return SetWeight( VEC( TSID( postproc ),

            TERM( static_pointer_cast< void >( make_shared< PostProcFunc >( move( pp ) ) ) ),

            t ), GetWeight( t ) );
    }

    optional< pair< Term, ptr< PostProcFunc > > > UnwrapPostprocFunc( const Term& ppt )
    {
        auto result = Decompose( ppt,
            Vec(
                Lit( "postproc"_sid ),
                Val< ptr< void > >(),
                SubTerm()
            )
        );

        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() )
                    return src;
            else
            {
                const auto& optVal = tcc.getValue( hole.name().id() );
                if( !optVal )
                    return HOLE( "_"_sid );

                return Postprocess( *optVal, 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
35
36
37
38
39
40
41
42
43
44
45
46
#include "sema.h"

namespace goose::sema
{
    Term WrapWithPostprocFunc( const Term& t, const ptr< PostProcFunc >& pp )
    {
        return SetWeight(
            VEC( TSID( postproc ), TERM( static_pointer_cast< void >( pp ) ), t ), GetWeight( t ) );

    }

    Term WrapWithPostprocFunc( const Term& t, PostProcFunc&& pp )
    {
        return SetWeight(
            VEC( TSID( postproc ),
                TERM( static_pointer_cast< void >( make_shared< PostProcFunc >( move( pp ) ) ) ),
                t ),
            GetWeight( t ) );
    }

    optional< pair< Term, ptr< PostProcFunc > > > UnwrapPostprocFunc( const Term& ppt )
    {
        auto result =

            Decompose( ppt, Vec( Lit( "postproc"_sid ), Val< ptr< void > >(), SubTerm() ) );





        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() )
                return src;
            else
            {
                const auto& optVal = tcc.getValue( hole.name().id() );
                if( !optVal )
                    return HOLE( "_"_sid );

                return Postprocess( *optVal, tcc );
84
85
86
87
88
89
90
91
                return nullopt;

            outputTerms->setRepetitionTerm( move( *newRpt ) );
        }

        return outputTerms;
    }
}







|
80
81
82
83
84
85
86
87
                return nullopt;

            outputTerms->setRepetitionTerm( move( *newRpt ) );
        }

        return outputTerms;
    }
} // namespace goose::sema
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, 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





|








|


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 );
} // namespace goose::sema

#endif
Changes to bs/sema/scope.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
#ifndef GOOSE_SEMA_SCOPE_H
#define GOOSE_SEMA_SCOPE_H

namespace goose::sema
{
    class LifetimeScopeGuard
    {
        public:
            LifetimeScopeGuard( const Context& c );
            ~LifetimeScopeGuard();

        private:
            const sema::Context& m_context;
    };

    class BreakableScopeGuard
    {
        public:
            BreakableScopeGuard( const Context& c );
            ~BreakableScopeGuard();

        private:
            const sema::Context& m_context;
    };

    class ContinuableScopeGuard
    {
        public:
            ContinuableScopeGuard( const Context& c );
            ~ContinuableScopeGuard();

        private:
            const sema::Context& m_context;
    };

    // Helper object that creates a nested visibility scope.
    class VisibilityScope
    {
        public:
            VisibilityScope( Context& c );

        private:
            Context::CurrentContextGuard m_identityGuard;
    };

    // Helper object that creates a nested visibility scope
    // along with a lifetime scope.
    class Scope : public VisibilityScope
    {
        public:
            Scope( Context& c ) :
                VisibilityScope( c ),
                m_lifetimeScopeGuard( c )
            {}



        private:
            LifetimeScopeGuard m_lifetimeScopeGuard;
    };
}


#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
#ifndef GOOSE_SEMA_SCOPE_H
#define GOOSE_SEMA_SCOPE_H

namespace goose::sema
{
    class LifetimeScopeGuard
    {
      public:
        LifetimeScopeGuard( const Context& c );
        ~LifetimeScopeGuard();

      private:
        const sema::Context& m_context;
    };

    class BreakableScopeGuard
    {
      public:
        BreakableScopeGuard( const Context& c );
        ~BreakableScopeGuard();

      private:
        const sema::Context& m_context;
    };

    class ContinuableScopeGuard
    {
      public:
        ContinuableScopeGuard( const Context& c );
        ~ContinuableScopeGuard();

      private:
        const sema::Context& m_context;
    };

    // Helper object that creates a nested visibility scope.
    class VisibilityScope
    {
      public:
        VisibilityScope( Context& c );

      private:
        Context::CurrentContextGuard m_identityGuard;
    };

    // Helper object that creates a nested visibility scope
    // along with a lifetime scope.
    class Scope : public VisibilityScope
    {
      public:
        Scope( Context& c ) :
            VisibilityScope( c ),
            m_lifetimeScopeGuard( c )

        {
        }

      private:
        LifetimeScopeGuard m_lifetimeScopeGuard;
    };

} // namespace goose::sema

#endif
Changes to bs/sema/sema.h.
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    using namespace eir;
    using namespace diagnostics;

    class Context;

    extern optional< Value > LowerType( const Context& c, const Value& type );
    extern optional< Value > LowerValue( const Context& c, const Value& type );
}

#include "hole.h"
#include "context.h"
#include "ctmm.h"
#include "env.h"

#include "tc-score.h"







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    using namespace eir;
    using namespace diagnostics;

    class Context;

    extern optional< Value > LowerType( const Context& c, const Value& type );
    extern optional< Value > LowerValue( const Context& c, const Value& type );
} // namespace goose::sema

#include "hole.h"
#include "context.h"
#include "ctmm.h"
#include "env.h"

#include "tc-score.h"
Changes to bs/sema/substitute.cpp.
33
34
35
36
37
38
39
40

        if( auto rpt = vec.repetitionTerm() )
            outputTerms->setRepetitionTerm( Substitute( *rpt, context ) );

        outputTerms->setWeightOverride( vec.weightOverride() );
        return outputTerms;
    }
}







|
33
34
35
36
37
38
39
40

        if( auto rpt = vec.repetitionTerm() )
            outputTerms->setRepetitionTerm( Substitute( *rpt, context ) );

        outputTerms->setWeightOverride( vec.weightOverride() );
        return outputTerms;
    }
} // namespace goose::sema
Changes to bs/sema/tc-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

#include "sema.h"

namespace goose::sema
{

    TCGen TypeCheckVectors( TCVecGenerator lvg, TCVecGenerator rvg, const Vector& solutionVec, TypeCheckingContext tcc )
    {

        for( auto&& [u,tcc] : TypeCheck( lvg( tcc.LHSSubContext() ), rvg( tcc.RHSSubContext() ), tcc ) )
        {
            auto vec = Vector::MakeAppend( solutionVec, move( u ) );

            if( lvg.fixedPartFinished() && rvg.fixedPartFinished() )
                co_yield { TERM( make_shared< Vector >( move( vec ) ) ), tcc };
            else
                co_yield TypeCheckVectors( lvg, rvg, vec, tcc );
        }
    }

    void SetupBasicTypeCheckingRules( TypeCheckingRuleSet& ruleSet )
    {
        // Identity type checking rule
        ruleSet.addTypeCheckingRule( ANYTERM( T ), 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 type checking 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 type checking rule
        ruleSet.addTypeCheckingRule( TERM( LocationId( 0 ) ), 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 type checking as much as possible. This only impacts the quality of
            // error messages.
            auto lloc = get< LocationId >( lhs ).index();
            auto rloc = get< LocationId >( rhs ).index();

            // 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 { LocationId( result ), tcc };
        } );

        // void* type checking rule
        ruleSet.addTypeCheckingRule( TERM( nullptr ), TERM( nullptr ), []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen

        {
            // Only preserve void* accross type checking 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 };
        } );

        // Type checking 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.addTypeCheckingRule( 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 );

            TCVecGenerator lvg( *lVector );
            TCVecGenerator rvg( *rVector );

            if( lvg.fixedPartFinished() && rvg.fixedPartFinished() )
            {
                co_yield { lhs, tcc };
                co_return;
            }

            Vector sol;
            co_yield TypeCheckVectors( lvg, rvg, sol, 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
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
#include "sema.h"

namespace goose::sema
{
    TCGen TypeCheckVectors(
        TCVecGenerator lvg, TCVecGenerator rvg, const Vector& solutionVec, TypeCheckingContext tcc )
    {
        for( auto&& [u, tcc] :
            TypeCheck( lvg( tcc.LHSSubContext() ), rvg( tcc.RHSSubContext() ), tcc ) )
        {
            auto vec = Vector::MakeAppend( solutionVec, move( u ) );

            if( lvg.fixedPartFinished() && rvg.fixedPartFinished() )
                co_yield { TERM( make_shared< Vector >( move( vec ) ) ), tcc };
            else
                co_yield TypeCheckVectors( lvg, rvg, vec, tcc );
        }
    }

    void SetupBasicTypeCheckingRules( TypeCheckingRuleSet& ruleSet )
    {
        // Identity type checking rule
        ruleSet.addTypeCheckingRule( ANYTERM( T ), 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 type checking 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 type checking rule
        ruleSet.addTypeCheckingRule( TERM( LocationId( 0 ) ), 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 type checking as much as possible. This only
                // impacts the quality of error messages.
                auto lloc = get< LocationId >( lhs ).index();
                auto rloc = get< LocationId >( rhs ).index();

                // 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 { LocationId( result ), tcc };
            } );

        // void* type checking rule
        ruleSet.addTypeCheckingRule( TERM( nullptr ), TERM( nullptr ),
            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                // Only preserve void* accross type checking 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 };
            } );

        // Type checking 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.addTypeCheckingRule( 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 );

                TCVecGenerator lvg( *lVector );
                TCVecGenerator rvg( *rVector );

                if( lvg.fixedPartFinished() && rvg.fixedPartFinished() )
                {
                    co_yield { lhs, tcc };
                    co_return;
                }

                Vector sol;
                co_yield TypeCheckVectors( lvg, rvg, sol, tcc );
            } );
    }

} // namespace goose::sema
Changes 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
#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 ) )
{}



optional< Term > TypeCheckingContext::getLHSHoleValue( StringId name, uint32_t repetitionIndex ) const

{
    auto index = getLHSHoleIndex( name, repetitionIndex );

    if( index == InvalidIndex )
        return nullopt;

    return getValue( index );
}

optional< Term > TypeCheckingContext::getRHSHoleValue( StringId name, uint32_t repetitionIndex ) const

{
    auto index = getRHSHoleIndex( name, repetitionIndex );

    if( index == InvalidIndex )
        return nullopt;

    return getValue( 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
#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 ) )

{
}

optional< Term > TypeCheckingContext::getLHSHoleValue(
    StringId name, uint32_t repetitionIndex ) const
{
    auto index = getLHSHoleIndex( name, repetitionIndex );

    if( index == InvalidIndex )
        return nullopt;

    return getValue( index );
}

optional< Term > TypeCheckingContext::getRHSHoleValue(
    StringId name, uint32_t repetitionIndex ) const
{
    auto index = getRHSHoleIndex( name, repetitionIndex );

    if( index == InvalidIndex )
        return nullopt;

    return getValue( index );
57
58
59
60
61
62
63

64
65
66
67
68

69
70
71
72
73
74
75
76
        return InvalidIndex;

    return it->second;
}

uint32_t TypeCheckingContext::getLHSHoleIndex( StringId name, Hole::Behavior bhv ) const
{

    return getLHSHoleIndex( name, bhv == Hole::Behavior::Pack ? LHSSubContext().repetitionIndex : 0 );
}

uint32_t TypeCheckingContext::getRHSHoleIndex( StringId name, Hole::Behavior bhv ) const
{

    return getRHSHoleIndex( name, bhv == Hole::Behavior::Pack ? LHSSubContext().repetitionIndex : 0 );
}

uint32_t TypeCheckingContext::createValue( bool required )
{
    CoW( m_pCow )->values.emplace_back( StoredValue{ nullopt, required } );
    uint32_t index = m_pCow->values.size() - 1;
    if( required )







>
|




>
|







61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
        return InvalidIndex;

    return it->second;
}

uint32_t TypeCheckingContext::getLHSHoleIndex( StringId name, Hole::Behavior bhv ) const
{
    return getLHSHoleIndex(
        name, bhv == Hole::Behavior::Pack ? LHSSubContext().repetitionIndex : 0 );
}

uint32_t TypeCheckingContext::getRHSHoleIndex( StringId name, Hole::Behavior bhv ) const
{
    return getRHSHoleIndex(
        name, bhv == Hole::Behavior::Pack ? LHSSubContext().repetitionIndex : 0 );
}

uint32_t TypeCheckingContext::createValue( bool required )
{
    CoW( m_pCow )->values.emplace_back( StoredValue{ nullopt, required } );
    uint32_t index = m_pCow->values.size() - 1;
    if( required )
Changes 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
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
#ifndef GOOSE_SEMA_TC_CONTEXT_H
#define GOOSE_SEMA_TC_CONTEXT_H

namespace goose::sema
{
#ifndef NDEBUG
    struct TCRuleInfo;
#endif

    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(); }

            optional< Term > getLHSHoleValue( StringId name, uint32_t repetitionIndex ) const;
            optional< Term > getRHSHoleValue( StringId name, uint32_t repetitionIndex ) const;

            uint32_t getLHSHoleIndex( StringId name, uint32_t index ) const;
            uint32_t getRHSHoleIndex( StringId name, uint32_t index ) const;

            uint32_t getLHSHoleIndex( StringId name, Hole::Behavior bhv ) const;
            uint32_t getRHSHoleIndex( StringId name, Hole::Behavior bhv ) const;

            uint32_t createValue( bool required = false );

            void setLHSHoleIndex( StringId name, Hole::Behavior bhv, uint32_t index );
            void setRHSHoleIndex( StringId name, Hole::Behavior bhv, uint32_t index );

            void eraseLHSName( StringId name );
            void eraseRHSName( StringId name );

            struct SubContext
            {
                uint32_t namespaceIndex = 0;
                uint32_t repetitionIndex = 0;
            };

            const SubContext& LHSSubContext() const { return m_lhsSubContext; }

            const SubContext& RHSSubContext() const { return m_rhsSubContext; }

            SubContext& LHSSubContext() { return m_lhsSubContext; }

            SubContext& RHSSubContext() { return m_rhsSubContext; }

            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;
            }

            bool isValueResolutionRequired() const
            {
                return m_valuesAreRequired;
            }

            const optional< Term >& getValue( uint32_t index ) const
            {
                assert( m_pCow->values.size() > index );
                return m_pCow->values[index].m_term;
            }

            template< typename T >
            void setValue( uint32_t index, T&& val )
            {
                assert( m_pCow->values.size() > index );

                if( m_pCow->values[index].m_required && !m_pCow->values[index].m_term )
                    --m_numUnknownValues;

                CoW( m_pCow )->values[index] = { forward< T >( val ), true };
            }

            TypeCheckingContext& flip()
            {
                swap( m_lhsSubContext, m_rhsSubContext );
                return *this;
            }

            uint32_t numUnknownValues() const { return m_numUnknownValues; }

            int32_t cost() const { return m_cost; }

            void addCost( int32_t c ) { m_cost +=c; }

            void setCost( int32_t cost ) { m_cost = cost; }

            void addAnonymousHole() { ++m_numAnonymousHoles; }



            auto score() const { return TypeCheckingScore( m_cost, 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 );

        #ifndef NDEBUG
            void TCRuleTrace( const TCRuleInfo* pRule ) const;
            void PushRuleTraceForParam() const;
            void DumpParamsTraces( ostream& out ) const;
        #endif

        private:
            void setValueRequired( uint32_t index );

            Context                                     m_context;

            struct StoredValue
            {
                optional< Term >    m_term;
                bool                m_required = false;
            };

            SubContext                                  m_lhsSubContext{ 1, 0 };
            SubContext                                  m_rhsSubContext{ 2, 0 };
            uint32_t                                    m_nextNamespaceIndex = 3;

            uint32_t                                    m_numUnknownValues = 0;
            int32_t                                     m_cost = 0;

            uint32_t                                    m_numAnonymousHoles = 0;

            using HoleKey = tuple< StringId, uint32_t, uint32_t >;

            struct Cow
            {
                vector< StoredValue >       values;
                map< HoleKey, uint32_t >    holeDict;
                unordered_set< uint32_t >   lockedHoles;

            #ifndef NDEBUG
                // In debug, keep track of which "path" was taken through the various
                // rules to end up with the result.
                using TCTrace = vector< const TCRuleInfo* >;
                mutable TCTrace currentTypeCheckingTrace;
                mutable vector< TCTrace > paramsTypeCheckingTrace;
            #endif
            };

            mutable ptr< Cow >                          m_pCow = make_shared< Cow >();

            bool                                        m_valuesAreRequired = true;
    };
}

#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



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
#ifndef GOOSE_SEMA_TC_CONTEXT_H
#define GOOSE_SEMA_TC_CONTEXT_H

namespace goose::sema
{
#ifndef NDEBUG
    struct TCRuleInfo;
#endif

    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(); }

        optional< Term > getLHSHoleValue( StringId name, uint32_t repetitionIndex ) const;
        optional< Term > getRHSHoleValue( StringId name, uint32_t repetitionIndex ) const;

        uint32_t getLHSHoleIndex( StringId name, uint32_t index ) const;
        uint32_t getRHSHoleIndex( StringId name, uint32_t index ) const;

        uint32_t getLHSHoleIndex( StringId name, Hole::Behavior bhv ) const;
        uint32_t getRHSHoleIndex( StringId name, Hole::Behavior bhv ) const;

        uint32_t createValue( bool required = false );

        void setLHSHoleIndex( StringId name, Hole::Behavior bhv, uint32_t index );
        void setRHSHoleIndex( StringId name, Hole::Behavior bhv, uint32_t index );

        void eraseLHSName( StringId name );
        void eraseRHSName( StringId name );

        struct SubContext
        {
            uint32_t namespaceIndex = 0;
            uint32_t repetitionIndex = 0;
        };

        const SubContext& LHSSubContext() const { return m_lhsSubContext; }

        const SubContext& RHSSubContext() const { return m_rhsSubContext; }

        SubContext& LHSSubContext() { return m_lhsSubContext; }

        SubContext& RHSSubContext() { return m_rhsSubContext; }

        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; }




        bool isValueResolutionRequired() const { return m_valuesAreRequired; }




        const optional< Term >& getValue( uint32_t index ) const
        {
            assert( m_pCow->values.size() > index );
            return m_pCow->values[index].m_term;
        }

        template< typename T > void setValue( uint32_t index, T&& val )

        {
            assert( m_pCow->values.size() > index );

            if( m_pCow->values[index].m_required && !m_pCow->values[index].m_term )
                --m_numUnknownValues;

            CoW( m_pCow )->values[index] = { forward< T >( val ), true };
        }

        TypeCheckingContext& flip()
        {
            swap( m_lhsSubContext, m_rhsSubContext );
            return *this;
        }

        uint32_t numUnknownValues() const { return m_numUnknownValues; }

        int32_t cost() const { return m_cost; }

        void addCost( int32_t c ) { m_cost += c; }

        void setCost( int32_t cost ) { m_cost = cost; }

        void addAnonymousHole() { ++m_numAnonymousHoles; }

        auto score() const
        {
            return TypeCheckingScore( m_cost, 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 );

#ifndef NDEBUG
        void TCRuleTrace( const TCRuleInfo* pRule ) const;
        void PushRuleTraceForParam() const;
        void DumpParamsTraces( ostream& out ) const;
#endif

      private:
        void setValueRequired( uint32_t index );

        Context m_context;

        struct StoredValue
        {
            optional< Term > m_term;
            bool m_required = false;
        };

        SubContext m_lhsSubContext{ 1, 0 };
        SubContext m_rhsSubContext{ 2, 0 };
        uint32_t m_nextNamespaceIndex = 3;

        uint32_t m_numUnknownValues = 0;
        int32_t m_cost = 0;

        uint32_t m_numAnonymousHoles = 0;

        using HoleKey = tuple< StringId, uint32_t, uint32_t >;

        struct Cow
        {
            vector< StoredValue > values;
            map< HoleKey, uint32_t > holeDict;
            unordered_set< uint32_t > lockedHoles;

#ifndef NDEBUG
            // In debug, keep track of which "path" was taken through the various
            // rules to end up with the result.
            using TCTrace = vector< const TCRuleInfo* >;
            mutable TCTrace currentTypeCheckingTrace;
            mutable vector< TCTrace > paramsTypeCheckingTrace;
#endif
        };

        mutable ptr< Cow > m_pCow = make_shared< Cow >();

        bool m_valuesAreRequired = true;
    };
} // namespace goose::sema

#endif
Changes 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
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
#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.
    // Likewise when encountering them during type checking.
    void SetupPostProcUnificationRules( TypeCheckingRuleSet& ruleSet )
    {
        ruleSet.addUnificationRule(
            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,_] = *unwrap;

                co_yield Unify( t, rhs, tcc );
            } );

        ruleSet.addUnificationRule(
            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,_] = *unwrap;

                co_yield Unify( t, rhs, tcc );
            } );

        ruleSet.addTypeCheckingRule(
            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,_] = *unwrap;

                co_yield TypeCheck( t, rhs, tcc );
            } );

        ruleSet.addTypeCheckingRule(
            ANYTERM( _ ),
            VEC( TSID( postproc ), ANYTERM( _ ), ANYTERM( _ ) ),
            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                auto unwrap = UnwrapPostprocFunc( rhs );
                if( !unwrap )
                    co_return;
                auto&& [t,_] = *unwrap;

                co_yield TypeCheck( lhs, t, tcc );
            } );

        ruleSet.addTypeCheckingRule(
            VEC( TSID( postproc ), ANYTERM( _ ), ANYTERM( _ ) ),
            VEC( TSID( postproc ), ANYTERM( _ ), ANYTERM( _ ) ),
            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                auto lunwrap = UnwrapPostprocFunc( lhs );
                if( !lunwrap )
                    co_return;
                auto&& [lt,_] = *lunwrap;

                auto runwrap = UnwrapPostprocFunc( rhs );
                if( !runwrap )
                    co_return;
                auto&& [rt,__] = *runwrap;

                co_yield TypeCheck( lt, rt, 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
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
#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.
    // Likewise when encountering them during type checking.
    void SetupPostProcUnificationRules( TypeCheckingRuleSet& ruleSet )
    {
        ruleSet.addUnificationRule( 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, _] = *unwrap;

                co_yield Unify( t, rhs, tcc );
            } );


        ruleSet.addUnificationRule( 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, _] = *unwrap;

                co_yield Unify( t, rhs, tcc );
            } );


        ruleSet.addTypeCheckingRule( 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, _] = *unwrap;

                co_yield TypeCheck( t, rhs, tcc );
            } );

        ruleSet.addTypeCheckingRule( ANYTERM( _ ),

            VEC( TSID( postproc ), ANYTERM( _ ), ANYTERM( _ ) ),
            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                auto unwrap = UnwrapPostprocFunc( rhs );
                if( !unwrap )
                    co_return;
                auto&& [t, _] = *unwrap;

                co_yield TypeCheck( lhs, t, tcc );
            } );


        ruleSet.addTypeCheckingRule( VEC( TSID( postproc ), ANYTERM( _ ), ANYTERM( _ ) ),
            VEC( TSID( postproc ), ANYTERM( _ ), ANYTERM( _ ) ),
            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                auto lunwrap = UnwrapPostprocFunc( lhs );
                if( !lunwrap )
                    co_return;
                auto&& [lt, _] = *lunwrap;

                auto runwrap = UnwrapPostprocFunc( rhs );
                if( !runwrap )
                    co_return;
                auto&& [rt, __] = *runwrap;

                co_yield TypeCheck( lt, rt, tcc );
            } );
    }
} // namespace goose::sema
Changes to bs/sema/tc-ruleset.cpp.
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
    SetupBasicTypeCheckingRules( *this );
    SetupBasicUnificationRules( *this );
    SetupPostProcUnificationRules( *this );
    SetupHoleUnificationRules( *this );
    SetupQuoteUnificationRules( *this );
}

void TypeCheckingRuleSet::addTypeCheckingRule( const Term& pat1, const Term& pat2, BinaryFunc f, bool exclusive, TCRuleInfo infos )

{
    m_typeCheckingRules = Merge( m_typeCheckingRules, VEC( pat1, pat2 ), [&]( auto&& ){ return BinaryRule{ f, infos, exclusive }; } );

}

void TypeCheckingRuleSet::addUnificationRule( const Term& pat, BinaryFunc f, bool exclusive, TCRuleInfo infos )

{

    m_uniRules = Merge( m_uniRules, VEC( pat, pat ), [&]( auto&& ){ return BinaryRule{ f, infos, exclusive }; } );
}


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( const Term& pat1, const Term& pat2, BinaryFunc f, bool exclusive, TCRuleInfo infos )
{
    m_uniRules = Merge( m_uniRules, VEC( pat1, pat2 ), [&]( auto&& ){ return BinaryRule{ f, infos, exclusive }; } );

    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, exclusive }; }
    );
}

void TypeCheckingRuleSet::addHalfUnificationRule( const Term& pat, UnaryFunc f, TCRuleInfo infos )
{
    m_halfUniRules = Merge( m_halfUniRules, pat, [&]( auto&& ){ return UnaryRule{ f, infos }; } );
}







|
>

|
>


|
>

>
|


>
|

|



>
|

|
>

|

>
|
|
|




|

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
    SetupBasicTypeCheckingRules( *this );
    SetupBasicUnificationRules( *this );
    SetupPostProcUnificationRules( *this );
    SetupHoleUnificationRules( *this );
    SetupQuoteUnificationRules( *this );
}

void TypeCheckingRuleSet::addTypeCheckingRule(
    const Term& pat1, const Term& pat2, BinaryFunc f, bool exclusive, TCRuleInfo infos )
{
    m_typeCheckingRules = Merge( m_typeCheckingRules, VEC( pat1, pat2 ),
        [&]( auto&& ) { return BinaryRule{ f, infos, exclusive }; } );
}

void TypeCheckingRuleSet::addUnificationRule(
    const Term& pat, BinaryFunc f, bool exclusive, TCRuleInfo infos )
{
    m_uniRules = Merge(
        m_uniRules, VEC( pat, pat ), [&]( auto&& ) { return BinaryRule{ f, infos, exclusive }; } );
}

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(
    const Term& pat1, const Term& pat2, BinaryFunc f, bool exclusive, TCRuleInfo infos )
{
    m_uniRules = Merge( m_uniRules, VEC( pat1, pat2 ),
        [&]( auto&& ) { return BinaryRule{ f, infos, exclusive }; } );
    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, exclusive };
        } );
}

void TypeCheckingRuleSet::addHalfUnificationRule( const Term& pat, UnaryFunc f, TCRuleInfo infos )
{
    m_halfUniRules = Merge( m_halfUniRules, pat, [&]( auto&& ) { return UnaryRule{ f, infos }; } );
}
Changes 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
61
62
63
64

65

66
67
68
69
70
71
72
73

74
75
#ifndef GOOSE_SEMA_TC_RULESET_H
#define GOOSE_SEMA_TC_RULESET_H

#ifndef NDEBUG
#define TCRULES_DEBUG
#endif

namespace goose::sema
{
    class TypeCheckingContext;

#ifndef NDEBUG
    struct TCRuleInfo
    {
        TCRuleInfo() {}

        TCRuleInfo( const source_location& sl ) :
            pFilename( sl.file_name() ),
            line( sl.line() )
        {}



        const char* pFilename = nullptr;
        uint32_t line = 0;
    };
#else
    struct TCRuleInfo
    {
        TCRuleInfo() {}

        TCRuleInfo( const source_location& sl ) {}
    };
#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;
                bool exclusive = false;
            };

            struct UnaryRule
            {
                UnaryFunc func;
                TCRuleInfo infos;
            };

            TypeCheckingRuleSet();

            void addTypeCheckingRule( const Term& pat1, const Term& pat2, BinaryFunc f, bool exclusive = false,
                TCRuleInfo infos = source_location::current() );

            void addUnificationRule( const Term& pat, BinaryFunc f, bool exclusive = false,
                TCRuleInfo infos = source_location::current() );
            void addUnificationRule( const Term& pat1, const Term& pat2, BinaryFunc f, bool exclusive = false,
                TCRuleInfo infos = source_location::current() );

            void addHalfUnificationRule( const Term& pat, UnaryFunc f,
                TCRuleInfo infos = source_location::current() );

            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




|










>



<
>
>








>






|
>
|
|

|
|
|
|
|
|

|
|
|
|
|

|

|
|

|
|
|
|

|
|

|
>
|
>
|

|
|
|
|

<
>


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
#ifndef GOOSE_SEMA_TC_RULESET_H
#define GOOSE_SEMA_TC_RULESET_H

#ifndef NDEBUG
    #define TCRULES_DEBUG
#endif

namespace goose::sema
{
    class TypeCheckingContext;

#ifndef NDEBUG
    struct TCRuleInfo
    {
        TCRuleInfo() {}

        TCRuleInfo( const source_location& sl ) :
            pFilename( sl.file_name() ),
            line( sl.line() )

        {
        }

        const char* pFilename = nullptr;
        uint32_t line = 0;
    };
#else
    struct TCRuleInfo
    {
        TCRuleInfo() {}

        TCRuleInfo( const source_location& sl ) {}
    };
#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;
            bool exclusive = false;
        };

        struct UnaryRule
        {
            UnaryFunc func;
            TCRuleInfo infos;
        };

        TypeCheckingRuleSet();

        void addTypeCheckingRule( const Term& pat1, const Term& pat2, BinaryFunc f,
            bool exclusive = false, TCRuleInfo infos = source_location::current() );

        void addUnificationRule( const Term& pat, BinaryFunc f, bool exclusive = false,
            TCRuleInfo infos = source_location::current() );
        void addUnificationRule( const Term& pat1, const Term& pat2, BinaryFunc f,
            bool exclusive = false, TCRuleInfo infos = source_location::current() );

        void addHalfUnificationRule(
            const Term& pat, UnaryFunc f, TCRuleInfo infos = source_location::current() );

        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;
    };

} // namespace goose::sema

#endif
Changes 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
73
74
75
76
77
78
79
80

81
82
83
84
#ifndef GOOSE_SEMA_TC_SCORE_H
#define GOOSE_SEMA_TC_SCORE_H

namespace goose::sema
{
    class TypeCheckingScore
    {
        public:
            TypeCheckingScore() {}

            TypeCheckingScore( int32_t cost, uint32_t uniqueHoles ) :
                m_cost( cost ),
                m_uniqueHoles( uniqueHoles )
            {}



            auto& operator+=( const TypeCheckingScore& rhs )
            {
                m_cost += rhs.m_cost;
                m_uniqueHoles += rhs.m_uniqueHoles;
                return *this;
            }

            auto operator+( const TypeCheckingScore& rhs ) const
            {
                return TypeCheckingScore( m_cost + rhs.m_cost, m_uniqueHoles + rhs.m_uniqueHoles );
            }

            bool operator==( const TypeCheckingScore& rhs ) const
            {
                return m_cost == rhs.m_cost
                    && m_uniqueHoles == rhs.m_uniqueHoles;
            }

            bool operator!=( const TypeCheckingScore& rhs ) const
            {
                return !operator==( rhs );
            }

            bool operator<( const TypeCheckingScore& rhs ) const
            {
                if( m_cost != rhs.m_cost )
                    return m_cost > rhs.m_cost ;

                return m_uniqueHoles > rhs.m_uniqueHoles;
            }

            bool operator>( const TypeCheckingScore& rhs ) const
            {
                if( m_cost != rhs.m_cost )
                    return m_cost < rhs.m_cost ;

                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_cost << ',' << s.m_uniqueHoles << ')';
            }

        private:
            int32_t m_cost = 0;
            uint32_t m_uniqueHoles = 0;
    };
}

namespace std
{
    template< class CharT >
    struct std::formatter< goose::sema::TypeCheckingScore, CharT > :
        goose::util::SStreamFormatter< goose::sema::TypeCheckingScore, CharT >

    {};
}

#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
64
65
66
67
68
69
70
71
72
73
74
75
76
#ifndef GOOSE_SEMA_TC_SCORE_H
#define GOOSE_SEMA_TC_SCORE_H

namespace goose::sema
{
    class TypeCheckingScore
    {
      public:
        TypeCheckingScore() {}

        TypeCheckingScore( int32_t cost, uint32_t uniqueHoles ) :
            m_cost( cost ),
            m_uniqueHoles( uniqueHoles )

        {
        }

        auto& operator+=( const TypeCheckingScore& rhs )
        {
            m_cost += rhs.m_cost;
            m_uniqueHoles += rhs.m_uniqueHoles;
            return *this;
        }

        auto operator+( const TypeCheckingScore& rhs ) const
        {
            return TypeCheckingScore( m_cost + rhs.m_cost, m_uniqueHoles + rhs.m_uniqueHoles );
        }

        bool operator==( const TypeCheckingScore& rhs ) const
        {
            return m_cost == rhs.m_cost && m_uniqueHoles == rhs.m_uniqueHoles;

        }

        bool operator!=( const TypeCheckingScore& rhs ) const { return !operator==( rhs ); }




        bool operator<( const TypeCheckingScore& rhs ) const
        {
            if( m_cost != rhs.m_cost )
                return m_cost > rhs.m_cost;

            return m_uniqueHoles > rhs.m_uniqueHoles;
        }

        bool operator>( const TypeCheckingScore& rhs ) const
        {
            if( m_cost != rhs.m_cost )
                return m_cost < rhs.m_cost;

            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_cost << ',' << s.m_uniqueHoles << ')';
        }

      private:
        int32_t m_cost = 0;
        uint32_t m_uniqueHoles = 0;
    };
} // namespace goose::sema

namespace std
{
    template< class CharT >
    struct std::formatter< goose::sema::TypeCheckingScore, CharT >
        : goose::util::SStreamFormatter< goose::sema::TypeCheckingScore, CharT >
    {
    };
} // namespace std

#endif
Changes to bs/sema/tc-vecgenerator.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
#ifndef GOOSE_SEMA_TC_VECGENERATOR_H
#define GOOSE_SEMA_TC_VECGENERATOR_H

namespace goose::sema
{
    // VecGenerator wrapper that updates the current repetition state of the provided typechecking sub context

    class TCVecGenerator
    {
        public:
            TCVecGenerator( const Vector& cont ) :
                m_gen( cont )
            {}



            bool finished() const { return m_gen.finished(); }

            bool fixedPartFinished() const { return m_gen.fixedPartFinished(); }

            size_t repetitionIndex() const { return m_gen.repetitionIndex(); }

            const Term& operator()( TypeCheckingContext::SubContext& sc )
            {
                if( auto ri = repetitionIndex() )
                    sc.repetitionIndex = ri;

                return m_gen();
            }

        private:
            VecGenerator m_gen;
    };
}


#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
#ifndef GOOSE_SEMA_TC_VECGENERATOR_H
#define GOOSE_SEMA_TC_VECGENERATOR_H

namespace goose::sema
{
    // VecGenerator wrapper that updates the current repetition state of the provided typechecking
    // sub context
    class TCVecGenerator
    {
      public:
        TCVecGenerator( const Vector& cont ) :
            m_gen( cont )

        {
        }

        bool finished() const { return m_gen.finished(); }

        bool fixedPartFinished() const { return m_gen.fixedPartFinished(); }

        size_t repetitionIndex() const { return m_gen.repetitionIndex(); }

        const Term& operator()( TypeCheckingContext::SubContext& sc )
        {
            if( auto ri = repetitionIndex() )
                sc.repetitionIndex = ri;

            return m_gen();
        }

      private:
        VecGenerator m_gen;
    };

} // namespace goose::sema

#endif
Changes 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
#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( TCVecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec,
        const RepetitionNode& rptNode, const TypeCheckingContext& tcc )
    {
        auto newC = tcc;

        if( vgen.fixedPartFinished() )
        {
            newC.LHSSubContext().repetitionIndex = 0;
            co_yield { lhsVec, solutionVec, rptNode.m_next, newC };
        }
        else
        {
            const auto& vt = vgen( newC.RHSSubContext() );

            for( auto&& rt : Enumerate( rptNode.m_repetition ) )
            {
                auto localRhsVec = lhsVec;
                localRhsVec.setRepetitionTerm( rt );

                for( auto&& [s,tcc] : sema::TypeCheck( rt, vt, newC ) )
                {
                    #ifndef NDEBUG
                        tcc.PushRuleTraceForParam();
                    #endif

                    ++tcc.LHSSubContext().repetitionIndex;
                    auto v = Vector::MakeAppend( solutionVec, move( s ) );
                    co_yield TypeCheckRepetition( vgen, localRhsVec, v, rptNode, tcc );
                }
            }
        }
    }



    template< typename U > template< typename T > Generator< tuple< Vector, Vector, const T&, TypeCheckingContext, TCVecGenerator > >
    TCTrie< U >::TypeCheck( TCVecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec, const branch_t< T >& branch, const TypeCheckingContext& tcc )

    {
        if( branch.index() == 0 )
        {
            auto newC = tcc;
            const auto& vt = vgen( newC.RHSSubContext() );

            for( auto&& [t, payload] : Enumerate( get< 0 >( branch )->m_trie ) )
            {
                auto lvec = Vector::MakeAppend( lhsVec, t );
                for( auto&& [s, tcc] : sema::TypeCheck( t, vt, newC ) )
                {
                    #ifndef NDEBUG
                        tcc.PushRuleTraceForParam();
                    #endif

                    auto v = Vector::MakeAppend( solutionVec, move( s ) );
                    co_yield TypeCheck( vgen, lvec, v, payload, tcc );
                }
            }
        }
        else
            co_yield { lhsVec, solutionVec, 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();

        if( auto it = m_fixedLengthBranches.find( exprLen.minLength() ); it != m_fixedLengthBranches.end() )

        {
            TCVecGenerator 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 };





|
>
|
|

















|

|
|
|









>
>
|
|
>











|
|
|











|
>



|
>







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
#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( TCVecGenerator vgen,
        const Vector& lhsVec, const Vector& solutionVec, const RepetitionNode& rptNode,
        const TypeCheckingContext& tcc )
    {
        auto newC = tcc;

        if( vgen.fixedPartFinished() )
        {
            newC.LHSSubContext().repetitionIndex = 0;
            co_yield { lhsVec, solutionVec, rptNode.m_next, newC };
        }
        else
        {
            const auto& vt = vgen( newC.RHSSubContext() );

            for( auto&& rt : Enumerate( rptNode.m_repetition ) )
            {
                auto localRhsVec = lhsVec;
                localRhsVec.setRepetitionTerm( rt );

                for( auto&& [s, tcc] : sema::TypeCheck( rt, vt, newC ) )
                {
#ifndef NDEBUG
                    tcc.PushRuleTraceForParam();
#endif

                    ++tcc.LHSSubContext().repetitionIndex;
                    auto v = Vector::MakeAppend( solutionVec, move( s ) );
                    co_yield TypeCheckRepetition( vgen, localRhsVec, v, rptNode, tcc );
                }
            }
        }
    }

    template< typename U >
    template< typename T >
    Generator< tuple< Vector, Vector, const T&, TypeCheckingContext, TCVecGenerator > >
    TCTrie< U >::TypeCheck( TCVecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec,
        const branch_t< T >& branch, const TypeCheckingContext& tcc )
    {
        if( branch.index() == 0 )
        {
            auto newC = tcc;
            const auto& vt = vgen( newC.RHSSubContext() );

            for( auto&& [t, payload] : Enumerate( get< 0 >( branch )->m_trie ) )
            {
                auto lvec = Vector::MakeAppend( lhsVec, t );
                for( auto&& [s, tcc] : sema::TypeCheck( t, vt, newC ) )
                {
#ifndef NDEBUG
                    tcc.PushRuleTraceForParam();
#endif

                    auto v = Vector::MakeAppend( solutionVec, move( s ) );
                    co_yield TypeCheck( vgen, lvec, v, payload, tcc );
                }
            }
        }
        else
            co_yield { lhsVec, solutionVec, 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();

        if( auto it = m_fixedLengthBranches.find( exprLen.minLength() );
            it != m_fixedLengthBranches.end() )
        {
            TCVecGenerator 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 };
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
            for( auto&& [lv, s, pRptNode, tcc, g] : TypeCheck( vgen, lhs, sol, it->second, tcc ) )
                co_yield TypeCheckRepetition( g, lv, s, *pRptNode, tcc );

            ++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< T >& branch, const TypeCheckingContext& tcc )
    {
        if( branch.index() == 0 )
        {
            for( auto&& [t, payload] : Enumerate( get< 0 >( branch )->m_trie ) )
            {
                auto newC = tcc;
                auto lvec = Vector::MakeAppend( lhsVec, t );

                if( auto s = sema::HalfUnify( t, newC ) )

                    co_yield HalfUnify( lvec, Vector::MakeAppend( solutionVec, move( *s ) ), payload, newC );
            }
        }
        else
            co_yield { lhsVec, solutionVec, get< 1 >( branch ), tcc };
    }

    template< typename U >
    typename TCTrie< U >::template TCGen< U > TCTrie< U >::halfUnify( TypeCheckingContext tcc ) const

    {
        for( auto&& [len, branch] : m_fixedLengthBranches )
        {
            Vector lhs;
            Vector sol;

            for( auto&& [lv, s, content, c] : HalfUnify( lhs, sol, branch, tcc ) )
                co_yield { lv, s, content, c };
        }

        for( auto&& [minLen, branch] : m_variableLengthBranches )
        {
            Vector lhs;
            Vector sol;

            for( auto&& [lv, sol, pRptNode, tcc] : HalfUnify< p_rpt_node >( lhs, sol, branch, tcc ) )

            {
                for( auto&& rt : Enumerate( pRptNode->m_repetition ) )
                {
                    if( auto s = sema::HalfUnify( rt, tcc ) )
                    {
                        auto localLhsVec = lv;
                        localLhsVec.setRepetitionTerm( rt );

                        auto localSol = sol;
                        localSol.setRepetitionTerm( move( *s ) );
                        co_yield { localLhsVec, localSol, pRptNode->m_next, tcc };
                    }
                }
            }
        }
    }
}

#endif







|
>
>
|









>
|







|
>















|
>
















|


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
            for( auto&& [lv, s, pRptNode, tcc, g] : TypeCheck( vgen, lhs, sol, it->second, tcc ) )
                co_yield TypeCheckRepetition( g, lv, s, *pRptNode, tcc );

            ++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< T >& branch, const TypeCheckingContext& tcc )
    {
        if( branch.index() == 0 )
        {
            for( auto&& [t, payload] : Enumerate( get< 0 >( branch )->m_trie ) )
            {
                auto newC = tcc;
                auto lvec = Vector::MakeAppend( lhsVec, t );

                if( auto s = sema::HalfUnify( t, newC ) )
                    co_yield HalfUnify(
                        lvec, Vector::MakeAppend( solutionVec, move( *s ) ), payload, newC );
            }
        }
        else
            co_yield { lhsVec, solutionVec, get< 1 >( branch ), tcc };
    }

    template< typename U >
    typename TCTrie< U >::template TCGen< U > TCTrie< U >::halfUnify(
        TypeCheckingContext tcc ) const
    {
        for( auto&& [len, branch] : m_fixedLengthBranches )
        {
            Vector lhs;
            Vector sol;

            for( auto&& [lv, s, content, c] : HalfUnify( lhs, sol, branch, tcc ) )
                co_yield { lv, s, content, c };
        }

        for( auto&& [minLen, branch] : m_variableLengthBranches )
        {
            Vector lhs;
            Vector sol;

            for( auto&& [lv, sol, pRptNode, tcc] :
                HalfUnify< p_rpt_node >( lhs, sol, branch, tcc ) )
            {
                for( auto&& rt : Enumerate( pRptNode->m_repetition ) )
                {
                    if( auto s = sema::HalfUnify( rt, tcc ) )
                    {
                        auto localLhsVec = lv;
                        localLhsVec.setRepetitionTerm( rt );

                        auto localSol = sol;
                        localSol.setRepetitionTerm( move( *s ) );
                        co_yield { localLhsVec, localSol, pRptNode->m_next, tcc };
                    }
                }
            }
        }
    }
} // namespace goose::sema

#endif
Changes 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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#ifndef GOOSE_SEMA_TCTRIE_H
#define GOOSE_SEMA_TCTRIE_H

namespace goose::sema
{
    // Similar to an eir 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;
            Generator< const U* > enumerate() const;



            bool empty() const { return m_fixedLengthBranches.empty() && m_variableLengthBranches.empty(); }


        private:
            template< typename T > struct Node;
            struct RepetitionNode;

            template< typename T >
            using branch_t = variant< ptr< Node< T > >, T >;

            using p_rpt_node = ptr< RepetitionNode >;

            template< typename T, typename IT, typename F >
            static branch_t< T > Merge( const branch_t< 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( TCVecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec,

                const RepetitionNode& rptNode, const TypeCheckingContext& c );


            template< typename T > static Generator< tuple< Vector, Vector, const T&, TypeCheckingContext, TCVecGenerator > >
            TypeCheck( TCVecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec, const branch_t< T >& branch, const TypeCheckingContext& c );


            template< typename T > static TCGen< T >

            HalfUnify( const Vector& lhsVec, const Vector& solutionVec, const branch_t< T >& branch, const TypeCheckingContext& c );

            template< typename T >
            static Generator< const T* > EnumerateBranch( const branch_t< T >& branch );

            template< typename T > struct Node
            {
                Trie< branch_t< T > > m_trie;
            };

            struct RepetitionNode
            {
                Trie<> m_repetition;
                U m_next;
            };

            // All fixed length branches, by length.
            unordered_map< size_t, branch_t< U > > m_fixedLengthBranches;

            // All variable length branches, by min length.
            map< size_t, branch_t< p_rpt_node > > m_variableLengthBranches;
    };
}

#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
64
65
66
67
68
69
70
71
72
73
74
75
76
#ifndef GOOSE_SEMA_TCTRIE_H
#define GOOSE_SEMA_TCTRIE_H

namespace goose::sema
{
    // Similar to an eir 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;
        Generator< const U* > enumerate() const;

        bool empty() const
        {
            return m_fixedLengthBranches.empty() && m_variableLengthBranches.empty();
        }

      private:
        template< typename T > struct Node;
        struct RepetitionNode;


        template< typename T > using branch_t = variant< ptr< Node< T > >, T >;

        using p_rpt_node = ptr< RepetitionNode >;

        template< typename T, typename IT, typename F >
        static branch_t< T > Merge( const branch_t< 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( TCVecGenerator vgen, const Vector& lhsVec,
            const Vector& solutionVec, const RepetitionNode& rptNode,
            const TypeCheckingContext& c );

        template< typename T >
        static Generator< tuple< Vector, Vector, const T&, TypeCheckingContext, TCVecGenerator > >
        TypeCheck( TCVecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec,
            const branch_t< T >& branch, const TypeCheckingContext& c );

        template< typename T >
        static TCGen< T > HalfUnify( const Vector& lhsVec, const Vector& solutionVec,
            const branch_t< T >& branch, const TypeCheckingContext& c );

        template< typename T >
        static Generator< const T* > EnumerateBranch( const branch_t< T >& branch );

        template< typename T > struct Node
        {
            Trie< branch_t< T > > m_trie;
        };

        struct RepetitionNode
        {
            Trie<> m_repetition;
            U m_next;
        };

        // All fixed length branches, by length.
        unordered_map< size_t, branch_t< U > > m_fixedLengthBranches;

        // All variable length branches, by min length.
        map< size_t, branch_t< p_rpt_node > > m_variableLengthBranches;
    };
} // namespace goose::sema

#endif
Changes to bs/sema/tctrie.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
#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 >::template branch_t< T > TCTrie< U >::Merge( const branch_t< T >& b, const IT& it, const IT& end, F&& next )

    {
        if( it == end )
        {
            if( b.index() == 1 )
                return next( get< 1 >( b ) );

            return next( T() );
        }

        assert( b.index() == 0 );

        ptr< Node< T > > pNewNode;
        const auto& pNode = get< 0 >( b );

        if( pNode )
            pNewNode = make_shared< Node< T > >( *pNode );
        else
            pNewNode = make_shared< Node< T > >();

        function< branch_t< T > ( const branch_t< T >& ) > f( [&]( auto& payload )

        {
            auto nextIt = it;
            ++nextIt;

            return Merge( payload, nextIt, end, forward< F >( next ) );
        } );

        pNewNode->m_trie = eir::Merge( pNewNode->m_trie, *it, move( f ) );
        return pNewNode;
    }

    template< typename U > template< typename F >

    typename TCTrie< U >::p_rpt_node 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 = eir::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() )
        {
            auto f = [&]( auto&& payload )
            {
                // Merge the repetition
                return Merge( payload, *v.repetitionTerm(),
                    forward< F >( next ) );
            };

            auto it = pNewTrie->m_variableLengthBranches.find( length.minLength() );
            if( it == pNewTrie->m_variableLengthBranches.end() )
            {
                pNewTrie->m_variableLengthBranches.emplace( length.minLength(),

                    Merge( branch_t< p_rpt_node >(), v.terms().begin(), v.terms().end(),
                    move( f ) ) );
            }
            else
            {
                it->second = Merge( it->second, v.terms().begin(), v.terms().end(),
                    move( f ) );
            }
        }
        else
        {
            auto it = pNewTrie->m_fixedLengthBranches.find( length.minLength() );
            if( it == pNewTrie->m_fixedLengthBranches.end() )
            {
                pNewTrie->m_fixedLengthBranches.emplace( length.minLength(),
                    Merge( branch_t< U >(), v.terms().begin(), v.terms().end(),
                    forward< F >( next ) ) );
            }
            else
            {
                it->second = Merge( it->second, v.terms().begin(), v.terms().end(),
                    forward< F >( next ) );
            }
        }

        return pNewTrie;
    }

    template< typename U > template< typename T >

    Generator< const T* > TCTrie< U >::EnumerateBranch( const branch_t< T >& branch )
    {
        if( holds_alternative< T >( branch ) )
            co_yield &get< T >( branch );
        else
        {
            auto node = get< ptr< Node< T > > >( branch );
            if( !node )
                co_return;

            for( auto&& [_,branch] : Enumerate( node->m_trie ) )
                co_yield EnumerateBranch( branch );
        }
    }

    template< typename U >
    Generator< const U* > TCTrie< U >::enumerate() const
    {
        for( auto&& [_,branch] : m_fixedLengthBranches )
            co_yield EnumerateBranch( branch );

        for( auto&& [_,branch] : m_variableLengthBranches )
        {
            for( const auto& rptNode : EnumerateBranch( branch ) )
            {
                co_yield &( ( *rptNode )->m_next );
            }

        }
    }
}

#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
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
#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 >::template branch_t< T > TCTrie< U >::Merge(
        const branch_t< T >& b, const IT& it, const IT& end, F&& next )
    {
        if( it == end )
        {
            if( b.index() == 1 )
                return next( get< 1 >( b ) );

            return next( T() );
        }

        assert( b.index() == 0 );

        ptr< Node< T > > pNewNode;
        const auto& pNode = get< 0 >( b );

        if( pNode )
            pNewNode = make_shared< Node< T > >( *pNode );
        else
            pNewNode = make_shared< Node< T > >();

        function< branch_t< T >( const branch_t< T >& ) > f(
            [&]( auto& payload )
            {
                auto nextIt = it;
                ++nextIt;

                return Merge( payload, nextIt, end, forward< F >( next ) );
            } );

        pNewNode->m_trie = eir::Merge( pNewNode->m_trie, *it, move( f ) );
        return pNewNode;
    }

    template< typename U >
    template< typename F >
    typename TCTrie< U >::p_rpt_node 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 = eir::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() )
        {
            auto f = [&]( auto&& payload )
            {
                // Merge the repetition
                return Merge( payload, *v.repetitionTerm(), forward< F >( next ) );

            };

            auto it = pNewTrie->m_variableLengthBranches.find( length.minLength() );
            if( it == pNewTrie->m_variableLengthBranches.end() )
            {
                pNewTrie->m_variableLengthBranches.emplace( length.minLength(),
                    Merge(
                        branch_t< p_rpt_node >(), v.terms().begin(), v.terms().end(), move( f ) ) );

            }
            else
            {
                it->second = Merge( it->second, v.terms().begin(), v.terms().end(), move( f ) );

            }
        }
        else
        {
            auto it = pNewTrie->m_fixedLengthBranches.find( length.minLength() );
            if( it == pNewTrie->m_fixedLengthBranches.end() )
            {
                pNewTrie->m_fixedLengthBranches.emplace( length.minLength(),
                    Merge( branch_t< U >(), v.terms().begin(), v.terms().end(),
                        forward< F >( next ) ) );
            }
            else
            {
                it->second =
                    Merge( it->second, v.terms().begin(), v.terms().end(), forward< F >( next ) );
            }
        }

        return pNewTrie;
    }

    template< typename U >
    template< typename T >
    Generator< const T* > TCTrie< U >::EnumerateBranch( const branch_t< T >& branch )
    {
        if( holds_alternative< T >( branch ) )
            co_yield &get< T >( branch );
        else
        {
            auto node = get< ptr< Node< T > > >( branch );
            if( !node )
                co_return;

            for( auto&& [_, branch] : Enumerate( node->m_trie ) )
                co_yield EnumerateBranch( branch );
        }
    }


    template< typename U > Generator< const U* > TCTrie< U >::enumerate() const
    {
        for( auto&& [_, branch] : m_fixedLengthBranches )
            co_yield EnumerateBranch( branch );

        for( auto&& [_, branch] : m_variableLengthBranches )

            for( const auto& rptNode : EnumerateBranch( branch ) )

                co_yield &( ( *rptNode )->m_next );
    }
} // namespace goose::sema




#endif
Changes to bs/sema/template.cpp.
65
66
67
68
69
70
71

72
73
74
75
76
77
78
79
        const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
        if( !pTemplateRuleSet )
            return;

        pTemplateRuleSet->setup( c, tcc, tpl );
    }


    uint64_t TemplateHashTypePredicates( const Context& c, const TypeCheckingContext& tcc, const Term& tpl )
    {
        const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
        if( !pTemplateRuleSet )
            return 0;

        return pTemplateRuleSet->hashTypePredicates( c, tcc, tpl );
    }







>
|







65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
        const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
        if( !pTemplateRuleSet )
            return;

        pTemplateRuleSet->setup( c, tcc, tpl );
    }

    uint64_t TemplateHashTypePredicates(
        const Context& c, const TypeCheckingContext& tcc, const Term& tpl )
    {
        const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
        if( !pTemplateRuleSet )
            return 0;

        return pTemplateRuleSet->hashTypePredicates( c, tcc, tpl );
    }
91
92
93
94
95
96
97
98
    {
        const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
        if( !pTemplateRuleSet )
            return false;

        return pTemplateRuleSet->isPackExpr( c, tpl );
    }
}







|
92
93
94
95
96
97
98
99
    {
        const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
        if( !pTemplateRuleSet )
            return false;

        return pTemplateRuleSet->isPackExpr( c, tpl );
    }
} // namespace goose::sema
Changes to bs/sema/template.h.
1
2
3
4
5
6
7
8
9
10

11
12

13
14
15

16
17
#ifndef GOOSE_SEMA_TEMPLATE_H
#define GOOSE_SEMA_TEMPLATE_H

namespace goose::sema
{
    extern ptr< TemplateRule > GetTemplateRuleSet( const Context& c, const Term& tpl );

    extern bool PrepareTemplate( TemplateContext& c, const Term& tpl );
    extern optional< Term > BuildTemplateSignature( const TemplateContext& c, const Term& tpl );
    extern Generator< Value > BuildTemplateParamDecls( const Context& c, const Term& tpl, TermGen& args );

    extern void TemplateSetup( const Context& c, TypeCheckingContext& tcc, const Term& tpl );
    extern uint64_t TemplateHashTypePredicates( const Context& c, const TypeCheckingContext& tcc, const Term& tpl );

    extern optional< Term > BuildTemplateArgPattern( const Context& c, const Term& tpl );
    extern bool IsTemplatePackExpr( const Context& c, const Term& tpl );
}


#endif









|
>

|
>


<
>


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

17
18
19
#ifndef GOOSE_SEMA_TEMPLATE_H
#define GOOSE_SEMA_TEMPLATE_H

namespace goose::sema
{
    extern ptr< TemplateRule > GetTemplateRuleSet( const Context& c, const Term& tpl );

    extern bool PrepareTemplate( TemplateContext& c, const Term& tpl );
    extern optional< Term > BuildTemplateSignature( const TemplateContext& c, const Term& tpl );
    extern Generator< Value > BuildTemplateParamDecls(
        const Context& c, const Term& tpl, TermGen& args );
    extern void TemplateSetup( const Context& c, TypeCheckingContext& tcc, const Term& tpl );
    extern uint64_t TemplateHashTypePredicates(
        const Context& c, const TypeCheckingContext& tcc, const Term& tpl );
    extern optional< Term > BuildTemplateArgPattern( const Context& c, const Term& tpl );
    extern bool IsTemplatePackExpr( const Context& c, const Term& tpl );

} // namespace goose::sema

#endif
Changes to bs/sema/tests/tctrie-merge.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
56
57
58
59
60
61
62
63
64
65
66
67

        auto vec6 = Vector::Make( HOLE( "a"_sid ), HOLE( "b"_sid ) );
        auto vec7 = Vector::Make( TSTR( "foo" ), HOLE( "c"_sid ) );

        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; } );
        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( 0, 0 ) ) );
            REQUIRE( results[1] == make_pair( "vec10"s, TypeCheckingScore( 0, 3 ) ) );
            REQUIRE( results[2] == make_pair( "vec11"s, TypeCheckingScore( 0, 2 ) ) );
            REQUIRE( results[3] == make_pair( "vec12"s, TypeCheckingScore( 0, 1 ) ) );
            REQUIRE( results[4] == make_pair( "vec13"s, TypeCheckingScore( 0, 1 ) ) );







>
|
|


|

|
|
|
|
|
|
|
|
|
|
|
|
|








|


|
<
|
<







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

        auto vec6 = Vector::Make( HOLE( "a"_sid ), HOLE( "b"_sid ) );
        auto vec7 = Vector::Make( TSTR( "foo" ), HOLE( "c"_sid ) );

        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; } );
        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( 0, 0 ) ) );
            REQUIRE( results[1] == make_pair( "vec10"s, TypeCheckingScore( 0, 3 ) ) );
            REQUIRE( results[2] == make_pair( "vec11"s, TypeCheckingScore( 0, 2 ) ) );
            REQUIRE( results[3] == make_pair( "vec12"s, TypeCheckingScore( 0, 1 ) ) );
            REQUIRE( results[4] == make_pair( "vec13"s, TypeCheckingScore( 0, 1 ) ) );
Changes to bs/sema/tests/tctrie-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
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
    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() );
        }

        std::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( 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 vec14 = Vector::Make( TSTR( "gg" ) );
        vec14->setRepetitionTerm( HOLE( "_"_sid ) );
        auto vec15 = Vector::Make( TSTR( "gg" ), TERM( 456U ), TSTR( "blah" ) );

        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; } );
        trie = trie->merge( *vec14, []( auto&& ){ return "vec14"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 );
            auto sols15 = GetSortedSolutions( e, trie, *vec15 );

            REQUIRE( sols1.size() == 1 );


            REQUIRE( sols1[0] == make_tuple( VEC( TSTR( "foo" ), TSTR( "bar" ) ), "vec4"s, TypeCheckingScore( 0, 1 ) ) );

            REQUIRE( sols3.size() == 1 );


            REQUIRE( sols3[0] == make_tuple( VEC( TSTR( "meh" ), TSTR( "meh" ) ), "vec2"s, TypeCheckingScore( 0, 1 ) ) );

            REQUIRE( sols5.size() == 2 );


            REQUIRE( sols5[0] == make_tuple( VEC( TSTR( "foo" ), TSTR( "foo" ) ), "vec2"s, TypeCheckingScore( 0, 2 ) ) );


            REQUIRE( sols5[1] == make_tuple( VEC( TSTR( "foo" ), TSTR( "bar" ) ), "vec4"s, TypeCheckingScore( 0, 2 ) ) );

            REQUIRE( sols9.size() == 1 );
            REQUIRE( sols9[0] == make_tuple(
                VEC(

                    VEC( TSTR( "foo" ), TSTR( "bar" ) ),
                    VEC( TSTR( "foo" ), TSTR( "bar" ) )
                ), "vec2"s, TypeCheckingScore( 1, 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( 1, 5 ) )
            );

            REQUIRE( sols13.size() == 2 );


            REQUIRE( sols13[0] == make_tuple( VEC( TSTR( "gg" ), TSTR( "gg" ) ), "vec14"s, TypeCheckingScore( 0, 2 ) ) );


            REQUIRE( sols13[1] == make_tuple( VEC( TSTR( "bar" ), TSTR( "bar" ) ), "vec4"s, TypeCheckingScore( 0, 2 ) ) );

            REQUIRE( sols15.size() == 1 );
            REQUIRE( sols15[0] ==  make_tuple( vec15, "vec14"s, TypeCheckingScore( 0, 2 ) ) );
        }
    }
}







|








|
<
|
<



|






|
>


|
>
|
|
|
|











>
|
|


|





|
|
|
|
|












>
>
|


>
>
|


>
>
|
>
>
|


|
<
>
|
<
|
<


|
<
>
|
|
<
<
|
<


>
>
|
>
>
|


|



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
    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() );
        }

        std::sort( solutions.begin(), solutions.end(),

            []( auto&& a, auto&& b ) { return get< 1 >( a ) < get< 1 >( b ); } );


        return solutions;
    }
} // namespace

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( 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 vec14 = Vector::Make( TSTR( "gg" ) );
        vec14->setRepetitionTerm( HOLE( "_"_sid ) );
        auto vec15 = Vector::Make( TSTR( "gg" ), TERM( 456U ), TSTR( "blah" ) );

        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; } );
        trie = trie->merge( *vec14, []( auto&& ) { return "vec14"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 );
            auto sols15 = GetSortedSolutions( e, trie, *vec15 );

            REQUIRE( sols1.size() == 1 );
            REQUIRE( sols1[0]
                == make_tuple(
                    VEC( TSTR( "foo" ), TSTR( "bar" ) ), "vec4"s, TypeCheckingScore( 0, 1 ) ) );

            REQUIRE( sols3.size() == 1 );
            REQUIRE( sols3[0]
                == make_tuple(
                    VEC( TSTR( "meh" ), TSTR( "meh" ) ), "vec2"s, TypeCheckingScore( 0, 1 ) ) );

            REQUIRE( sols5.size() == 2 );
            REQUIRE( sols5[0]
                == make_tuple(
                    VEC( TSTR( "foo" ), TSTR( "foo" ) ), "vec2"s, TypeCheckingScore( 0, 2 ) ) );
            REQUIRE( sols5[1]
                == make_tuple(
                    VEC( TSTR( "foo" ), TSTR( "bar" ) ), "vec4"s, TypeCheckingScore( 0, 2 ) ) );

            REQUIRE( sols9.size() == 1 );
            REQUIRE( sols9[0]

                == make_tuple(
                    VEC( VEC( TSTR( "foo" ), TSTR( "bar" ) ), VEC( TSTR( "foo" ), TSTR( "bar" ) ) ),

                    "vec2"s, TypeCheckingScore( 1, 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( 1, 5 ) ) );


            REQUIRE( sols13.size() == 2 );
            REQUIRE( sols13[0]
                == make_tuple(
                    VEC( TSTR( "gg" ), TSTR( "gg" ) ), "vec14"s, TypeCheckingScore( 0, 2 ) ) );
            REQUIRE( sols13[1]
                == make_tuple(
                    VEC( TSTR( "bar" ), TSTR( "bar" ) ), "vec4"s, TypeCheckingScore( 0, 2 ) ) );

            REQUIRE( sols15.size() == 1 );
            REQUIRE( sols15[0] == make_tuple( vec15, "vec14"s, TypeCheckingScore( 0, 2 ) ) );
        }
    }
}
Changes to bs/sema/tests/unify-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
#include <catch2/catch_all.hpp>
#include "sema/sema.h"

using namespace std;
using namespace goose;
using namespace goose::eir;
using namespace goose::sema;

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 );

        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 );













|
>









|







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
#include <catch2/catch_all.hpp>
#include "sema/sema.h"

using namespace std;
using namespace goose;
using namespace goose::eir;
using namespace goose::sema;

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 );

        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 );

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
        TypeCheckingContext tcc( ctxt );

        auto g = Unify( lhs, rhs, tcc );

        auto it = g.begin();
        REQUIRE( it == g.end() );
    }
}

SCENARIO( "Unify works", "[unify]" )
{
    WHEN( "Unifying various expressions" )
    {
        auto expr0 = HOLE( "a"_sid );
        auto expr1 = VEC( TSTR( "foo" ), TSTR( "bar" ) );

        auto expr2 = VEC( HOLE( "a"_sid ), HOLE( "a"_sid ) );
        auto expr3 = VEC( TSTR( "meh" ),   TSTR( "meh" ) );

        auto expr4 = VEC( HOLE( "a"_sid ), TSTR( "bar" ) );
        auto expr5 = VEC( TSTR( "foo" ),   HOLE( "b"_sid ) );

        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 ) );

        auto expr14 = VEC( TSTR( "gg" ), REPEAT( HOLE( "_"_sid ) ) );
        auto expr15 = VEC( TSTR( "gg" ), TERM( 456U ), TSTR( "blah" ) );

        THEN( "Unifications yields the expected solutions" )
        {
            CheckForUniqueSolution( expr0, expr1, VEC( TSTR( "foo" ), TSTR( "bar" ) ), { 1, 1 } );
            CheckForUniqueSolution( expr2, expr3, VEC( TSTR( "meh" ), TSTR( "meh" ) ), { 0, 1 } );
            CheckForNoSolution( expr2, expr1 );
            CheckForUniqueSolution( expr4, expr5, VEC( TSTR( "foo" ), TSTR( "bar" ) ), { 0, 2 } );
            CheckForUniqueSolution( expr6, expr7, VEC( TSTR( "foo" ), TSTR( "foo" ) ), { 0, 2 } );

            CheckForUniqueSolution( expr8, expr9,
                VEC(
                    VEC( TSTR( "foo" ), TSTR( "bar" ) ),
                    VEC( TSTR( "foo" ), TSTR( "bar" ) )
                ),
                { 1, 2 }
            );

            CheckForUniqueSolution( expr10, expr11,
                VEC(
                    VEC( TSTR( "foo" ), TSTR( "bar" ) ),
                    VEC( TSTR( "foo" ), TSTR( "bar" ) ),
                    VEC( TSTR( "foo" ), TSTR( "bar" ) ),
                    VEC( TSTR( "foo" ), TSTR( "bar" ) )
                ),
                { 1, 5 }
            );

            CheckForNoSolution( expr12, expr13 );

            CheckForUniqueSolution( expr14, expr13, VEC( TSTR( "gg" ), TSTR( "gg" ) ), { 0, 2 } );
            CheckForUniqueSolution( expr14, expr15, expr15, { 0, 2 } );
        }
    }
}







|









|


|


|




|
|


|













<
|
<
<
|
<


<
|
|
<
<
<
|
<








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
        TypeCheckingContext tcc( ctxt );

        auto g = Unify( lhs, rhs, tcc );

        auto it = g.begin();
        REQUIRE( it == g.end() );
    }
} // namespace

SCENARIO( "Unify works", "[unify]" )
{
    WHEN( "Unifying various expressions" )
    {
        auto expr0 = HOLE( "a"_sid );
        auto expr1 = VEC( TSTR( "foo" ), TSTR( "bar" ) );

        auto expr2 = VEC( HOLE( "a"_sid ), HOLE( "a"_sid ) );
        auto expr3 = VEC( TSTR( "meh" ), TSTR( "meh" ) );

        auto expr4 = VEC( HOLE( "a"_sid ), TSTR( "bar" ) );
        auto expr5 = VEC( TSTR( "foo" ), HOLE( "b"_sid ) );

        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 ) );

        auto expr14 = VEC( TSTR( "gg" ), REPEAT( HOLE( "_"_sid ) ) );
        auto expr15 = VEC( TSTR( "gg" ), TERM( 456U ), TSTR( "blah" ) );

        THEN( "Unifications yields the expected solutions" )
        {
            CheckForUniqueSolution( expr0, expr1, VEC( TSTR( "foo" ), TSTR( "bar" ) ), { 1, 1 } );
            CheckForUniqueSolution( expr2, expr3, VEC( TSTR( "meh" ), TSTR( "meh" ) ), { 0, 1 } );
            CheckForNoSolution( expr2, expr1 );
            CheckForUniqueSolution( expr4, expr5, VEC( TSTR( "foo" ), TSTR( "bar" ) ), { 0, 2 } );
            CheckForUniqueSolution( expr6, expr7, VEC( TSTR( "foo" ), TSTR( "foo" ) ), { 0, 2 } );

            CheckForUniqueSolution( expr8, expr9,

                VEC( VEC( TSTR( "foo" ), TSTR( "bar" ) ), VEC( TSTR( "foo" ), TSTR( "bar" ) ) ),


                { 1, 2 } );


            CheckForUniqueSolution( expr10, expr11,

                VEC( VEC( TSTR( "foo" ), TSTR( "bar" ) ), VEC( TSTR( "foo" ), TSTR( "bar" ) ),
                    VEC( TSTR( "foo" ), TSTR( "bar" ) ), VEC( TSTR( "foo" ), TSTR( "bar" ) ) ),



                { 1, 5 } );


            CheckForNoSolution( expr12, expr13 );

            CheckForUniqueSolution( expr14, expr13, VEC( TSTR( "gg" ), TSTR( "gg" ) ), { 0, 2 } );
            CheckForUniqueSolution( expr14, expr15, expr15, { 0, 2 } );
        }
    }
}
Changes to bs/sema/tpl-ruleset.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "sema.h"

using namespace goose;
using namespace goose::sema;

void TemplateRuleSet::addRule( const Term& paramPat, ptr< TemplateRule >&& r )
{
    m_rules = Merge( m_rules, paramPat, [&]( auto&& ){ return move( r ); } );
}

void TemplateContext::TVarOccurred( StringId name )
{
    auto it = m_TVarPackMap.find( name );
    if( it == m_TVarPackMap.end() )
    {







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "sema.h"

using namespace goose;
using namespace goose::sema;

void TemplateRuleSet::addRule( const Term& paramPat, ptr< TemplateRule >&& r )
{
    m_rules = Merge( m_rules, paramPat, [&]( auto&& ) { return move( r ); } );
}

void TemplateContext::TVarOccurred( StringId name )
{
    auto it = m_TVarPackMap.find( name );
    if( it == m_TVarPackMap.end() )
    {
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
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_TPL_RULESET_H
#define GOOSE_SEMA_TPL_RULESET_H

namespace goose::sema
{
    class TemplateContext
    {
        public:
            TemplateContext( const Context& c ) :
                m_semaContext( c )
            {}



            const auto& semaContext() const { return m_semaContext; }

            uint32_t currentPackIndex() const { return m_currentPackIndex; }

            void setCurrentPackIndex( uint32_t index ) { m_currentPackIndex = index; }

            uint32_t newPackIndex() { return m_nextPackIndex++; }

            void TVarOccurred( StringId name );
            void TTVarOccurred( StringId name );

            uint32_t getTVarPackIndex( StringId name ) const;
            uint32_t getTTVarPackIndex( StringId name ) const;

        private:
            const Context& m_semaContext;

            unordered_map< StringId, uint32_t > m_TVarPackMap;
            unordered_map< StringId, uint32_t > m_TTVarPackMap;

            uint32_t m_currentPackIndex = 0;
            uint32_t m_nextPackIndex = 1;
    };

    class TemplateRule
    {
        public:
            virtual ~TemplateRule() {}

            virtual bool prepare( TemplateContext& c, const Term& tpl ) const { return true; }

            virtual optional< Term > buildSignature( const TemplateContext& c, const Term& tpl ) const = 0;

            virtual Generator< Value > buildParamDecls( const Context& c, const Term& param, TermGen& args ) const = 0;

            virtual void setup( const Context& c, TypeCheckingContext& tcc, const Term& tpl ) const {};


            virtual uint64_t hashTypePredicates( const Context& c, const TypeCheckingContext& tcc, const Term& tpl ) const { return 0; };




            virtual optional< Term > buildArgPattern( const Context& c, const Term& tpl ) const = 0;
            virtual bool isPackExpr( const Context& c, const Term& tpl ) const = 0;
    };

    class TemplateRuleSet
    {
        public:
            TemplateRuleSet() {}

            void addRule( const Term& paramPat, ptr< TemplateRule >&& r );

            const auto& rules() const { return m_rules; }

        private:
            Trie< ptr< TemplateRule > > m_rules;
    };
}


#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
64
65
66
67
68
69
70
71
72

73
74
75
#ifndef GOOSE_SEMA_TPL_RULESET_H
#define GOOSE_SEMA_TPL_RULESET_H

namespace goose::sema
{
    class TemplateContext
    {
      public:
        TemplateContext( const Context& c ) :
            m_semaContext( c )

        {
        }

        const auto& semaContext() const { return m_semaContext; }

        uint32_t currentPackIndex() const { return m_currentPackIndex; }

        void setCurrentPackIndex( uint32_t index ) { m_currentPackIndex = index; }

        uint32_t newPackIndex() { return m_nextPackIndex++; }

        void TVarOccurred( StringId name );
        void TTVarOccurred( StringId name );

        uint32_t getTVarPackIndex( StringId name ) const;
        uint32_t getTTVarPackIndex( StringId name ) const;

      private:
        const Context& m_semaContext;

        unordered_map< StringId, uint32_t > m_TVarPackMap;
        unordered_map< StringId, uint32_t > m_TTVarPackMap;

        uint32_t m_currentPackIndex = 0;
        uint32_t m_nextPackIndex = 1;
    };

    class TemplateRule
    {
      public:
        virtual ~TemplateRule() {}

        virtual bool prepare( TemplateContext& c, const Term& tpl ) const { return true; }

        virtual optional< Term > buildSignature(
            const TemplateContext& c, const Term& tpl ) const = 0;
        virtual Generator< Value > buildParamDecls(
            const Context& c, const Term& param, TermGen& args ) const = 0;
        virtual void setup( const Context& c, TypeCheckingContext& tcc, const Term& tpl ) const {};

        virtual uint64_t hashTypePredicates(
            const Context& c, const TypeCheckingContext& tcc, const Term& tpl ) const
        {
            return 0;
        }

        virtual optional< Term > buildArgPattern( const Context& c, const Term& tpl ) const = 0;
        virtual bool isPackExpr( const Context& c, const Term& tpl ) const = 0;
    };

    class TemplateRuleSet
    {
      public:
        TemplateRuleSet() {}

        void addRule( const Term& paramPat, ptr< TemplateRule >&& r );

        const auto& rules() const { return m_rules; }

      private:
        Trie< ptr< TemplateRule > > m_rules;
    };

} // namespace goose::sema

#endif
Changes to bs/sema/typecheck.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "sema.h"

//#define VERBOSE_TC

#if !defined( NDEBUG ) && defined( VERBOSE_TC )
#define TC_LOG_RULES
#endif

namespace goose::sema
{
    TypeCheckingSolution FindBestTyping( const Term& lhs, const Term& rhs, const Context& context )
    {
        optional< TypeCheckingContext > bestTCC;


|


|







1
2
3
4
5
6
7
8
9
10
11
12
13
#include "sema.h"

// #define VERBOSE_TC

#if !defined( NDEBUG ) && defined( VERBOSE_TC )
    #define TC_LOG_RULES
#endif

namespace goose::sema
{
    TypeCheckingSolution FindBestTyping( const Term& lhs, const Term& rhs, const Context& context )
    {
        optional< TypeCheckingContext > bestTCC;
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
        const auto& rules = tcc.rules()->typeCheckingRules();

        struct Candidate
        {
            MatchScore score;
            const TypeCheckingRuleSet::BinaryRule* rule = nullptr;

            bool operator<( const Candidate& rhs ) const
            {
                return score < rhs.score;
            }
        };

        const TypeCheckingRuleSet::BinaryRule* exclusiveRule = nullptr;
        priority_queue< Candidate > candidates;

        auto expr = VEC( lhs, rhs );
        for( auto&& [s, rule] : Match( expr, rules ) )
        {
            if( rule.exclusive )
            {
                if( exclusiveRule )

                    G_VAL_ERROR( *EIRToValuePattern( rhs ), "ambiguous exclusive type checking rule" );

                exclusiveRule = &rule;
            }
            else if( !exclusiveRule )
                candidates.push( { s.score(), &rule } );
        }

        if( exclusiveRule )
        {
#ifdef TC_LOG_RULES
            cout << "calling exclusive typechecking rule " << exclusiveRule->infos.pFilename << ':' << exclusiveRule->infos.line << endl;

            cout << "  lhs " << lhs << endl;
            cout << "  rhs " << rhs << endl;
#endif

#ifndef NDEBUG
            tcc.TCRuleTrace( &exclusiveRule->infos );
#endif







|
<
<
<











>
|










|
>







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
        const auto& rules = tcc.rules()->typeCheckingRules();

        struct Candidate
        {
            MatchScore score;
            const TypeCheckingRuleSet::BinaryRule* rule = nullptr;

            bool operator<( const Candidate& rhs ) const { return score < rhs.score; }



        };

        const TypeCheckingRuleSet::BinaryRule* exclusiveRule = nullptr;
        priority_queue< Candidate > candidates;

        auto expr = VEC( lhs, rhs );
        for( auto&& [s, rule] : Match( expr, rules ) )
        {
            if( rule.exclusive )
            {
                if( exclusiveRule )
                    G_VAL_ERROR(
                        *EIRToValuePattern( rhs ), "ambiguous exclusive type checking rule" );

                exclusiveRule = &rule;
            }
            else if( !exclusiveRule )
                candidates.push( { s.score(), &rule } );
        }

        if( exclusiveRule )
        {
#ifdef TC_LOG_RULES
            cout << "calling exclusive typechecking rule " << exclusiveRule->infos.pFilename << ':'
                 << exclusiveRule->infos.line << endl;
            cout << "  lhs " << lhs << endl;
            cout << "  rhs " << rhs << endl;
#endif

#ifndef NDEBUG
            tcc.TCRuleTrace( &exclusiveRule->infos );
#endif
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
            const Candidate& c = candidates.top();
            if( c.score < bestScore )
                break;

            auto gen = c.rule->func( lhs, rhs, tcc );
            if( gen.begin() != gen.end() )
            {
        #ifdef TCRULES_DEBUG
                G_VAL_ERROR( *EIRToValuePattern( rhs ), format( "ambiguous type checking rule: {}:{} and {}:{} both match",

                   bestInfos->pFilename, bestInfos->line, c.rule->infos.pFilename, c.rule->infos.line ) );

        #else
                G_VAL_ERROR( *EIRToValuePattern( rhs ), "ambiguous type checking rule" );
        #endif
            }

            candidates.pop();
        }

#ifdef TC_LOG_RULES
        cout << "calling typechecking rule " << bestInfos->pFilename << ':' << bestInfos->line << endl;

        cout << "  lhs " << lhs << endl;
        cout << "  rhs " << rhs << endl;
#endif

        co_yield move( *bestGen );
    }








|
|
>
|
>
|

|






|
>







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
            const Candidate& c = candidates.top();
            if( c.score < bestScore )
                break;

            auto gen = c.rule->func( lhs, rhs, tcc );
            if( gen.begin() != gen.end() )
            {
#ifdef TCRULES_DEBUG
                G_VAL_ERROR( *EIRToValuePattern( rhs ),
                    format( "ambiguous type checking rule: {}:{} and {}:{} both match",
                        bestInfos->pFilename, bestInfos->line, c.rule->infos.pFilename,
                        c.rule->infos.line ) );
#else
                G_VAL_ERROR( *EIRToValuePattern( rhs ), "ambiguous type checking rule" );
#endif
            }

            candidates.pop();
        }

#ifdef TC_LOG_RULES
        cout << "calling typechecking rule " << bestInfos->pFilename << ':' << bestInfos->line
             << endl;
        cout << "  lhs " << lhs << endl;
        cout << "  rhs " << rhs << endl;
#endif

        co_yield move( *bestGen );
    }

195
196
197
198
199
200
201
202

203
204
205
206
207
208
209
        if( ambiguous )
            G_ERROR( "ambiguous unification rule" );

        if( !bestRule )
            co_return;

#ifdef TC_LOG_RULES
        cout << "calling unification rule " << bestRule->infos.pFilename << ':' << bestRule->infos.line << endl;

        cout << "  lhs " << lhs << endl;
        cout << "  rhs " << rhs << endl;
#endif

        co_yield bestRule->func( lhs, rhs, tcc );
    }








|
>







197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
        if( ambiguous )
            G_ERROR( "ambiguous unification rule" );

        if( !bestRule )
            co_return;

#ifdef TC_LOG_RULES
        cout << "calling unification rule " << bestRule->infos.pFilename << ':'
             << bestRule->infos.line << endl;
        cout << "  lhs " << lhs << endl;
        cout << "  rhs " << rhs << endl;
#endif

        co_yield bestRule->func( lhs, rhs, tcc );
    }

242
243
244
245
246
247
248
249
        // (ns index 2) is skipped (due to being caught in a hole),
        // we increase the cost of the current solution accordingly.
        if( tcc.LHSSubContext().namespaceIndex == 2 )
            tcc.addCost( GetWeight( lhs ) );

        return bestRule->func( lhs, tcc );
    }
}







|
245
246
247
248
249
250
251
252
        // (ns index 2) is skipped (due to being caught in a hole),
        // we increase the cost of the current solution accordingly.
        if( tcc.LHSSubContext().namespaceIndex == 2 )
            tcc.addCost( GetWeight( lhs ) );

        return bestRule->func( lhs, tcc );
    }
} // namespace goose::sema
Changes 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
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& tcc );

    TCGen Unify( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc );
    optional< Term > HalfUnify( const Term& lhs, TypeCheckingContext& tcc );
}

#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
#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& tcc );

    TCGen Unify( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc );
    optional< Term > HalfUnify( const Term& lhs, TypeCheckingContext& tcc );
} // namespace goose::sema

#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

#include "sema.h"

namespace goose::sema
{

    TCGen UnifyVectors( TCVecGenerator lvg, TCVecGenerator rvg, const Vector& solutionVec, TypeCheckingContext tcc )
    {

        for( auto&& [u,tcc] : Unify( lvg( tcc.LHSSubContext() ), rvg( tcc.RHSSubContext() ), tcc ) )
        {
            auto vec = Vector::MakeAppend( solutionVec, move( u ) );

            if( lvg.fixedPartFinished() && rvg.fixedPartFinished() )
                co_yield { TERM( make_shared< Vector >( move( vec ) ) ), tcc };
            else
                co_yield UnifyVectors( lvg, rvg, vec, tcc );
        }
    }

    void SetupBasicUnificationRules( TypeCheckingRuleSet& ruleSet )
    {
        // Default half-unification rule
        ruleSet.addHalfUnificationRule( ANYTERM( _ ), []( const Term& lhs, TypeCheckingContext& tcc )

        {
            // 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.

            return lhs;
        } );

        // Identity unification rule

        ruleSet.addUnificationRule( 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( 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 = get< LocationId >( lhs ).index();
            auto rloc = get< LocationId >( rhs ).index();

            // 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 { LocationId( result ), tcc };
        } );

        // void* unification rule
        ruleSet.addUnificationRule( 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
        ruleSet.addHalfUnificationRule( VECOFLENGTH( _ ), []( const Term& lhs, TypeCheckingContext& tcc )
            -> optional< Term >
        {
            assert( holds_alternative< pvec >( lhs ) );

            const auto& lVector = get< pvec >( lhs );
            const auto& lvg = lVector->terms();

            Vector result;

            for( auto&& t : lvg )
            {
                auto s = HalfUnify( t, tcc );
                if( !s )
                    return nullopt;
                result.append( *s );
            }

            return TERM( make_shared< Vector >( move( result ) ) );
        } );

        // 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( 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 );

            TCVecGenerator lvg( *lVector );
            TCVecGenerator rvg( *rVector );

            if( lvg.fixedPartFinished() && rvg.fixedPartFinished() )
            {
                co_yield { lhs, tcc };
                co_return;
            }

            Vector sol;
            co_yield UnifyVectors( lvg, rvg, sol, 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
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
#include "sema.h"

namespace goose::sema
{
    TCGen UnifyVectors(
        TCVecGenerator lvg, TCVecGenerator rvg, const Vector& solutionVec, TypeCheckingContext tcc )
    {
        for( auto&& [u, tcc] :
            Unify( lvg( tcc.LHSSubContext() ), rvg( tcc.RHSSubContext() ), tcc ) )
        {
            auto vec = Vector::MakeAppend( solutionVec, move( u ) );

            if( lvg.fixedPartFinished() && rvg.fixedPartFinished() )
                co_yield { TERM( make_shared< Vector >( move( vec ) ) ), tcc };
            else
                co_yield UnifyVectors( lvg, rvg, vec, tcc );
        }
    }

    void SetupBasicUnificationRules( TypeCheckingRuleSet& ruleSet )
    {
        // Default half-unification rule
        ruleSet.addHalfUnificationRule( ANYTERM( _ ),
            []( const Term& lhs, TypeCheckingContext& tcc )
            {
                // 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.
                return lhs;
            } );

        // Identity unification rule
        ruleSet.addUnificationRule( 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( 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 = get< LocationId >( lhs ).index();
                auto rloc = get< LocationId >( rhs ).index();

                // 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 { LocationId( result ), tcc };
            } );

        // void* unification rule
        ruleSet.addUnificationRule( 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
        ruleSet.addHalfUnificationRule( VECOFLENGTH( _ ),
            []( const Term& lhs, TypeCheckingContext& tcc ) -> optional< Term >
            {
                assert( holds_alternative< pvec >( lhs ) );

                const auto& lVector = get< pvec >( lhs );
                const auto& lvg = lVector->terms();

                Vector result;

                for( auto&& t : lvg )
                {
                    auto s = HalfUnify( t, tcc );
                    if( !s )
                        return nullopt;
                    result.append( *s );
                }

                return TERM( make_shared< Vector >( move( result ) ) );
            } );

        // 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( 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 );

                TCVecGenerator lvg( *lVector );
                TCVecGenerator rvg( *rVector );

                if( lvg.fixedPartFinished() && rvg.fixedPartFinished() )
                {
                    co_yield { lhs, tcc };
                    co_return;
                }

                Vector sol;
                co_yield UnifyVectors( lvg, rvg, sol, tcc );
            } );
    }

} // namespace goose::sema
Changes to bs/sema/uni-holes.cpp.
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
        tcc.lockHole( index );

        auto holeExpr = HOLE( StringId( index ), h.kind(), h.behavior() );

        auto& maybeVal = tcc.getValue( index );
        if( maybeVal )
        {
            for( auto&& [e,tcc] : Unify( *maybeVal, rhs, tcc ) )
            {
                tcc.unlockHole( index );
                tcc.setValue( index, move( e ) );
                co_yield { move( holeExpr ), tcc };
            }
        }
        else if( auto s = HalfUnify( rhs, tcc.flip() ) )







|







96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
        tcc.lockHole( index );

        auto holeExpr = HOLE( StringId( index ), h.kind(), h.behavior() );

        auto& maybeVal = tcc.getValue( index );
        if( maybeVal )
        {
            for( auto&& [e, tcc] : Unify( *maybeVal, rhs, tcc ) )
            {
                tcc.unlockHole( index );
                tcc.setValue( index, move( e ) );
                co_yield { move( holeExpr ), tcc };
            }
        }
        else if( auto s = HalfUnify( rhs, tcc.flip() ) )
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
            rname = rh.name();
            rindex = tcc.getRHSHoleIndex( rname, rh.behavior() );
        }
        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, lh.behavior(), index );
            tcc.setRHSHoleIndex( rname, rh.behavior(), index );

            auto lk = TERM( lh.kind() );
            auto rk = TERM( rh.kind() );
            for( auto&& [k,tcc] : Unify( lk, rk, tcc ) )
            {
                assert( holds_alternative< StringId >( k ) );
                if( holds_alternative< StringId >( k ) )
                    co_yield { HOLE( StringId( index ), get< StringId >( k ) ), tcc };
            }
            co_return;
        }

        // If both holes actually point to the same value, just yield it as the solution.
        if( lindex == rindex )
        {
            auto lk = TERM( lh.kind() );
            auto rk = TERM( rh.kind() );
            for( auto&& [k,tcc] : Unify( lk, rk, tcc ) )
            {
                assert( holds_alternative< StringId >( k ) );
                if( holds_alternative< StringId >( k ) )
                    co_yield { HOLE( StringId( lindex ), get< StringId >( k ) ), 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, lh.behavior(), rindex );
            auto lk = TERM( lh.kind() );
            auto rk = TERM( rh.kind() );
            for( auto&& [k,tcc] : Unify( lk, rk, tcc ) )
            {
                assert( holds_alternative< StringId >( k ) );
                if( holds_alternative< StringId >( k ) )
                    co_yield { HOLE( StringId( rindex ), get< StringId >( k ) ), tcc };
            }
            co_return;
        }

        if( rindex == TypeCheckingContext::InvalidIndex )
        {
            tcc.setRHSHoleIndex( rname, rh.behavior(), lindex );
            auto lk = TERM( lh.kind() );
            auto rk = TERM( rh.kind() );
            for( auto&& [k,tcc] : Unify( lk, rk, tcc ) )
            {
                assert( holds_alternative< StringId >( k ) );
                if( holds_alternative< StringId >( k ) )
                    co_yield { HOLE( StringId( lindex ), get< StringId >( k ) ), 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 )
        {
            if( HalfUnify( *lval, tcc ) )
            {
                tcc.unlockHole( lindex );
                tcc.unlockHole( rindex );

                auto lk = TERM( lh.kind() );
                auto rk = TERM( rh.kind() );
                for( auto&& [k,tcc] : Unify( lk, rk, tcc ) )
                {
                    assert( holds_alternative< StringId >( k ) );
                    if( !holds_alternative< StringId >( k ) )
                        continue;

                    auto kind = get< StringId >( k );
                    tcc.setValue( rindex, HOLE( StringId( lindex ), kind ) );







|
>







|













|














|













|


















|
|












|







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
            rname = rh.name();
            rindex = tcc.getRHSHoleIndex( rname, rh.behavior() );
        }
        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, lh.behavior(), index );
            tcc.setRHSHoleIndex( rname, rh.behavior(), index );

            auto lk = TERM( lh.kind() );
            auto rk = TERM( rh.kind() );
            for( auto&& [k, tcc] : Unify( lk, rk, tcc ) )
            {
                assert( holds_alternative< StringId >( k ) );
                if( holds_alternative< StringId >( k ) )
                    co_yield { HOLE( StringId( index ), get< StringId >( k ) ), tcc };
            }
            co_return;
        }

        // If both holes actually point to the same value, just yield it as the solution.
        if( lindex == rindex )
        {
            auto lk = TERM( lh.kind() );
            auto rk = TERM( rh.kind() );
            for( auto&& [k, tcc] : Unify( lk, rk, tcc ) )
            {
                assert( holds_alternative< StringId >( k ) );
                if( holds_alternative< StringId >( k ) )
                    co_yield { HOLE( StringId( lindex ), get< StringId >( k ) ), 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, lh.behavior(), rindex );
            auto lk = TERM( lh.kind() );
            auto rk = TERM( rh.kind() );
            for( auto&& [k, tcc] : Unify( lk, rk, tcc ) )
            {
                assert( holds_alternative< StringId >( k ) );
                if( holds_alternative< StringId >( k ) )
                    co_yield { HOLE( StringId( rindex ), get< StringId >( k ) ), tcc };
            }
            co_return;
        }

        if( rindex == TypeCheckingContext::InvalidIndex )
        {
            tcc.setRHSHoleIndex( rname, rh.behavior(), lindex );
            auto lk = TERM( lh.kind() );
            auto rk = TERM( rh.kind() );
            for( auto&& [k, tcc] : Unify( lk, rk, tcc ) )
            {
                assert( holds_alternative< StringId >( k ) );
                if( holds_alternative< StringId >( k ) )
                    co_yield { HOLE( StringId( lindex ), get< StringId >( k ) ), 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 )
        {
            if( HalfUnify( *lval, tcc ) )
            {
                tcc.unlockHole( lindex );
                tcc.unlockHole( rindex );

                auto lk = TERM( lh.kind() );
                auto rk = TERM( rh.kind() );
                for( auto&& [k, tcc] : Unify( lk, rk, tcc ) )
                {
                    assert( holds_alternative< StringId >( k ) );
                    if( !holds_alternative< StringId >( k ) )
                        continue;

                    auto kind = get< StringId >( k );
                    tcc.setValue( rindex, HOLE( StringId( lindex ), kind ) );
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424

            if( HalfUnify( *rval, tcc.flip() ) )
            {
                tcc.unlockHole( lindex );
                tcc.unlockHole( rindex );

                auto lk = TERM( lh.kind() );
                auto rk = TERM( rh.kind() );
                for( auto&& [k,tcc] : Unify( lk, rk, tcc ) )
                {
                    assert( holds_alternative< StringId >( k ) );
                    if( !holds_alternative< StringId >( k ) )
                        continue;

                    auto kind = get< StringId >( k );
                    tcc.setValue( lindex, HOLE( StringId( rindex ), kind ) );
                    co_yield { HOLE( StringId( rindex ), kind ), 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.
        for( auto&& [e,tcc] : Unify( *lval, *rval, tcc ) )
        {
            tcc.unlockHole( lindex );
            tcc.unlockHole( rindex );

            tcc.setValue( lindex, move( e ) );

            auto lk = TERM( lh.kind() );
            auto rk = TERM( rh.kind() );
            for( auto&& [k,tcc] : Unify( lk, rk, tcc ) )
            {
                assert( holds_alternative< StringId >( k ) );
                if( !holds_alternative< StringId >( k ) )
                        continue;

                auto kind = get< StringId >( k );
                tcc.setValue( rindex, HOLE( StringId( lindex ), kind ) );
                co_yield { HOLE( StringId( lindex ), kind ), tcc };
            }
        }
    }
}

namespace goose::sema
{
    void SetupHoleUnificationRules( TypeCheckingRuleSet& ruleSet )
    {
        ruleSet.addUnificationRule(
            TSID( anykind ),
            ANYTERM( _ ),
            []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
            {
                co_yield { rhs, tcc };
            }
        );

        ruleSet.addUnificationRule(
            TSID( anykind ),
            TSID( anykind ),
            []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
            {
                co_yield { rhs, tcc };
            }
        );

        ruleSet.addHalfUnificationRule(
            HOLE( "_"_sid, ""_sid, Hole::Behavior::Any ),
            HalfUniAnonHole
        );

        ruleSet.addHalfUnificationRule(
            HolePattern(),
            HalfUniHole
        );

        ruleSet.addUnificationRule(
            HOLE( "_"_sid, ""_sid, Hole::Behavior::Any ),
            ANYTERM( _ ),
            UniAnonHoleAny
        );

        ruleSet.addUnificationRule(
            HolePattern(),
            ANYTERM( _ ),
            UniHoleAny
        );

        ruleSet.addUnificationRule(
            HolePattern(),
            UniHoles
        );

        // Any typecheck involving a hole on one side
        // becomes an unification
        ruleSet.addTypeCheckingRule(
            HolePattern(),
            ANYTERM( _ ),
        []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
        {
            co_yield Unify( lhs, rhs, tcc );
        } );

        ruleSet.addTypeCheckingRule(
            ANYTERM( _ ),
            HolePattern(),
        []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
        {
            co_yield Unify( lhs, rhs, tcc );
        } );

        ruleSet.addTypeCheckingRule(
            HolePattern(),
            HolePattern(),
        []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
        {
            co_yield Unify( lhs, rhs, tcc );
        } );

        // Forwarding holes unification
        ruleSet.addUnificationRule(
            HOLE( "_"_sid, "fwd"_sid, Hole::Behavior::Any ),
            ANYTERM( _ ),
            UniAnonHoleAny, true
        );

        ruleSet.addUnificationRule(
            ForwardingHolePattern(),
            ANYTERM( _ ),
            UniHoleAny, true
        );

        ruleSet.addUnificationRule(
            ForwardingHolePattern(),
            UniHoles, true
        );

        // Typechecking forwarding holes is the same as typechecking holes
        // except that we directly call the hole unification rules,
        // rather than going through Unify: this way, we prevent any rule
        // from overriding the default behavior, such as the reference
        // unification rules that forbid unification against anything else
        // than a ref
        ruleSet.addTypeCheckingRule(

            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                ForwardingHolePattern(),
                ANYTERM( _ ) ) ),

            ANYTERM( _ ),

            []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
            {
                auto lVal = *EIRToValuePattern( lhs );
                auto h = *ForwardingHoleFromIRExpr( lVal.type() );

                auto rVal = *EIRToValue( rhs );

                TCGen g;
                if( h.name() == "_"_sid )
                    g = UniAnonHoleAny( lVal.type(), rVal.type(), tcc );
                else
                    g = UniHoleAny( lVal.type(), rVal.type(), tcc );

                for( auto&& [s, tcc] : g )
                {
                    if( auto result = HalfUnify( rhs, tcc.flip() ) )
                        co_yield { move( *result ), tcc.flip() };
                }
            }, true
        );
    }
}








|
















|








|



|







|





|
<
<

<
|
|
<
<
|
<
<

<
|
|
<
<

|
<
<

|
<
<
<


|
<
<
<

|
<
<
<
<

|
<
<
<



|
<
<
|
<
|
<

|
<
<
|
<
|
<

|
<
<
|
<
|
<



|
<
<
<

|
<
<
<
<

|
<
<
<









|
<
<
<

















<


|
|
<

<
>
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300


301

302
303


304


305

306
307


308
309


310
311



312
313
314



315
316




317
318



319
320
321
322


323

324

325
326


327

328

329
330


331

332

333
334
335
336



337
338




339
340



341
342
343
344
345
346
347
348
349
350



351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367

368
369
370
371

372

373
            if( HalfUnify( *rval, tcc.flip() ) )
            {
                tcc.unlockHole( lindex );
                tcc.unlockHole( rindex );

                auto lk = TERM( lh.kind() );
                auto rk = TERM( rh.kind() );
                for( auto&& [k, tcc] : Unify( lk, rk, tcc ) )
                {
                    assert( holds_alternative< StringId >( k ) );
                    if( !holds_alternative< StringId >( k ) )
                        continue;

                    auto kind = get< StringId >( k );
                    tcc.setValue( lindex, HOLE( StringId( rindex ), kind ) );
                    co_yield { HOLE( StringId( rindex ), kind ), 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.
        for( auto&& [e, tcc] : Unify( *lval, *rval, tcc ) )
        {
            tcc.unlockHole( lindex );
            tcc.unlockHole( rindex );

            tcc.setValue( lindex, move( e ) );

            auto lk = TERM( lh.kind() );
            auto rk = TERM( rh.kind() );
            for( auto&& [k, tcc] : Unify( lk, rk, tcc ) )
            {
                assert( holds_alternative< StringId >( k ) );
                if( !holds_alternative< StringId >( k ) )
                    continue;

                auto kind = get< StringId >( k );
                tcc.setValue( rindex, HOLE( StringId( lindex ), kind ) );
                co_yield { HOLE( StringId( lindex ), kind ), tcc };
            }
        }
    }
} // namespace

namespace goose::sema
{
    void SetupHoleUnificationRules( TypeCheckingRuleSet& ruleSet )
    {
        ruleSet.addUnificationRule( TSID( anykind ), ANYTERM( _ ),


            []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen

            { co_yield { rhs, tcc }; } );



        ruleSet.addUnificationRule( TSID( anykind ), TSID( anykind ),


            []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen

            { co_yield { rhs, tcc }; } );



        ruleSet.addHalfUnificationRule(
            HOLE( "_"_sid, ""_sid, Hole::Behavior::Any ), HalfUniAnonHole );



        ruleSet.addHalfUnificationRule( HolePattern(), HalfUniHole );




        ruleSet.addUnificationRule(
            HOLE( "_"_sid, ""_sid, Hole::Behavior::Any ), ANYTERM( _ ), UniAnonHoleAny );




        ruleSet.addUnificationRule( HolePattern(), ANYTERM( _ ), UniHoleAny );





        ruleSet.addUnificationRule( HolePattern(), UniHoles );




        // Any typecheck involving a hole on one side
        // becomes an unification
        ruleSet.addTypeCheckingRule( HolePattern(), ANYTERM( _ ),


            []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen

            { co_yield Unify( lhs, rhs, tcc ); } );


        ruleSet.addTypeCheckingRule( ANYTERM( _ ), HolePattern(),


            []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen

            { co_yield Unify( lhs, rhs, tcc ); } );


        ruleSet.addTypeCheckingRule( HolePattern(), HolePattern(),


            []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen

            { co_yield Unify( lhs, rhs, tcc ); } );


        // Forwarding holes unification
        ruleSet.addUnificationRule(
            HOLE( "_"_sid, "fwd"_sid, Hole::Behavior::Any ), ANYTERM( _ ), UniAnonHoleAny, true );




        ruleSet.addUnificationRule( ForwardingHolePattern(), ANYTERM( _ ), UniHoleAny, true );





        ruleSet.addUnificationRule( ForwardingHolePattern(), UniHoles, true );




        // Typechecking forwarding holes is the same as typechecking holes
        // except that we directly call the hole unification rules,
        // rather than going through Unify: this way, we prevent any rule
        // from overriding the default behavior, such as the reference
        // unification rules that forbid unification against anything else
        // than a ref
        ruleSet.addTypeCheckingRule(

            ValueToEIR( ValuePattern( ANYTERM( _ ), ForwardingHolePattern(), ANYTERM( _ ) ) ),




            ANYTERM( _ ),

            []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
            {
                auto lVal = *EIRToValuePattern( lhs );
                auto h = *ForwardingHoleFromIRExpr( lVal.type() );

                auto rVal = *EIRToValue( rhs );

                TCGen g;
                if( h.name() == "_"_sid )
                    g = UniAnonHoleAny( lVal.type(), rVal.type(), tcc );
                else
                    g = UniHoleAny( lVal.type(), rVal.type(), tcc );

                for( auto&& [s, tcc] : g )

                    if( auto result = HalfUnify( rhs, tcc.flip() ) )
                        co_yield { move( *result ), tcc.flip() };
            },
            true );

    }

} // namespace goose::sema
Changes to bs/sema/uni-quote.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include "sema.h"

namespace goose::sema
{
    Term Quote( const Term& t )
    {
        return SetWeight( VEC( TSID( quote ), t ), 0 );
    }

    optional< Term > Unquote( const Term& t )
    {
        auto result = Decompose( t,
            Vec(
                Lit( "quote"_sid ),
                SubTerm()
            )
        );

        if( !result )
            return nullopt;

        auto&& [quoted] = *result;
        return quoted;
    }

    void SetupQuoteUnificationRules( TypeCheckingRuleSet& ruleSet )
    {
        ruleSet.addHalfUnificationRule( VEC( TSID( quote ), ANYTERM( _ ) ),
            []( const Term& lhs, TypeCheckingContext& tcc )
            {
                return lhs;
            } );

        ruleSet.addUnificationRule( VEC( TSID( quote ), ANYTERM( _ ) ),
            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                if( lhs == rhs )
                    co_yield { lhs, 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
#include "sema.h"

namespace goose::sema
{
    Term Quote( const Term& t )
    {
        return SetWeight( VEC( TSID( quote ), t ), 0 );
    }

    optional< Term > Unquote( const Term& t )
    {
        auto result = Decompose( t, Vec( Lit( "quote"_sid ), SubTerm() ) );






        if( !result )
            return nullopt;

        auto&& [quoted] = *result;
        return quoted;
    }

    void SetupQuoteUnificationRules( TypeCheckingRuleSet& ruleSet )
    {
        ruleSet.addHalfUnificationRule( VEC( TSID( quote ), ANYTERM( _ ) ),
            []( const Term& lhs, TypeCheckingContext& tcc ) { return lhs; } );




        ruleSet.addUnificationRule( VEC( TSID( quote ), ANYTERM( _ ) ),
            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                if( lhs == rhs )
                    co_yield { lhs, tcc };
            } );
48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
        ruleSet.addTypeCheckingRule( ANYTERM( _ ), VEC( TSID( quote ), ANYTERM( _ ) ),
            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                if( lhs == rhs )
                    co_yield { lhs, tcc };
            } );

        ruleSet.addTypeCheckingRule( VEC( TSID( quote ), ANYTERM( _ ) ), VEC( TSID( quote ), ANYTERM( _ ) ),

            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                if( lhs == rhs )
                    co_yield { lhs, tcc };
            } );
    }
}







|
>






|
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
        ruleSet.addTypeCheckingRule( ANYTERM( _ ), VEC( TSID( quote ), ANYTERM( _ ) ),
            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                if( lhs == rhs )
                    co_yield { lhs, tcc };
            } );

        ruleSet.addTypeCheckingRule( VEC( TSID( quote ), ANYTERM( _ ) ),
            VEC( TSID( quote ), ANYTERM( _ ) ),
            []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
            {
                if( lhs == rhs )
                    co_yield { lhs, tcc };
            } );
    }
} // namespace goose::sema
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( TypeCheckingRuleSet& 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 );
} // namespace goose::sema

#endif
Changes to bs/util/bigint.cpp.
37
38
39
40
41
42
43
44
        return APInt( APInt::getBitsNeeded( str, 10 ) + 1, str, 10 );
    }

    BigInt BigInt::FromHexString( const string& str )
    {
        return APInt( str.size() * 4 + 1, str, 16 );
    }
}







|
37
38
39
40
41
42
43
44
        return APInt( APInt::getBitsNeeded( str, 10 ) + 1, str, 10 );
    }

    BigInt BigInt::FromHexString( const string& str )
    {
        return APInt( str.size() * 4 + 1, str, 16 );
    }
} // namespace goose::util
Changes to bs/util/bigint.h.
1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

69
70
71

72

73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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
#ifndef GOOSE_UTIL_BIGINT_H
#define GOOSE_UTIL_BIGINT_H

namespace goose::util
{
    using llvm::APInt;
    using llvm::APSInt;

    // This is a wrapper around llvm::APSInt used to represent compile time integer with "infinite" width.

    //
    // It wraps the various operations so that they work on integers of different sizes, where the smallest
    // one is automatically sign extended first.
    //
    // It also wraps arithmetic operations to automatically extend the size to make sure that the result
    // won't overflow.
    //
    // They are always signed.
    class BigInt
    {
        public:
            BigInt()
            {
                m_value.setIsSigned( true );
            }

            BigInt( APSInt&& v );
            BigInt( const APSInt& v );

            BigInt( APInt&& v ) :
                m_value( move( v ) )
            {
                m_value.setIsSigned( true );
            }

            BigInt( const APInt& v ) :
                m_value( v )
            {
                m_value.setIsSigned( true );
            }

            static BigInt FromBinString( const string& str );
            static BigInt FromDecString( const string& str );
            static BigInt FromHexString( const string& str );

            static BigInt FromU32( uint32_t val )
            {
                return APSInt::getUnsigned( val ).trunc( 33 );
            }

            static BigInt FromU64( uint64_t val )
            {
                return APSInt::getUnsigned( val ).zext( 65 );
            }

            uint64_t toU64() const { return m_value.getLimitedValue(); }
            int64_t toS64() const
            {
                return isNegative() ? -m_value.getLimitedValue() : m_value.getLimitedValue();
            }

            auto toString() const
            {
                llvm::SmallString< 16 > s;
                m_value.toString( s, 10 );
                return s;
            }

            const auto& getAPSInt() const { return m_value; }

            operator const APSInt&() { return m_value; }

            auto getActiveBits() const { return m_value.getActiveBits(); }

            auto getSignificantBits() const { return m_value.getSignificantBits(); }

            bool isNegative() const { return m_value.isNegative(); }

            bool fitsAsS64() const
            {
                return getSignificantBits() <= 63;
            }

            auto zext( uint32_t size ) const { return m_value.zext( size ); }
            auto sext( uint32_t size ) const { return m_value.sext( size ); }

            friend ostream& operator<<( ostream& out, const BigInt& bi )
            {
                llvm::SmallString< 16 > s;
                bi.m_value.toString( s, 10 );
                return out << s.c_str();
            }

            void toHex( ostream& out ) const
            {
                llvm::SmallString< 16 > s;
                m_value.toString( s, 16 );
                out << s.c_str();
            }

        private:
            template< typename F >
            static auto applyOpOnMixedSizes(
                const BigInt& lhs, const BigInt& rhs, F&& func )
            {
                if( lhs.m_value.getBitWidth() < rhs.m_value.getBitWidth() )
                    return func( lhs.m_value.sext( rhs.m_value.getBitWidth() ), rhs.m_value );

                if( lhs.m_value.getBitWidth() > rhs.m_value.getBitWidth() )
                    return func( lhs.m_value, rhs.m_value.sext( lhs.m_value.getBitWidth() ) );

                return func( lhs.m_value, rhs.m_value );
            }

            template< typename F >
            static auto extendAndApplyOp( uint32_t wantedSize,
                const BigInt& lhs, const BigInt& rhs, F&& func )
            {
                return func( lhs.m_value.sext( wantedSize ), rhs.m_value.sext( wantedSize ) );
            }

        public:
            auto operator==( const BigInt& rhs ) const
            {
                return applyOpOnMixedSizes( *this, rhs,
                    []( const APInt& l, const APInt& r ) { return l == r; } );
            }

            auto operator!=( const BigInt& rhs ) const
            {
                return applyOpOnMixedSizes( *this, rhs,
                    []( const APInt& l, const APInt& r ) { return l != r; } );
            }

            auto operator>( const BigInt& rhs ) const
            {
                return applyOpOnMixedSizes( *this, rhs,
                    []( const APInt& l, const APInt& r ) { return l.sgt( r ); } );
            }

            auto operator>=( const BigInt& rhs ) const
            {
                return applyOpOnMixedSizes( *this, rhs,
                    []( const APInt& l, const APInt& r ) { return l.sge( r ); } );
            }

            auto operator<( const BigInt& rhs ) const
            {
                return applyOpOnMixedSizes( *this, rhs,
                    []( const APInt& l, const APInt& r ) { return l.slt( r ); } );
            }

            auto operator<=( const BigInt& rhs ) const
            {
                return applyOpOnMixedSizes( *this, rhs,
                    []( const APInt& l, const APInt& r ) { return l.sle( r ); } );
            }

            BigInt operator&( const BigInt& rhs ) const
            {
                return applyOpOnMixedSizes( *this, rhs,
                    []( const APInt& l, const APInt& r ) { return l & r; } );
            }

            BigInt operator|( const BigInt& rhs ) const
            {
                return applyOpOnMixedSizes( *this, rhs,
                    []( const APInt& l, const APInt& r ) { return l | r; } );
            }

            BigInt operator^( const BigInt& rhs ) const
            {
                return applyOpOnMixedSizes( *this, rhs,
                    []( const APInt& l, const APInt& r ) { return l ^ r; } );
            }

            BigInt shl( const APSInt& rhs ) const
            {
                return sext( getSignificantBits() + rhs.getLimitedValue() ).shl( rhs );
            }

            BigInt ashr( const APSInt& rhs ) const
            {
                return m_value.ashr( rhs );
            }

            BigInt lshr( const APSInt& rhs ) const
            {
                return m_value.lshr( rhs );
            }

            BigInt operator+( const BigInt& rhs ) const
            {
                return extendAndApplyOp(
                    max( getSignificantBits(), rhs.getSignificantBits() ) + 1,
                    *this, rhs,
                    []( const APInt& l, const APInt& r ) { return l + r; } );
            }

            BigInt operator-( const BigInt& rhs ) const
            {
                return extendAndApplyOp(
                    max( getSignificantBits(), rhs.getSignificantBits() ) + 1,
                    *this, rhs,
                    []( const APInt& l, const APInt& r ) { return l - r; } );
            }

            BigInt operator*( const BigInt& rhs ) const
            {
                return extendAndApplyOp(
                    getSignificantBits() + rhs.getSignificantBits(),
                    *this, rhs,
                    []( const APInt& l, const APInt& r ) { return l * r; } );
            }

            BigInt operator/( const BigInt& rhs ) const
            {
                return applyOpOnMixedSizes( *this, rhs,
                    []( const APInt& l, const APInt& r ) { return l.sdiv( r ); } );
            }

            BigInt operator%( const BigInt& rhs ) const
            {
                return applyOpOnMixedSizes( *this, rhs,
                    []( const APInt& l, const APInt& r ) { return l.srem( r ); } );
            }

        private:
            llvm::APSInt m_value;
    };
}

namespace std
{
    template<> struct hash< goose::util::BigInt >
    {
        size_t operator()( const goose::util::BigInt& x ) const
        {
            return llvm::hash_value( x.getAPSInt() );
        }
    };
}

#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
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
#ifndef GOOSE_UTIL_BIGINT_H
#define GOOSE_UTIL_BIGINT_H

namespace goose::util
{
    using llvm::APInt;
    using llvm::APSInt;

    // This is a wrapper around llvm::APSInt used to represent compile time integer with "infinite"
    // width.
    //
    // It wraps the various operations so that they work on integers of different sizes, where the
    // smallest one is automatically sign extended first.
    //
    // It also wraps arithmetic operations to automatically extend the size to make sure that the
    // result won't overflow.
    //
    // They are always signed.
    class BigInt
    {
      public:


        BigInt() { m_value.setIsSigned( true ); }


        BigInt( APSInt&& v );
        BigInt( const APSInt& v );

        BigInt( APInt&& v ) :
            m_value( move( v ) )
        {
            m_value.setIsSigned( true );
        }

        BigInt( const APInt& v ) :
            m_value( v )
        {
            m_value.setIsSigned( true );
        }

        static BigInt FromBinString( const string& str );
        static BigInt FromDecString( const string& str );
        static BigInt FromHexString( const string& str );

        static BigInt FromU32( uint32_t val ) { return APSInt::getUnsigned( val ).trunc( 33 ); }




        static BigInt FromU64( uint64_t val ) { return APSInt::getUnsigned( val ).zext( 65 ); }

        uint64_t toU64() const { return m_value.getLimitedValue(); }



        int64_t toS64() const
        {
            return isNegative() ? -m_value.getLimitedValue() : m_value.getLimitedValue();
        }

        auto toString() const
        {
            llvm::SmallString< 16 > s;
            m_value.toString( s, 10 );
            return s;
        }

        const auto& getAPSInt() const { return m_value; }

        operator const APSInt &() { return m_value; }

        auto getActiveBits() const { return m_value.getActiveBits(); }

        auto getSignificantBits() const { return m_value.getSignificantBits(); }

        bool isNegative() const { return m_value.isNegative(); }

        bool fitsAsS64() const { return getSignificantBits() <= 63; }

        auto zext( uint32_t size ) const { return m_value.zext( size ); }



        auto sext( uint32_t size ) const { return m_value.sext( size ); }

        friend ostream& operator<<( ostream& out, const BigInt& bi )
        {
            llvm::SmallString< 16 > s;
            bi.m_value.toString( s, 10 );
            return out << s.c_str();
        }

        void toHex( ostream& out ) const
        {
            llvm::SmallString< 16 > s;
            m_value.toString( s, 16 );
            out << s.c_str();
        }

      private:
        template< typename F >
        static auto applyOpOnMixedSizes( const BigInt& lhs, const BigInt& rhs, F&& func )

        {
            if( lhs.m_value.getBitWidth() < rhs.m_value.getBitWidth() )
                return func( lhs.m_value.sext( rhs.m_value.getBitWidth() ), rhs.m_value );

            if( lhs.m_value.getBitWidth() > rhs.m_value.getBitWidth() )
                return func( lhs.m_value, rhs.m_value.sext( lhs.m_value.getBitWidth() ) );

            return func( lhs.m_value, rhs.m_value );
        }

        template< typename F >
        static auto extendAndApplyOp(
            uint32_t wantedSize, const BigInt& lhs, const BigInt& rhs, F&& func )
        {
            return func( lhs.m_value.sext( wantedSize ), rhs.m_value.sext( wantedSize ) );
        }

      public:
        auto operator==( const BigInt& rhs ) const
        {
            return applyOpOnMixedSizes(
                *this, rhs, []( const APInt& l, const APInt& r ) { return l == r; } );
        }

        auto operator!=( const BigInt& rhs ) const
        {
            return applyOpOnMixedSizes(
                *this, rhs, []( const APInt& l, const APInt& r ) { return l != r; } );
        }

        auto operator>( const BigInt& rhs ) const
        {
            return applyOpOnMixedSizes(
                *this, rhs, []( const APInt& l, const APInt& r ) { return l.sgt( r ); } );
        }

        auto operator>=( const BigInt& rhs ) const
        {
            return applyOpOnMixedSizes(
                *this, rhs, []( const APInt& l, const APInt& r ) { return l.sge( r ); } );
        }

        auto operator<( const BigInt& rhs ) const
        {
            return applyOpOnMixedSizes(
                *this, rhs, []( const APInt& l, const APInt& r ) { return l.slt( r ); } );
        }

        auto operator<=( const BigInt& rhs ) const
        {
            return applyOpOnMixedSizes(
                *this, rhs, []( const APInt& l, const APInt& r ) { return l.sle( r ); } );
        }

        BigInt operator&( const BigInt& rhs ) const
        {
            return applyOpOnMixedSizes(
                *this, rhs, []( const APInt& l, const APInt& r ) { return l & r; } );
        }

        BigInt operator|( const BigInt& rhs ) const
        {
            return applyOpOnMixedSizes(
                *this, rhs, []( const APInt& l, const APInt& r ) { return l | r; } );
        }

        BigInt operator^( const BigInt& rhs ) const
        {
            return applyOpOnMixedSizes(
                *this, rhs, []( const APInt& l, const APInt& r ) { return l ^ r; } );
        }

        BigInt shl( const APSInt& rhs ) const
        {
            return sext( getSignificantBits() + rhs.getLimitedValue() ).shl( rhs );
        }

        BigInt ashr( const APSInt& rhs ) const { return m_value.ashr( rhs ); }




        BigInt lshr( const APSInt& rhs ) const { return m_value.lshr( rhs ); }




        BigInt operator+( const BigInt& rhs ) const
        {

            return extendAndApplyOp( max( getSignificantBits(), rhs.getSignificantBits() ) + 1,

                *this, rhs, []( const APInt& l, const APInt& r ) { return l + r; } );
        }

        BigInt operator-( const BigInt& rhs ) const
        {

            return extendAndApplyOp( max( getSignificantBits(), rhs.getSignificantBits() ) + 1,

                *this, rhs, []( const APInt& l, const APInt& r ) { return l - r; } );
        }

        BigInt operator*( const BigInt& rhs ) const
        {

            return extendAndApplyOp( getSignificantBits() + rhs.getSignificantBits(), *this, rhs,

                []( const APInt& l, const APInt& r ) { return l * r; } );
        }

        BigInt operator/( const BigInt& rhs ) const
        {
            return applyOpOnMixedSizes(
                *this, rhs, []( const APInt& l, const APInt& r ) { return l.sdiv( r ); } );
        }

        BigInt operator%( const BigInt& rhs ) const
        {
            return applyOpOnMixedSizes(
                *this, rhs, []( const APInt& l, const APInt& r ) { return l.srem( r ); } );
        }

      private:
        llvm::APSInt m_value;
    };
} // namespace goose::util

namespace std
{
    template<> struct hash< goose::util::BigInt >
    {
        size_t operator()( const goose::util::BigInt& x ) const
        {
            return llvm::hash_value( x.getAPSInt() );
        }
    };
} // namespace std

#endif
Changes to bs/util/depgraph.cpp.
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
        if( haveUnmetDependencies( dependee ) )
            return;

        llvm::SmallVector< const void*, 8 > pendingDeps;

        auto [first, last] = m_reverseDeps.equal_range( dependee );

        for( auto&& [_,depender] : ranges::subrange( first, last ) )
        {
            m_deps.erase( depender );

            if( m_deps.count( depender ) == 0 )
                pendingDeps.emplace_back( depender );
        }








|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
        if( haveUnmetDependencies( dependee ) )
            return;

        llvm::SmallVector< const void*, 8 > pendingDeps;

        auto [first, last] = m_reverseDeps.equal_range( dependee );

        for( auto&& [_, depender] : ranges::subrange( first, last ) )
        {
            m_deps.erase( depender );

            if( m_deps.count( depender ) == 0 )
                pendingDeps.emplace_back( depender );
        }

35
36
37
38
39
40
41
42
            satisfy( dep );
    }

    bool DependencyGraph::haveUnmetDependencies( const void* depender ) const
    {
        return m_deps.count( depender ) != 0;
    }
}







|
35
36
37
38
39
40
41
42
            satisfy( dep );
    }

    bool DependencyGraph::haveUnmetDependencies( const void* depender ) const
    {
        return m_deps.count( depender ) != 0;
    }
} // namespace goose::util
Changes to bs/util/depgraph.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
#ifndef GOOSE_UTIL_DEPGRAPH_H
#define GOOSE_UTIL_DEPGRAPH_H

namespace goose::util
{
    class DependencyGraph
    {
        public:
            template< typename F >
            void setCallBack( const void* depender, F&& callback )
            {
                m_callbacks.emplace( depender, callback );
            }

            void add( const void* depender, const void* dependee );
            void satisfy( const void* dependee );
            bool haveUnmetDependencies( const void* depender ) const;

            bool empty() const { return m_deps.empty(); }

            // TODO add something to detect and diagnose cyclic dependencies

        private:
            unordered_multimap< const void*, const void* > m_deps;
            unordered_multimap< const void*, const void* > m_reverseDeps;

            using callback = function< void() >;
            unordered_map< const void*, callback > m_callbacks;
    };
}


#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
#ifndef GOOSE_UTIL_DEPGRAPH_H
#define GOOSE_UTIL_DEPGRAPH_H

namespace goose::util
{
    class DependencyGraph
    {
      public:

        template< typename F > void setCallBack( const void* depender, F&& callback )
        {
            m_callbacks.emplace( depender, callback );
        }

        void add( const void* depender, const void* dependee );
        void satisfy( const void* dependee );
        bool haveUnmetDependencies( const void* depender ) const;

        bool empty() const { return m_deps.empty(); }

        // TODO add something to detect and diagnose cyclic dependencies

      private:
        unordered_multimap< const void*, const void* > m_deps;
        unordered_multimap< const void*, const void* > m_reverseDeps;

        using callback = function< void() >;
        unordered_map< const void*, callback > m_callbacks;
    };

} // namespace goose::util

#endif
Changes to bs/util/fixedint.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
#ifndef GOOSE_UTIL_FIXEDINT_H
#define GOOSE_UTIL_FIXEDINT_H

namespace goose::util
{
    using llvm::APInt;
    using llvm::APSInt;

    // This is a wrapper around llvm::APSInt used to represent compile time integer with "fixed" width.

    //
    // The only thing that it does is to provide equality comparison even when signedness/bit size differ,
    // so that we can put them in hash maps.
    class FixedInt : public APSInt
    {
        public:
            template< typename... T >
            FixedInt( T&&... args ) : APSInt( forward< T >( args )... ) {}




            FixedInt( const FixedInt& rhs ) = default;
            FixedInt( FixedInt&& rhs ) = default;

            FixedInt& operator=( const FixedInt& ) = default;
            FixedInt& operator=( FixedInt&& ) = default;

            bool operator==( const FixedInt& rhs ) const
            {
                return isUnsigned() == rhs.isUnsigned()
                    && getBitWidth() == rhs.getBitWidth()
                    && eq( rhs );
            }
    };

}

namespace std
{
    template<> struct hash< goose::util::FixedInt >
    {
        size_t operator()( const goose::util::FixedInt& x ) const
        {
            return llvm::hash_value( x );
        }
    };
}

#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
#ifndef GOOSE_UTIL_FIXEDINT_H
#define GOOSE_UTIL_FIXEDINT_H

namespace goose::util
{
    using llvm::APInt;
    using llvm::APSInt;

    // This is a wrapper around llvm::APSInt used to represent compile time integer with "fixed"
    // width.
    //
    // The only thing that it does is to provide equality comparison even when signedness/bit size
    // differ, so that we can put them in hash maps.
    class FixedInt : public APSInt
    {
      public:
        template< typename... T >
        FixedInt( T&&... args ) :
            APSInt( forward< T >( args )... )
        {
        }

        FixedInt( const FixedInt& rhs ) = default;
        FixedInt( FixedInt&& rhs ) = default;

        FixedInt& operator=( const FixedInt& ) = default;
        FixedInt& operator=( FixedInt&& ) = default;

        bool operator==( const FixedInt& rhs ) const
        {
            return isUnsigned() == rhs.isUnsigned() && getBitWidth() == rhs.getBitWidth()

                && eq( rhs );
        }
    };
} // namespace goose::util


namespace std
{
    template<> struct hash< goose::util::FixedInt >
    {
        size_t operator()( const goose::util::FixedInt& x ) const { return llvm::hash_value( x ); }



    };
} // namespace std

#endif
Changes to bs/util/generator.h.
1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

25

26

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

90

91
92
93
94
95
96
97
98
99
100
101
102
103
104

105

106
107
108
109
110
111
112
113
114
115
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
#ifndef GOOSE_UTIL_GENERATOR_H
#define GOOSE_UTIL_GENERATOR_H

namespace goose::util
{
    // A generator have a source: the coroutine that it currently needs to resume to obtain a new value.
    // We say that this coroutine currently have control (it is where the whole interruptible call tree continues)
    // A generator also have a parent: the parent coroutine, to which control should be given (back) upon completion
    // of the current control coroutine. (ie "where to return to")


    // A promise have a consumer: this is the generator currently consuming our coro's output.

    // When yielding a value, this value is passed to our consummer.

    // When yielding a generator, we should transfer control to that generator's source coroutine
    // by setting its parent to the current coro, and making the generator's source the source of
    // our consumer.
    template< typename T >
    class Generator
    {
        public:
            struct suspend
            {
                bool ready = false;

                bool await_ready() const noexcept { return ready; }

                constexpr void await_suspend( coroutine_handle<> ) const noexcept {}

                constexpr void await_resume() const noexcept {}
            };

            struct promise_type
            {
                auto initial_suspend() noexcept
                {
                    // We suspend the coro at the start because if it is a nested generator that
                    // is yielded by value, we need a chance to setup the necessary control transfer
                    // links before we start it.
                    return suspend_always();
                }

                auto final_suspend() noexcept
                {
                    m_state = State::Returned;
                    return suspend_never();
                }

                void unhandled_exception() noexcept
                {
                    terminate();
                }

                auto get_return_object() noexcept
                {
                    return Generator( *this );
                }

                // Yield a T
                auto yield_value( T&& v ) noexcept
                {
                    m_state = State::Ready;
                    m_pResult = &v;
                    return suspend_always();
                }

                auto yield_value( const T& v ) noexcept
                {
                    m_state = State::Ready;
                    m_pResult = &v;
                    return suspend_always();
                }

                // Yield a nested generator (to be used when we want to pass everything
                // yielded from one generator to our caller as is)
                suspend yield_value( Generator&& g ) noexcept
                {
                    m_state = State::Yielding;

                    // Our consumer becomes the consumer of the nested coroutine.
                    auto& other_p = g.m_source.promise();
                    other_p.m_pConsumer = m_pConsumer;
                    m_pConsumer->m_source = g.m_source;
                    g.m_source = nullptr;

                    auto* otherParentCaller = &other_p;
                    while( otherParentCaller->m_returnCoro )
                        otherParentCaller = &otherParentCaller->m_returnCoro.promise();

                    assert( !otherParentCaller->m_returnCoro );

                    // Set ourself up as the coroutine to transfer control to once the nested one is finished.

                    otherParentCaller->m_returnCoro = coroutine_handle< promise_type >::from_promise( *this );

                    assert( otherParentCaller->m_returnCoro );

                    // For our consumer the fact that its next values are coming from a nested coroutine is
                    // transparent, so resume the nested coroutine right away to give it the next value that
                    // it is already expecting.
                    // Unless it has already been resumed, for instance to compare iterators to check whether
                    // the nested coroutine yielded anything.
                    if( other_p.m_state == State::InitialSuspend )
                        coroutine_handle< promise_type >::from_promise( other_p ).resume();

                    m_state = State::Ready;

                    // If the nested coroutine was an empty generator, it will have set us back as the source
                    // for the consumer generator, but couldn't resume us directly since we are already running.

                    // In this case, simply request not to be suspended now so that we can proceed.

                    return { m_pConsumer->m_source == coroutine_handle< promise_type >::from_promise( *this ) };
                }

                void return_void()
                {
                    m_state = State::Returned;
                }

                auto& value() { return *m_pResult; }

                ~promise_type()
                {
                    m_pConsumer->m_source = m_returnCoro;

                    if( m_returnCoro  )
                    {
                        // Set the consumer back to the caller coroutine: it may
                        // have been moved since the last time our caller was
                        // active.
                        m_returnCoro.promise().m_pConsumer = m_pConsumer;

                        if( m_state == State::Returned
                            && m_returnCoro.promise().m_state != State::Yielding )
                        {
                            m_returnCoro.resume();
                        }
                    }
                }

                T* m_pResult = nullptr;

                // The generator currently consuming our results.
                Generator* m_pConsumer = nullptr;

                // Coroutine to transfer control back to once we end, if any.
                coroutine_handle< promise_type > m_returnCoro;

                // Indicate if we have encountered a final return in the coroutine,
                // In which case, upon destruction of this promise, we'll resume
                // the return coro, if any.

                enum class State
                {
                    InitialSuspend, // At initial suspend, waiting to be started once everything is set up

                    Ready,
                    Returned,       // Encountered a return, meaning its calling generator should be resumed

                    Yielding,       // Inside of yield_value, which means that it can't be resumed at the moment

                };

                State m_state = State::InitialSuspend;
            };

            class iterator
            {
                public:
                    using iterator_category = input_iterator_tag;

                    using difference_type = size_t;
                    using value_type = remove_reference_t< T >;
                    using reference = value_type&;
                    using pointer = value_type*;

                    iterator( Generator* pGen ) :
                        m_pGenerator( pGen )
                    {}

                    bool operator==( const iterator& rhs ) const
                    {
                        return coro() == rhs.coro();
                    }

                    bool operator!=( const iterator& rhs ) const
                    {
                        return coro() != rhs.coro();
                    }

                    iterator& operator++()
                    {
                        assert( m_pGenerator );
                        assert( m_pGenerator->m_source );

                        m_pGenerator->m_source.resume();

                        if( !m_pGenerator->m_source )
                            m_pGenerator = nullptr;

                        return *this;
                    }

                    reference operator*() const noexcept
                    {
                        assert( m_pGenerator );
                        assert( m_pGenerator->m_source );
                        return m_pGenerator->m_source.promise().value();
                    }

                    pointer operator->() const noexcept
                    {
                        return addressof( operator*() );
                    }

                private:
                    coroutine_handle< promise_type > coro() const
                    {
                        if( !m_pGenerator )
                            return {};

                        if( m_pGenerator->m_source.done() )
                            return {};

                        return m_pGenerator->m_source;
                    }

                    Generator* m_pGenerator = nullptr;
            };

            bool finished() const
            {
                return !m_source || m_source.done();
            }

            // Consume the next result from the generator
            // (will CRASH if called on a finished() generator)
            T consume()
            {
                assert( !finished() );

                // This is fine.
                // FOR A CLOWN
                // TO CONSUME DATA FROM THE CIRCUS' GENERATOR
                auto it = begin();
                auto result = *it;
                ++it;
                return result;
            }

            // should be called "current()" really, but it's easier like this
            iterator begin()
            {
                if( !m_source )
                    return end();

                // Since the coro starts suspended, we have to kick it
                // to obtain the first element if it hasn't been started
                // yet.
                if( m_source.promise().m_state == promise_type::State::InitialSuspend )
                {
                    m_source.resume();
                    if( !m_source )
                        return end();
                }

                return iterator( this );
            }

            iterator end() noexcept
            {
                return iterator( nullptr );
            }

            Generator() {}

            Generator( Generator&& rhs ) :
                m_source( rhs.m_source )
            {
                assert( m_source );
                *this = move( rhs );
            }

            ~Generator()
            {
                // If a generator is destroyed before consuming all of its content,
                // make sure that the coroutine of every nested generators currently
                // active are destroyed starting from the leaf (ie in the same order
                // that c++ function scopes are normally deleted)
                while( m_source )
                {
                    auto src = m_source;
                    m_source = nullptr;
                    src.destroy();
                }
            }

            // Move operator: if the source is the current consumer
            // of the source coroutine, update the consumer pointer
            // in that coro's promise.
            Generator& operator=( Generator&& rhs )
            {
                m_source = rhs.m_source;

                if( !m_source )
                    return *this;

                rhs.m_source = nullptr;

                auto& p = m_source.promise();
                if( p.m_pConsumer == &rhs )
                    p.m_pConsumer = this;

                return *this;
            }

        private:
            Generator( const Generator& ) = delete;

            Generator( promise_type& p ) :
                m_source( coroutine_handle< promise_type >::from_promise( p ) )
            {
                assert( m_source );
                p.m_pConsumer = this;
            }

            coroutine_handle< promise_type > m_source;
    };
}

#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
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
#ifndef GOOSE_UTIL_GENERATOR_H
#define GOOSE_UTIL_GENERATOR_H

namespace goose::util
{
    // A generator have a source: the coroutine that it currently needs to resume to obtain a new
    // value. We say that this coroutine currently have control (it is where the whole interruptible
    // call tree continues) A generator also have a parent: the parent coroutine, to which control
    // should be given (back) upon completion of the current control coroutine. (ie "where to return
    // to")

    // A promise have a consumer: this is the generator currently consuming our coro's output.

    // When yielding a value, this value is passed to our consummer.

    // When yielding a generator, we should transfer control to that generator's source coroutine
    // by setting its parent to the current coro, and making the generator's source the source of
    // our consumer.
    template< typename T > class Generator

    {
      public:
        struct suspend
        {
            bool ready = false;

            bool await_ready() const noexcept { return ready; }

            constexpr void await_suspend( coroutine_handle<> ) const noexcept {}

            constexpr void await_resume() const noexcept {}
        };

        struct promise_type
        {
            auto initial_suspend() noexcept
            {
                // We suspend the coro at the start because if it is a nested generator that
                // is yielded by value, we need a chance to setup the necessary control
                // transfer links before we start it.
                return suspend_always();
            }

            auto final_suspend() noexcept
            {
                m_state = State::Returned;
                return suspend_never();
            }

            void unhandled_exception() noexcept { terminate(); }




            auto get_return_object() noexcept { return Generator( *this ); }




            // Yield a T
            auto yield_value( T&& v ) noexcept
            {
                m_state = State::Ready;
                m_pResult = &v;
                return suspend_always();
            }

            auto yield_value( const T& v ) noexcept
            {
                m_state = State::Ready;
                m_pResult = &v;
                return suspend_always();
            }

            // Yield a nested generator (to be used when we want to pass everything
            // yielded from one generator to our caller as is)
            suspend yield_value( Generator&& g ) noexcept
            {
                m_state = State::Yielding;

                // Our consumer becomes the consumer of the nested coroutine.
                auto& other_p = g.m_source.promise();
                other_p.m_pConsumer = m_pConsumer;
                m_pConsumer->m_source = g.m_source;
                g.m_source = nullptr;

                auto* otherParentCaller = &other_p;
                while( otherParentCaller->m_returnCoro )
                    otherParentCaller = &otherParentCaller->m_returnCoro.promise();

                assert( !otherParentCaller->m_returnCoro );

                // Set ourself up as the coroutine to transfer control to once the nested
                // one is finished.
                otherParentCaller->m_returnCoro =
                    coroutine_handle< promise_type >::from_promise( *this );
                assert( otherParentCaller->m_returnCoro );

                // For our consumer the fact that its next values are coming from a nested
                // coroutine is transparent, so resume the nested coroutine right away to
                // give it the next value that it is already expecting. Unless it has
                // already been resumed, for instance to compare iterators to check whether
                // the nested coroutine yielded anything.
                if( other_p.m_state == State::InitialSuspend )
                    coroutine_handle< promise_type >::from_promise( other_p ).resume();

                m_state = State::Ready;

                // If the nested coroutine was an empty generator, it will have set us back
                // as the source for the consumer generator, but couldn't resume us directly
                // since we are already running. In this case, simply request not to be
                // suspended now so that we can proceed.
                return { m_pConsumer->m_source
                    == coroutine_handle< promise_type >::from_promise( *this ) };
            }

            void return_void() { m_state = State::Returned; }




            auto& value() { return *m_pResult; }

            ~promise_type()
            {
                m_pConsumer->m_source = m_returnCoro;

                if( m_returnCoro )
                {
                    // Set the consumer back to the caller coroutine: it may
                    // have been moved since the last time our caller was
                    // active.
                    m_returnCoro.promise().m_pConsumer = m_pConsumer;

                    if( m_state == State::Returned
                        && m_returnCoro.promise().m_state != State::Yielding )
                    {
                        m_returnCoro.resume();
                    }
                }
            }

            T* m_pResult = nullptr;

            // The generator currently consuming our results.
            Generator* m_pConsumer = nullptr;

            // Coroutine to transfer control back to once we end, if any.
            coroutine_handle< promise_type > m_returnCoro;

            // Indicate if we have encountered a final return in the coroutine,
            // In which case, upon destruction of this promise, we'll resume
            // the return coro, if any.

            enum class State
            {
                InitialSuspend, // At initial suspend, waiting to be started once everything
                                // is set up
                Ready,
                Returned, // Encountered a return, meaning its calling generator should be
                          // resumed
                Yielding, // Inside of yield_value, which means that it can't be resumed at
                          // the moment
            };

            State m_state = State::InitialSuspend;
        };

        class iterator
        {
          public:
            using iterator_category = input_iterator_tag;

            using difference_type = size_t;
            using value_type = remove_reference_t< T >;
            using reference = value_type&;
            using pointer = value_type*;

            iterator( Generator* pGen ) :
                m_pGenerator( pGen )

            {



            }

            bool operator==( const iterator& rhs ) const { return coro() == rhs.coro(); }

            bool operator!=( const iterator& rhs ) const { return coro() != rhs.coro(); }


            iterator& operator++()
            {
                assert( m_pGenerator );
                assert( m_pGenerator->m_source );

                m_pGenerator->m_source.resume();

                if( !m_pGenerator->m_source )
                    m_pGenerator = nullptr;

                return *this;
            }

            reference operator*() const noexcept
            {
                assert( m_pGenerator );
                assert( m_pGenerator->m_source );
                return m_pGenerator->m_source.promise().value();
            }

            pointer operator->() const noexcept { return addressof( operator*() ); }




          private:
            coroutine_handle< promise_type > coro() const
            {
                if( !m_pGenerator )
                    return {};

                if( m_pGenerator->m_source.done() )
                    return {};

                return m_pGenerator->m_source;
            }

            Generator* m_pGenerator = nullptr;
        };

        bool finished() const { return !m_source || m_source.done(); }




        // Consume the next result from the generator
        // (will CRASH if called on a finished() generator)
        T consume()
        {
            assert( !finished() );

            // This is fine.
            // FOR A CLOWN
            // TO CONSUME DATA FROM THE CIRCUS' GENERATOR
            auto it = begin();
            auto result = *it;
            ++it;
            return result;
        }

        // should be called "current()" really, but it's easier like this
        iterator begin()
        {
            if( !m_source )
                return end();

            // Since the coro starts suspended, we have to kick it
            // to obtain the first element if it hasn't been started
            // yet.
            if( m_source.promise().m_state == promise_type::State::InitialSuspend )
            {
                m_source.resume();
                if( !m_source )
                    return end();
            }

            return iterator( this );
        }

        iterator end() noexcept { return iterator( nullptr ); }




        Generator() {}

        Generator( Generator&& rhs ) :
            m_source( rhs.m_source )
        {
            assert( m_source );
            *this = move( rhs );
        }

        ~Generator()
        {
            // If a generator is destroyed before consuming all of its content,
            // make sure that the coroutine of every nested generators currently
            // active are destroyed starting from the leaf (ie in the same order
            // that c++ function scopes are normally deleted)
            while( m_source )
            {
                auto src = m_source;
                m_source = nullptr;
                src.destroy();
            }
        }

        // Move operator: if the source is the current consumer
        // of the source coroutine, update the consumer pointer
        // in that coro's promise.
        Generator& operator=( Generator&& rhs )
        {
            m_source = rhs.m_source;

            if( !m_source )
                return *this;

            rhs.m_source = nullptr;

            auto& p = m_source.promise();
            if( p.m_pConsumer == &rhs )
                p.m_pConsumer = this;

            return *this;
        }

      private:
        Generator( const Generator& ) = delete;

        Generator( promise_type& p ) :
            m_source( coroutine_handle< promise_type >::from_promise( p ) )
        {
            assert( m_source );
            p.m_pConsumer = this;
        }

        coroutine_handle< promise_type > m_source;
    };
} // namespace goose::util

#endif
Changes to bs/util/graphviz.cpp.
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
    const char* base09 = "dc9656";
    const char* base0A = "f7ca88";
    const char* base0B = "a1b56c";
    const char* base0C = "86c1b9";
    const char* base0D = "7cafc2";
    const char* base0E = "ba8baf";
    const char* base0F = "a16946";
}

namespace
{
    const char* accent_colors[] =
    {
        colors::base08,
        colors::base09,
        colors::base0A,
        colors::base0B,
        colors::base0C,
        colors::base0D,
        colors::base0E,
        colors::base0F
    };
}

namespace goose::util
{
    const char* GraphVizBuilder::BGColor = colors::base00;
    const char* GraphVizBuilder::TextColor = colors::base05;

    GraphVizBuilder::GraphVizBuilder( ostream& output, bool vertical ) :
        m_output( output )
    {
        m_output <<
            "digraph \"goose\"\n"
            "{\n"
            "\tbgcolor=\"#" << BGColor << "\";\n"



            "\trankdir=" << ( vertical ? "TB" : "LR" ) << ";\n"

            "\tnode [ shape=none ];\n"
            "\tedge [ color=\"#" << TextColor << "\" ];\n";

    }

    GraphVizBuilder::~GraphVizBuilder()
    {
        while( !m_workQueue.empty() )
        {
            m_workQueue.front()();







|



|
<
<
<
<
<
|
<
<
<
<










<
|
|
|
>
>
>
|
>
|
|
>







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
    const char* base09 = "dc9656";
    const char* base0A = "f7ca88";
    const char* base0B = "a1b56c";
    const char* base0C = "86c1b9";
    const char* base0D = "7cafc2";
    const char* base0E = "ba8baf";
    const char* base0F = "a16946";
} // namespace goose::util::colors

namespace
{
    const char* accent_colors[] = { colors::base08, colors::base09, colors::base0A, colors::base0B,





        colors::base0C, colors::base0D, colors::base0E, colors::base0F };




}

namespace goose::util
{
    const char* GraphVizBuilder::BGColor = colors::base00;
    const char* GraphVizBuilder::TextColor = colors::base05;

    GraphVizBuilder::GraphVizBuilder( ostream& output, bool vertical ) :
        m_output( output )
    {

        m_output << "digraph \"goose\"\n"
                    "{\n"
                    "\tbgcolor=\"#"
                 << BGColor
                 << "\";\n"
                    "\trankdir="
                 << ( vertical ? "TB" : "LR" )
                 << ";\n"
                    "\tnode [ shape=none ];\n"
                    "\tedge [ color=\"#"
                 << TextColor << "\" ];\n";
    }

    GraphVizBuilder::~GraphVizBuilder()
    {
        while( !m_workQueue.empty() )
        {
            m_workQueue.front()();
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
    {
        m_output << '\n';
        return indent();
    }

    const char* GraphVizBuilder::GetNodeColor( uint32_t id )
    {
        return accent_colors[ id % ( sizeof( accent_colors ) / sizeof( const char* ) ) ];
    }

    uint32_t GraphVizBuilder::getNodeId( const void* pNodeAddress )
    {
        auto it = m_nodesDict.find( pNodeAddress );
        if( it != m_nodesDict.end() )
            return it->second;







|







73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
    {
        m_output << '\n';
        return indent();
    }

    const char* GraphVizBuilder::GetNodeColor( uint32_t id )
    {
        return accent_colors[id % ( sizeof( accent_colors ) / sizeof( const char* ) )];
    }

    uint32_t GraphVizBuilder::getNodeId( const void* pNodeAddress )
    {
        auto it = m_nodesDict.find( pNodeAddress );
        if( it != m_nodesDict.end() )
            return it->second;
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
        return m_generatedNodes.find( pNodeAddress ) != m_generatedNodes.end();
    }

    void GraphVizBuilder::addEdge( uint32_t fromId, uint32_t toId )
    {
        auto portId = m_portCount - 1;

        queueWork( [=, this]

        {
            newLine()
                << "object" << fromId << ':' << portId
                << " -> object" << toId
                << "[ color=\"#" << GetNodeColor( toId ) << "\" ]";
        } );
    }

    GraphVizBuilder::Indention::Indention( GraphVizBuilder& builder ) :
        m_builder( builder )
    {
        ++m_builder.m_indention;
    }

    GraphVizBuilder::Indention::~Indention()
    {
        --m_builder.m_indention;
    }

    GraphVizBuilder::Table::Table( GraphVizBuilder& builder, const char* pColor ) :
        m_builder( builder )
    {
        m_builder.newLine() <<
            "<table border=\"0\" cellborder=\"1\" cellspacing=\"0\""
            " color=\"#" << pColor << "\">";


        ++m_builder.m_indention;
    }

    GraphVizBuilder::Table::~Table()
    {
        --m_builder.m_indention;







|
>
|
<
|
<
|
|
















<
|
|
>







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
        return m_generatedNodes.find( pNodeAddress ) != m_generatedNodes.end();
    }

    void GraphVizBuilder::addEdge( uint32_t fromId, uint32_t toId )
    {
        auto portId = m_portCount - 1;

        queueWork(
            [=, this]
            {

                newLine() << "object" << fromId << ':' << portId << " -> object" << toId

                          << "[ color=\"#" << GetNodeColor( toId ) << "\" ]";
            } );
    }

    GraphVizBuilder::Indention::Indention( GraphVizBuilder& builder ) :
        m_builder( builder )
    {
        ++m_builder.m_indention;
    }

    GraphVizBuilder::Indention::~Indention()
    {
        --m_builder.m_indention;
    }

    GraphVizBuilder::Table::Table( GraphVizBuilder& builder, const char* pColor ) :
        m_builder( builder )
    {

        m_builder.newLine() << "<table border=\"0\" cellborder=\"1\" cellspacing=\"0\""
                               " color=\"#"
                            << pColor << "\">";

        ++m_builder.m_indention;
    }

    GraphVizBuilder::Table::~Table()
    {
        --m_builder.m_indention;
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
        --m_builder.m_indention;
        m_builder.newLine() << "</td>";
    }

    GraphVizBuilder::Color::Color( GraphVizBuilder& builder, const char* pColor ) :
        m_builder( builder )
    {
        m_builder.newLine()
            << "<font face=\"Noto Sans\" point-size=\"12\" color=\"#" << pColor << "\">";


        ++m_builder.m_indention;
    }

    GraphVizBuilder::Color::~Color()
    {
        --m_builder.m_indention;
        m_builder.newLine() << "</font>";
    }


    GraphVizBuilder::Node::Node( GraphVizBuilder& builder, const void* pAddress, const char* pTitle ) :
        m_builder( builder )
    {
        m_builder.m_generatedNodes.insert( pAddress );
        m_builder.m_currentNode = m_builder.getNodeId( pAddress );

        m_builder.newLine() << "object" << m_builder.m_currentNode;
        m_builder.newLine() << "[ label=<";

        ++m_builder.m_indention;

        auto color = GetNodeColor( m_builder.m_currentNode ) ;

        m_builder.newLine() <<
            "<table border=\"0\" cellborder=\"1\" cellspacing=\"0\""
            " color=\"#" << color << "\">";


        ++m_builder.m_indention;

        if( pTitle )
        {
            Row row( m_builder );
            Cell cell( m_builder );







<
|
>










>
|










|

<
|
|
>







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
        --m_builder.m_indention;
        m_builder.newLine() << "</td>";
    }

    GraphVizBuilder::Color::Color( GraphVizBuilder& builder, const char* pColor ) :
        m_builder( builder )
    {

        m_builder.newLine() << "<font face=\"Noto Sans\" point-size=\"12\" color=\"#" << pColor
                            << "\">";

        ++m_builder.m_indention;
    }

    GraphVizBuilder::Color::~Color()
    {
        --m_builder.m_indention;
        m_builder.newLine() << "</font>";
    }

    GraphVizBuilder::Node::Node(
        GraphVizBuilder& builder, const void* pAddress, const char* pTitle ) :
        m_builder( builder )
    {
        m_builder.m_generatedNodes.insert( pAddress );
        m_builder.m_currentNode = m_builder.getNodeId( pAddress );

        m_builder.newLine() << "object" << m_builder.m_currentNode;
        m_builder.newLine() << "[ label=<";

        ++m_builder.m_indention;

        auto color = GetNodeColor( m_builder.m_currentNode );


        m_builder.newLine() << "<table border=\"0\" cellborder=\"1\" cellspacing=\"0\""
                               " color=\"#"
                            << color << "\">";

        ++m_builder.m_indention;

        if( pTitle )
        {
            Row row( m_builder );
            Cell cell( m_builder );
215
216
217
218
219
220
221
222
    {
        --m_builder.m_indention;
        m_builder.newLine() << "</table>";

        --m_builder.m_indention;
        m_builder.newLine() << "> ]";
    }
}







|
210
211
212
213
214
215
216
217
    {
        --m_builder.m_indention;
        m_builder.newLine() << "</table>";

        --m_builder.m_indention;
        m_builder.newLine() << "> ]";
    }
} // namespace goose::util
Changes to bs/util/graphviz.h.
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
        extern const char* base09;
        extern const char* base0A;
        extern const char* base0B;
        extern const char* base0C;
        extern const char* base0D;
        extern const char* base0E;
        extern const char* base0F;
    }

    // Helper class to dump data structures into a graphiz dot file.
    class GraphVizBuilder
    {
        public:
            GraphVizBuilder( ostream& output, bool vertical = false );
            ~GraphVizBuilder();

            ostream& indent() const;
            ostream& newLine() const;

            ostream& output() const { return m_output; }

            static const char* BGColor;
            static const char* TextColor;

            static const char* GetNodeColor( uint32_t id );
            uint32_t getNodeId( const void* pNodeAddress );

            uint32_t currentNodeId() const { return m_currentNode; }

            bool wasNodeGenerated( const void* pNodeAddress ) const;

            template< typename F > void queueWork( F&& func )
            {
                m_workQueue.push( forward< F >( func ) );
            }

            void addEdge( uint32_t fromId, uint32_t toId );

            class Indention
            {
                public:
                    Indention( GraphVizBuilder& builder );
                    ~Indention();

                private:
                    GraphVizBuilder& m_builder;
            };

            class Table
            {
                public:
                    Table( GraphVizBuilder& builder, const char* pColor = TextColor );
                    ~Table();

                private:
                    GraphVizBuilder& m_builder;
            };

            class Row
            {
                public:
                    Row( GraphVizBuilder& builder );
                    ~Row();

                private:
                    GraphVizBuilder& m_builder;
            };

            class Cell
            {
                public:
                    Cell( GraphVizBuilder& builder );
                    ~Cell();

                private:
                    GraphVizBuilder& m_builder;
            };

            class Color
            {
                public:
                    Color( GraphVizBuilder& builder, const char* pColor = TextColor );
                    ~Color();

                private:
                    GraphVizBuilder& m_builder;
            };

            class Node
            {
                public:
                    Node( GraphVizBuilder& builder, const void* pAddress, const char* pTitle = nullptr );
                    ~Node();

                private:
                    GraphVizBuilder& m_builder;
            };

        private:
            ostream&                                m_output;
            unordered_map< const void*, uint32_t >  m_nodesDict;
            unordered_set< const void* >            m_generatedNodes;
            queue< function< void() > >             m_workQueue;

            uint32_t                                m_indention = 1;
            uint32_t                                m_nodeCount = 0;
            uint32_t                                m_currentNode = 0;
            uint32_t                                m_portCount = 0;
    };
}

#endif







|




|
|
|

|
|
>
|

|
|

|
|
>
|

|

|
|
|
|

|

|
|
|
|
|

|
|
|

|
|
|
|
|

|
|
|

|
|
|
|
|

|
|
|

|
|
|
|
|

|
|
|

|
|
|
|
|

|
|
|

|
|
|
|
|

|
|
|

|
|
|
|
|

|
|
|
|

|


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
        extern const char* base09;
        extern const char* base0A;
        extern const char* base0B;
        extern const char* base0C;
        extern const char* base0D;
        extern const char* base0E;
        extern const char* base0F;
    } // namespace colors

    // Helper class to dump data structures into a graphiz dot file.
    class GraphVizBuilder
    {
      public:
        GraphVizBuilder( ostream& output, bool vertical = false );
        ~GraphVizBuilder();

        ostream& indent() const;
        ostream& newLine() const;

        ostream& output() const { return m_output; }

        static const char* BGColor;
        static const char* TextColor;

        static const char* GetNodeColor( uint32_t id );
        uint32_t getNodeId( const void* pNodeAddress );

        uint32_t currentNodeId() const { return m_currentNode; }

        bool wasNodeGenerated( const void* pNodeAddress ) const;

        template< typename F > void queueWork( F&& func )
        {
            m_workQueue.push( forward< F >( func ) );
        }

        void addEdge( uint32_t fromId, uint32_t toId );

        class Indention
        {
          public:
            Indention( GraphVizBuilder& builder );
            ~Indention();

          private:
            GraphVizBuilder& m_builder;
        };

        class Table
        {
          public:
            Table( GraphVizBuilder& builder, const char* pColor = TextColor );
            ~Table();

          private:
            GraphVizBuilder& m_builder;
        };

        class Row
        {
          public:
            Row( GraphVizBuilder& builder );
            ~Row();

          private:
            GraphVizBuilder& m_builder;
        };

        class Cell
        {
          public:
            Cell( GraphVizBuilder& builder );
            ~Cell();

          private:
            GraphVizBuilder& m_builder;
        };

        class Color
        {
          public:
            Color( GraphVizBuilder& builder, const char* pColor = TextColor );
            ~Color();

          private:
            GraphVizBuilder& m_builder;
        };

        class Node
        {
          public:
            Node( GraphVizBuilder& builder, const void* pAddress, const char* pTitle = nullptr );
            ~Node();

          private:
            GraphVizBuilder& m_builder;
        };

      private:
        ostream& m_output;
        unordered_map< const void*, uint32_t > m_nodesDict;
        unordered_set< const void* > m_generatedNodes;
        queue< function< void() > > m_workQueue;

        uint32_t m_indention = 1;
        uint32_t m_nodeCount = 0;
        uint32_t m_currentNode = 0;
        uint32_t m_portCount = 0;
    };
} // namespace goose::util

#endif
Changes to bs/util/location.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "util.h"

namespace goose::util
{
    unordered_set< string > Location::ms_filenames;
    vector< Location >      Location::ms_locations;

    uint32_t LocationPoint::toLoc( uint32_t length ) const
    {
        return Location::Create( *this, length );
    }

    uint32_t LocationPoint::toLoc( const LocationPoint& end ) const





|







1
2
3
4
5
6
7
8
9
10
11
12
13
#include "util.h"

namespace goose::util
{
    unordered_set< string > Location::ms_filenames;
    vector< Location > Location::ms_locations;

    uint32_t LocationPoint::toLoc( uint32_t length ) const
    {
        return Location::Create( *this, length );
    }

    uint32_t LocationPoint::toLoc( const LocationPoint& end ) const
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
    }

    optional< Location > Location::Get( LocationId locationId )
    {
        if( locationId.invalid() )
            return nullopt;

        uint32_t index = ( locationId.index() & 0x7fffffff ) - 1;
        if( index >= ms_locations.size() )
            return nullopt;

        return ms_locations[index];
    }

    LocationId Location::CreateSpanningLocation( LocationId loc )







|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
    }

    optional< Location > Location::Get( LocationId locationId )
    {
        if( locationId.invalid() )
            return nullopt;

        uint32_t index = ( locationId.index() & 0x7fff'ffff ) - 1;
        if( index >= ms_locations.size() )
            return nullopt;

        return ms_locations[index];
    }

    LocationId Location::CreateSpanningLocation( LocationId loc )
98
99
100
101
102
103
104
105
            return false;

        if( l2end < l1->column() )
            return false;

        return true;
    }
}







|
98
99
100
101
102
103
104
105
            return false;

        if( l2end < l1->column() )
            return false;

        return true;
    }
} // namespace goose::util
Changes to bs/util/location.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19


20

21

22

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

41
42
43
44
45
46
47


48
49
50
51
52
53
54


55
56
57
58
59
60
61
62
63


64

65

66

67

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#ifndef GOOSE_UTIL_LOCATION_H
#define GOOSE_UTIL_LOCATION_H

namespace goose::util
{
    class LocationId;

    // Partial representation of a location, useful to construct a location
    // in two steps.
    class LocationPoint
    {
        public:
            LocationPoint( const string& filename, uint32_t offset, uint32_t line, uint32_t column ) :
                m_filename( &filename ),
                m_offset( offset ),
                m_line( line ),
                m_column( column )
            {}



            const auto& filename() const { return *m_filename; }

            auto offset() const { return m_offset; }

            auto line() const { return m_line; }

            auto column() const { return m_column; }

            uint32_t toLoc( uint32_t length = 1 ) const;
            uint32_t toLoc( const LocationPoint& end ) const;

        private:
            const string* m_filename = nullptr;

            uint32_t m_offset = 0;

            uint32_t m_line = 0;
            uint32_t m_column = 0;
    };

    class Location
    {
        public:
            Location( const string& filename, uint32_t offset, uint32_t length, uint32_t line, uint32_t column ) :

                m_filename( filename ),
                m_offset( offset ),
                m_length( length ),
                m_line( line ),
                m_column( column )
            {}



            Location( const LocationPoint& ls, uint32_t length ) :
                m_filename( ls.filename() ),
                m_offset( ls.offset() ),
                m_length( length ),
                m_line( ls.line() ),
                m_column( ls.column() )
            {}



            Location( const source_location& sloc ) :
                m_filename( GetCachedFilename( sloc.file_name() ) ),
                m_offset( ~0U ),
                m_length( 1 ),
                m_line( sloc.line() ),
                m_column( sloc.column() )
            {}



            const auto& filename() const { return m_filename; }

            auto offset() const { return m_offset; }

            auto length() const { return m_length; }

            auto line() const { return m_line; }

            auto column() const { return m_column; }

            bool isOffsetValid() const { return m_offset != ~0U; }

            static const string& GetCachedFilename( const string& filename );

            template< typename... T >
            static uint32_t Create( T&&... args )
            {
                ms_locations.emplace_back( forward< T >( args )... );
                return ms_locations.size();
            }

            static optional< Location > Get( LocationId locationId );

            // Given two input locations, create and return a location that spans both.
            // The two input locations must be from the same file.
            static LocationId CreateSpanningLocation( LocationId loc1, LocationId loc2 );

            static LocationId CreateSpanningLocation( LocationId loc );

            static LocationId CreateSpanningLocation();

            template< typename H, typename... T >
            static LocationId CreateSpanningLocation( H locHead, T... locTail );

            // Checks if two locations overlap. Only consider the first line spanned by
            // each location.
            static bool Overlaps( LocationId loc1, LocationId loc2 );

        private:
            const string& m_filename;

            uint32_t m_offset = 0;
            uint32_t m_length = 0;

            uint32_t m_line = 0;
            uint32_t m_column = 0;

            static unordered_set< string > ms_filenames;
            static vector< Location >      ms_locations;
    };
}

#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
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
#ifndef GOOSE_UTIL_LOCATION_H
#define GOOSE_UTIL_LOCATION_H

namespace goose::util
{
    class LocationId;

    // Partial representation of a location, useful to construct a location
    // in two steps.
    class LocationPoint
    {
      public:
        LocationPoint( const string& filename, uint32_t offset, uint32_t line, uint32_t column ) :
            m_filename( &filename ),
            m_offset( offset ),
            m_line( line ),
            m_column( column )

        {
        }

        const auto& filename() const { return *m_filename; }

        auto offset() const { return m_offset; }

        auto line() const { return m_line; }

        auto column() const { return m_column; }

        uint32_t toLoc( uint32_t length = 1 ) const;
        uint32_t toLoc( const LocationPoint& end ) const;

      private:
        const string* m_filename = nullptr;

        uint32_t m_offset = 0;

        uint32_t m_line = 0;
        uint32_t m_column = 0;
    };

    class Location
    {
      public:
        Location( const string& filename, uint32_t offset, uint32_t length, uint32_t line,
            uint32_t column ) :
            m_filename( filename ),
            m_offset( offset ),
            m_length( length ),
            m_line( line ),
            m_column( column )

        {
        }

        Location( const LocationPoint& ls, uint32_t length ) :
            m_filename( ls.filename() ),
            m_offset( ls.offset() ),
            m_length( length ),
            m_line( ls.line() ),
            m_column( ls.column() )

        {
        }

        Location( const source_location& sloc ) :
            m_filename( GetCachedFilename( sloc.file_name() ) ),
            m_offset( ~0U ),
            m_length( 1 ),
            m_line( sloc.line() ),
            m_column( sloc.column() )

        {
        }

        const auto& filename() const { return m_filename; }

        auto offset() const { return m_offset; }

        auto length() const { return m_length; }

        auto line() const { return m_line; }

        auto column() const { return m_column; }

        bool isOffsetValid() const { return m_offset != ~0U; }

        static const string& GetCachedFilename( const string& filename );

        template< typename... T > static uint32_t Create( T&&... args )

        {
            ms_locations.emplace_back( forward< T >( args )... );
            return ms_locations.size();
        }

        static optional< Location > Get( LocationId locationId );

        // Given two input locations, create and return a location that spans both.
        // The two input locations must be from the same file.
        static LocationId CreateSpanningLocation( LocationId loc1, LocationId loc2 );

        static LocationId CreateSpanningLocation( LocationId loc );

        static LocationId CreateSpanningLocation();

        template< typename H, typename... T >
        static LocationId CreateSpanningLocation( H locHead, T... locTail );

        // Checks if two locations overlap. Only consider the first line spanned by
        // each location.
        static bool Overlaps( LocationId loc1, LocationId loc2 );

      private:
        const string& m_filename;

        uint32_t m_offset = 0;
        uint32_t m_length = 0;

        uint32_t m_line = 0;
        uint32_t m_column = 0;

        static unordered_set< string > ms_filenames;
        static vector< Location > ms_locations;
    };
} // namespace goose::util

#endif
Changes to bs/util/location.inl.
1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef GOOSE_UTIL_LOCATION_INL
#define GOOSE_UTIL_LOCATION_INL

namespace goose::util
{
    template< typename H, typename... T >
    LocationId Location::CreateSpanningLocation( H locHead, T... locTail )
    {
        return CreateSpanningLocation( locHead, CreateSpanningLocation( locTail... ) );
    }
}

#endif










|


1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef GOOSE_UTIL_LOCATION_INL
#define GOOSE_UTIL_LOCATION_INL

namespace goose::util
{
    template< typename H, typename... T >
    LocationId Location::CreateSpanningLocation( H locHead, T... locTail )
    {
        return CreateSpanningLocation( locHead, CreateSpanningLocation( locTail... ) );
    }
} // namespace goose::util

#endif
Changes to bs/util/locationid.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
#ifndef GOOSE_UTIL_LOCATIONID_H
#define GOOSE_UTIL_LOCATIONID_H

namespace goose::util
{
    class LocationId
    {
        public:
            LocationId() = default;

            LocationId( uint32_t index ) : m_index( index ) {}




            bool invalid() const { return m_index == 0; }

            uint32_t index() const { return m_index; }

            bool isPoison() const { return m_index & 0x80000000; }

            void setPoison() { m_index |= 0x80000000; }

            auto operator<=>( const LocationId& ) const = default;

            static auto Poison( LocationId loc = Location::Create( source_location::current() ) )
            {
                return LocationId( loc.m_index | 0x800000000 );
            }

            friend ostream& operator<<( ostream& o, const LocationId& loc ) { return o << loc.m_index; }

        private:
            uint32_t m_index = 0;
    };

}

namespace std
{
    template<> struct hash< goose::util::LocationId >
    {
        size_t operator()( const goose::util::LocationId& x ) const
        {
            return x.index();
        }
    };
}

#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
#ifndef GOOSE_UTIL_LOCATIONID_H
#define GOOSE_UTIL_LOCATIONID_H

namespace goose::util
{
    class LocationId
    {
      public:
        LocationId() = default;

        LocationId( uint32_t index ) :
            m_index( index )
        {
        }

        bool invalid() const { return m_index == 0; }

        uint32_t index() const { return m_index; }

        bool isPoison() const { return m_index & 0x8000'0000; }

        void setPoison() { m_index |= 0x8000'0000; }

        auto operator<=>( const LocationId& ) const = default;

        static auto Poison( LocationId loc = Location::Create( source_location::current() ) )
        {
            return LocationId( loc.m_index | 0x8'0000'0000 );
        }

        friend ostream& operator<<( ostream& o, const LocationId& loc ) { return o << loc.m_index; }

      private:
        uint32_t m_index = 0;
    };
} // namespace goose::util


namespace std
{
    template<> struct hash< goose::util::LocationId >
    {
        size_t operator()( const goose::util::LocationId& x ) const { return x.index(); }



    };
} // namespace std

#endif
Changes to bs/util/profiling.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef GOOSE_UTIL_PROFILING_H
#define GOOSE_UTIL_PROFILING_H

// To avoid requiring the tracy dependency to be downloaded even when not building
// a profiling version, we provide dummy versions of the profiling macros here.
#ifdef TRACY_ENABLE
#include "public/tracy/Tracy.hpp"

#define ProfileZoneScoped ZoneScoped
#define ProfileZoneScopedN ZoneScopedN
#define ProfileZoneName ZoneName

#else

#define ProfileZoneScoped
#define ProfileZoneScopedN( n )
#define ProfileZoneName( n, s )

#endif

#endif






|

|
|
|



|
|
|




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef GOOSE_UTIL_PROFILING_H
#define GOOSE_UTIL_PROFILING_H

// To avoid requiring the tracy dependency to be downloaded even when not building
// a profiling version, we provide dummy versions of the profiling macros here.
#ifdef TRACY_ENABLE
    #include "public/tracy/Tracy.hpp"

    #define ProfileZoneScoped ZoneScoped
    #define ProfileZoneScopedN ZoneScopedN
    #define ProfileZoneName ZoneName

#else

    #define ProfileZoneScoped
    #define ProfileZoneScopedN( n )
    #define ProfileZoneName( n, s )

#endif

#endif
Changes to bs/util/stringid.cpp.
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
    auto it = ms_strings().find( s );
    if( it != ms_strings().end() )
    {
        m_id = it->second;

#ifndef NDEBUG
    m_dbgName = str().c_str();
#endif
        return;
    }

    m_id = ms_nextUniqueId();
    ms_strings().emplace( s, m_id );
    ms_IDs().emplace( m_id, s );







|







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
    auto it = ms_strings().find( s );
    if( it != ms_strings().end() )
    {
        m_id = it->second;

#ifndef NDEBUG
        m_dbgName = str().c_str();
#endif
        return;
    }

    m_id = ms_nextUniqueId();
    ms_strings().emplace( s, m_id );
    ms_IDs().emplace( m_id, s );
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
{
    auto it = ms_strings().find( s );
    if( it != ms_strings().end() )
    {
        m_id = it->second;

#ifndef NDEBUG
    m_dbgName = str().c_str();
#endif
        return;
    }

    m_id = ms_nextUniqueId();
    ms_strings().emplace( s, m_id );
    ms_IDs().emplace( m_id, move( s ) );







|







47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
{
    auto it = ms_strings().find( s );
    if( it != ms_strings().end() )
    {
        m_id = it->second;

#ifndef NDEBUG
        m_dbgName = str().c_str();
#endif
        return;
    }

    m_id = ms_nextUniqueId();
    ms_strings().emplace( s, m_id );
    ms_IDs().emplace( m_id, move( s ) );
Changes to bs/util/stringid.h.
1
2
3
4
5
6
7
8
9




10
11
12
13
14
15
16


17
18
19
20
21
22
23
24
25

26

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#ifndef GOOSE_UTIL_STRINGID_H
#define GOOSE_UTIL_STRINGID_H

namespace goose::util
{
    class StringId
    {
        public:
            StringId() : StringId( "" ) {}




            explicit StringId( const string& str );
            explicit StringId( string&& str );

            explicit StringId( const char* pString ) :
                StringId( string( pString ) )
            {}



            explicit StringId( const char* pString, size_t size ) :
                StringId( string( pString, size ) )
            {}

            explicit StringId( uint32_t uniqueId );

            auto operator<=>( StringId rhs ) const
            {
                return m_id <=> rhs.m_id;

            }


            auto operator==( StringId rhs ) const
            {
                return m_id == rhs.m_id;
            }

            auto operator<( StringId rhs ) const
            {
                return m_id < rhs.m_id;
            }

            const string& str() const;

            friend ostream& operator<<( ostream& out, StringId sid )
            {
                return out << sid.str();
            }

            bool isNumerical() const { return !( m_id & stringMask ); }
            auto id() const { return m_id; }

        private:
            static constexpr uint32_t stringMask = 1 << 31;
            uint32_t m_id = 0;

#ifndef NDEBUG
            // To be able to see the name in the debugger
            const char* m_dbgName = nullptr;
#endif

            static unordered_map< string, uint32_t >& ms_strings();
            static unordered_map< uint32_t, string >& ms_IDs();

            static uint32_t ms_nextUniqueId();
    };
}

namespace std
{
    template<> struct hash< goose::util::StringId >
    {
        size_t operator()( const goose::util::StringId& x ) const
        {
            if( x.isNumerical() )
                return hash< uint32_t >()( x.id() );
            return hash< string >()( x.str() );
        }
    };
}

static inline auto operator "" _sid( const char* pString, std::size_t s )
{
    return goose::util::StringId( pString, s );
}

#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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#ifndef GOOSE_UTIL_STRINGID_H
#define GOOSE_UTIL_STRINGID_H

namespace goose::util
{
    class StringId
    {
      public:
        StringId() :
            StringId( "" )
        {
        }

        explicit StringId( const string& str );
        explicit StringId( string&& str );

        explicit StringId( const char* pString ) :
            StringId( string( pString ) )

        {
        }

        explicit StringId( const char* pString, size_t size ) :
            StringId( string( pString, size ) )

        {

        }



        explicit StringId( uint32_t uniqueId );

        auto operator<=>( StringId rhs ) const { return m_id <=> rhs.m_id; }

        auto operator==( StringId rhs ) const { return m_id == rhs.m_id; }




        auto operator<( StringId rhs ) const { return m_id < rhs.m_id; }




        const string& str() const;

        friend ostream& operator<<( ostream& out, StringId sid ) { return out << sid.str(); }

        bool isNumerical() const { return !( m_id & stringMask ); }



        auto id() const { return m_id; }

      private:
        static constexpr uint32_t stringMask = 1 << 31;
        uint32_t m_id = 0;

#ifndef NDEBUG
        // To be able to see the name in the debugger
        const char* m_dbgName = nullptr;
#endif

        static unordered_map< string, uint32_t >& ms_strings();
        static unordered_map< uint32_t, string >& ms_IDs();

        static uint32_t ms_nextUniqueId();
    };
} // namespace goose::util

namespace std
{
    template<> struct hash< goose::util::StringId >
    {
        size_t operator()( const goose::util::StringId& x ) const
        {
            if( x.isNumerical() )
                return hash< uint32_t >()( x.id() );
            return hash< string >()( x.str() );
        }
    };
} // namespace std

static inline auto operator"" _sid( const char* pString, std::size_t s )
{
    return goose::util::StringId( pString, s );
}

#endif
Changes to bs/util/tests/generator.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
#include <catch2/catch_all.hpp>
#include "util/util.h"

using namespace std;
using namespace goose::util;

namespace
{
    // Sentinel object to verify that everything in every generator scopes
    // gets properly deleted
    class Sentinel
    {
        public:
            Sentinel() { ++ms_count; }

            ~Sentinel() { --ms_count; }

            static size_t Count() { return ms_count; }

        private:
            static size_t ms_count;
    };

    size_t Sentinel::ms_count = 0;

    // Simple generator outputing three strings
    Generator< string > TestGen3()
    {












|
|
>
|

|

|
|







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
#include <catch2/catch_all.hpp>
#include "util/util.h"

using namespace std;
using namespace goose::util;

namespace
{
    // Sentinel object to verify that everything in every generator scopes
    // gets properly deleted
    class Sentinel
    {
      public:
        Sentinel() { ++ms_count; }

        ~Sentinel() { --ms_count; }

        static size_t Count() { return ms_count; }

      private:
        static size_t ms_count;
    };

    size_t Sentinel::ms_count = 0;

    // Simple generator outputing three strings
    Generator< string > TestGen3()
    {
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

        for( auto&& x : TestGen3() )
            co_yield x + "!";

        co_yield "bar";
        co_yield EmptyGen();
        co_yield "blah";


        auto g = TestGen3();

        // Peeking at the content of a nested generator
        // before yielding it shouldn't affect the result.
        if( g.begin() == g.end() )
            co_return;







<







49
50
51
52
53
54
55

56
57
58
59
60
61
62

        for( auto&& x : TestGen3() )
            co_yield x + "!";

        co_yield "bar";
        co_yield EmptyGen();
        co_yield "blah";


        auto g = TestGen3();

        // Peeking at the content of a nested generator
        // before yielding it shouldn't affect the result.
        if( g.begin() == g.end() )
            co_return;
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
        auto gg = TestGen4();

        if( gg.begin() != gg.end() )
            co_yield "ok";

        co_yield move( gg );
    }
}

SCENARIO( "Generators work", "[generator]" )
{
    WHEN( "Consuming the test generator's content" )
    {
        vector< string > result;
        for( auto&& x : TestGen() )
            result.emplace_back( x );

        THEN( "The results are correct" )
        {
            REQUIRE( ( result == vector< string >
            {
                "fuck",
                "foo",
                "aa!",
                "ggggg!",
                "bbbb!",
                "bar",
                "blah",
                "aa",
                "ggggg",
                "bbbb",
                "shit"
            } ) );

            REQUIRE( Sentinel::Count() == 0 );
        }
    }

    WHEN( "Destroying a generator after consuming only part of its results" )
    {
        {
            auto g = TestGen();
            auto it = g.begin();
            advance( it, 3 );
        }

        THEN( "There should be no leak" )
        {
            REQUIRE( Sentinel::Count() == 0 );
        }
    }


    WHEN( "Yielding a generator whose coroutine already started and which yielded another nested generator" )

    {
        vector< string > result;
        for( auto&& x : TestGen5() )
            result.emplace_back( x );

        THEN( "The results are correct" )
        {
            REQUIRE( ( result == vector< string >
            {
                "ok",
                "aa",
                "ggggg",
                "bbbb",
                "fuck"
            } ) );

            REQUIRE( Sentinel::Count() == 0 );
        }
    }
}







|











|
<
<
<
<
|
<
<
<
<
|
<
<
<



















>
|
>







|
<
<
<
<
<
<
<





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
        auto gg = TestGen4();

        if( gg.begin() != gg.end() )
            co_yield "ok";

        co_yield move( gg );
    }
} // namespace

SCENARIO( "Generators work", "[generator]" )
{
    WHEN( "Consuming the test generator's content" )
    {
        vector< string > result;
        for( auto&& x : TestGen() )
            result.emplace_back( x );

        THEN( "The results are correct" )
        {
            REQUIRE( result




                == vector< string >{ "fuck", "foo", "aa!", "ggggg!", "bbbb!", "bar", "blah", "aa",




                    "ggggg", "bbbb", "shit" } );




            REQUIRE( Sentinel::Count() == 0 );
        }
    }

    WHEN( "Destroying a generator after consuming only part of its results" )
    {
        {
            auto g = TestGen();
            auto it = g.begin();
            advance( it, 3 );
        }

        THEN( "There should be no leak" )
        {
            REQUIRE( Sentinel::Count() == 0 );
        }
    }

    WHEN(
        "Yielding a generator whose coroutine already started and which yielded another nested "
        "generator" )
    {
        vector< string > result;
        for( auto&& x : TestGen5() )
            result.emplace_back( x );

        THEN( "The results are correct" )
        {
            REQUIRE( result == vector< string >{ "ok", "aa", "ggggg", "bbbb", "fuck" } );








            REQUIRE( Sentinel::Count() == 0 );
        }
    }
}
Changes to bs/util/util.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "util.h"
#include "diagnostics/diagnostics.h"

namespace goose::util
{
    using namespace diagnostics;

    uint32_t GenerateNewUID()
    {
        static uint32_t nextUid = 0x80000000;
        return nextUid++;
    }
}









|


|
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "util.h"
#include "diagnostics/diagnostics.h"

namespace goose::util
{
    using namespace diagnostics;

    uint32_t GenerateNewUID()
    {
        static uint32_t nextUid = 0x8000'0000;
        return nextUid++;
    }
} // namespace goose::util
Changes to bs/util/util.h.
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
    // since it's not in the standard yet... (based on
    // boost's hash_combine)
    template< typename A, typename B > struct hash< pair< A, B > >
    {
        template< typename P > size_t operator()( P&& x ) const
        {
            size_t result = hash< A >()( x.first );
            result ^= hash< B >()( x.second ) + 0x9e3779b9
                      + ( result << 6 ) + ( result >> 2 );
            return result;
        }
    };

    template<> struct hash< llvm::APSInt >
    {
        size_t operator()( const llvm::APSInt& x ) const
        {
            return llvm::hash_value( x );
        }
    };
}

namespace goose
{
    using namespace std;
}

#include "generator.h"
#include "stringid.h"
#include "fixedint.h"
#include "bigint.h"
#include "depgraph.h"
#include "location.h"
#include "locationid.h"
#include "location.inl"
#include "graphviz.h"

namespace goose::util
{
    // C++ representation of a compilation time sequence
    template< typename T >
    using Sequence = llvm::SmallVector< T, 4 >;

    template< class T, class CharT >
    struct SStreamFormatter : std::formatter< string, CharT >
    {
        template< class TT, class FormatContext >
        auto format( TT&& t, FormatContext& fc ) const
        {
            stringstream sstr;
            sstr << t;
            return std::formatter< string, CharT >::format( sstr.str(), fc );
        }
    };

    template< typename T >
    auto ComputeHash( const T& x )
    {
        return std::hash< T >()( x );
    }

    template< typename C >
    Generator< uint64_t > ContainerHashGenerator( const C& c )
    {
        for( const auto& x : c )
            co_yield ComputeHash( x );
    }

    template< typename T >
    class NullInit
    {
        public:
            NullInit() {}

            template< typename TT >
            NullInit( TT&& other ) :
                m_value( forward< TT >( other ) )
            {}

            operator T()

            {
                return m_value;
            }

            template< typename TT >
            NullInit& operator=( TT&& rhs )
            {
                m_value = forward< TT >( rhs );
                return *this;
            }

            T& operator->()
            {
                return m_value;
            }

            const T& operator->() const
            {
                return m_value;
            }

            T& operator*()
            {
                return m_value;
            }

            const T& operator*() const
            {
                return m_value;
            }

        private:
            T m_value = nullptr;
    };

    template< typename T >
    using ptr = shared_ptr< T >;

    template< typename T >
    using wptr = weak_ptr< T >;

    template< typename T >
    struct remove_sptr
    {
        using type = T;
    };

    template< typename T >
    struct remove_sptr< ptr< T > >
    {
        using type = T;
    };

    template< typename T >
    using remove_sptr_t = typename remove_sptr< T >::type;

    // moo
    template< typename T >
    static auto& CoW( ptr< T >& ptr )
    {
        if( ptr.use_count() > 1 )
            ptr = make_shared< T >( *ptr );
        return ptr;
    }

    template< typename T >
    struct is_optional : public false_type

    {};

    template< typename T >
    struct is_optional< optional< T > > : public true_type

    {};

    template< typename T >
    constexpr bool is_optional_v = is_optional< T >::value;

    template< typename T >
    struct remove_optional
    {
        using type = T;
    };

    template< typename T >
    struct remove_optional< optional< T > >
    {
        using type = T;
    };

    template< typename T >
    using remove_optional_t = typename remove_optional< T >::type;

    // FFS why can't is_convertible just tell me that a type isn't convertible to a variant
    // Just look at this garbage
    template< typename T, typename variant_type, int i = 0, int size = variant_size_v< variant_type > >

    struct SIsTypeInVariant : public
        conditional_t<
            is_same_v< T, variant_alternative_t< i, variant_type > >,
            true_type,
            SIsTypeInVariant< T, variant_type, i + 1 >
        >

    {};

    template< typename T, typename variant_type, int size >
    struct SIsTypeInVariant< T, variant_type, size, size > :
        public false_type

    {};

    template< typename T, typename variant_type >
    constexpr bool IsTypeInVariant = SIsTypeInVariant< T, variant_type >::value;

    // Simple observer pattern implementation. Can't unsubscribe, don't need it
    // for now anyway.
    template< typename... T >
    class Signal
    {
        public:
            void operator()( const T&... args )
            {
                for( auto&& o : m_observers )
                    o( args... );
            }

            template< typename F >
            void subscribe( F&& observer )
            {
                m_observers.emplace_back( forward< F >( observer ) );
            }

        private:
            vector< function< void( T... ) > > m_observers;
    };

    // Simple uid generator
    extern uint32_t GenerateNewUID();

    static inline bool IsUid( uint32_t x ) { return x & 0x80000000; }


}


#endif







|
<






|
<
<
<

|



















<
|

|
<

|
<







|
<




<
|





|
<

|
|

|
|
|
<
|
<
>
|
|
|
<
|
<
|
|
|
|

|
|
<
<
<
|
|
<
<
<
|
|
<
<
<
|
<
<
<

|
|


|
<

|
<

|
<




|
<




<
|


|
<






|
<
>
|

<
|
>
|

<
|

|
<




|
<




<
|



|
>
|
<
|
<
|
<
>
|


|
<
>
|






|
<

|
|
|
|
|
|

|
<
|
|
|

|
|





|
>
>
|
>


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
    // since it's not in the standard yet... (based on
    // boost's hash_combine)
    template< typename A, typename B > struct hash< pair< A, B > >
    {
        template< typename P > size_t operator()( P&& x ) const
        {
            size_t result = hash< A >()( x.first );
            result ^= hash< B >()( x.second ) + 0x9e37'79b9 + ( result << 6 ) + ( result >> 2 );

            return result;
        }
    };

    template<> struct hash< llvm::APSInt >
    {
        size_t operator()( const llvm::APSInt& x ) const { return llvm::hash_value( x ); }



    };
} // namespace std

namespace goose
{
    using namespace std;
}

#include "generator.h"
#include "stringid.h"
#include "fixedint.h"
#include "bigint.h"
#include "depgraph.h"
#include "location.h"
#include "locationid.h"
#include "location.inl"
#include "graphviz.h"

namespace goose::util
{
    // C++ representation of a compilation time sequence

    template< typename T > using Sequence = llvm::SmallVector< T, 4 >;

    template< class T, class CharT > struct SStreamFormatter : std::formatter< string, CharT >

    {
        template< class TT, class FormatContext > auto format( TT&& t, FormatContext& fc ) const

        {
            stringstream sstr;
            sstr << t;
            return std::formatter< string, CharT >::format( sstr.str(), fc );
        }
    };

    template< typename T > auto ComputeHash( const T& x )

    {
        return std::hash< T >()( x );
    }


    template< typename C > Generator< uint64_t > ContainerHashGenerator( const C& c )
    {
        for( const auto& x : c )
            co_yield ComputeHash( x );
    }

    template< typename T > class NullInit

    {
      public:
        NullInit() {}

        template< typename TT >
        NullInit( TT&& other ) :
            m_value( forward< TT >( other ) )

        {

        }

        operator T() { return m_value; }


        template< typename TT > NullInit& operator=( TT&& rhs )

        {
            m_value = forward< TT >( rhs );
            return *this;
        }

        T& operator->() { return m_value; }




        const T& operator->() const { return m_value; }




        T& operator*() { return m_value; }




        const T& operator*() const { return m_value; }




      private:
        T m_value = nullptr;
    };

    template< typename T > using ptr = shared_ptr< T >;


    template< typename T > using wptr = weak_ptr< T >;


    template< typename T > struct remove_sptr

    {
        using type = T;
    };

    template< typename T > struct remove_sptr< ptr< T > >

    {
        using type = T;
    };


    template< typename T > using remove_sptr_t = typename remove_sptr< T >::type;

    // moo
    template< typename T > static auto& CoW( ptr< T >& ptr )

    {
        if( ptr.use_count() > 1 )
            ptr = make_shared< T >( *ptr );
        return ptr;
    }

    template< typename T > struct is_optional : public false_type

    {
    };


    template< typename T > struct is_optional< optional< T > > : public true_type
    {
    };


    template< typename T > constexpr bool is_optional_v = is_optional< T >::value;

    template< typename T > struct remove_optional

    {
        using type = T;
    };

    template< typename T > struct remove_optional< optional< T > >

    {
        using type = T;
    };


    template< typename T > using remove_optional_t = typename remove_optional< T >::type;

    // FFS why can't is_convertible just tell me that a type isn't convertible to a variant
    // Just look at this garbage
    template< typename T, typename variant_type, int i = 0,
        int size = variant_size_v< variant_type > >
    struct SIsTypeInVariant

        : public conditional_t< is_same_v< T, variant_alternative_t< i, variant_type > >, true_type,

              SIsTypeInVariant< T, variant_type, i + 1 > >

    {
    };

    template< typename T, typename variant_type, int size >
    struct SIsTypeInVariant< T, variant_type, size, size > : public false_type

    {
    };

    template< typename T, typename variant_type >
    constexpr bool IsTypeInVariant = SIsTypeInVariant< T, variant_type >::value;

    // Simple observer pattern implementation. Can't unsubscribe, don't need it
    // for now anyway.
    template< typename... T > class Signal

    {
      public:
        void operator()( const T&... args )
        {
            for( auto&& o : m_observers )
                o( args... );
        }

        template< typename F > void subscribe( F&& observer )

        {
            m_observers.emplace_back( forward< F >( observer ) );
        }

      private:
        vector< function< void( T... ) > > m_observers;
    };

    // Simple uid generator
    extern uint32_t GenerateNewUID();

    static inline bool IsUid( uint32_t x )
    {
        return x & 0x8000'0000;
    }
} // namespace goose::util

#endif
Changes to bs/verify/basicblock.cpp.
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
        m_builder.convertedBBIndices().emplace( bbid );
        m_viz.addBlock( bbid, bb.index() );

        m_builder.setCurrentBB( bbid );

        optional< z3::expr > pred;

        m_remapper.forEachIncomingEdge( bbid, [&]( auto&& predIndex, auto&& expr )

        {
            auto edgeExpr = makeEdgeExpression( predIndex, bbid, expr );

            if( pred )
                pred = *pred || edgeExpr;
            else
                pred = edgeExpr;
        } );

        auto& c = GetZ3Context();

        auto predVar = c.bool_const( format( "b{}", bbid ).c_str() );
        if( pred )
            m_builder.add( predVar == *pred );
        m_builder.setCurrentBBPredicate( predVar );

        for( auto&& x : bb.instructions() )
        {
            if( !BuildZ3Op( m_builder, x ) )
                m_builder.setCheckFailed();
        }

        // We pass the non-remapped index here because handleTerminator will do its own remapping.
        if( bb.terminator() )
            handleTerminator( bb.index(), *bb.terminator() );
        return true;
    }
}







|
>
|
|

|
|
|
|
|









<


<






|
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
        m_builder.convertedBBIndices().emplace( bbid );
        m_viz.addBlock( bbid, bb.index() );

        m_builder.setCurrentBB( bbid );

        optional< z3::expr > pred;

        m_remapper.forEachIncomingEdge( bbid,
            [&]( auto&& predIndex, auto&& expr )
            {
                auto edgeExpr = makeEdgeExpression( predIndex, bbid, expr );

                if( pred )
                    pred = *pred || edgeExpr;
                else
                    pred = edgeExpr;
            } );

        auto& c = GetZ3Context();

        auto predVar = c.bool_const( format( "b{}", bbid ).c_str() );
        if( pred )
            m_builder.add( predVar == *pred );
        m_builder.setCurrentBBPredicate( predVar );

        for( auto&& x : bb.instructions() )

            if( !BuildZ3Op( m_builder, x ) )
                m_builder.setCheckFailed();


        // We pass the non-remapped index here because handleTerminator will do its own remapping.
        if( bb.terminator() )
            handleTerminator( bb.index(), *bb.terminator() );
        return true;
    }
} // namespace goose::verify
Changes to bs/verify/builder.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

19
20

21

22

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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
#ifndef GOOSE_VERIFY_BUILDER_H
#define GOOSE_VERIFY_BUILDER_H

namespace goose::builtins
{
    class Func;
}

namespace goose::verify
{
    using AssHandler = function< bool ( const z3::expr&, const z3::expr&, LocationId ) >;

    // Helper class that tracks the states of a few things
    // to build z3 expressions from a cfg.
    class Builder
    {
        public:
            Builder( const sema::Context& c, z3::solver& solver, size_t numVars, Remapper* remapper = nullptr );


            const auto& context() const { return *m_context; }

            auto* solver() { return m_solver; }

            auto* remapper() { return m_remapper; }

            uint32_t currentBBIndex() const { return m_currentBBIndex; }

            void setTraceMode( bool b ) { m_traceMode = b; }

            const auto& cfg() const { return m_cfg; }
            void setCFG( const ptr< cir::CFG >& cfg )
            {
                m_cfg = cfg;
            }

            void setCurrentBB( uint32_t index )
            {
                m_currentBBIndex = index;
            }

            void setCurrentBBPredicate( const z3::expr& predicate )
            {
                m_currentPredicate = predicate;
            }

            void add( const z3::expr& e );
            void assume( const z3::expr& e );
            bool checkAssertion( const z3::expr& e, LocationId locationId );

            void setLoadMustAssume( bool lma ) { m_loadMustAssume = lma; }

            bool mustLoadAssume() const { return m_loadMustAssume; }

            void resetVarStorage( uint32_t size )
            {
                m_varTracker = VarTracker( size );
            }

            optional< Z3Val > retrieveVar( uint32_t index, uint32_t bbIndex = ~0 );
            optional< Z3Val > setVar( uint32_t index, Z3Val&& v );

            optional< Z3Val > retrieveGFC( const GhostFuncApplication& gfa, uint32_t bbIndex = ~0 );
            void setGFC( const GhostFuncApplication& gfa, Z3Val&& v );

            optional< Z3Val > getTemporary( uint32_t index );
            void setTemporary( uint32_t index, Z3Val&& v );

            const z3::expr* retrievePlaceholder( StringId sid ) const;
            bool setPlaceholder( StringId sid, const z3::expr& expr );
            void unsetPlaceholder( StringId sid );

            void beginForAll( size_t argCount );
            bool declareForAllVar( StringId name, Term&& type );
            bool pushForAll( Z3Val&& expr, LocationId loc );

            auto& convertedBBIndices() { return m_convertedBBIndices; }

            uint32_t newUniqueId()
            {
                assert( m_nextUniqueId );
                return ( *m_nextUniqueId )++;
            }

            bool hasCheckFailed() const { return m_checkFailed; }
            void setCheckFailed() { m_checkFailed = true; }

            template< typename T >
            void push( T&& val )
            {
                m_stack.push( forward< T >( val ) );
            }

            template< typename T = Z3Val >
            optional< T > pop()
            {
                return m_stack.pop< T >( *this );
            }

            template< typename F >
            void setAssertionHandler( F&& handler )
            {
                m_assertionHandler = forward< F >( handler );
            }

            auto assertionHandler() const { return m_assertionHandler; }

        private:
            optional< z3::expr > getCurrentPredicatesExpr();

            const sema::Context* m_context;
            z3::solver* m_solver = nullptr;
            Remapper* m_remapper = nullptr;

            ptr< cir::CFG > m_cfg;
            uint32_t m_currentBBIndex = 1;

            // All emitted assumptions and assertions are wrapped
            // with implications from this predicate.
            // This is used to model the control flow, where each
            // basic block gets a predicate that indicates that
            // the execution flow is going through that block.
            optional< z3::expr > m_currentPredicate;

            AssHandler m_assertionHandler;

            unordered_set< uint32_t > m_convertedBBIndices;
            TempStorage< optional< Z3Val > > m_temporaries;
            unordered_map< StringId, z3::expr > m_placeholders;

            VarTracker m_varTracker;
            GFCTracker m_gfcTracker;

            Stack m_stack;

            using NameList = llvm::SmallVector< StringId, 8 >;
            using PendingForAlls = pair< NameList, z3::expr_vector >;
            stack< PendingForAlls > m_pendingForAlls;

            shared_ptr< uint32_t > m_nextUniqueId = make_shared< uint32_t >( 0 );

            bool m_traceMode = false;
            bool m_checkFailed = false;
            bool m_loadMustAssume = false;
    };

    class AssertionHandlerGuard
    {
        public:
            AssertionHandlerGuard( Builder& b ) :
                m_builder( b ),
                m_savedAssHandler( b.assertionHandler() )
            {}

            ~AssertionHandlerGuard()

            {
                m_builder.setAssertionHandler( m_savedAssHandler );
            }

        private:
            Builder&    m_builder;
            AssHandler  m_savedAssHandler;
    };
}


#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
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
#ifndef GOOSE_VERIFY_BUILDER_H
#define GOOSE_VERIFY_BUILDER_H

namespace goose::builtins
{
    class Func;
}

namespace goose::verify
{
    using AssHandler = function< bool( const z3::expr&, const z3::expr&, LocationId ) >;

    // Helper class that tracks the states of a few things
    // to build z3 expressions from a cfg.
    class Builder
    {
      public:
        Builder( const sema::Context& c, z3::solver& solver, size_t numVars,
            Remapper* remapper = nullptr );

        const auto& context() const { return *m_context; }

        auto* solver() { return m_solver; }

        auto* remapper() { return m_remapper; }

        uint32_t currentBBIndex() const { return m_currentBBIndex; }

        void setTraceMode( bool b ) { m_traceMode = b; }

        const auto& cfg() const { return m_cfg; }


        void setCFG( const ptr< cir::CFG >& cfg ) { m_cfg = cfg; }


        void setCurrentBB( uint32_t index ) { m_currentBBIndex = index; }




        void setCurrentBBPredicate( const z3::expr& predicate ) { m_currentPredicate = predicate; }




        void add( const z3::expr& e );
        void assume( const z3::expr& e );
        bool checkAssertion( const z3::expr& e, LocationId locationId );

        void setLoadMustAssume( bool lma ) { m_loadMustAssume = lma; }

        bool mustLoadAssume() const { return m_loadMustAssume; }

        void resetVarStorage( uint32_t size ) { m_varTracker = VarTracker( size ); }




        optional< Z3Val > retrieveVar( uint32_t index, uint32_t bbIndex = ~0 );
        optional< Z3Val > setVar( uint32_t index, Z3Val&& v );

        optional< Z3Val > retrieveGFC( const GhostFuncApplication& gfa, uint32_t bbIndex = ~0 );
        void setGFC( const GhostFuncApplication& gfa, Z3Val&& v );

        optional< Z3Val > getTemporary( uint32_t index );
        void setTemporary( uint32_t index, Z3Val&& v );

        const z3::expr* retrievePlaceholder( StringId sid ) const;
        bool setPlaceholder( StringId sid, const z3::expr& expr );
        void unsetPlaceholder( StringId sid );

        void beginForAll( size_t argCount );
        bool declareForAllVar( StringId name, Term&& type );
        bool pushForAll( Z3Val&& expr, LocationId loc );

        auto& convertedBBIndices() { return m_convertedBBIndices; }

        uint32_t newUniqueId()
        {
            assert( m_nextUniqueId );
            return ( *m_nextUniqueId )++;
        }

        bool hasCheckFailed() const { return m_checkFailed; }



        void setCheckFailed() { m_checkFailed = true; }

        template< typename T > void push( T&& val ) { m_stack.push( forward< T >( val ) ); }


        template< typename T = Z3Val > optional< T > pop() { return m_stack.pop< T >( *this ); }





        template< typename F > void setAssertionHandler( F&& handler )

        {
            m_assertionHandler = forward< F >( handler );
        }

        auto assertionHandler() const { return m_assertionHandler; }

      private:
        optional< z3::expr > getCurrentPredicatesExpr();

        const sema::Context* m_context;
        z3::solver* m_solver = nullptr;
        Remapper* m_remapper = nullptr;

        ptr< cir::CFG > m_cfg;
        uint32_t m_currentBBIndex = 1;

        // All emitted assumptions and assertions are wrapped
        // with implications from this predicate.
        // This is used to model the control flow, where each
        // basic block gets a predicate that indicates that
        // the execution flow is going through that block.
        optional< z3::expr > m_currentPredicate;

        AssHandler m_assertionHandler;

        unordered_set< uint32_t > m_convertedBBIndices;
        TempStorage< optional< Z3Val > > m_temporaries;
        unordered_map< StringId, z3::expr > m_placeholders;

        VarTracker m_varTracker;
        GFCTracker m_gfcTracker;

        Stack m_stack;

        using NameList = llvm::SmallVector< StringId, 8 >;
        using PendingForAlls = pair< NameList, z3::expr_vector >;
        stack< PendingForAlls > m_pendingForAlls;

        shared_ptr< uint32_t > m_nextUniqueId = make_shared< uint32_t >( 0 );

        bool m_traceMode = false;
        bool m_checkFailed = false;
        bool m_loadMustAssume = false;
    };

    class AssertionHandlerGuard
    {
      public:
        AssertionHandlerGuard( Builder& b ) :
            m_builder( b ),
            m_savedAssHandler( b.assertionHandler() )

        {

        }

        ~AssertionHandlerGuard() { m_builder.setAssertionHandler( m_savedAssHandler ); }


      private:
        Builder& m_builder;
        AssHandler m_savedAssHandler;
    };

} // namespace goose::verify

#endif
Changes to bs/verify/call.cpp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "verify.h"
#include "builtins/builtins.h"
#include "helpers.inl"

using namespace goose::diagnostics;

namespace goose::verify
{
    template< bool CheckResult, typename I >
    bool BuildZ3CallCheck( Builder& b, const I& instr )
    {
        Value ft;

        auto fVal = b.pop< Value >();
        if( fVal )
            ft = *EIRToValue( fVal->type() );
        else








|
<







1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
#include "verify.h"
#include "builtins/builtins.h"
#include "helpers.inl"

using namespace goose::diagnostics;

namespace goose::verify
{
    template< bool CheckResult, typename I > bool BuildZ3CallCheck( Builder& b, const I& instr )

    {
        Value ft;

        auto fVal = b.pop< Value >();
        if( fVal )
            ft = *EIRToValue( fVal->type() );
        else
27
28
29
30
31
32
33

34
35
36
37
38
39
40
41
            return false;

        optional< Z3Val > retExpr;

        if constexpr( CheckResult )
        {
            if( fvd->returnType != GetValueType< void >() )

                retExpr = BuildZ3ConstantFromType( b, fvd->returnType, format( "r{}", b.newUniqueId() ) );
        }

        auto fvi = fvd->verifInfos;
        if( !fvi )
        {
            if( retExpr )
                b.push( move( *retExpr ) );







>
|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
            return false;

        optional< Z3Val > retExpr;

        if constexpr( CheckResult )
        {
            if( fvd->returnType != GetValueType< void >() )
                retExpr =
                    BuildZ3ConstantFromType( b, fvd->returnType, format( "r{}", b.newUniqueId() ) );
        }

        auto fvi = fvd->verifInfos;
        if( !fvi )
        {
            if( retExpr )
                b.push( move( *retExpr ) );
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

            cb.setVar( argCount - argIndex - 1, move( *zv ) );
        }

        // Asserts the parameter types's predicates.
        uint32_t varId = 0;

        ForEachInVectorTerm( fvd->params, [&]( auto&& param )

        {
            auto result = Decompose( param,
                Vec(
                    Lit( "value"_sid ),
                    Lit( "param"_sid ),
                    SubTerm(),
                    SubTerm(),
                    Val< LocationId >()
                )
            );

            if( !result )
                return true;

            auto&& [type, val, locId] = *result;

            auto paramVal = BuildComputedValue( type, VarAddr( varId++, locId ), Load( type, locId ) );

            auto zv = BuildZ3ExprFromValue( cb, paramVal );
            ForEachPredicate( cb, type, zv.expr, [&]( auto&& z3expr, auto locId )

            {
                DiagnosticsContext dc( fVal->locationId(), "At this call." );
                b.checkAssertion( z3expr, locId );
            } );

            return true;
        } );

        if constexpr( CheckResult )
        {
            // Setup the return value placeholder
            if( retExpr )
                cb.setPlaceholder( "@result"_sid, retExpr->expr );
        }

        // Check preconditions.
        const auto& preConds = fvi->preConds();
        preConds->forEach( [&]( auto&& val )

        {
            if( auto zv = TryBuildZ3ExprFromValue( cb, val ) )
            {
                DiagnosticsContext dc( fVal->locationId(), "At this call." );
                b.checkAssertion( zv->expr, val.locationId() );
            }
        } );

        if constexpr( CheckResult )
        {
            // Add the return type's predicates as assumptions.
            ForEachPredicate( cb, fvd->returnType, retExpr->expr, [&]( auto&& z3expr, auto locId )
            {
                b.assume( z3expr );
            } );

            // Add postconditions as assumptions.
            const auto& postConds = fvi->postConds();
            postConds->forEach( [&]( auto&& val )

            {
                if( auto zv = TryBuildZ3ExprFromValue( cb, val ) )
                    b.assume( zv->expr );
            } );

            if( retExpr )
                b.push( move( *retExpr ) );
        }

        return true;
    }

    bool BuildZ3Op( Builder& b, const Call& instr )
    {
        return BuildZ3CallCheck< true >( b, instr );
    }

    bool BuildZ3Op( Builder& b, const CallCheck& instr )
    {
        return BuildZ3CallCheck< false >( b, instr );
    }
}







|
>
|
|
<
|
<
<
<
|
|
<
<
|
|

|
>
|

|
|
>
|
|
|
|

|
|










|
>
|
|
|
|
|
|
|




|
<
|
<



|
>
|
|
|
|

















|
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

            cb.setVar( argCount - argIndex - 1, move( *zv ) );
        }

        // Asserts the parameter types's predicates.
        uint32_t varId = 0;

        ForEachInVectorTerm( fvd->params,
            [&]( auto&& param )
            {
                auto result = Decompose( param,

                    Vec( Lit( "value"_sid ), Lit( "param"_sid ), SubTerm(), SubTerm(),



                        Val< LocationId >() ) );



                if( !result )
                    return true;

                auto&& [type, val, locId] = *result;
                auto paramVal =
                    BuildComputedValue( type, VarAddr( varId++, locId ), Load( type, locId ) );

                auto zv = BuildZ3ExprFromValue( cb, paramVal );
                ForEachPredicate( cb, type, zv.expr,
                    [&]( auto&& z3expr, auto locId )
                    {
                        DiagnosticsContext dc( fVal->locationId(), "At this call." );
                        b.checkAssertion( z3expr, locId );
                    } );

                return true;
            } );

        if constexpr( CheckResult )
        {
            // Setup the return value placeholder
            if( retExpr )
                cb.setPlaceholder( "@result"_sid, retExpr->expr );
        }

        // Check preconditions.
        const auto& preConds = fvi->preConds();
        preConds->forEach(
            [&]( auto&& val )
            {
                if( auto zv = TryBuildZ3ExprFromValue( cb, val ) )
                {
                    DiagnosticsContext dc( fVal->locationId(), "At this call." );
                    b.checkAssertion( zv->expr, val.locationId() );
                }
            } );

        if constexpr( CheckResult )
        {
            // Add the return type's predicates as assumptions.
            ForEachPredicate( cb, fvd->returnType, retExpr->expr,

                [&]( auto&& z3expr, auto locId ) { b.assume( z3expr ); } );


            // Add postconditions as assumptions.
            const auto& postConds = fvi->postConds();
            postConds->forEach(
                [&]( auto&& val )
                {
                    if( auto zv = TryBuildZ3ExprFromValue( cb, val ) )
                        b.assume( zv->expr );
                } );

            if( retExpr )
                b.push( move( *retExpr ) );
        }

        return true;
    }

    bool BuildZ3Op( Builder& b, const Call& instr )
    {
        return BuildZ3CallCheck< true >( b, instr );
    }

    bool BuildZ3Op( Builder& b, const CallCheck& instr )
    {
        return BuildZ3CallCheck< false >( b, instr );
    }
} // namespace goose::verify
Changes to bs/verify/cfg.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
#include "verify.h"
#include "builtins/builtins.h"
#include "diagnostics/diagnostics.h"

using namespace goose::diagnostics;

namespace goose::verify
{

    bool Func::buildZ3Expressions( const cir::BasicBlock& bb, queue< const BasicBlock* >* parentWorkQueue )
    {
        uint32_t currentLoopId = m_remapper.getCurrentLoopId();

        queue< const BasicBlock* > workQueue;
        workQueue.push( &bb );

        while( !workQueue.empty() )
        {
            const auto* pBB = workQueue.front();
            workQueue.pop();

            if( currentLoopId && pBB->loopId() != currentLoopId && pBB->index() != currentLoopId )
            {
                // If we encounter a successor BB that belongs to a different loop than the
                // current one, pass it up to be handled by our parent work queue (which should belong
                // to a checkLoop call).
                // The only case where we don't have a parent work queue is at the root level of the loop
                // nesting tree, and we should never have an edge from a BB there into a loop BB without
                // en countering that loop header first, because we only handle reducible CFGs.
                assert( parentWorkQueue );
                parentWorkQueue->push( pBB );
                continue;
            }

            // If we encounter a loop header for a different
            // loop than the current one, check the loop.








>
|














|
|
|
|
|







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
#include "verify.h"
#include "builtins/builtins.h"
#include "diagnostics/diagnostics.h"

using namespace goose::diagnostics;

namespace goose::verify
{
    bool Func::buildZ3Expressions(
        const cir::BasicBlock& bb, queue< const BasicBlock* >* parentWorkQueue )
    {
        uint32_t currentLoopId = m_remapper.getCurrentLoopId();

        queue< const BasicBlock* > workQueue;
        workQueue.push( &bb );

        while( !workQueue.empty() )
        {
            const auto* pBB = workQueue.front();
            workQueue.pop();

            if( currentLoopId && pBB->loopId() != currentLoopId && pBB->index() != currentLoopId )
            {
                // If we encounter a successor BB that belongs to a different loop than the
                // current one, pass it up to be handled by our parent work queue (which should
                // belong to a checkLoop call). The only case where we don't have a parent work
                // queue is at the root level of the loop nesting tree, and we should never have an
                // edge from a BB there into a loop BB without en countering that loop header first,
                // because we only handle reducible CFGs.
                assert( parentWorkQueue );
                parentWorkQueue->push( pBB );
                continue;
            }

            // If we encounter a loop header for a different
            // loop than the current one, check the loop.
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
                workQueue.push( pBB );
                continue;
            }

            if( buildZ3ExpressionsForBB( *pBB ) )
            {
                const cir::GhostBranch* pGB = nullptr;

                if( pBB->terminator() && ( pGB = get_if< cir::GhostBranch >( &pBB->terminator()->content() ) ) )
                {
                    const auto& pSuccBB = pGB->ghostCode().lock();
                    if( pSuccBB->index() != pBB->loopId() )
                        workQueue.push( pSuccBB.get() );
                }
                else
                {
                    pBB->forEachSuccessor( [&]( auto&& pSuccBB )

                    {
                        if( pSuccBB->index() != pBB->loopId() )
                            workQueue.push( pSuccBB.get() );
                    } );
                }
            }
        }

        return !m_builder.hasCheckFailed();
    }

    z3::expr Func::makeEdgeExpression( uint32_t srcBBId, uint32_t destBBId, const z3::expr& cond )
    {
        auto& c = GetZ3Context();
        auto srcBBExpr = c.bool_const( format( "b{}", srcBBId ).c_str() );
        auto edgeExpr = c.bool_const( format( "e{}_{}", srcBBId, destBBId ).c_str() );
        m_builder.add( edgeExpr == ( srcBBExpr && cond ) );
        return edgeExpr;
    }
}







>
|







|
>
|
|
|
|















|
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
                workQueue.push( pBB );
                continue;
            }

            if( buildZ3ExpressionsForBB( *pBB ) )
            {
                const cir::GhostBranch* pGB = nullptr;
                if( pBB->terminator()
                    && ( pGB = get_if< cir::GhostBranch >( &pBB->terminator()->content() ) ) )
                {
                    const auto& pSuccBB = pGB->ghostCode().lock();
                    if( pSuccBB->index() != pBB->loopId() )
                        workQueue.push( pSuccBB.get() );
                }
                else
                {
                    pBB->forEachSuccessor(
                        [&]( auto&& pSuccBB )
                        {
                            if( pSuccBB->index() != pBB->loopId() )
                                workQueue.push( pSuccBB.get() );
                        } );
                }
            }
        }

        return !m_builder.hasCheckFailed();
    }

    z3::expr Func::makeEdgeExpression( uint32_t srcBBId, uint32_t destBBId, const z3::expr& cond )
    {
        auto& c = GetZ3Context();
        auto srcBBExpr = c.bool_const( format( "b{}", srcBBId ).c_str() );
        auto edgeExpr = c.bool_const( format( "e{}_{}", srcBBId, destBBId ).c_str() );
        m_builder.add( edgeExpr == ( srcBBExpr && cond ) );
        return edgeExpr;
    }
} // namespace goose::verify
Changes to bs/verify/func.cpp.
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
    Func::Func( const sema::Context& c, const builtins::Func& func ) :
        m_func( &func ),
        m_cfg( func.cir()->body() ),
        m_retType( func.type().returnType() ),
        m_solver( GetZ3Context(), z3::solver::simple() ),
        m_remapper( m_cfg->count() + 1 ),
        m_builder( c, m_solver, m_cfg->temporariesCount(), &m_remapper )
    {}



    Func::Func( const sema::Context& c, const ptr< cir::CFG >& cfg, const Term& retType ) :
        m_cfg( cfg ),
        m_retType( retType ),
        m_solver( GetZ3Context(), z3::solver::simple() ),
        m_remapper( m_cfg->count() + 1 ),
        m_builder( c, m_solver, m_cfg->temporariesCount(), &m_remapper )
    {}



    bool Func::verify()
    {
        if( !ms_EnableVerification )
            return true;

        ProfileZoneScoped;

        if( m_func )
            m_viz.setFunctionIdentity( m_func->cir()->identity() );

        IdentifyLoops( m_cfg );
        IdentifyLoopModifiedAddrs( m_cfg );

        try
        {
            m_builder.setTraceMode( ms_TraceMode );
            if( ms_TraceMode )
                cout << "=== Begin function verification trace ===\n";


            m_builder.setAssertionHandler( [&]( auto&& expr, auto&& exprToCheck, LocationId locationId )
            {
                return checkAssertion( expr, exprToCheck, locationId );
            } );

            m_builder.setCFG( m_cfg );

            if( m_func )
            {
                // Add assumptions of the function's parameter types predicates
                uint32_t varId = 0;







<
>
>







<
>
>




















>
|
<
|
<







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
    Func::Func( const sema::Context& c, const builtins::Func& func ) :
        m_func( &func ),
        m_cfg( func.cir()->body() ),
        m_retType( func.type().returnType() ),
        m_solver( GetZ3Context(), z3::solver::simple() ),
        m_remapper( m_cfg->count() + 1 ),
        m_builder( c, m_solver, m_cfg->temporariesCount(), &m_remapper )

    {
    }

    Func::Func( const sema::Context& c, const ptr< cir::CFG >& cfg, const Term& retType ) :
        m_cfg( cfg ),
        m_retType( retType ),
        m_solver( GetZ3Context(), z3::solver::simple() ),
        m_remapper( m_cfg->count() + 1 ),
        m_builder( c, m_solver, m_cfg->temporariesCount(), &m_remapper )

    {
    }

    bool Func::verify()
    {
        if( !ms_EnableVerification )
            return true;

        ProfileZoneScoped;

        if( m_func )
            m_viz.setFunctionIdentity( m_func->cir()->identity() );

        IdentifyLoops( m_cfg );
        IdentifyLoopModifiedAddrs( m_cfg );

        try
        {
            m_builder.setTraceMode( ms_TraceMode );
            if( ms_TraceMode )
                cout << "=== Begin function verification trace ===\n";

            m_builder.setAssertionHandler(
                [&]( auto&& expr, auto&& exprToCheck, LocationId locationId )

                { return checkAssertion( expr, exprToCheck, locationId ); } );


            m_builder.setCFG( m_cfg );

            if( m_func )
            {
                // Add assumptions of the function's parameter types predicates
                uint32_t varId = 0;
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
                    // Skip wrapped params
                    if( builtins::IsWrappedType( t ) )
                    {
                        ++varId;
                        return true;
                    }


                    auto paramVal = BuildComputedValue( t, VarAddr( varId, locId ), Load( t, locId ) )
                        .setLocationId( locId );

                    // Initialize every parameter containing variable with a freshly named constant of the right type.


                    auto paramInit = BuildZ3ConstantFromType( m_builder, t, format( "p{}", varId ) );
                    m_builder.setVar( varId, move( paramInit ) );

                    ++varId;

                    auto zv = BuildZ3ExprFromValue( m_builder, paramVal );
                    ForEachPredicate( m_builder, t, zv.expr, [&]( auto&& z3expr, auto locId )

                    {
                        if( z3expr.is_bool() )
                            m_builder.assume( z3expr );
                    } );
                }

                // Add assumptions of the function's requirements
                if( const auto& fvi = m_func->type().verifInfos() )
                {
                    const auto& reqs = fvi->preConds();
                    reqs->forEach( [&]( auto&& val )

                    {
                        // Don't do any error handling here, it should already have been taken care of
                        // by the condition verifier.
                        if( auto zv = TryBuildZ3ExprFromValue( m_builder, val ) )
                            m_builder.assume( zv->expr );
                    } );
                }
            }

            bool result = buildZ3Expressions( *m_cfg->entryBB(), nullptr );

            if( ms_TraceMode )
                cout << "=== End function verification trace ===\n\n";

            return result;
        }
        catch( const z3::exception& e )
        {
            cerr << "Func: z3 exception: " << e << endl;

            // rethrow so we get a stack trace dump to know where the error happened
            throw;
        }
    }


    bool Func::checkAssertion( const z3::expr& expr, const z3::expr& exprToCheck, LocationId locationId )
    {
        if( ms_TraceMode )
            return true;

        ProfileZoneScoped;

        z3::expr_vector exprVec( GetZ3Context() );
        exprVec.push_back( exprToCheck );

        m_solver.push();

        bool result = false;

        switch( m_solver.check( exprVec ) )
        {
            case z3::check_result::unsat:
                // We are using the solver to try and find a counter example,
                // so if it's unsat, it's good.

                if( ms_DumpSolverOnSuccess )

                    cout << "Solver dump:\n" << m_solver << "check_unsat: " << exprToCheck << endl << endl;


                result = true;
                break;

            case z3::check_result::sat:
                m_viz.assertionFailed();

                DiagnosticsManager::GetInstance().emitErrorMessage( locationId, "assert"_sid,
                    "this condition may not be met." );

                if( ms_DumpSolverOnFailure )
                {

                    cout << "Solver dump:\n" << m_solver << "check_unsat: " << exprToCheck << endl << endl;

                    cout << "Model dump:\n" << m_solver.get_model() << endl << endl;
                }

                result = false;
                break;

            default:
                m_viz.assertionFailed();

                DiagnosticsManager::GetInstance().emitErrorMessage( locationId, "assert"_sid,
                    "couldn't verify this condition." );

                if( ms_DumpSolverOnFailure )

                    cout << "Solver dump:\n" << m_solver << "check_unsat: " << exprToCheck << endl << endl;


                result = false;
                break;
        }

        m_solver.pop();
        return result;
    }
}







>
|
|

|
>
>
|





|
>
|
|
|
|






|
>
|
|
|
|
|
|



















>
|




















>
|
>







|
|



>
|
>









|
|


>
|
>








|
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
                    // Skip wrapped params
                    if( builtins::IsWrappedType( t ) )
                    {
                        ++varId;
                        return true;
                    }

                    auto paramVal =
                        BuildComputedValue( t, VarAddr( varId, locId ), Load( t, locId ) )
                            .setLocationId( locId );

                    // Initialize every parameter containing variable with a freshly named constant
                    // of the right type.
                    auto paramInit =
                        BuildZ3ConstantFromType( m_builder, t, format( "p{}", varId ) );
                    m_builder.setVar( varId, move( paramInit ) );

                    ++varId;

                    auto zv = BuildZ3ExprFromValue( m_builder, paramVal );
                    ForEachPredicate( m_builder, t, zv.expr,
                        [&]( auto&& z3expr, auto locId )
                        {
                            if( z3expr.is_bool() )
                                m_builder.assume( z3expr );
                        } );
                }

                // Add assumptions of the function's requirements
                if( const auto& fvi = m_func->type().verifInfos() )
                {
                    const auto& reqs = fvi->preConds();
                    reqs->forEach(
                        [&]( auto&& val )
                        {
                            // Don't do any error handling here, it should already have been taken
                            // care of by the condition verifier.
                            if( auto zv = TryBuildZ3ExprFromValue( m_builder, val ) )
                                m_builder.assume( zv->expr );
                        } );
                }
            }

            bool result = buildZ3Expressions( *m_cfg->entryBB(), nullptr );

            if( ms_TraceMode )
                cout << "=== End function verification trace ===\n\n";

            return result;
        }
        catch( const z3::exception& e )
        {
            cerr << "Func: z3 exception: " << e << endl;

            // rethrow so we get a stack trace dump to know where the error happened
            throw;
        }
    }

    bool Func::checkAssertion(
        const z3::expr& expr, const z3::expr& exprToCheck, LocationId locationId )
    {
        if( ms_TraceMode )
            return true;

        ProfileZoneScoped;

        z3::expr_vector exprVec( GetZ3Context() );
        exprVec.push_back( exprToCheck );

        m_solver.push();

        bool result = false;

        switch( m_solver.check( exprVec ) )
        {
            case z3::check_result::unsat:
                // We are using the solver to try and find a counter example,
                // so if it's unsat, it's good.

                if( ms_DumpSolverOnSuccess )
                    cout << "Solver dump:\n"
                         << m_solver << "check_unsat: " << exprToCheck << endl
                         << endl;

                result = true;
                break;

            case z3::check_result::sat:
                m_viz.assertionFailed();

                DiagnosticsManager::GetInstance().emitErrorMessage(
                    locationId, "assert"_sid, "this condition may not be met." );

                if( ms_DumpSolverOnFailure )
                {
                    cout << "Solver dump:\n"
                         << m_solver << "check_unsat: " << exprToCheck << endl
                         << endl;
                    cout << "Model dump:\n" << m_solver.get_model() << endl << endl;
                }

                result = false;
                break;

            default:
                m_viz.assertionFailed();

                DiagnosticsManager::GetInstance().emitErrorMessage(
                    locationId, "assert"_sid, "couldn't verify this condition." );

                if( ms_DumpSolverOnFailure )
                    cout << "Solver dump:\n"
                         << m_solver << "check_unsat: " << exprToCheck << endl
                         << endl;

                result = false;
                break;
        }

        m_solver.pop();
        return result;
    }
} // namespace goose::verify
Changes to bs/verify/func.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
#ifndef GOOSE_VERIFY_FUNC_H
#define GOOSE_VERIFY_FUNC_H

namespace goose::builtins
{
    class Func;
}

namespace goose::verify
{
    class Func
    {
        public:
            Func( const sema::Context& c, const builtins::Func& func );
            Func( const sema::Context& c, const ptr< cir::CFG >& cfg, const Term& retType );
            bool verify();

            static void SetEnableVerification( bool b ) { ms_EnableVerification = b; }

            static void SetTraceMode( bool b ) { ms_TraceMode = b; }

            static void SetDumpSolverOnFailure( bool b ) { ms_DumpSolverOnFailure = b; }

            static void SetDumpSolverOnSuccess( bool b ) { ms_DumpSolverOnSuccess = b; }

            static bool TraceMode() { return ms_TraceMode; }

            static bool DumpSolverOnFailure() { return ms_DumpSolverOnFailure; }

            static bool DumpSolverOnSuccess() { return ms_DumpSolverOnSuccess; }

        private:

            bool buildZ3Expressions( const cir::BasicBlock& bb, queue< const BasicBlock* >* parentWorkQueue );
            bool buildZ3ExpressionsForBB( const cir::BasicBlock& bb );

            bool checkLoop( const cir::BasicBlock& header, queue< const BasicBlock* >& parentWorkQueue );

            bool handleTerminator( uint32_t bbIndex, const cir::Terminator& t );

            template< typename T >
            bool handleTerminator( uint32_t bbIndex, const T& t )
            {
                return true;
            }

            bool handleTerminator( uint32_t bbIndex, const cir::RetVoid& t );
            bool handleTerminator( uint32_t bbIndex, const cir::Ret& t );
            bool handleTerminator( uint32_t bbIndex, const cir::Branch& t );
            bool handleTerminator( uint32_t bbIndex, const cir::CondBranch& t );
            bool handleTerminator( uint32_t bbIndex, const cir::GhostBranch& t );

            z3::expr makeEdgeExpression( uint32_t srcBBId, uint32_t destBBId, const z3::expr& cond );


            bool checkAssertion( const z3::expr& expr, const z3::expr& exprToCheck, LocationId locationId );

            const builtins::Func* m_func = nullptr;
            ptr< cir::CFG > m_cfg;
            Term m_retType;

            z3::solver m_solver;
            Remapper m_remapper;
            Builder m_builder;

            VerifyViz m_viz;

            static bool ms_EnableVerification;
            static bool ms_TraceMode;
            static bool ms_DumpSolverOnFailure;
            static bool ms_DumpSolverOnSuccess;
    };
}

#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
64
65
66
67
68
69
70
71
72
73
74
#ifndef GOOSE_VERIFY_FUNC_H
#define GOOSE_VERIFY_FUNC_H

namespace goose::builtins
{
    class Func;
}

namespace goose::verify
{
    class Func
    {
      public:
        Func( const sema::Context& c, const builtins::Func& func );
        Func( const sema::Context& c, const ptr< cir::CFG >& cfg, const Term& retType );
        bool verify();

        static void SetEnableVerification( bool b ) { ms_EnableVerification = b; }

        static void SetTraceMode( bool b ) { ms_TraceMode = b; }

        static void SetDumpSolverOnFailure( bool b ) { ms_DumpSolverOnFailure = b; }

        static void SetDumpSolverOnSuccess( bool b ) { ms_DumpSolverOnSuccess = b; }

        static bool TraceMode() { return ms_TraceMode; }

        static bool DumpSolverOnFailure() { return ms_DumpSolverOnFailure; }

        static bool DumpSolverOnSuccess() { return ms_DumpSolverOnSuccess; }

      private:
        bool buildZ3Expressions(
            const cir::BasicBlock& bb, queue< const BasicBlock* >* parentWorkQueue );
        bool buildZ3ExpressionsForBB( const cir::BasicBlock& bb );
        bool checkLoop(
            const cir::BasicBlock& header, queue< const BasicBlock* >& parentWorkQueue );

        bool handleTerminator( uint32_t bbIndex, const cir::Terminator& t );


        template< typename T > bool handleTerminator( uint32_t bbIndex, const T& t )
        {
            return true;
        }

        bool handleTerminator( uint32_t bbIndex, const cir::RetVoid& t );
        bool handleTerminator( uint32_t bbIndex, const cir::Ret& t );
        bool handleTerminator( uint32_t bbIndex, const cir::Branch& t );
        bool handleTerminator( uint32_t bbIndex, const cir::CondBranch& t );
        bool handleTerminator( uint32_t bbIndex, const cir::GhostBranch& t );

        z3::expr makeEdgeExpression( uint32_t srcBBId, uint32_t destBBId, const z3::expr& cond );

        bool checkAssertion(
            const z3::expr& expr, const z3::expr& exprToCheck, LocationId locationId );

        const builtins::Func* m_func = nullptr;
        ptr< cir::CFG > m_cfg;
        Term m_retType;

        z3::solver m_solver;
        Remapper m_remapper;
        Builder m_builder;

        VerifyViz m_viz;

        static bool ms_EnableVerification;
        static bool ms_TraceMode;
        static bool ms_DumpSolverOnFailure;
        static bool ms_DumpSolverOnSuccess;
    };
} // namespace goose::verify

#endif
Changes to bs/verify/gfctracker.cpp.
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
        gfIndex = m_ghostFuncIndices.size();
        m_ghostFuncIndices.emplace( gfa.func(), gfIndex );
    }

    return gfIndex;
}


z3::expr GFCTracker::buildFunctionApplication( Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const Value& gf )
{
    auto gft = *FromValue< builtins::FuncType >( *EIRToValue( gf.type() ) );

    llvm::SmallVector< z3::expr, 8 > z3args;
    z3args.reserve( VecSize( gft.params() ) + 1 );
    z3args.emplace_back( GetZ3Context().int_val( uid ) );

    uint32_t argIndex = 0;

    ForEachInVectorTerm( gft.params(), [&]( auto&& t )

    {
        auto result = Decompose( t,
            Vec(
                Lit( "value"_sid ),
                SubTerm(),
                SubTerm(),
                SubTerm(),
                Val< LocationId >()
            )
        );
        assert( result );
        auto&& [sort, type, val, locId] = *result;


        z3args.emplace_back( BuildZ3ConstantFromType( b, type, format( "gca{}_{}", argIndex, uid ) ).expr );
        ++argIndex;
        return true;
    } );

    return gfDecl.z3decl( z3args.size(), z3args.data() );
}

z3::expr GFCTracker::buildFunctionApplication( Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const GhostFuncApplication& gfa )

{
    llvm::SmallVector< z3::expr, 8 > z3args;
    z3args.reserve( gfa.args().size() + 1 );
    z3args.emplace_back( GetZ3Context().int_val( uid ) );

    for( auto&& zv : gfa.args() )
        z3args.emplace_back( zv.expr );

    return gfDecl.z3decl( z3args.size(), z3args.data() );
}

uint32_t GFCTracker::getCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex ) const
{
    const auto* gfcs = m_gfcStorage.get( gfIndex );
    if( !gfcs )
        return 0;

    if( gfcs->size() <= bbIndex )
        return 0;

    return ( *gfcs )[ bbIndex ];
}

void GFCTracker::setCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex, uint32_t uid )
{
    auto* gfcs = m_gfcStorage.get( gfIndex );
    if( !gfcs )
    {
        GFCState newGFCS;
        newGFCS.resize( bbIndex + 1, 0 );
        auto& gfcsRef = m_gfcStorage.set( gfIndex, move( newGFCS ) );
        gfcs = &gfcsRef;
    }
    else if( gfcs->size() <= bbIndex )
        gfcs->resize( bbIndex + 1, 0 );

    (*gfcs)[ bbIndex ] = uid;
}

void GFCTracker::setForBasicBlock( Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa,
    uint32_t gfIndex, const GhostFuncDeclCache::GFDecl& gfDecl, z3::expr&& expr )
{
    uint32_t oldUid = getCurrentUidForBasicBlock( bbIndex, gfIndex );
    uint32_t newUid = b.newUniqueId();

    auto& c = GetZ3Context();
    auto predicate = c.bool_const( format( "b{}", bbIndex ).c_str() );

    if( oldUid == 0 )
    {
        auto apply = buildFunctionApplication( b, newUid, gfDecl, gfa );
        b.add( z3::implies( predicate, apply == Coerce( expr, { apply, gfDecl.returnType,
            gfa.locationId() } ) ) );
    }
    else
    {
        // Not the first time we set a value for this ghost func:
        // we need to express that its new value at the current assignation point is the new one only if
        // the args are equal to the passed args, otherwise it is equal to whatever it was at its previous
        // assignment.
        auto argCount = gfa.args().size();

        llvm::SmallVector< z3::expr, 8 > z3args;
        z3args.reserve( argCount + 1 );
        z3args.emplace_back( c.int_val( newUid ) );

        uint32_t argIndex = 0;

        z3::expr_vector z3argComparisons( c );
        z3argComparisons.resize( argCount );

        z3::expr_vector z3argsVars( c );
        z3argsVars.resize( argCount );

        for( auto&& arg : gfa.args() )
        {

            auto argConst = BuildZ3ConstantFromType( b, arg.type, format( "gca{}_{}", argIndex, newUid ) ).expr;
            z3args.emplace_back( argConst );
            z3argsVars.set( argIndex, argConst );

            auto comp = argConst == arg.expr;
            z3argComparisons.set( argIndex, comp );

            ++argIndex;
        }

        auto newApply = gfDecl.z3decl( z3args.size(), z3args.data() );

        z3args[0] = c.int_val( oldUid );
        auto previousApply = gfDecl.z3decl( z3args.size(), z3args.data() );

        // AAAAAAAH
        b.add( z3::forall( z3argsVars, z3::implies( predicate, newApply == z3::ite(


            z3::mk_and( z3argComparisons ),
            Coerce( expr, { newApply, gfDecl.returnType, gfa.locationId() } ),
            previousApply
        ) ) ) );
    }

    setCurrentUidForBasicBlock( bbIndex, gfIndex, newUid );
}

optional< Z3Val > GFCTracker::retrieve( Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa )

{
    if( bbIndex == b.currentBBIndex() )
        return nullopt;

    if( bbIndex == ~0U )
        bbIndex = b.currentBBIndex();

    auto gfDecl = GhostFuncDeclCache::GetInstance()->getDecl( b.context(), gfa.func() );
    if( !gfDecl )
        return nullopt;

    auto gft = *FromValue< builtins::FuncType >( *EIRToValue( gfa.func().type() ) );

    uint32_t gfIndex = getGFIndex( gfa );
    auto uid = getCurrentUidForBasicBlock( bbIndex, gfIndex );
    if( uid )
        return Z3Val{ buildFunctionApplication( b, uid, *gfDecl, gfa ), *EIRToValue( gft.returnType() ),
            gfa.locationId() };

    if( !b.cfg() )
        return nullopt;

    uid = b.newUniqueId();
    auto& c = GetZ3Context();

    optional< z3::expr > lhsExpr;
    llvm::SmallVector< z3::expr, 8 > z3args;
    z3::expr_vector z3argsVars( c );
    optional< z3::expr > newApply;

    // Model the ssa phi operation by creating a series of equality assertions, each implied by
    // one of the possible incoming edge condition for the current basic block.
    if( b.remapper() )
    {
        b.remapper()->forEachIncomingEdge( bbIndex, [&]( auto&& predIndex, auto&& expr )

        {
            if( auto predVal = retrieve( b, predIndex, gfa ) )
            {
                if( !lhsExpr )
                    lhsExpr = buildFunctionApplication( b, uid, *gfDecl, gfa );

                if( z3args.empty() )
                {
                    auto argCount = gfa.args().size();

                    z3args.reserve( argCount + 1 );
                    z3args.emplace_back( c.int_val( uid ) );

                    z3argsVars.resize( argCount );

                    uint32_t argIndex = 0;

                    for( auto&& arg : gfa.args() )
                    {
                        auto argConst = BuildZ3ConstantFromType( b, arg.type, format( "gca{}_{}", argIndex, uid ) ).expr;


                        z3args.emplace_back( argConst );
                        z3argsVars.set( argIndex, argConst );

                        ++argIndex;
                    }

                    newApply = gfDecl->z3decl( z3args.size(), z3args.data() );
                }

                auto predUid = getCurrentUidForBasicBlock( predIndex, gfIndex );
                z3args[0] = c.int_val( predUid );
                auto previousApply = gfDecl->z3decl( z3args.size(), z3args.data() );


                b.add( z3::forall( z3argsVars, z3::implies( c.bool_const( format( "e{}_{}", predIndex, bbIndex ).c_str() ),
                    *newApply == previousApply ) ) );
            }
        } );
    }

    if( !lhsExpr )
        return nullopt;

    setCurrentUidForBasicBlock( bbIndex, gfIndex, uid );
    setForBasicBlock( b, bbIndex, gfa, gfIndex, *gfDecl, move( *lhsExpr ) );







>
|









|
>
|
|
<
|
<
<
<
<
<
<
|
|

>
|
|
|
|




|
>




















|















|














|
|




|
|
|
















>
|















|
>
>
|
|
|
<





|
>
















|
|
















|
>
|
|
|
|
|

|
|
|

|
|

|

|

|
|
|
>
>
|
|

|
|

|
|

|
|
|

>
|
|
|
|







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
        gfIndex = m_ghostFuncIndices.size();
        m_ghostFuncIndices.emplace( gfa.func(), gfIndex );
    }

    return gfIndex;
}

z3::expr GFCTracker::buildFunctionApplication(
    Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const Value& gf )
{
    auto gft = *FromValue< builtins::FuncType >( *EIRToValue( gf.type() ) );

    llvm::SmallVector< z3::expr, 8 > z3args;
    z3args.reserve( VecSize( gft.params() ) + 1 );
    z3args.emplace_back( GetZ3Context().int_val( uid ) );

    uint32_t argIndex = 0;

    ForEachInVectorTerm( gft.params(),
        [&]( auto&& t )
        {
            auto result = Decompose( t,

                Vec( Lit( "value"_sid ), SubTerm(), SubTerm(), SubTerm(), Val< LocationId >() ) );






            assert( result );
            auto&& [sort, type, val, locId] = *result;

            z3args.emplace_back(
                BuildZ3ConstantFromType( b, type, format( "gca{}_{}", argIndex, uid ) ).expr );
            ++argIndex;
            return true;
        } );

    return gfDecl.z3decl( z3args.size(), z3args.data() );
}

z3::expr GFCTracker::buildFunctionApplication( Builder& b, uint32_t uid,
    const GhostFuncDeclCache::GFDecl& gfDecl, const GhostFuncApplication& gfa )
{
    llvm::SmallVector< z3::expr, 8 > z3args;
    z3args.reserve( gfa.args().size() + 1 );
    z3args.emplace_back( GetZ3Context().int_val( uid ) );

    for( auto&& zv : gfa.args() )
        z3args.emplace_back( zv.expr );

    return gfDecl.z3decl( z3args.size(), z3args.data() );
}

uint32_t GFCTracker::getCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex ) const
{
    const auto* gfcs = m_gfcStorage.get( gfIndex );
    if( !gfcs )
        return 0;

    if( gfcs->size() <= bbIndex )
        return 0;

    return ( *gfcs )[bbIndex];
}

void GFCTracker::setCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex, uint32_t uid )
{
    auto* gfcs = m_gfcStorage.get( gfIndex );
    if( !gfcs )
    {
        GFCState newGFCS;
        newGFCS.resize( bbIndex + 1, 0 );
        auto& gfcsRef = m_gfcStorage.set( gfIndex, move( newGFCS ) );
        gfcs = &gfcsRef;
    }
    else if( gfcs->size() <= bbIndex )
        gfcs->resize( bbIndex + 1, 0 );

    ( *gfcs )[bbIndex] = uid;
}

void GFCTracker::setForBasicBlock( Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa,
    uint32_t gfIndex, const GhostFuncDeclCache::GFDecl& gfDecl, z3::expr&& expr )
{
    uint32_t oldUid = getCurrentUidForBasicBlock( bbIndex, gfIndex );
    uint32_t newUid = b.newUniqueId();

    auto& c = GetZ3Context();
    auto predicate = c.bool_const( format( "b{}", bbIndex ).c_str() );

    if( oldUid == 0 )
    {
        auto apply = buildFunctionApplication( b, newUid, gfDecl, gfa );
        b.add( z3::implies(
            predicate, apply == Coerce( expr, { apply, gfDecl.returnType, gfa.locationId() } ) ) );
    }
    else
    {
        // Not the first time we set a value for this ghost func:
        // we need to express that its new value at the current assignation point is the new one
        // only if the args are equal to the passed args, otherwise it is equal to whatever it was
        // at its previous assignment.
        auto argCount = gfa.args().size();

        llvm::SmallVector< z3::expr, 8 > z3args;
        z3args.reserve( argCount + 1 );
        z3args.emplace_back( c.int_val( newUid ) );

        uint32_t argIndex = 0;

        z3::expr_vector z3argComparisons( c );
        z3argComparisons.resize( argCount );

        z3::expr_vector z3argsVars( c );
        z3argsVars.resize( argCount );

        for( auto&& arg : gfa.args() )
        {
            auto argConst =
                BuildZ3ConstantFromType( b, arg.type, format( "gca{}_{}", argIndex, newUid ) ).expr;
            z3args.emplace_back( argConst );
            z3argsVars.set( argIndex, argConst );

            auto comp = argConst == arg.expr;
            z3argComparisons.set( argIndex, comp );

            ++argIndex;
        }

        auto newApply = gfDecl.z3decl( z3args.size(), z3args.data() );

        z3args[0] = c.int_val( oldUid );
        auto previousApply = gfDecl.z3decl( z3args.size(), z3args.data() );

        // AAAAAAAH
        b.add( z3::forall( z3argsVars,
            z3::implies( predicate,
                newApply
                    == z3::ite( z3::mk_and( z3argComparisons ),
                        Coerce( expr, { newApply, gfDecl.returnType, gfa.locationId() } ),
                        previousApply ) ) ) );

    }

    setCurrentUidForBasicBlock( bbIndex, gfIndex, newUid );
}

optional< Z3Val > GFCTracker::retrieve(
    Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa )
{
    if( bbIndex == b.currentBBIndex() )
        return nullopt;

    if( bbIndex == ~0U )
        bbIndex = b.currentBBIndex();

    auto gfDecl = GhostFuncDeclCache::GetInstance()->getDecl( b.context(), gfa.func() );
    if( !gfDecl )
        return nullopt;

    auto gft = *FromValue< builtins::FuncType >( *EIRToValue( gfa.func().type() ) );

    uint32_t gfIndex = getGFIndex( gfa );
    auto uid = getCurrentUidForBasicBlock( bbIndex, gfIndex );
    if( uid )
        return Z3Val{ buildFunctionApplication( b, uid, *gfDecl, gfa ),
            *EIRToValue( gft.returnType() ), gfa.locationId() };

    if( !b.cfg() )
        return nullopt;

    uid = b.newUniqueId();
    auto& c = GetZ3Context();

    optional< z3::expr > lhsExpr;
    llvm::SmallVector< z3::expr, 8 > z3args;
    z3::expr_vector z3argsVars( c );
    optional< z3::expr > newApply;

    // Model the ssa phi operation by creating a series of equality assertions, each implied by
    // one of the possible incoming edge condition for the current basic block.
    if( b.remapper() )
    {
        b.remapper()->forEachIncomingEdge( bbIndex,
            [&]( auto&& predIndex, auto&& expr )
            {
                if( auto predVal = retrieve( b, predIndex, gfa ) )
                {
                    if( !lhsExpr )
                        lhsExpr = buildFunctionApplication( b, uid, *gfDecl, gfa );

                    if( z3args.empty() )
                    {
                        auto argCount = gfa.args().size();

                        z3args.reserve( argCount + 1 );
                        z3args.emplace_back( c.int_val( uid ) );

                        z3argsVars.resize( argCount );

                        uint32_t argIndex = 0;

                        for( auto&& arg : gfa.args() )
                        {
                            auto argConst = BuildZ3ConstantFromType(
                                b, arg.type, format( "gca{}_{}", argIndex, uid ) )
                                                .expr;
                            z3args.emplace_back( argConst );
                            z3argsVars.set( argIndex, argConst );

                            ++argIndex;
                        }

                        newApply = gfDecl->z3decl( z3args.size(), z3args.data() );
                    }

                    auto predUid = getCurrentUidForBasicBlock( predIndex, gfIndex );
                    z3args[0] = c.int_val( predUid );
                    auto previousApply = gfDecl->z3decl( z3args.size(), z3args.data() );

                    b.add( z3::forall( z3argsVars,
                        z3::implies( c.bool_const( format( "e{}_{}", predIndex, bbIndex ).c_str() ),
                            *newApply == previousApply ) ) );
                }
            } );
    }

    if( !lhsExpr )
        return nullopt;

    setCurrentUidForBasicBlock( bbIndex, gfIndex, uid );
    setForBasicBlock( b, bbIndex, gfa, gfIndex, *gfDecl, move( *lhsExpr ) );
Changes to bs/verify/gfctracker.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
#ifndef GOOSE_VERIFY_GFCTRACKER_H
#define GOOSE_VERIFY_GFCTRACKER_H

namespace goose::verify
{
    class Builder;

    class GFCTracker
    {
        public:
            GFCTracker( size_t size ) :
                m_gfcStorage( size )
            {}



            optional< Z3Val > retrieve( Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa );
            void set( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val );

        private:
            uint32_t getGFIndex( const GhostFuncApplication& gfa );

            z3::expr buildFunctionApplication( Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const Value& gf );
            z3::expr buildFunctionApplication( Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const GhostFuncApplication& gfa );


            uint32_t getCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex ) const;
            void setCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex, uint32_t uid );

            void setForBasicBlock( Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa,
                uint32_t gfIndex, const GhostFuncDeclCache::GFDecl& gfDecl, z3::expr&& expr );

            // Each ghost func have a unique index within the current function.
            // (0: invalid index)
            unordered_map< Value, uint32_t > m_ghostFuncIndices;

            // For each ghostfunc closure, store its last index.
            using GFCState = llvm::SmallVector< uint32_t, 16 >;
            using GFCStorage = cir::TempStorage< GFCState >;

            GFCStorage m_gfcStorage;
    };
}


#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
#ifndef GOOSE_VERIFY_GFCTRACKER_H
#define GOOSE_VERIFY_GFCTRACKER_H

namespace goose::verify
{
    class Builder;

    class GFCTracker
    {
      public:
        GFCTracker( size_t size ) :
            m_gfcStorage( size )

        {
        }

        optional< Z3Val > retrieve( Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa );
        void set( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val );

      private:
        uint32_t getGFIndex( const GhostFuncApplication& gfa );
        z3::expr buildFunctionApplication(
            Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const Value& gf );
        z3::expr buildFunctionApplication( Builder& b, uint32_t uid,
            const GhostFuncDeclCache::GFDecl& gfDecl, const GhostFuncApplication& gfa );

        uint32_t getCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex ) const;
        void setCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex, uint32_t uid );

        void setForBasicBlock( Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa,
            uint32_t gfIndex, const GhostFuncDeclCache::GFDecl& gfDecl, z3::expr&& expr );

        // Each ghost func have a unique index within the current function.
        // (0: invalid index)
        unordered_map< Value, uint32_t > m_ghostFuncIndices;

        // For each ghostfunc closure, store its last index.
        using GFCState = llvm::SmallVector< uint32_t, 16 >;
        using GFCStorage = cir::TempStorage< GFCState >;

        GFCStorage m_gfcStorage;
    };

} // namespace goose::verify

#endif
Changes to bs/verify/ghostfunc.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
53
54
55
56
57
58
59
60
61
62
63
64
65

66
67
68

GhostFuncDeclCache* GhostFuncDeclCache::GetInstance()
{
    static unique_ptr< GhostFuncDeclCache > gfdcache( new GhostFuncDeclCache() );
    return gfdcache.get();
}

optional< GhostFuncDeclCache::GFDecl > GhostFuncDeclCache::getDecl( const sema::Context& c, const Value& gf )

{
    auto it = m_decls.find( gf );
    if( it != m_decls.end() )
        return it->second;

    auto fdecl = createDecl( c, gf );
    if( !fdecl )
        return nullopt;

    m_decls.emplace( gf, *fdecl );
    return fdecl;
}

optional< GhostFuncDeclCache::GFDecl > GhostFuncDeclCache::createDecl( const sema::Context& c, const Value& gf )

{
    auto f = *FromValue< builtins::Func >( gf );
    if( !f.cir() )
        return nullopt;

    auto mangledName = codegen::Mangle( f.cir()->identity() );
    if( !mangledName )
        return nullopt;

    const auto& ft = f.type();

    auto& tc = TypeCache::GetInstance();

    // Return type sort (aka range)
    auto rtinfo = tc.getTypeInfo( c, ft.returnType() );
    if( !rtinfo || !rtinfo->sort )
        return nullopt;

    llvm::SmallVector< z3::sort, 8 > domain;    // param sorts aka domain
    domain.reserve( VecSize( ft.params() ) + 1 );
    domain.emplace_back( GetZ3Context().int_sort() );

    const auto& fli = ft.loweringInfos();
    for( const auto& loweredType : fli->loweredParamTypes )
    {
        auto tinfo = tc.getTypeInfo( c, ValueToEIR( loweredType ) );
        if( !tinfo || !tinfo->sort )
            return nullopt;

        domain.emplace_back( *tinfo->sort );
    }

    return GFDecl
    {
        *EIRToValue( ft.returnType() ),

        GetZ3Context().function( mangledName->c_str(), domain.size(), domain.data(), *rtinfo->sort )
    };
}







|
>













|
>


















|













<
<
|
>
|
<

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

GhostFuncDeclCache* GhostFuncDeclCache::GetInstance()
{
    static unique_ptr< GhostFuncDeclCache > gfdcache( new GhostFuncDeclCache() );
    return gfdcache.get();
}

optional< GhostFuncDeclCache::GFDecl > GhostFuncDeclCache::getDecl(
    const sema::Context& c, const Value& gf )
{
    auto it = m_decls.find( gf );
    if( it != m_decls.end() )
        return it->second;

    auto fdecl = createDecl( c, gf );
    if( !fdecl )
        return nullopt;

    m_decls.emplace( gf, *fdecl );
    return fdecl;
}

optional< GhostFuncDeclCache::GFDecl > GhostFuncDeclCache::createDecl(
    const sema::Context& c, const Value& gf )
{
    auto f = *FromValue< builtins::Func >( gf );
    if( !f.cir() )
        return nullopt;

    auto mangledName = codegen::Mangle( f.cir()->identity() );
    if( !mangledName )
        return nullopt;

    const auto& ft = f.type();

    auto& tc = TypeCache::GetInstance();

    // Return type sort (aka range)
    auto rtinfo = tc.getTypeInfo( c, ft.returnType() );
    if( !rtinfo || !rtinfo->sort )
        return nullopt;

    llvm::SmallVector< z3::sort, 8 > domain; // param sorts aka domain
    domain.reserve( VecSize( ft.params() ) + 1 );
    domain.emplace_back( GetZ3Context().int_sort() );

    const auto& fli = ft.loweringInfos();
    for( const auto& loweredType : fli->loweredParamTypes )
    {
        auto tinfo = tc.getTypeInfo( c, ValueToEIR( loweredType ) );
        if( !tinfo || !tinfo->sort )
            return nullopt;

        domain.emplace_back( *tinfo->sort );
    }



    return GFDecl{ *EIRToValue( ft.returnType() ),
        GetZ3Context().function(
            mangledName->c_str(), domain.size(), domain.data(), *rtinfo->sort ) };

}
Changes to bs/verify/ghostfunc.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
#ifndef GOOSE_VERIFY_GHOSTFUNC_H
#define GOOSE_VERIFY_GHOSTFUNC_H

namespace goose::verify
{
    class GhostFuncDeclCache
    {
        public:
            static GhostFuncDeclCache* GetInstance();

            struct GFDecl
            {
                eir::Value returnType;
                z3::func_decl z3decl;
            };

            optional< GFDecl > getDecl( const sema::Context& c, const Value& gf );

        private:
            optional< GFDecl > createDecl( const sema::Context& c, const Value& gf );

            unordered_map< Value, GFDecl > m_decls;
    };

    class GhostFuncApplication
    {
        public:
            GhostFuncApplication( const cir::Value& func, LocationId loc ) :
                m_func( func ),
                m_loc( loc )
            {}



            GhostFuncApplication( cir::Value&& func, LocationId loc ) :
                m_func( move( func ) ),
                m_loc( loc )
            {}



            const auto& func() const { return m_func; }

            const auto& args() const { return m_args; }

            auto& args() { return m_args; }

            auto locationId() const { return m_loc; }

        private:
            eir::Value m_func;
            llvm::SmallVector< Z3Val, 4 > m_args;
            LocationId m_loc;
    };
}


#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
#ifndef GOOSE_VERIFY_GHOSTFUNC_H
#define GOOSE_VERIFY_GHOSTFUNC_H

namespace goose::verify
{
    class GhostFuncDeclCache
    {
      public:
        static GhostFuncDeclCache* GetInstance();

        struct GFDecl
        {
            eir::Value returnType;
            z3::func_decl z3decl;
        };

        optional< GFDecl > getDecl( const sema::Context& c, const Value& gf );

      private:
        optional< GFDecl > createDecl( const sema::Context& c, const Value& gf );

        unordered_map< Value, GFDecl > m_decls;
    };

    class GhostFuncApplication
    {
      public:
        GhostFuncApplication( const cir::Value& func, LocationId loc ) :
            m_func( func ),
            m_loc( loc )

        {
        }

        GhostFuncApplication( cir::Value&& func, LocationId loc ) :
            m_func( move( func ) ),
            m_loc( loc )

        {
        }

        const auto& func() const { return m_func; }

        const auto& args() const { return m_args; }

        auto& args() { return m_args; }

        auto locationId() const { return m_loc; }

      private:
        eir::Value m_func;
        llvm::SmallVector< Z3Val, 4 > m_args;
        LocationId m_loc;
    };

} // namespace goose::verify

#endif
Changes to bs/verify/helpers.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
#ifndef GOOSE_HELPERS_H
#define GOOSE_HELPERS_H

namespace goose::verify
{
    template< typename F >
    void ForEachPredicate( Builder& b, const Term& t, const z3::expr& valExpr, F&& func );

    template< typename F >
    void ForEachPredicate( Builder& b, const Value& type, const z3::expr& valExpr, F&& func );

    // A modified version of z3::context::tuple_sort that returns both the tuple constructor's func_decl and the tuple's sort because

    // I don't understand how I am supposed to get the tuple's sort when using the original C++ function, especially since it seems
    // straightforward in the C api (other than the normal suckage expected from a C api).
    //
    // Maybe I'm just missing something but it's hard to tell from the barren doxygen dump offered by z3 in lieu of api documentation

    template< typename NameVec, typename SortVec, typename ProjVec > static inline pair< z3::sort, z3::func_decl >

    mkZ3TupleSort( const char* name, unsigned n, const NameVec& names, const SortVec& sorts, ProjVec& projs )
    {
        auto& ctx = GetZ3Context();

        z3::array<Z3_symbol> _names(n);
        z3::array<Z3_sort> _sorts(n);

        for( unsigned i = 0; i < n; ++i )
        {
            _names[i] = Z3_mk_string_symbol( ctx, names[i].c_str() );
            _sorts[i] = sorts[i];
        }

        z3::array<Z3_func_decl> _projs(n);
        Z3_symbol _name = Z3_mk_string_symbol(ctx, name);
        Z3_func_decl tuple;

        z3::sort _s = to_sort(ctx, Z3_mk_tuple_sort(ctx, _name, n, _names.ptr(), _sorts.ptr(), &tuple, _projs.ptr()));
        ctx.check_error();

        for( unsigned i = 0; i < n; ++i )
            projs.emplace_back( z3::func_decl(ctx, _projs[i]) );

        return make_pair( move( _s ), z3::func_decl(ctx, tuple) );
    }
}

#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
#ifndef GOOSE_HELPERS_H
#define GOOSE_HELPERS_H

namespace goose::verify
{
    template< typename F >
    void ForEachPredicate( Builder& b, const Term& t, const z3::expr& valExpr, F&& func );

    template< typename F >
    void ForEachPredicate( Builder& b, const Value& type, const z3::expr& valExpr, F&& func );

    // A modified version of z3::context::tuple_sort that returns both the tuple constructor's
    // func_decl and the tuple's sort because I don't understand how I am supposed to get the
    // tuple's sort when using the original C++ function, especially since it seems straightforward
    // in the C api (other than the normal suckage expected from a C api).
    //
    // Maybe I'm just missing something but it's hard to tell from the barren doxygen dump offered
    // by z3 in lieu of api documentation
    template< typename NameVec, typename SortVec, typename ProjVec >
    static inline pair< z3::sort, z3::func_decl > mkZ3TupleSort(
        const char* name, unsigned n, const NameVec& names, const SortVec& sorts, ProjVec& projs )
    {
        auto& ctx = GetZ3Context();

        z3::array< Z3_symbol > _names( n );
        z3::array< Z3_sort > _sorts( n );

        for( unsigned i = 0; i < n; ++i )
        {
            _names[i] = Z3_mk_string_symbol( ctx, names[i].c_str() );
            _sorts[i] = sorts[i];
        }

        z3::array< Z3_func_decl > _projs( n );
        Z3_symbol _name = Z3_mk_string_symbol( ctx, name );
        Z3_func_decl tuple;
        z3::sort _s = to_sort( ctx,
            Z3_mk_tuple_sort( ctx, _name, n, _names.ptr(), _sorts.ptr(), &tuple, _projs.ptr() ) );
        ctx.check_error();

        for( unsigned i = 0; i < n; ++i )
            projs.emplace_back( z3::func_decl( ctx, _projs[i] ) );

        return make_pair( move( _s ), z3::func_decl( ctx, tuple ) );
    }
} // namespace goose::verify

#endif
Changes to bs/verify/helpers.inl.
20
21
22
23
24
25
26
27

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
        if( !optTypePreds || !*optTypePreds )
            return;

        auto& tp = **optTypePreds;
        const auto* prevValPH = b.retrievePlaceholder( "@val"_sid );
        b.setPlaceholder( "@val"_sid, valExpr );

        tp.forEach( [&]( auto&& p )

        {
            auto zv = BuildZ3ExprFromValue( b, p );
            func( zv.expr, p.locationId() );

            return true;
        } );

        if( prevValPH )
            b.setPlaceholder( "@val"_sid, *prevValPH );
        else
            b.unsetPlaceholder( "@val"_sid );
    }
}

#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
        if( !optTypePreds || !*optTypePreds )
            return;

        auto& tp = **optTypePreds;
        const auto* prevValPH = b.retrievePlaceholder( "@val"_sid );
        b.setPlaceholder( "@val"_sid, valExpr );

        tp.forEach(
            [&]( auto&& p )
            {
                auto zv = BuildZ3ExprFromValue( b, p );
                func( zv.expr, p.locationId() );

                return true;
            } );

        if( prevValPH )
            b.setPlaceholder( "@val"_sid, *prevValPH );
        else
            b.unsetPlaceholder( "@val"_sid );
    }
} // namespace goose::verify

#endif
Changes to bs/verify/instrseq.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
#include "verify.h"
#include "builtins/builtins.h"
#include "diagnostics/diagnostics.h"
#include "helpers.inl"

using namespace goose;
using namespace goose::verify;
using namespace goose::diagnostics;

namespace
{
    bool CheckAssertion( z3::solver& solver, const z3::expr& expr, const z3::expr& exprToCheck, LocationId locationId )

    {
        if( verify::Func::TraceMode() )
            return true;

        ProfileZoneScoped;

        z3::expr_vector exprVec( GetZ3Context() );
        exprVec.push_back( exprToCheck );

        solver.push();

        bool result = false;

        switch( solver.check( exprVec ) )
        {
            case z3::check_result::unsat:
                // We are using the solver to try and find a counter example,
                // so if it's unsat, it's good.

                if( verify::Func::DumpSolverOnSuccess() )

                    cout << "Solver dump:\n" << solver << "check_unsat: " << exprToCheck << endl << endl;


                result = true;
                break;

            case z3::check_result::sat:
                DiagnosticsManager::GetInstance().emitErrorMessage( locationId, "assert"_sid,
                    "this condition may not be met." );

                if( verify::Func::DumpSolverOnFailure() )
                {

                    cout << "Solver dump:\n" << solver << "check_unsat: " << exprToCheck << endl << endl;

                    cout << "Model dump:\n" << solver.get_model() << endl << endl;
                }

                result = false;
                break;

            default:
                DiagnosticsManager::GetInstance().emitErrorMessage( locationId, "assert"_sid,
                    "couldn't verify this condition." );

                if( verify::Func::DumpSolverOnFailure() )

                    cout << "Solver dump:\n" << solver << "check_unsat: " << exprToCheck << endl << endl;


                result = false;
                break;
        }

        solver.pop();
        return result;
    }

}

namespace goose::verify
{
    Result VerifyInstrSeq( const sema::Context& c, const InstrSeq& is )
    {
        z3::solver solver( GetZ3Context(), z3::solver::simple() );
        Builder b( c, solver, 0 );

        if( Func::TraceMode() )
        {
            cout << "=== Checking instr seq ===\n";
            b.setTraceMode( true );
        }

        bool passes = true;


        b.setAssertionHandler( [&]( auto&& expr, auto&& exprToCheck, LocationId locationId )
        {
            if( !CheckAssertion( solver, expr, exprToCheck, locationId ) )
            {
                passes = false;
                return false;
            }

            return true;
        } );

        if( !BuildZ3Op( b, is ) )
            return Result::Unverifiable;

        return passes ? Result::Pass : Result::Fails;
    }
}











|
>




















>
|
>





|
|



>
|
>







|
|


>
|
>









|
















>
|
|
|
|
|
|
|

|
|






|
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
#include "verify.h"
#include "builtins/builtins.h"
#include "diagnostics/diagnostics.h"
#include "helpers.inl"

using namespace goose;
using namespace goose::verify;
using namespace goose::diagnostics;

namespace
{
    bool CheckAssertion( z3::solver& solver, const z3::expr& expr, const z3::expr& exprToCheck,
        LocationId locationId )
    {
        if( verify::Func::TraceMode() )
            return true;

        ProfileZoneScoped;

        z3::expr_vector exprVec( GetZ3Context() );
        exprVec.push_back( exprToCheck );

        solver.push();

        bool result = false;

        switch( solver.check( exprVec ) )
        {
            case z3::check_result::unsat:
                // We are using the solver to try and find a counter example,
                // so if it's unsat, it's good.

                if( verify::Func::DumpSolverOnSuccess() )
                    cout << "Solver dump:\n"
                         << solver << "check_unsat: " << exprToCheck << endl
                         << endl;

                result = true;
                break;

            case z3::check_result::sat:
                DiagnosticsManager::GetInstance().emitErrorMessage(
                    locationId, "assert"_sid, "this condition may not be met." );

                if( verify::Func::DumpSolverOnFailure() )
                {
                    cout << "Solver dump:\n"
                         << solver << "check_unsat: " << exprToCheck << endl
                         << endl;
                    cout << "Model dump:\n" << solver.get_model() << endl << endl;
                }

                result = false;
                break;

            default:
                DiagnosticsManager::GetInstance().emitErrorMessage(
                    locationId, "assert"_sid, "couldn't verify this condition." );

                if( verify::Func::DumpSolverOnFailure() )
                    cout << "Solver dump:\n"
                         << solver << "check_unsat: " << exprToCheck << endl
                         << endl;

                result = false;
                break;
        }

        solver.pop();
        return result;
    }

} // namespace

namespace goose::verify
{
    Result VerifyInstrSeq( const sema::Context& c, const InstrSeq& is )
    {
        z3::solver solver( GetZ3Context(), z3::solver::simple() );
        Builder b( c, solver, 0 );

        if( Func::TraceMode() )
        {
            cout << "=== Checking instr seq ===\n";
            b.setTraceMode( true );
        }

        bool passes = true;

        b.setAssertionHandler(
            [&]( auto&& expr, auto&& exprToCheck, LocationId locationId )
            {
                if( !CheckAssertion( solver, expr, exprToCheck, locationId ) )
                {
                    passes = false;
                    return false;
                }

                return true;
            } );

        if( !BuildZ3Op( b, is ) )
            return Result::Unverifiable;

        return passes ? Result::Pass : Result::Fails;
    }
} // namespace goose::verify
Changes to bs/verify/loop.cpp.
1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
17
#include "verify.h"
#include "builtins/builtins.h"
using namespace goose::diagnostics;

// This is an implementation of the "Software Verification Using k-Induction" algorithm.
// http://www.cprover.org/kinduction/

namespace goose::verify
{

    bool Func::checkLoop( const cir::BasicBlock& header, queue< const BasicBlock* >& parentWorkQueue )
    {
        ProfileZoneScoped;

        if( ms_TraceMode )
            cout << "  == Checking loop " << header.index() << endl;

        DiagnosticsContext dc( header.locationId(), "In this loop." );









>
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "verify.h"
#include "builtins/builtins.h"
using namespace goose::diagnostics;

// This is an implementation of the "Software Verification Using k-Induction" algorithm.
// http://www.cprover.org/kinduction/

namespace goose::verify
{
    bool Func::checkLoop(
        const cir::BasicBlock& header, queue< const BasicBlock* >& parentWorkQueue )
    {
        ProfileZoneScoped;

        if( ms_TraceMode )
            cout << "  == Checking loop " << header.index() << endl;

        DiagnosticsContext dc( header.locationId(), "In this loop." );
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
        {
            ++k;

            if( ms_TraceMode )
                cout << "  == Unrolling base case " << k << " for loop " << header.index() << endl;

            {
                // Unroll the base case just once for each increasing K: we save the state of the z3 solver
                // before generating the induction case, so we can incrementally add unrolling of the base
                // case one at a time to save some work.
                m_viz.nextLoopIteration();
                m_remapper.nextLoopIteration();
                if( !buildZ3Expressions( header, &parentWorkQueue ) )
                {
                    // If a counter example was found while emitting the base case,
                    // we know the verification failed and we can stop here.
                    completed = true;
                    break;
                }
            }

            // Push the solver state, builder state and remapper before unrolling the induction case,
            // so that we can rollback all of this for the next attempt, if any.
            m_solver.push();

            auto savedBuilder = m_builder;
            auto savedRemapper = m_remapper;

            // Set a different assertion handler for the induction case: we want to just emit them as assumptions.


            m_builder.setAssertionHandler( [&]( const z3::expr& expr, const z3::expr& exprToCheck, LocationId locationId )
            {
                m_builder.assume( expr );
                return true;
            } );

            VerifyViz::Guard vvg( m_viz );
            m_viz.beginLoopInduction();

            m_remapper.setLoopEdgesOnly();

            // Havoc all the addresses modified during the current loop and emit
            // the first unrolling for the induction case.
            m_remapper.nextLoopIteration();
            uint32_t firstInductionIterationHeaderIndex = m_remapper.remapBBId( header );
            m_builder.setCurrentBB( firstInductionIterationHeaderIndex );

            if( ms_TraceMode )
                cout << "  == Havocing modified storage locations for loop " << header.index() << endl;


            m_cfg->forEachDataLocationModifiedByLoop( header.index(), [&]( auto&& type, auto&& sloc )
            {

                HavocDataLocation( m_builder, firstInductionIterationHeaderIndex, type, sloc );
            } );

            if( ms_TraceMode )
                cout << "  == Unrolling first induction case for loop " << header.index() << endl;

            buildZ3Expressions( header, &parentWorkQueue );

            // Emit the rest of the induction case.
            uint32_t i = 1;
            while( i < k )
            {
                if( ms_TraceMode )
                    cout << "  == Unrolling induction case " << i << " for loop " << header.index() << endl;


                m_viz.nextLoopIteration();
                m_remapper.nextLoopIteration();
                buildZ3Expressions( header, &parentWorkQueue );
                ++i;
            }

            // For the final loop instance, if we aren't at the last value of k before giving up,
            // we want to resolve the assertions without emitting diagnostics:
            // if they fail it means we need to increase k and try again.
            // Otherwise, we want to use the regular assertion handler that will emit error
            // messages for all failed assertions.
            if( k == maxK )
                m_builder.setAssertionHandler( origAssHandler );
            else
            {

                m_builder.setAssertionHandler( [&]( const z3::expr& expr, const z3::expr& exprToCheck, LocationId locationId )
                {
                    if( ms_TraceMode )
                        return true;

                    z3::expr_vector exprVec( GetZ3Context() );
                    exprVec.push_back( exprToCheck );
                    bool result = m_solver.check( exprVec ) == z3::check_result::unsat;

                    if( ( !result && ms_DumpSolverOnFailure ) || ( result && ms_DumpSolverOnSuccess ) )


                        cout << "Solver dump:\n" << m_solver << "check_unsat_ind: " << exprToCheck << endl << endl;


                    return result;
                } );
            }

            m_viz.nextLoopIteration();
            m_remapper.nextLoopIteration();
            if( buildZ3Expressions( header, &parentWorkQueue ) && !m_builder.hasCheckFailed() )
            {
                vvg.commit();
                completed = true;

                // Emit the loop header one last time, but only with its exit edge
                // so that the results of the induction are exposed to the
                // rest of the verification process
                m_viz.nextLoopIteration();
                m_remapper.nextLoopIteration();
                m_remapper.setExitEdgesOnly();


                m_builder.setAssertionHandler( [&]( const z3::expr& expr, const z3::expr& exprToCheck, LocationId locationId )
                {
                    return true;
                } );

                buildZ3Expressions( header, &parentWorkQueue );
                m_builder.setAssertionHandler( origAssHandler );
                break;
            }

            m_solver.pop();

            m_remapper = move( savedRemapper );
            m_builder = move( savedBuilder );
        }

        m_remapper.endLoopUnrolling();

        m_viz.endLoopVerification();

        if( ms_TraceMode )
            cout << "  == Finished checking loop " << header.index() << endl;
        return completed && !m_builder.hasCheckFailed();
    }
}







|
|
|











|
|





|
>
>
|
|
|
|
|













|
>

|
<
>
|
|











|
>
















>
|
|
|
|

|
|
|

|
>
>
|
>

|
|
















>
|
<
|
<




















|
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
        {
            ++k;

            if( ms_TraceMode )
                cout << "  == Unrolling base case " << k << " for loop " << header.index() << endl;

            {
                // Unroll the base case just once for each increasing K: we save the state of the z3
                // solver before generating the induction case, so we can incrementally add
                // unrolling of the base case one at a time to save some work.
                m_viz.nextLoopIteration();
                m_remapper.nextLoopIteration();
                if( !buildZ3Expressions( header, &parentWorkQueue ) )
                {
                    // If a counter example was found while emitting the base case,
                    // we know the verification failed and we can stop here.
                    completed = true;
                    break;
                }
            }

            // Push the solver state, builder state and remapper before unrolling the induction
            // case, so that we can rollback all of this for the next attempt, if any.
            m_solver.push();

            auto savedBuilder = m_builder;
            auto savedRemapper = m_remapper;

            // Set a different assertion handler for the induction case: we want to just emit them
            // as assumptions.
            m_builder.setAssertionHandler(
                [&]( const z3::expr& expr, const z3::expr& exprToCheck, LocationId locationId )
                {
                    m_builder.assume( expr );
                    return true;
                } );

            VerifyViz::Guard vvg( m_viz );
            m_viz.beginLoopInduction();

            m_remapper.setLoopEdgesOnly();

            // Havoc all the addresses modified during the current loop and emit
            // the first unrolling for the induction case.
            m_remapper.nextLoopIteration();
            uint32_t firstInductionIterationHeaderIndex = m_remapper.remapBBId( header );
            m_builder.setCurrentBB( firstInductionIterationHeaderIndex );

            if( ms_TraceMode )
                cout << "  == Havocing modified storage locations for loop " << header.index()
                     << endl;

            m_cfg->forEachDataLocationModifiedByLoop( header.index(),

                [&]( auto&& type, auto&& sloc ) {
                    HavocDataLocation( m_builder, firstInductionIterationHeaderIndex, type, sloc );
                } );

            if( ms_TraceMode )
                cout << "  == Unrolling first induction case for loop " << header.index() << endl;

            buildZ3Expressions( header, &parentWorkQueue );

            // Emit the rest of the induction case.
            uint32_t i = 1;
            while( i < k )
            {
                if( ms_TraceMode )
                    cout << "  == Unrolling induction case " << i << " for loop " << header.index()
                         << endl;

                m_viz.nextLoopIteration();
                m_remapper.nextLoopIteration();
                buildZ3Expressions( header, &parentWorkQueue );
                ++i;
            }

            // For the final loop instance, if we aren't at the last value of k before giving up,
            // we want to resolve the assertions without emitting diagnostics:
            // if they fail it means we need to increase k and try again.
            // Otherwise, we want to use the regular assertion handler that will emit error
            // messages for all failed assertions.
            if( k == maxK )
                m_builder.setAssertionHandler( origAssHandler );
            else
            {
                m_builder.setAssertionHandler(
                    [&]( const z3::expr& expr, const z3::expr& exprToCheck, LocationId locationId )
                    {
                        if( ms_TraceMode )
                            return true;

                        z3::expr_vector exprVec( GetZ3Context() );
                        exprVec.push_back( exprToCheck );
                        bool result = m_solver.check( exprVec ) == z3::check_result::unsat;

                        if( ( !result && ms_DumpSolverOnFailure )
                            || ( result && ms_DumpSolverOnSuccess ) )
                            cout << "Solver dump:\n"
                                 << m_solver << "check_unsat_ind: " << exprToCheck << endl
                                 << endl;

                        return result;
                    } );
            }

            m_viz.nextLoopIteration();
            m_remapper.nextLoopIteration();
            if( buildZ3Expressions( header, &parentWorkQueue ) && !m_builder.hasCheckFailed() )
            {
                vvg.commit();
                completed = true;

                // Emit the loop header one last time, but only with its exit edge
                // so that the results of the induction are exposed to the
                // rest of the verification process
                m_viz.nextLoopIteration();
                m_remapper.nextLoopIteration();
                m_remapper.setExitEdgesOnly();

                m_builder.setAssertionHandler(
                    [&]( const z3::expr& expr, const z3::expr& exprToCheck, LocationId locationId )

                    { return true; } );


                buildZ3Expressions( header, &parentWorkQueue );
                m_builder.setAssertionHandler( origAssHandler );
                break;
            }

            m_solver.pop();

            m_remapper = move( savedRemapper );
            m_builder = move( savedBuilder );
        }

        m_remapper.endLoopUnrolling();

        m_viz.endLoopVerification();

        if( ms_TraceMode )
            cout << "  == Finished checking loop " << header.index() << endl;
        return completed && !m_builder.hasCheckFailed();
    }
} // namespace goose::verify
Changes to bs/verify/phi.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 "verify.h"
#include "builtins/builtins.h"

namespace goose::verify
{
    bool BuildZ3Op( Builder& b, const Phi& instr )
    {
        const auto* remapper = b.remapper();
        if( !remapper )
            return false;


        auto newVar = BuildZ3ConstantFromType( b, instr.type().get(), format( "v{}", b.newUniqueId() ) );

        auto& c = GetZ3Context();
        uint32_t bbIndex = b.currentBBIndex();

        // Model the ssa phi operation by creating a series of equality assertions, each implied by
        // one of the possible incoming edge condition for the current basic block.
        remapper->forEachIncomingEdge( bbIndex, [&]( auto&& predIndex, auto&& predExpr )

        {
            uint32_t origPredIndex = remapper->getOriginalBBIndex( predIndex );

            // Match the incoming edge with the correct one in the phi instruction.
            instr.forAllIncomings( [&]( auto&& bb, auto&& val )

            {
                if( bb->index() != origPredIndex )
                    return true;

                auto zv = BuildZ3ExprFromValue( b, val );

                b.add( z3::implies( c.bool_const( format( "e{}_{}", predIndex, bbIndex ).c_str() ),
                    newVar.expr == Coerce( zv, newVar ) ) );

                return true;
            } );
        } );

        b.setTemporary( instr.destIndex(), move( newVar ) );
        return true;
    }
}












>
|






|
>
|
|

|
|
>
|
|
|

|
>
|
|

|
|
|




<
>
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 "verify.h"
#include "builtins/builtins.h"

namespace goose::verify
{
    bool BuildZ3Op( Builder& b, const Phi& instr )
    {
        const auto* remapper = b.remapper();
        if( !remapper )
            return false;

        auto newVar =
            BuildZ3ConstantFromType( b, instr.type().get(), format( "v{}", b.newUniqueId() ) );

        auto& c = GetZ3Context();
        uint32_t bbIndex = b.currentBBIndex();

        // Model the ssa phi operation by creating a series of equality assertions, each implied by
        // one of the possible incoming edge condition for the current basic block.
        remapper->forEachIncomingEdge( bbIndex,
            [&]( auto&& predIndex, auto&& predExpr )
            {
                uint32_t origPredIndex = remapper->getOriginalBBIndex( predIndex );

                // Match the incoming edge with the correct one in the phi instruction.
                instr.forAllIncomings(
                    [&]( auto&& bb, auto&& val )
                    {
                        if( bb->index() != origPredIndex )
                            return true;

                        auto zv = BuildZ3ExprFromValue( b, val );
                        b.add( z3::implies(
                            c.bool_const( format( "e{}_{}", predIndex, bbIndex ).c_str() ),
                            newVar.expr == Coerce( zv, newVar ) ) );

                        return true;
                    } );
            } );

        b.setTemporary( instr.destIndex(), move( newVar ) );
        return true;
    }

} // namespace goose::verify
Changes to bs/verify/propositions.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
#include "verify.h"
#include "diagnostics/diagnostics.h"
#include "builtins/builtins.h"

using namespace goose::verify;
using namespace goose::diagnostics;

Propositions::Propositions( const sema::Context& c ) :
    m_context( c ),
    m_solver( GetZ3Context() ),
    m_builder( m_context, m_solver, 0 )
{}



bool Propositions::addPropositions( const ValueVec& assList )
{
    for( auto&& val : assList )
    {
        if( val.isPoison() )
            return false;

        if( val.type() != GetValueType< bool >() )
            continue;

        auto zv = TryBuildZ3ExprFromValue( m_builder, val );
        if( !zv )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( val.locationId(),
                "invalid proposition." );
            return false;
        }

        stringstream sstr;
        sstr << '$' << m_nextIndex++;

        auto boolCst = GetZ3Context().bool_const( sstr.str().c_str() );











<
>
>














|
|







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
#include "verify.h"
#include "diagnostics/diagnostics.h"
#include "builtins/builtins.h"

using namespace goose::verify;
using namespace goose::diagnostics;

Propositions::Propositions( const sema::Context& c ) :
    m_context( c ),
    m_solver( GetZ3Context() ),
    m_builder( m_context, m_solver, 0 )

{
}

bool Propositions::addPropositions( const ValueVec& assList )
{
    for( auto&& val : assList )
    {
        if( val.isPoison() )
            return false;

        if( val.type() != GetValueType< bool >() )
            continue;

        auto zv = TryBuildZ3ExprFromValue( m_builder, val );
        if( !zv )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage(
                val.locationId(), "invalid proposition." );
            return false;
        }

        stringstream sstr;
        sstr << '$' << m_nextIndex++;

        auto boolCst = GetZ3Context().bool_const( sstr.str().c_str() );
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
    auto unsatCore = m_solver.unsat_core();

    llvm::SmallVector< LocationId, 8 > errorLocations;

    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 propositions by location ids, because z3 doesn't
    // provide them in a deterministic order.
    sort( errorLocations.begin(), errorLocations.end() );

    for( auto&& locId : errorLocations )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage( locId,
            "this proposition is unsatisfiable." );
    }

    return false;
}







|













|
|




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 unsatCore = m_solver.unsat_core();

    llvm::SmallVector< LocationId, 8 > errorLocations;

    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 propositions by location ids, because z3 doesn't
    // provide them in a deterministic order.
    sort( errorLocations.begin(), errorLocations.end() );

    for( auto&& locId : errorLocations )
    {
        DiagnosticsManager::GetInstance().emitErrorMessage(
            locId, "this proposition is unsatisfiable." );
    }

    return false;
}
Changes to bs/verify/propositions.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
#ifndef GOOSE_VERIFY_PROPOSITIONS_H
#define GOOSE_VERIFY_PROPOSITIONS_H

namespace goose::verify
{
    // Checks that propositions are satisfiable,
    // and emits any necessary diagnostic.
    class Propositions
    {
        public:
            Propositions( const sema::Context& c );

            // Emits a diagnostic and returns false if any of the expression
            // can't be represented as a z3 expression.
            bool addPropositions( const ValueVec& assList );

            // Checks the satisfiability of the Propositionss. Emits diagnostics and return
            // false if they aren't.
            bool verify();

        private:
            const sema::Context& m_context;
            z3::solver m_solver;
            Builder m_builder;

            // Keep track of the ids of bool constants we generated to track which
            // Propositionss have failed, along with the location of the corresponding expressions.
            vector< pair< uint32_t, LocationId > > m_idAndLocs;

            uint32_t m_nextIndex = 0;
    };
}

#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
#ifndef GOOSE_VERIFY_PROPOSITIONS_H
#define GOOSE_VERIFY_PROPOSITIONS_H

namespace goose::verify
{
    // Checks that propositions are satisfiable,
    // and emits any necessary diagnostic.
    class Propositions
    {
      public:
        Propositions( const sema::Context& c );

        // Emits a diagnostic and returns false if any of the expression
        // can't be represented as a z3 expression.
        bool addPropositions( const ValueVec& assList );

        // Checks the satisfiability of the Propositionss. Emits diagnostics and return
        // false if they aren't.
        bool verify();

      private:
        const sema::Context& m_context;
        z3::solver m_solver;
        Builder m_builder;

        // Keep track of the ids of bool constants we generated to track which
        // Propositionss have failed, along with the location of the corresponding expressions.
        vector< pair< uint32_t, LocationId > > m_idAndLocs;

        uint32_t m_nextIndex = 0;
    };
} // namespace goose::verify

#endif
Changes to bs/verify/remapper.cpp.
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
        return bb.index();

    auto& lus = m_loopUnrollingStack.back();
    auto blUnrollId = make_pair( bb.index(), lus.currentUnrollIndex );
    return remap( lus.loopRemapping, blUnrollId );
}


bool Remapper::shouldIncludeEdge( EdgeFilter filter, const cir::BasicBlock& currentBB, const cir::BasicBlock& succBB )
{
    auto edgeCat = CFG::GetEdgeLoopCategory( currentBB, succBB );

    if( edgeCat != CFG::EdgeLoopCategory::None )
    {
        if( filter == EdgeFilter::LoopOnly && edgeCat != CFG::EdgeLoopCategory::LoopEdge )
            return false;

        if( filter == EdgeFilter::ExitOnly && edgeCat != CFG::EdgeLoopCategory::LoopExitEdge )
            return false;
    }

    return true;
}

uint32_t Remapper::remapOutgoingEdge( const cir::BasicBlock& currentBB, const cir::BasicBlock& succBB )

{
    if( !m_loopUnrollingStack.empty() )
    {
        const auto& lus = m_loopUnrollingStack.back();
        if( !shouldIncludeEdge( lus.edgeFilter, currentBB, succBB ) )
            return InvalidEdgeId;
    }

    if( succBB.isLoopHeader() )
    {
        // Exiting into a loop header. If it's an active loop, we are exiting into its next iteration.
        // Otherwise, we are exiting into its first iteration.
        auto it = find_if( m_loopUnrollingStack.rbegin(), m_loopUnrollingStack.rend(), [&]( auto&& lus )
        {
            return lus.loopId == succBB.index();
        } );

        if( it == m_loopUnrollingStack.rend() )
        {
            // First iteration of a new loop.
            auto blUnrollId = make_pair( succBB.index(), 1 );
            return remap( m_nextLoopRemapping, blUnrollId );
        }







>
|















|
>










|
|
|
<
|
<







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
        return bb.index();

    auto& lus = m_loopUnrollingStack.back();
    auto blUnrollId = make_pair( bb.index(), lus.currentUnrollIndex );
    return remap( lus.loopRemapping, blUnrollId );
}

bool Remapper::shouldIncludeEdge(
    EdgeFilter filter, const cir::BasicBlock& currentBB, const cir::BasicBlock& succBB )
{
    auto edgeCat = CFG::GetEdgeLoopCategory( currentBB, succBB );

    if( edgeCat != CFG::EdgeLoopCategory::None )
    {
        if( filter == EdgeFilter::LoopOnly && edgeCat != CFG::EdgeLoopCategory::LoopEdge )
            return false;

        if( filter == EdgeFilter::ExitOnly && edgeCat != CFG::EdgeLoopCategory::LoopExitEdge )
            return false;
    }

    return true;
}

uint32_t Remapper::remapOutgoingEdge(
    const cir::BasicBlock& currentBB, const cir::BasicBlock& succBB )
{
    if( !m_loopUnrollingStack.empty() )
    {
        const auto& lus = m_loopUnrollingStack.back();
        if( !shouldIncludeEdge( lus.edgeFilter, currentBB, succBB ) )
            return InvalidEdgeId;
    }

    if( succBB.isLoopHeader() )
    {
        // Exiting into a loop header. If it's an active loop, we are exiting into its next
        // iteration. Otherwise, we are exiting into its first iteration.
        auto it = find_if( m_loopUnrollingStack.rbegin(), m_loopUnrollingStack.rend(),

            [&]( auto&& lus ) { return lus.loopId == succBB.index(); } );


        if( it == m_loopUnrollingStack.rend() )
        {
            // First iteration of a new loop.
            auto blUnrollId = make_pair( succBB.index(), 1 );
            return remap( m_nextLoopRemapping, blUnrollId );
        }
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
    {
        // Exiting towards the function's non loop code: no remapping necessary.
        if( !succBB.loopId() )
            return succBB.index();

        // Exiting into a non loop header: if its part of an active loop, remap to the current
        // iteration of that loop.
        auto it = find_if( m_loopUnrollingStack.rbegin(), m_loopUnrollingStack.rend(), [&]( auto&& lus )
        {
            return lus.loopId == succBB.loopId();
        } );
        assert( it != m_loopUnrollingStack.rend() );

        auto& lus = *it;
        auto blUnrollId = make_pair( succBB.index(), lus.currentUnrollIndex );
        return remap( lus.loopRemapping, blUnrollId );
    }
}







|
<
|
<







77
78
79
80
81
82
83
84

85

86
87
88
89
90
91
92
    {
        // Exiting towards the function's non loop code: no remapping necessary.
        if( !succBB.loopId() )
            return succBB.index();

        // Exiting into a non loop header: if its part of an active loop, remap to the current
        // iteration of that loop.
        auto it = find_if( m_loopUnrollingStack.rbegin(), m_loopUnrollingStack.rend(),

            [&]( auto&& lus ) { return lus.loopId == succBB.loopId(); } );

        assert( it != m_loopUnrollingStack.rend() );

        auto& lus = *it;
        auto blUnrollId = make_pair( succBB.index(), lus.currentUnrollIndex );
        return remap( lus.loopRemapping, blUnrollId );
    }
}
Changes to bs/verify/remapper.h.
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
    using RemappingMap = unordered_map< BlockAndUnrollingId, uint32_t >;

    // Helper class that tracks the state of loop unrolling
    // and provides methods to remap basic block indices
    // to their current unrolled instances.
    class Remapper
    {
        public:
            Remapper( uint32_t firstRemappedId ) :
                m_nextUniqueId( firstRemappedId )
            {}



            static constexpr uint32_t InvalidEdgeId = ~0U;

            uint32_t remapBBId( const cir::BasicBlock& bb );

            uint32_t remapOutgoingEdge( const cir::BasicBlock& currentBB, const cir::BasicBlock& succBB );

            uint32_t getCurrentLoopId() const;

            void beginLoopUnrolling( uint32_t loopId );
            void endLoopUnrolling();
            void nextLoopIteration();

            void setExitEdgesOnly();
            void setLoopEdgesOnly();

            template< typename F >
            void forEachIncomingEdge( uint32_t bbid, F&& func ) const
            {
                auto begin = m_edges.lower_bound( { bbid, 0U } );
                auto end = m_edges.upper_bound( { bbid, ~0U } );

                for( auto it = begin; it != end; ++it )
                    func( it->first.second, it->second );
            }

            template< typename Z >
            void addEdge( uint32_t srcBBId, uint32_t destBBId, Z&& expr )
            {
                m_edges.emplace( pair{ destBBId, srcBBId }, forward< Z >( expr ) );
            }

            // Given a basic block index, if it's a remapped index (an invented index
            // for an unrolled basic block), return the original BB index.
            uint32_t getOriginalBBIndex( uint32_t remappedId ) const;

            // Indicate whether all the (possibly unrolled) predecessors of a
            // basic block have been processed by checking if the number of
            // generated edges matches the number of incoming edges of the block.
            bool areAllPredecessorsProcessed( const cir::BasicBlock& bb );

        private:
            enum class EdgeFilter
            {
                All,
                LoopOnly,
                ExitOnly
            };


            bool shouldIncludeEdge( EdgeFilter filter, const cir::BasicBlock& currentBB, const cir::BasicBlock& succBB );

            struct LoopUnrollingState
            {
                RemappingMap loopRemapping;
                uint32_t loopId = 0;
                uint32_t currentUnrollIndex = 0;
                EdgeFilter edgeFilter = EdgeFilter::All;
            };

            uint32_t remap( RemappingMap& rm, BlockAndUnrollingId blUnrollId );

            vector< LoopUnrollingState > m_loopUnrollingStack;

            // The remapping state for the next loop before we enter it
            // (so that we can store the remapping for its incoming edges before we begin
            // checking it)
            RemappingMap m_nextLoopRemapping;

            // For each edge of the CFG, store the z3 boolean expression
            // representing the condition for going through that edge.
            map< pair< uint32_t, uint32_t >, z3::expr > m_edges;

            // For each BB index "invented" by unrolling, store
            // the original index.
            unordered_map< uint32_t, uint32_t > m_originalBBIndices;

            uint32_t m_nextUniqueId = 0;
    };
}

#endif







|
|
|
<
|
>
>
|

|
>
|

|

|
|
|

|
|

<
|
|
|
|

|
|
|

<
|
|
|
|

|
|
|

|
|
|
|

|
|
|
|
|
|
|

>
|

|
|
|
|
|
|
|

|

|

|
|
|
|

|
|
|

|
|
|

|

|


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
    using RemappingMap = unordered_map< BlockAndUnrollingId, uint32_t >;

    // Helper class that tracks the state of loop unrolling
    // and provides methods to remap basic block indices
    // to their current unrolled instances.
    class Remapper
    {
      public:
        Remapper( uint32_t firstRemappedId ) :
            m_nextUniqueId( firstRemappedId )

        {
        }

        static constexpr uint32_t InvalidEdgeId = ~0U;

        uint32_t remapBBId( const cir::BasicBlock& bb );
        uint32_t remapOutgoingEdge(
            const cir::BasicBlock& currentBB, const cir::BasicBlock& succBB );

        uint32_t getCurrentLoopId() const;

        void beginLoopUnrolling( uint32_t loopId );
        void endLoopUnrolling();
        void nextLoopIteration();

        void setExitEdgesOnly();
        void setLoopEdgesOnly();


        template< typename F > void forEachIncomingEdge( uint32_t bbid, F&& func ) const
        {
            auto begin = m_edges.lower_bound( { bbid, 0U } );
            auto end = m_edges.upper_bound( { bbid, ~0U } );

            for( auto it = begin; it != end; ++it )
                func( it->first.second, it->second );
        }


        template< typename Z > void addEdge( uint32_t srcBBId, uint32_t destBBId, Z&& expr )
        {
            m_edges.emplace( pair{ destBBId, srcBBId }, forward< Z >( expr ) );
        }

        // Given a basic block index, if it's a remapped index (an invented index
        // for an unrolled basic block), return the original BB index.
        uint32_t getOriginalBBIndex( uint32_t remappedId ) const;

        // Indicate whether all the (possibly unrolled) predecessors of a
        // basic block have been processed by checking if the number of
        // generated edges matches the number of incoming edges of the block.
        bool areAllPredecessorsProcessed( const cir::BasicBlock& bb );

      private:
        enum class EdgeFilter
        {
            All,
            LoopOnly,
            ExitOnly
        };

        bool shouldIncludeEdge(
            EdgeFilter filter, const cir::BasicBlock& currentBB, const cir::BasicBlock& succBB );

        struct LoopUnrollingState
        {
            RemappingMap loopRemapping;
            uint32_t loopId = 0;
            uint32_t currentUnrollIndex = 0;
            EdgeFilter edgeFilter = EdgeFilter::All;
        };

        uint32_t remap( RemappingMap& rm, BlockAndUnrollingId blUnrollId );

        vector< LoopUnrollingState > m_loopUnrollingStack;

        // The remapping state for the next loop before we enter it
        // (so that we can store the remapping for its incoming edges before we begin
        // checking it)
        RemappingMap m_nextLoopRemapping;

        // For each edge of the CFG, store the z3 boolean expression
        // representing the condition for going through that edge.
        map< pair< uint32_t, uint32_t >, z3::expr > m_edges;

        // For each BB index "invented" by unrolling, store
        // the original index.
        unordered_map< uint32_t, uint32_t > m_originalBBIndices;

        uint32_t m_nextUniqueId = 0;
    };
} // namespace goose::verify

#endif
Changes to bs/verify/stack.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
#include "verify.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::builtins;

namespace goose::verify
{
    optional< Z3Val > Stack::PopAsZ3Val( Builder& b )
    {

        auto result = visit( [&]< typename T >( const T& elem ) -> optional< Z3Val >
        {
            if constexpr( is_same_v< T, Value > )
                return BuildZ3ExprFromValue( b, elem );
            else if constexpr( is_same_v< T, Address > )
                return BuildAddressExpr( b, elem );
            else
                return nullopt;

        }, m_stack.top() );

        if( result )
            m_stack.pop();

        return result;
    }
}










>
|
|
|
|
|
|
|
|
>
|






|
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
#include "verify.h"
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::builtins;

namespace goose::verify
{
    optional< Z3Val > Stack::PopAsZ3Val( Builder& b )
    {
        auto result = visit(
            [&]< typename T >( const T& elem ) -> optional< Z3Val >
            {
                if constexpr( is_same_v< T, Value > )
                    return BuildZ3ExprFromValue( b, elem );
                else if constexpr( is_same_v< T, Address > )
                    return BuildAddressExpr( b, elem );
                else
                    return nullopt;
            },
            m_stack.top() );

        if( result )
            m_stack.pop();

        return result;
    }
} // namespace goose::verify
Changes to bs/verify/stack.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
#ifndef GOOSE_VERIFY_STACK_H
#define GOOSE_VERIFY_STACK_H

namespace goose::verify
{
    class Stack
    {
        public:
            using Slot = variant< Value, Z3Val, Address, GhostFuncApplication, Term, StringId >;

            template< typename T >
            void push( T&& x )
            {
                m_stack.push( forward< T >( x ) );
            }

            template< typename T >
            optional< T > pop( Builder& b )
            {
                if( m_stack.empty() )
                    return nullopt;

                if constexpr( is_same_v< T, Slot > )
                {
                    optional< Slot > result = move( m_stack.top() );
                    m_stack.pop();
                    return result;
                }
                else
                {
                    if( !holds_alternative< T >( m_stack.top() ) )
                    {
                        if constexpr( is_same_v< T, Z3Val > )
                            return PopAsZ3Val( b );
                        else
                            return nullopt;
                    }

                    optional< T > result = move( get< T >( m_stack.top() ) );
                    m_stack.pop();
                    return result;
                }
            }

        private:
            optional< Z3Val > PopAsZ3Val( Builder& b );

            stack< Slot > m_stack;
    };
}

#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
#ifndef GOOSE_VERIFY_STACK_H
#define GOOSE_VERIFY_STACK_H

namespace goose::verify
{
    class Stack
    {
      public:
        using Slot = variant< Value, Z3Val, Address, GhostFuncApplication, Term, StringId >;

        template< typename T > void push( T&& x ) { m_stack.push( forward< T >( x ) ); }





        template< typename T > optional< T > pop( Builder& b )

        {
            if( m_stack.empty() )
                return nullopt;

            if constexpr( is_same_v< T, Slot > )
            {
                optional< Slot > result = move( m_stack.top() );
                m_stack.pop();
                return result;
            }
            else
            {
                if( !holds_alternative< T >( m_stack.top() ) )
                {
                    if constexpr( is_same_v< T, Z3Val > )
                        return PopAsZ3Val( b );
                    else
                        return nullopt;
                }

                optional< T > result = move( get< T >( m_stack.top() ) );
                m_stack.pop();
                return result;
            }
        }

      private:
        optional< Z3Val > PopAsZ3Val( Builder& b );

        stack< Slot > m_stack;
    };
} // namespace goose::verify

#endif
Changes to bs/verify/storage.cpp.
65
66
67
68
69
70
71

72
73
74
75
76
77
78
79
            val = LoadFromBaseAddress( b, addr );

        if( !val )
            return nullopt;

        for( uint32_t i = 1; i < addr.path().size(); ++i )
        {

            auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), ValueToEIR( val->type ) );
            if( !tinfo )
                return nullopt;

            // The aggregate types that we handle here are:
            //  - tuples
            //  - addresses (we treat them as a tuple of (lifetime, origin, path))
            //







>
|







65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
            val = LoadFromBaseAddress( b, addr );

        if( !val )
            return nullopt;

        for( uint32_t i = 1; i < addr.path().size(); ++i )
        {
            auto tinfo =
                TypeCache::GetInstance().getTypeInfo( b.context(), ValueToEIR( val->type ) );
            if( !tinfo )
                return nullopt;

            // The aggregate types that we handle here are:
            //  - tuples
            //  - addresses (we treat them as a tuple of (lifetime, origin, path))
            //
122
123
124
125
126
127
128
129

130

131
132
133
134
135
136
137
138
    }

    optional< Z3Val > LoadFromAddress( Builder& b, const GhostFuncApplication& gfa )
    {
        return b.retrieveGFC( gfa );
    }

    optional< z3::expr > ModifyAggregate( Builder& b, const Z3Val& aggregate, const SelectPath& path, uint32_t index, Z3Val&& valToStore )

    {

        auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), ValueToEIR( aggregate.type ) );
        if( !tinfo )
            return nullopt;

        // The only aggregate type that we handle for now are tuples.
        // TODO: arrays
        auto elemCount = TupleTypeSize( aggregate.type );








|
>

>
|







123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
    }

    optional< Z3Val > LoadFromAddress( Builder& b, const GhostFuncApplication& gfa )
    {
        return b.retrieveGFC( gfa );
    }

    optional< z3::expr > ModifyAggregate( Builder& b, const Z3Val& aggregate,
        const SelectPath& path, uint32_t index, Z3Val&& valToStore )
    {
        auto tinfo =
            TypeCache::GetInstance().getTypeInfo( b.context(), ValueToEIR( aggregate.type ) );
        if( !tinfo )
            return nullopt;

        // The only aggregate type that we handle for now are tuples.
        // TODO: arrays
        auto elemCount = TupleTypeSize( aggregate.type );

147
148
149
150
151
152
153
154
155
156
157
158

159
160
161
162
163
164
165
166
            auto elemExpr = tinfo->proj( aggregate.expr, i );

            // Copy all elements from the existing value as is,
            // except for the one pointed to by the current path index.
            if( i == elemIndex )
            {
                // If we didn't reach the end of the path yet, recurse.
                // Otherwise, it means we finally reached the nested member that we wanted to modify,
                // so push the new value.
                if( ++index < path.size() )
                {
                    auto newElem = ModifyAggregate( b, Z3Val{ move( elemExpr ), *EIRToValue( elemType ), aggregate.loc },

                        path, index, move( valToStore ) );
                    if( !newElem )
                        return nullopt;

                    args.push_back( move( *newElem ) );
                }
                else
                    args.push_back( valToStore.expr );







|
|


|
>
|







150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
            auto elemExpr = tinfo->proj( aggregate.expr, i );

            // Copy all elements from the existing value as is,
            // except for the one pointed to by the current path index.
            if( i == elemIndex )
            {
                // If we didn't reach the end of the path yet, recurse.
                // Otherwise, it means we finally reached the nested member that we wanted to
                // modify, so push the new value.
                if( ++index < path.size() )
                {
                    auto newElem = ModifyAggregate( b,
                        Z3Val{ move( elemExpr ), *EIRToValue( elemType ), aggregate.loc }, path,
                        index, move( valToStore ) );
                    if( !newElem )
                        return nullopt;

                    args.push_back( move( *newElem ) );
                }
                else
                    args.push_back( valToStore.expr );
198
199
200
201
202
203
204

205

206
207
208
209
210
211
212
213
        // it means we are storing into a member (of a member) of an aggregate type.
        // In this case, we need to load the existing value of the aggregate,
        // rebuild it with the modified member, and store it.
        auto aggregate = LoadFromBaseAddress( b, addr );
        if( !aggregate )
            return;


        if( auto newAggregateToStore = ModifyAggregate( b, *aggregate, addr.path(), 1, move( val ) ) )

            StoreToBaseAddress( b, addr, Z3Val{ *newAggregateToStore, aggregate->type, addr.locationId() } );
    }

    void StoreToAddress( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val )
    {
        b.setGFC( gfa, move( val ) );
    }








>
|
>
|







202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
        // it means we are storing into a member (of a member) of an aggregate type.
        // In this case, we need to load the existing value of the aggregate,
        // rebuild it with the modified member, and store it.
        auto aggregate = LoadFromBaseAddress( b, addr );
        if( !aggregate )
            return;

        if( auto newAggregateToStore =
                ModifyAggregate( b, *aggregate, addr.path(), 1, move( val ) ) )
            StoreToBaseAddress(
                b, addr, Z3Val{ *newAggregateToStore, aggregate->type, addr.locationId() } );
    }

    void StoreToAddress( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val )
    {
        b.setGFC( gfa, move( val ) );
    }

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

282
283
284
285
286
287
288
289
290
291
292
293
294

295

        uint32_t i = 0;
        for( auto&& index : addr.path() )
        {
            auto zindex = zc.int_val( index ).unit();
            pathVec.set( i++, zindex );
        }

        return
        {
            TypeCache::GetInstance().addrCtor()(
                ltExpr,
                zc.int_val( static_cast< int >( addr.origin() ) ),
                z3::concat( pathVec )
            ),
            *EIRToValue( GetValueType< cir::Address >() ),
            addr.locationId()
        };
    }

    optional< verify::GhostFuncApplication > CIRGFAToVerifyGFA( Builder& b, const cir::GhostFuncApplication& cgfa )

    {
        verify::GhostFuncApplication vgfa( cgfa.func(), cgfa.locationId() );
        vgfa.args().reserve( cgfa.args().size() );

        for( auto&& a : cgfa.args() )
        {

            visit( [&]< typename T >( const T& x )
            {
                if constexpr( is_same_v< T, eir::Value > )
                {
                    vgfa.args().emplace_back( BuildZ3ExprFromValue( b, x ) );
                }
                else if constexpr( is_same_v< T, Address > )
                {
                    vgfa.args().emplace_back( BuildAddressExpr( b, x ) );
                }
            }, a );
        }

        if( vgfa.args().size() != cgfa.args().size() )
            return nullopt;

        return vgfa;
    }


    void HavocDataLocation( Builder& b, uint32_t bbIndex, const Term& type, const DataLocation& sloc )
    {
        auto valToStore = BuildZ3ConstantFromType( b, type, format( "v{}", b.newUniqueId() ) );


        visit( [&]< typename T >( const T& x )
        {
            if constexpr( is_same_v< T, Address > )
            {
                StoreToAddress( b, x, move( valToStore ) );
            }
            else if constexpr( is_same_v< T, cir::GhostFuncApplication >)
            {
                if( auto vgfa = CIRGFAToVerifyGFA( b, x ) )
                    StoreToAddress( b, *vgfa, move( valToStore ) );
            }
        }, sloc );
    }

}








<
<
|
<
|
<
<
|
<
<


|
>






>
|
|
|
<
|
<
|
<
|
|
|








>
|



>
|
|
|
|
|
|
|
|
|
|
|
<
|
>
|
>
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
282
283
284
285
286
287
288
289
290
291
292

293
294
295
296
        uint32_t i = 0;
        for( auto&& index : addr.path() )
        {
            auto zindex = zc.int_val( index ).unit();
            pathVec.set( i++, zindex );
        }



        return { TypeCache::GetInstance().addrCtor()( ltExpr,

                     zc.int_val( static_cast< int >( addr.origin() ) ), z3::concat( pathVec ) ),


            *EIRToValue( GetValueType< cir::Address >() ), addr.locationId() };


    }

    optional< verify::GhostFuncApplication > CIRGFAToVerifyGFA(
        Builder& b, const cir::GhostFuncApplication& cgfa )
    {
        verify::GhostFuncApplication vgfa( cgfa.func(), cgfa.locationId() );
        vgfa.args().reserve( cgfa.args().size() );

        for( auto&& a : cgfa.args() )
        {
            visit(
                [&]< typename T >( const T& x )
                {
                    if constexpr( is_same_v< T, eir::Value > )

                        vgfa.args().emplace_back( BuildZ3ExprFromValue( b, x ) );

                    else if constexpr( is_same_v< T, Address > )

                        vgfa.args().emplace_back( BuildAddressExpr( b, x ) );
                },
                a );
        }

        if( vgfa.args().size() != cgfa.args().size() )
            return nullopt;

        return vgfa;
    }

    void HavocDataLocation(
        Builder& b, uint32_t bbIndex, const Term& type, const DataLocation& sloc )
    {
        auto valToStore = BuildZ3ConstantFromType( b, type, format( "v{}", b.newUniqueId() ) );

        visit(
            [&]< typename T >( const T& x )
            {
                if constexpr( is_same_v< T, Address > )
                {
                    StoreToAddress( b, x, move( valToStore ) );
                }
                else if constexpr( is_same_v< T, cir::GhostFuncApplication > )
                {
                    if( auto vgfa = CIRGFAToVerifyGFA( b, x ) )
                        StoreToAddress( b, *vgfa, move( valToStore ) );
                }

            },
            sloc );
    }
} // namespace goose::verify
Changes to bs/verify/storage.h.
19
20
21
22
23
24
25

26
27

28
29
    extern optional< Z3Val > LoadFromAddress( Builder& b, const GhostFuncApplication& gfa );

    extern void StoreToAddress( Builder& b, const Address& addr, Z3Val&& val );
    extern void StoreToAddress( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val );

    extern Z3Val BuildAddressExpr( Builder& b, const Address& addr );


    extern void HavocDataLocation( Builder& b, uint32_t bbIndex, const Term& type, const DataLocation& sloc );
}


#endif







>
|
<
>


19
20
21
22
23
24
25
26
27

28
29
30
    extern optional< Z3Val > LoadFromAddress( Builder& b, const GhostFuncApplication& gfa );

    extern void StoreToAddress( Builder& b, const Address& addr, Z3Val&& val );
    extern void StoreToAddress( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val );

    extern Z3Val BuildAddressExpr( Builder& b, const Address& addr );

    extern void HavocDataLocation(
        Builder& b, uint32_t bbIndex, const Term& type, const DataLocation& sloc );

} // namespace goose::verify

#endif
Changes to bs/verify/terminator.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 "verify.h"
#include "builtins/builtins.h"
#include "diagnostics/diagnostics.h"
#include "helpers.inl"

using namespace goose::diagnostics;

namespace goose::verify
{
    bool Func::handleTerminator( uint32_t bbIndex, const cir::Terminator& t )
    {
        return visit( [&]( auto&& t )
        {
            return handleTerminator( bbIndex, t );
        }, t.content() );
    }

    bool Func::handleTerminator( uint32_t bbIndex, const cir::RetVoid& tr )
    {
        // Emit the "ensures" expressions as assertions.
        if( !m_func )
            return true;

        DiagnosticsContext dc( tr.locationId(), "When returning here." );

        Builder cb = m_builder;

        const auto& fvi = m_func->type().verifInfos();
        if( !fvi )
            return true;

        const auto& postConds = fvi->postConds();
        bool success = true;

        postConds->forEach( [&]( auto&& cond )

        {
            // Don't do any error handling here, it should already have been taken care of
            // by the condition verifier.
            if( auto zv = TryBuildZ3ExprFromValue( cb, cond ) )
            {
                bool succ = m_builder.checkAssertion( zv->expr, cond.locationId() );
                success = success && succ;
            }
        } );

        return success;
    }

    bool Func::handleTerminator( uint32_t bbIndex, const cir::Ret& tr )
    {
        auto retExpr = m_builder.pop();











|
<
<
<



















|
>
|
|
|
|
|
|
|
|
|







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
#include "verify.h"
#include "builtins/builtins.h"
#include "diagnostics/diagnostics.h"
#include "helpers.inl"

using namespace goose::diagnostics;

namespace goose::verify
{
    bool Func::handleTerminator( uint32_t bbIndex, const cir::Terminator& t )
    {
        return visit( [&]( auto&& t ) { return handleTerminator( bbIndex, t ); }, t.content() );



    }

    bool Func::handleTerminator( uint32_t bbIndex, const cir::RetVoid& tr )
    {
        // Emit the "ensures" expressions as assertions.
        if( !m_func )
            return true;

        DiagnosticsContext dc( tr.locationId(), "When returning here." );

        Builder cb = m_builder;

        const auto& fvi = m_func->type().verifInfos();
        if( !fvi )
            return true;

        const auto& postConds = fvi->postConds();
        bool success = true;

        postConds->forEach(
            [&]( auto&& cond )
            {
                // Don't do any error handling here, it should already have been taken care of
                // by the condition verifier.
                if( auto zv = TryBuildZ3ExprFromValue( cb, cond ) )
                {
                    bool succ = m_builder.checkAssertion( zv->expr, cond.locationId() );
                    success = success && succ;
                }
            } );

        return success;
    }

    bool Func::handleTerminator( uint32_t bbIndex, const cir::Ret& tr )
    {
        auto retExpr = m_builder.pop();
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

        if( retExpr )
        {
            // Set the @result placeholder for the func's post conditions
            cb.setPlaceholder( "@result"_sid, retExpr->expr );

            // Emit the return type's predicates as assertions.
            ForEachPredicate( cb, m_retType, retExpr->expr, [&]( auto&& z3expr, auto locId )
            {
                m_builder.checkAssertion( z3expr, locId );
            } );
        }

        const auto& fvi = m_func->type().verifInfos();
        if( !fvi )
            return true;

        const auto& postConds = fvi->postConds();
        bool success = true;

        postConds->forEach( [&]( auto&& cond )

        {
            // Don't do any error handling here, it should already have been taken care of
            // by the condition verifier.
            if( auto zv = TryBuildZ3ExprFromValue( cb, cond ) )
            {
                bool succ = m_builder.checkAssertion( zv->expr, cond.locationId() );
                success = success && succ;
            }
        } );

        return success;
    }

    bool Func::handleTerminator( uint32_t bbIndex, const cir::Branch& t )
    {
        const auto& bb = m_cfg->getBB( bbIndex );







|
<
|
<









|
>
|
|
|
|
|
|
|
|
|







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

        if( retExpr )
        {
            // Set the @result placeholder for the func's post conditions
            cb.setPlaceholder( "@result"_sid, retExpr->expr );

            // Emit the return type's predicates as assertions.
            ForEachPredicate( cb, m_retType, retExpr->expr,

                [&]( auto&& z3expr, auto locId ) { m_builder.checkAssertion( z3expr, locId ); } );

        }

        const auto& fvi = m_func->type().verifInfos();
        if( !fvi )
            return true;

        const auto& postConds = fvi->postConds();
        bool success = true;

        postConds->forEach(
            [&]( auto&& cond )
            {
                // Don't do any error handling here, it should already have been taken care of
                // by the condition verifier.
                if( auto zv = TryBuildZ3ExprFromValue( cb, cond ) )
                {
                    bool succ = m_builder.checkAssertion( zv->expr, cond.locationId() );
                    success = success && succ;
                }
            } );

        return success;
    }

    bool Func::handleTerminator( uint32_t bbIndex, const cir::Branch& t )
    {
        const auto& bb = m_cfg->getBB( bbIndex );
147
148
149
150
151
152
153
154

        uint32_t destBBId = m_remapper.remapOutgoingEdge( *bb, *pDestBB );
        m_viz.addEdge( srcBBId, destBBId );

        m_remapper.addEdge( srcBBId, destBBId, move( cond ) );
        return true;
    }
}







|
144
145
146
147
148
149
150
151

        uint32_t destBBId = m_remapper.remapOutgoingEdge( *bb, *pDestBB );
        m_viz.addEdge( srcBBId, destBBId );

        m_remapper.addEdge( srcBBId, destBBId, move( cond ) );
        return true;
    }
} // namespace goose::verify
Changes to bs/verify/type.cpp.
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

    m_typeInfos.emplace( type, *tinfo );
    return tinfo;
}

const TypeInfo& TypeCache::uninterpretedTypeInfo() const
{
    static TypeInfo UnintTypeInfo { m_uninterpretedSort,
        [&]( auto&& b )
        {

            return GetZ3Context().constant( format( "uc{}", b.newUniqueId() ).c_str(), m_uninterpretedSort );
        },
        [&]( auto&& b, auto&& val )
        {

            return GetZ3Context().constant( format( "uv{}", b.newUniqueId() ).c_str(), m_uninterpretedSort );
        },
        {}, {}
    };

    return UnintTypeInfo;
}

optional< TypeInfo > TypeCache::createTypeInfo( const sema::Context& c, const Term& type )
{
    // TODO: some types are missing and will be dealt with later on.
    if( type == GetValueType< bool >() )
    {
        return TypeInfo { GetZ3Context().bool_sort(),
            []( auto&& b )
            {
                return GetZ3Context().bool_const( format( "u{}", b.newUniqueId() ).c_str() );
            },
            []( auto&& b, auto&& val )
            {
                return GetZ3Context().bool_val( *FromValue< bool >( val ) );
            },
            {}, {}
        };
    }

    if( type == GetValueType< BigInt >() )
    {
        return TypeInfo { GetZ3Context().int_sort(),
            []( auto&& b )
            {
                return GetZ3Context().int_const( format( "bi{}", b.newUniqueId() ).c_str() );
            },
            []( auto&& b, auto&& val )
            {
                auto intVal = FromValue< BigInt >( val );

                if( intVal->fitsAsS64() )
                    return GetZ3Context().int_val( intVal->toS64() );
                else
                    return GetZ3Context().int_val( intVal->toString().c_str() );
            },
            {}, {}
        };
    }

    if( type == GetValueType< string >() )
    {
        return TypeInfo { GetZ3Context().string_sort(),
            []( auto&& b )
            {
                return GetZ3Context().string_const( format( "s{}", b.newUniqueId() ).c_str() );
            },
            []( auto&& b, auto&& val ) -> z3::expr
            {
                // TODO need to see how unicode convcersion works, does z3 do
                // it automatically?
                // we'll have to see when we start verifying strings in earnest
                // (for now it's just there so the verifier doesn't fail when
                // ecountering a string)
                if( val.isConstant() )
                    return GetZ3Context().string_val( *FromValue< string >( val ) );

                if( !BuildZ3Op( b, *val.cir() ) )
                    return GetZ3Context().string_const( format( "str{}", b.newUniqueId() ).c_str() );


                return b.pop()->expr;
            },
            {}, {}
        };
    }

    auto typeVal = EIRToValue( type );
    if( !typeVal )
        return nullopt;

    // Lower pointer types into addresses: we're only interested in knowing about aliasing, for now.
    if( type == GetValueType< Address >() || FromValue< PointerType >( *typeVal ) )
    {
        return TypeInfo { m_addressSort,
            [&]( auto&& b )
            {

                return GetZ3Context().constant( format( "ml{}", b.newUniqueId() ).c_str(), m_addressSort );
            },
            [&]( auto&& b, auto&& val )
            {
                // TODO: handle constant addresses (not needed for now, perhaps we'll never need it)
               // G_VAL_ASSERT( val, !val.isConstant() );

                if( val.isConstant() || !BuildZ3Op( b, *val.cir() ) )

                    return GetZ3Context().constant( format( "ml{}", b.newUniqueId() ).c_str(), m_addressSort );

                return b.pop()->expr;
            },
            [&]( auto&& tup, auto&& index )
            {
                assert( index < m_addressProjs.size() );
                return m_addressProjs[index]( tup );
            }, {}
        };
    }

    if( IsTupleType( *typeVal ) )
        return createTupleTypeInfo( c, *typeVal );

    if( IsSequenceType( *typeVal ) )
        return createSequenceTypeInfo( c, *typeVal );

    if( auto intType = FromValue< builtins::IntegerType >( *typeVal ) )
    {
        if( intType->m_signed )
        {
            return TypeInfo { GetZ3Context().int_sort(),
                []( auto&& b )
                {
                    return GetZ3Context().int_const( format( "u{}", b.newUniqueId() ).c_str() );
                },
                []( auto&& b, auto&& val )
                {
                    auto intVal = FromValue< APSInt >( val );
                    return GetZ3Context().int_val( intVal->getExtValue() );
                },
                {}, {}
            };
        }
        else
        {
            auto numBits = intType->m_numBits;

            return TypeInfo { GetZ3Context().bv_sort( intType->m_numBits ),
                [numBits]( auto&& b )
                {
                    return GetZ3Context().bv_const( format( "u{}", b.newUniqueId() ).c_str(), numBits );

                },
                [numBits]( auto&& b, auto&& val )
                {
                    auto intVal = FromValue< APSInt >( val );
                    return GetZ3Context().bv_val( intVal->getExtValue(), numBits );
                },
                {}, {}
            };
        }
    }

    return nullopt;
}

optional< TypeInfo > TypeCache::createTupleTypeInfo( const sema::Context& c, const Value& tupType )







|


>
|



>
|

|
<









|
<
<
|
<

<
|
<
<
<




|
<
<
|
<









|
<




|
<
<
|
<











|
>



|
<









|
|
<
>
|




|


>
|







|
|












|
<
<
|
<





|
<





|
|
<
|
>






|
<







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

    m_typeInfos.emplace( type, *tinfo );
    return tinfo;
}

const TypeInfo& TypeCache::uninterpretedTypeInfo() const
{
    static TypeInfo UnintTypeInfo{ m_uninterpretedSort,
        [&]( auto&& b )
        {
            return GetZ3Context().constant(
                format( "uc{}", b.newUniqueId() ).c_str(), m_uninterpretedSort );
        },
        [&]( auto&& b, auto&& val )
        {
            return GetZ3Context().constant(
                format( "uv{}", b.newUniqueId() ).c_str(), m_uninterpretedSort );
        },
        {}, {} };


    return UnintTypeInfo;
}

optional< TypeInfo > TypeCache::createTypeInfo( const sema::Context& c, const Term& type )
{
    // TODO: some types are missing and will be dealt with later on.
    if( type == GetValueType< bool >() )
    {
        return TypeInfo{ GetZ3Context().bool_sort(), []( auto&& b )


            { return GetZ3Context().bool_const( format( "u{}", b.newUniqueId() ).c_str() ); },

            []( auto&& b, auto&& val )

            { return GetZ3Context().bool_val( *FromValue< bool >( val ) ); }, {}, {} };



    }

    if( type == GetValueType< BigInt >() )
    {
        return TypeInfo{ GetZ3Context().int_sort(), []( auto&& b )


            { return GetZ3Context().int_const( format( "bi{}", b.newUniqueId() ).c_str() ); },

            []( auto&& b, auto&& val )
            {
                auto intVal = FromValue< BigInt >( val );

                if( intVal->fitsAsS64() )
                    return GetZ3Context().int_val( intVal->toS64() );
                else
                    return GetZ3Context().int_val( intVal->toString().c_str() );
            },
            {}, {} };

    }

    if( type == GetValueType< string >() )
    {
        return TypeInfo{ GetZ3Context().string_sort(), []( auto&& b )


            { return GetZ3Context().string_const( format( "s{}", b.newUniqueId() ).c_str() ); },

            []( auto&& b, auto&& val ) -> z3::expr
            {
                // TODO need to see how unicode convcersion works, does z3 do
                // it automatically?
                // we'll have to see when we start verifying strings in earnest
                // (for now it's just there so the verifier doesn't fail when
                // ecountering a string)
                if( val.isConstant() )
                    return GetZ3Context().string_val( *FromValue< string >( val ) );

                if( !BuildZ3Op( b, *val.cir() ) )
                    return GetZ3Context().string_const(
                        format( "str{}", b.newUniqueId() ).c_str() );

                return b.pop()->expr;
            },
            {}, {} };

    }

    auto typeVal = EIRToValue( type );
    if( !typeVal )
        return nullopt;

    // Lower pointer types into addresses: we're only interested in knowing about aliasing, for now.
    if( type == GetValueType< Address >() || FromValue< PointerType >( *typeVal ) )
    {
        return TypeInfo{ m_addressSort,
            [&]( auto&& b ) {

                return GetZ3Context().constant(
                    format( "ml{}", b.newUniqueId() ).c_str(), m_addressSort );
            },
            [&]( auto&& b, auto&& val )
            {
                // TODO: handle constant addresses (not needed for now, perhaps we'll never need it)
                // G_VAL_ASSERT( val, !val.isConstant() );

                if( val.isConstant() || !BuildZ3Op( b, *val.cir() ) )
                    return GetZ3Context().constant(
                        format( "ml{}", b.newUniqueId() ).c_str(), m_addressSort );

                return b.pop()->expr;
            },
            [&]( auto&& tup, auto&& index )
            {
                assert( index < m_addressProjs.size() );
                return m_addressProjs[index]( tup );
            },
            {} };
    }

    if( IsTupleType( *typeVal ) )
        return createTupleTypeInfo( c, *typeVal );

    if( IsSequenceType( *typeVal ) )
        return createSequenceTypeInfo( c, *typeVal );

    if( auto intType = FromValue< builtins::IntegerType >( *typeVal ) )
    {
        if( intType->m_signed )
        {
            return TypeInfo{ GetZ3Context().int_sort(), []( auto&& b )


                { return GetZ3Context().int_const( format( "u{}", b.newUniqueId() ).c_str() ); },

                []( auto&& b, auto&& val )
                {
                    auto intVal = FromValue< APSInt >( val );
                    return GetZ3Context().int_val( intVal->getExtValue() );
                },
                {}, {} };

        }
        else
        {
            auto numBits = intType->m_numBits;

            return TypeInfo{ GetZ3Context().bv_sort( intType->m_numBits ),
                [numBits]( auto&& b ) {

                    return GetZ3Context().bv_const(
                        format( "u{}", b.newUniqueId() ).c_str(), numBits );
                },
                [numBits]( auto&& b, auto&& val )
                {
                    auto intVal = FromValue< APSInt >( val );
                    return GetZ3Context().bv_val( intVal->getExtValue(), numBits );
                },
                {}, {} };

        }
    }

    return nullopt;
}

optional< TypeInfo > TypeCache::createTupleTypeInfo( const sema::Context& c, const Value& tupType )
228
229
230
231
232
233
234
235
236
237
238
239

240
241
242
243
244
245
246
247
        auto tinfo = getTypeInfo( c, elemType );
        if( !tinfo )
            return nullopt;

        elemTInfos.push_back( *tinfo );

        elemNames.push_back( format( "{}_{}", sortName, i ) );
        assert(tinfo->sort );
        elemSorts.push_back( *tinfo->sort );
    }

    llvm::SmallVector< z3::func_decl, 8 > projs;

    auto&& [tupSort, tupCtor] = mkZ3TupleSort( sortName.c_str(), size, elemNames, elemSorts, projs );
    const auto& ctor = tupCtor;

    auto undefined = [size, elemTInfos, ctor]( auto&& b )
    {
        llvm::SmallVector< z3::expr, 8 > args;
        args.reserve( size );








|




>
|







211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
        auto tinfo = getTypeInfo( c, elemType );
        if( !tinfo )
            return nullopt;

        elemTInfos.push_back( *tinfo );

        elemNames.push_back( format( "{}_{}", sortName, i ) );
        assert( tinfo->sort );
        elemSorts.push_back( *tinfo->sort );
    }

    llvm::SmallVector< z3::func_decl, 8 > projs;
    auto&& [tupSort, tupCtor] =
        mkZ3TupleSort( sortName.c_str(), size, elemNames, elemSorts, projs );
    const auto& ctor = tupCtor;

    auto undefined = [size, elemTInfos, ctor]( auto&& b )
    {
        llvm::SmallVector< z3::expr, 8 > args;
        args.reserve( size );

258
259
260
261
262
263
264
265

266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289

290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
    };

    auto build = [size, elemTInfos, ctor]( auto&& b, auto&& val )
    {
        llvm::SmallVector< z3::expr, 8 > args;
        args.reserve( size );

        ForEachInTuple( val, [&]( auto&& elemVal )

        {
            auto elem = BuildZ3ExprFromValue( b, elemVal );
            args.push_back( move( elem.expr ) );
            return true;
        } );

        return ctor( args.size(), &args.front() );
    };

    auto proj = [projs]( auto&& tup, auto&& index )
    {
        assert( index < projs.size() );
        return projs[index]( tup );
    };

    auto ctorWrapper = [ctor]( auto&& args )
    {
        return ctor( args.size(), &args.front() );
    };

    return TypeInfo { tupSort, undefined, build, proj, ctorWrapper };
}

optional< TypeInfo > TypeCache::createSequenceTypeInfo( const sema::Context& c, const Value& seqType )

{
    auto subTypeInfo = createTypeInfo( c, GetSequenceSubType( seqType ) );
    if( !subTypeInfo || !subTypeInfo->sort )
        return nullopt;

    auto sort = GetZ3Context().seq_sort( *subTypeInfo->sort );

    // TODO: implement build, but need to implement bridge for seq values and to decide on a representation!
    // Not needed until we actually use compilation time sequences for more than representing calculated
    // addresses during verification). It will be needed some day, but not yet.
    return TypeInfo { sort,
        [sort]( auto&& b )
        {
            return GetZ3Context().constant( format( "seq{}", b.newUniqueId() ).c_str(), sort );
        },
        {}, {}, {}
    };
}







|
>
|
|
|
|
|










|
|
<
<
<
|


|
>







|
|
|
|
|
<
|
<
|
<

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
282
283
284

285

286

287
    };

    auto build = [size, elemTInfos, ctor]( auto&& b, auto&& val )
    {
        llvm::SmallVector< z3::expr, 8 > args;
        args.reserve( size );

        ForEachInTuple( val,
            [&]( auto&& elemVal )
            {
                auto elem = BuildZ3ExprFromValue( b, elemVal );
                args.push_back( move( elem.expr ) );
                return true;
            } );

        return ctor( args.size(), &args.front() );
    };

    auto proj = [projs]( auto&& tup, auto&& index )
    {
        assert( index < projs.size() );
        return projs[index]( tup );
    };

    auto ctorWrapper = [ctor]( auto&& args ) { return ctor( args.size(), &args.front() ); };




    return TypeInfo{ tupSort, undefined, build, proj, ctorWrapper };
}

optional< TypeInfo > TypeCache::createSequenceTypeInfo(
    const sema::Context& c, const Value& seqType )
{
    auto subTypeInfo = createTypeInfo( c, GetSequenceSubType( seqType ) );
    if( !subTypeInfo || !subTypeInfo->sort )
        return nullopt;

    auto sort = GetZ3Context().seq_sort( *subTypeInfo->sort );

    // TODO: implement build, but need to implement bridge for seq values and to decide on a
    // representation! Not needed until we actually use compilation time sequences for more than
    // representing calculated addresses during verification). It will be needed some day, but not
    // yet.
    return TypeInfo{ sort, [sort]( auto&& b )

        { return GetZ3Context().constant( format( "seq{}", b.newUniqueId() ).c_str(), sort ); }, {},

        {}, {} };

}
Changes to bs/verify/type.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
#ifndef GOOSE_VERIFY_TYPE_H
#define GOOSE_VERIFY_TYPE_H

namespace goose::verify
{
    struct TypeInfo
    {
        optional< z3::sort > sort;
        function< z3::expr ( Builder& ) > undefined;
        function< z3::expr ( Builder&, const Value& ) > build;
        function< z3::expr ( const z3::expr&, uint32_t index ) > proj;
        function< z3::expr ( llvm::SmallVector< z3::expr, 8 > ) > ctor;
    };

    // For complex types that require the creation of custom constructor funcs in z3
    // (for instances tuples), we keep a cache to avoid recreating them multiple times.
    class TypeCache
    {
        public:
            static TypeCache& GetInstance();

            optional< TypeInfo > getTypeInfo( const sema::Context& c, const Term& type );
            const TypeInfo& uninterpretedTypeInfo() const;
            const z3::func_decl& addrCtor() const
            {
                return m_addressCtor;
            }

            const z3::func_decl& addrLifetimeGetter() const
            {
                return m_addressProjs[0];
            }

            const z3::func_decl& addrOriginGetter() const
            {
                return m_addressProjs[1];
            }

            const z3::func_decl& addrPathGetter() const
            {
                return m_addressProjs[2];
            }

        private:
            TypeCache();

            optional< TypeInfo > createTupleTypeInfo( const sema::Context& c, const Value& tupType );
            optional< TypeInfo > createSequenceTypeInfo( const sema::Context& c, const Value& seqType );
            optional< TypeInfo > createTypeInfo( const sema::Context& c, const Term& type );

            unordered_map< Term, TypeInfo > m_typeInfos;

            z3::sort m_memPathSort;
            z3::sort m_addressSort;
            z3::func_decl m_addressCtor;

            llvm::SmallVector< z3::func_decl, 3 > m_addressProjs;

            z3::sort m_uninterpretedSort;

            static uint32_t ms_nextUniqueId;
    };
}

#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
#ifndef GOOSE_VERIFY_TYPE_H
#define GOOSE_VERIFY_TYPE_H

namespace goose::verify
{
    struct TypeInfo
    {
        optional< z3::sort > sort;
        function< z3::expr( Builder& ) > undefined;
        function< z3::expr( Builder&, const Value& ) > build;
        function< z3::expr( const z3::expr&, uint32_t index ) > proj;
        function< z3::expr( llvm::SmallVector< z3::expr, 8 > ) > ctor;
    };

    // For complex types that require the creation of custom constructor funcs in z3
    // (for instances tuples), we keep a cache to avoid recreating them multiple times.
    class TypeCache
    {
      public:
        static TypeCache& GetInstance();

        optional< TypeInfo > getTypeInfo( const sema::Context& c, const Term& type );
        const TypeInfo& uninterpretedTypeInfo() const;


        const z3::func_decl& addrCtor() const { return m_addressCtor; }


        const z3::func_decl& addrLifetimeGetter() const { return m_addressProjs[0]; }




        const z3::func_decl& addrOriginGetter() const { return m_addressProjs[1]; }




        const z3::func_decl& addrPathGetter() const { return m_addressProjs[2]; }




      private:
        TypeCache();

        optional< TypeInfo > createTupleTypeInfo( const sema::Context& c, const Value& tupType );
        optional< TypeInfo > createSequenceTypeInfo( const sema::Context& c, const Value& seqType );
        optional< TypeInfo > createTypeInfo( const sema::Context& c, const Term& type );

        unordered_map< Term, TypeInfo > m_typeInfos;

        z3::sort m_memPathSort;
        z3::sort m_addressSort;
        z3::func_decl m_addressCtor;

        llvm::SmallVector< z3::func_decl, 3 > m_addressProjs;

        z3::sort m_uninterpretedSort;

        static uint32_t ms_nextUniqueId;
    };
} // namespace goose::verify

#endif
Changes to bs/verify/value.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
75
76
77
78

        tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), val.type() );

        if( !tinfo )
            tinfo = TypeCache::GetInstance().uninterpretedTypeInfo();

        auto zexpr = tinfo->build( b, val );
        return Z3Val { move( zexpr ), *EIRToValue( val.type() ), val.locationId() };
    }

    optional< Z3Val > TryBuildZ3ValFromConstant( Builder& b, const Value& val )
    {
        auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), val.type() );
        if( !tinfo )
            return nullopt;

        auto zexpr = tinfo->build( b, val );
        return Z3Val { move( zexpr ), *EIRToValue( val.type() ), val.locationId() };
    }

    Z3Val BuildZ3ConstantFromType( Builder& b, const Value& type, const string& name )
    {
        auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), ValueToEIR( type ) );
        if( !tinfo )
            tinfo = TypeCache::GetInstance().uninterpretedTypeInfo();

        assert( tinfo->sort );
        return Z3Val { GetZ3Context().constant( name.c_str(), *tinfo->sort ), type, type.locationId() };

    }

    optional< Z3Val > TryBuildZ3ConstantFromType( Builder& b, const Value& type, const string& name )

    {
        auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), ValueToEIR( type ) );
        if( !tinfo )
            return nullopt;

        assert( tinfo->sort );
        return Z3Val { GetZ3Context().constant( name.c_str(), *tinfo->sort ), type, type.locationId() };

    }

    Z3Val BuildZ3ConstantFromType( Builder& b, const Term& type, const string& name )
    {
        auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), type );
        if( !tinfo )
            tinfo = TypeCache::GetInstance().uninterpretedTypeInfo();

        assert( tinfo->sort );
        auto tval = *EIRToValue( type );
        return Z3Val { GetZ3Context().constant( name.c_str(), *tinfo->sort ), tval, tval.locationId() };

    }

    optional< Z3Val > TryBuildZ3ConstantFromType( Builder& b, const Term& type, const string& name )
    {
        auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), type );
        if( !tinfo )
            return nullopt;

        assert( tinfo->sort );
        auto tval = *EIRToValue( type );
        return Z3Val { GetZ3Context().constant( name.c_str(), *tinfo->sort ), tval, tval.locationId() };

    }

    z3::expr GetAsBitVec( const z3::expr& expr, const Value& type )
    {
        if( expr.is_bv() )
            return expr;








|









|









|
>


|
>






|
>










|
>










|
>







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

        tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), val.type() );

        if( !tinfo )
            tinfo = TypeCache::GetInstance().uninterpretedTypeInfo();

        auto zexpr = tinfo->build( b, val );
        return Z3Val{ move( zexpr ), *EIRToValue( val.type() ), val.locationId() };
    }

    optional< Z3Val > TryBuildZ3ValFromConstant( Builder& b, const Value& val )
    {
        auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), val.type() );
        if( !tinfo )
            return nullopt;

        auto zexpr = tinfo->build( b, val );
        return Z3Val{ move( zexpr ), *EIRToValue( val.type() ), val.locationId() };
    }

    Z3Val BuildZ3ConstantFromType( Builder& b, const Value& type, const string& name )
    {
        auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), ValueToEIR( type ) );
        if( !tinfo )
            tinfo = TypeCache::GetInstance().uninterpretedTypeInfo();

        assert( tinfo->sort );
        return Z3Val{ GetZ3Context().constant( name.c_str(), *tinfo->sort ), type,
            type.locationId() };
    }

    optional< Z3Val > TryBuildZ3ConstantFromType(
        Builder& b, const Value& type, const string& name )
    {
        auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), ValueToEIR( type ) );
        if( !tinfo )
            return nullopt;

        assert( tinfo->sort );
        return Z3Val{ GetZ3Context().constant( name.c_str(), *tinfo->sort ), type,
            type.locationId() };
    }

    Z3Val BuildZ3ConstantFromType( Builder& b, const Term& type, const string& name )
    {
        auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), type );
        if( !tinfo )
            tinfo = TypeCache::GetInstance().uninterpretedTypeInfo();

        assert( tinfo->sort );
        auto tval = *EIRToValue( type );
        return Z3Val{ GetZ3Context().constant( name.c_str(), *tinfo->sort ), tval,
            tval.locationId() };
    }

    optional< Z3Val > TryBuildZ3ConstantFromType( Builder& b, const Term& type, const string& name )
    {
        auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), type );
        if( !tinfo )
            return nullopt;

        assert( tinfo->sort );
        auto tval = *EIRToValue( type );
        return Z3Val{ GetZ3Context().constant( name.c_str(), *tinfo->sort ), tval,
            tval.locationId() };
    }

    z3::expr GetAsBitVec( const z3::expr& expr, const Value& type )
    {
        if( expr.is_bv() )
            return expr;

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
            return GetAsInt( expr, to.type );
        else if( to.expr.is_bv() )
            return GetAsBitVec( expr, to.type );
        else
            return expr;
    }

    template< typename I, typename F >
    bool BuildZ3UnaryExpr( Builder& b, const I& instr, F&& func )
    {
        auto operand = b.pop();
        if( !operand )
            return false;

        b.push( Z3Val{ func( operand->expr ), operand->type, instr.locationId() } );
        return true;
    }

    template< typename I, typename F >
    bool BuildZ3BinExpr( Builder& b, const I& instr, F&& func )
    {
        auto rhs = b.pop();
        if( !rhs )
            return false;

        auto lhs = b.pop();
        if( !lhs )
            return false;

        if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )
        {

            b.push( Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), lhs->type, instr.locationId() } );
            return true;
        }

        // If we are trying to do an operation on a mix of bitvec and int,
        // convert the int to a bitvec first.
        if( lhs->expr.is_bv() )
        {
            assert( rhs->expr.is_int() );
            b.push( Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), lhs->type, instr.locationId() } );

            return true;
        }
        else
        {
            assert( lhs->expr.is_int() );
            b.push( Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), lhs->type, instr.locationId() } );

            return true;
        }

        return false;
    }

    template< typename I, typename F >
    bool BuildZ3BinBitwiseExpr( Builder& b, const I& instr, F&& func )
    {
        auto rhs = b.pop();
        if( !rhs )
            return false;

        auto lhs = b.pop();
        if( !lhs )
            return false;

        if( ValueToEIR( lhs->type ) == GetValueType< BigInt >() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( 0, "verifier error: bitwise operands can't be ct_int." );

            return false;
        }

        if( ValueToEIR( rhs->type ) == GetValueType< BigInt >() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage( 0, "verifier error: bitwise operands can't be ct_int." );

            return false;
        }

        if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )
        {

            b.push( Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), lhs->type, instr.locationId() } );
            return true;
        }

        // If we are trying to do an operation on a mix of bitvec and int,
        // convert the int to a bitvec first.
        if( lhs->expr.is_bv() )
        {
            assert( rhs->expr.is_int() );
            b.push( Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), lhs->type, instr.locationId() } );

            return true;
        }
        else
        {
            assert( lhs->expr.is_int() );
            b.push( Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), lhs->type, instr.locationId() } );

            return true;
        }

        return false;
    }

    template< typename I, typename F >
    bool BuildZ3BinBoolExpr( Builder& b, const I& instr, F&& func )
    {
        auto rhs = b.pop();
        if( !rhs )
            return false;

        auto lhs = b.pop();
        if( !lhs )
            return false;

        if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )
        {
            b.push( Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), *EIRToValue( GetValueType< bool >() ), instr.locationId() } );

            return true;
        }

        // If we are trying to do an operation on a mix of bitvec and int,
        // convert the int to a bitvec first.
        if( lhs->expr.is_bv() )
        {
            assert( rhs->expr.is_int() );
            b.push( Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), *EIRToValue( GetValueType< bool >() ), instr.locationId() } );

            return true;
        }
        else
        {
            cout << lhs->expr << endl;
            assert( lhs->expr.is_int() );
            b.push( Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), *EIRToValue( GetValueType< bool >() ), instr.locationId() } );

            return true;
        }

        return false;
    }

    template< typename I, typename F >







|
<









|
<











>
|








|
>





|
>



















|
>





|
>





>
|








|
>





|
>



















|
>








|
>






|
>







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
            return GetAsInt( expr, to.type );
        else if( to.expr.is_bv() )
            return GetAsBitVec( expr, to.type );
        else
            return expr;
    }

    template< typename I, typename F > bool BuildZ3UnaryExpr( Builder& b, const I& instr, F&& func )

    {
        auto operand = b.pop();
        if( !operand )
            return false;

        b.push( Z3Val{ func( operand->expr ), operand->type, instr.locationId() } );
        return true;
    }

    template< typename I, typename F > bool BuildZ3BinExpr( Builder& b, const I& instr, F&& func )

    {
        auto rhs = b.pop();
        if( !rhs )
            return false;

        auto lhs = b.pop();
        if( !lhs )
            return false;

        if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )
        {
            b.push(
                Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), lhs->type, instr.locationId() } );
            return true;
        }

        // If we are trying to do an operation on a mix of bitvec and int,
        // convert the int to a bitvec first.
        if( lhs->expr.is_bv() )
        {
            assert( rhs->expr.is_int() );
            b.push( Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), lhs->type,
                instr.locationId() } );
            return true;
        }
        else
        {
            assert( lhs->expr.is_int() );
            b.push( Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), lhs->type,
                instr.locationId() } );
            return true;
        }

        return false;
    }

    template< typename I, typename F >
    bool BuildZ3BinBitwiseExpr( Builder& b, const I& instr, F&& func )
    {
        auto rhs = b.pop();
        if( !rhs )
            return false;

        auto lhs = b.pop();
        if( !lhs )
            return false;

        if( ValueToEIR( lhs->type ) == GetValueType< BigInt >() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage(
                0, "verifier error: bitwise operands can't be ct_int." );
            return false;
        }

        if( ValueToEIR( rhs->type ) == GetValueType< BigInt >() )
        {
            DiagnosticsManager::GetInstance().emitErrorMessage(
                0, "verifier error: bitwise operands can't be ct_int." );
            return false;
        }

        if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )
        {
            b.push(
                Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), lhs->type, instr.locationId() } );
            return true;
        }

        // If we are trying to do an operation on a mix of bitvec and int,
        // convert the int to a bitvec first.
        if( lhs->expr.is_bv() )
        {
            assert( rhs->expr.is_int() );
            b.push( Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), lhs->type,
                instr.locationId() } );
            return true;
        }
        else
        {
            assert( lhs->expr.is_int() );
            b.push( Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), lhs->type,
                instr.locationId() } );
            return true;
        }

        return false;
    }

    template< typename I, typename F >
    bool BuildZ3BinBoolExpr( Builder& b, const I& instr, F&& func )
    {
        auto rhs = b.pop();
        if( !rhs )
            return false;

        auto lhs = b.pop();
        if( !lhs )
            return false;

        if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )
        {
            b.push( Z3Val{ func( lhs->expr, rhs->expr, lhs->type ),
                *EIRToValue( GetValueType< bool >() ), instr.locationId() } );
            return true;
        }

        // If we are trying to do an operation on a mix of bitvec and int,
        // convert the int to a bitvec first.
        if( lhs->expr.is_bv() )
        {
            assert( rhs->expr.is_int() );
            b.push( Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ),
                *EIRToValue( GetValueType< bool >() ), instr.locationId() } );
            return true;
        }
        else
        {
            cout << lhs->expr << endl;
            assert( lhs->expr.is_int() );
            b.push( Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ),
                *EIRToValue( GetValueType< bool >() ), instr.locationId() } );
            return true;
        }

        return false;
    }

    template< typename I, typename F >
264
265
266
267
268
269
270
271

272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324

325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351

352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368

        if( !lhs->expr.is_bool() )
            return false;

        if( !rhs->expr.is_bool() )
            return false;

        b.push( Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), *EIRToValue( GetValueType< bool >() ), instr.locationId() } );

        return true;
    }

    template< typename RT, typename I, typename F >
    bool BuildZ3BinSeqExpr( Builder& b, const I& instr, F&& func )
    {
        auto rhs = b.pop();
        if( !rhs )
            return false;

        auto lhs = b.pop();
        if( !lhs )
            return false;

        if( !lhs->expr.is_seq() )
            return false;

        if( !rhs->expr.is_seq() )
            return false;

        b.push( Z3Val{
            func( lhs->expr, rhs->expr ),
            *EIRToValue( GetValueType< RT >() ),
            instr.locationId() } );
        return true;
    }

    bool BuildZ3Op( Builder& b, const cir::InstrSeq& is )
    {
        for( const auto& instr : is )
        {
            if( !BuildZ3Op( b, instr ) )
                return false;
        }

        return true;
    }

    template< typename T >
    bool BuildZ3Op( Builder& b, const T& instr )
    {
        return false;
    }

    bool BuildZ3Op( Builder& b, const Load& instr )
    {
        optional< Z3Val > zv;

        if( auto cstAddr = b.pop< Address >() )
            zv = LoadFromAddress( b, *cstAddr, instr.type().get() );
        else if( auto gfa = b.pop< GhostFuncApplication >() )
            zv = LoadFromAddress( b, *gfa );
        else if( auto symAddr = b.pop< Z3Val >() )

            zv = BuildZ3ConstantFromType( b, instr.type().get(), format( "val{}", b.newUniqueId() ) );

        if( !zv )
            return false;

        if( b.mustLoadAssume() )
            ForEachPredicate( b, instr.type().get(), zv->expr, [&]( auto&& z3expr, auto locId )
            {
                b.assume( z3expr );
            } );

        zv->loc = instr.locationId();
        b.push( move( *zv ) );
        return true;
    }

    bool BuildZ3Op( Builder& b, const Store& instr )
    {
        auto addr = b.pop< Stack::Slot >();
        if( !addr )
            return false;

        auto zv = b.pop();
        if( !zv )
            return false;

        ForEachPredicate( b, instr.type().get(), zv->expr, [&]( auto&& z3expr, auto locId )

        {
            if( !instr.srcLocId().invalid() && !instr.destLocId().invalid() )
            {
                DiagnosticsContext dc( instr.destLocId(), "...to this." );
                DiagnosticsContext dc2( instr.srcLocId(), "When assigning this..." );
                b.checkAssertion( z3expr, locId );
            }
            else
                b.checkAssertion( z3expr, locId );
        } );

        if( const auto* cstAddr = get_if< Address >( &*addr ) )
            StoreToAddress( b, *cstAddr, move( *zv ) );
        else if( const auto* gfa = get_if< GhostFuncApplication >( &*addr ) )
            StoreToAddress( b, *gfa, move( *zv ) );

        return true;







|
>




















<
<
|







<


<




<
|













>
|





|
<
|
<
















|
>
|
|
|
|
|
|
|
|
|
|







278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306


307
308
309
310
311
312
313
314

315
316

317
318
319
320

321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342

343

344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378

        if( !lhs->expr.is_bool() )
            return false;

        if( !rhs->expr.is_bool() )
            return false;

        b.push( Z3Val{ func( lhs->expr, rhs->expr, lhs->type ),
            *EIRToValue( GetValueType< bool >() ), instr.locationId() } );
        return true;
    }

    template< typename RT, typename I, typename F >
    bool BuildZ3BinSeqExpr( Builder& b, const I& instr, F&& func )
    {
        auto rhs = b.pop();
        if( !rhs )
            return false;

        auto lhs = b.pop();
        if( !lhs )
            return false;

        if( !lhs->expr.is_seq() )
            return false;

        if( !rhs->expr.is_seq() )
            return false;



        b.push( Z3Val{ func( lhs->expr, rhs->expr ), *EIRToValue( GetValueType< RT >() ),
            instr.locationId() } );
        return true;
    }

    bool BuildZ3Op( Builder& b, const cir::InstrSeq& is )
    {
        for( const auto& instr : is )

            if( !BuildZ3Op( b, instr ) )
                return false;


        return true;
    }


    template< typename T > bool BuildZ3Op( Builder& b, const T& instr )
    {
        return false;
    }

    bool BuildZ3Op( Builder& b, const Load& instr )
    {
        optional< Z3Val > zv;

        if( auto cstAddr = b.pop< Address >() )
            zv = LoadFromAddress( b, *cstAddr, instr.type().get() );
        else if( auto gfa = b.pop< GhostFuncApplication >() )
            zv = LoadFromAddress( b, *gfa );
        else if( auto symAddr = b.pop< Z3Val >() )
            zv = BuildZ3ConstantFromType(
                b, instr.type().get(), format( "val{}", b.newUniqueId() ) );

        if( !zv )
            return false;

        if( b.mustLoadAssume() )
            ForEachPredicate( b, instr.type().get(), zv->expr,

                [&]( auto&& z3expr, auto locId ) { b.assume( z3expr ); } );


        zv->loc = instr.locationId();
        b.push( move( *zv ) );
        return true;
    }

    bool BuildZ3Op( Builder& b, const Store& instr )
    {
        auto addr = b.pop< Stack::Slot >();
        if( !addr )
            return false;

        auto zv = b.pop();
        if( !zv )
            return false;

        ForEachPredicate( b, instr.type().get(), zv->expr,
            [&]( auto&& z3expr, auto locId )
            {
                if( !instr.srcLocId().invalid() && !instr.destLocId().invalid() )
                {
                    DiagnosticsContext dc( instr.destLocId(), "...to this." );
                    DiagnosticsContext dc2( instr.srcLocId(), "When assigning this..." );
                    b.checkAssertion( z3expr, locId );
                }
                else
                    b.checkAssertion( z3expr, locId );
            } );

        if( const auto* cstAddr = get_if< Address >( &*addr ) )
            StoreToAddress( b, *cstAddr, move( *zv ) );
        else if( const auto* gfa = get_if< GhostFuncApplication >( &*addr ) )
            StoreToAddress( b, *gfa, move( *zv ) );

        return true;
394
395
396
397
398
399
400

401
402
403
404
405
406
407
408
409
410

411
412
413
414
415
416
417
418
419
420
421
422

423
424
425
426
427
428
429
430
431
432
433
434
435

436
437
438
439
440
441
442
443
444
445
446
447
448

449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477

478
479
480
481
482
483
484
485
486
487

488
489
490
491
492
493
494
495
496
497

498
499
500
501
502
503
504
505
506

507
508
509
510
511

512
513
514
515
516

517
518
519
520
521

522
523
524
525
526

527
528
529
530
531

532
533
534
535
536
537

538
539
540
541
542
543
544
545
546
547

548
549
550
551
552

553
554
555
556
557

558
559
560
561
562

563
564
565
566
567

568
569
570
571
572

573
574
575
576
577

578
579
580
581
582

583
584
585
586
587

588
589
590
591
592

593
594
595
596
597
598
599
600

    bool BuildZ3Op( Builder& b, const AllocVar& instr )
    {
        auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), instr.type().get() );
        if( !tinfo )
            tinfo = TypeCache::GetInstance().uninterpretedTypeInfo();


        b.setVar( instr.index(), Z3Val{ tinfo->undefined( b ), *EIRToValue( instr.type().get() ), instr.locationId() } );
        return true;
    }

    // Implemented in phi.cpp
    bool BuildZ3Op( Builder& b, const Phi& instr );

    bool BuildZ3Op( Builder& b, const Not& instr )
    {
        return BuildZ3UnaryExpr( b, instr, []( auto&& operand )

        {
            if( operand.is_bool() )
                return !operand;

            assert( operand.is_bv() );
            return ~operand;
        } );
    }

    bool BuildZ3Op( Builder& b, const And& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )

        {
            if( lhs.is_bool() && rhs.is_bool() )
                return lhs && rhs;

            auto lhsBV = GetAsBitVec( lhs, type );
            auto rhsBV = GetAsBitVec( rhs, type );
            return lhsBV & rhsBV;
        } );
    }

    bool BuildZ3Op( Builder& b, const Or& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )

        {
            if( lhs.is_bool() && rhs.is_bool() )
                return lhs || rhs;

            auto lhsBV = GetAsBitVec( lhs, type );
            auto rhsBV = GetAsBitVec( rhs, type );
            return lhsBV | rhsBV;
        } );
    }

    bool BuildZ3Op( Builder& b, const Xor& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )

        {
            if( lhs.is_bool() && rhs.is_bool() )
                return lhs ^ rhs;

            auto lhsBV = GetAsBitVec( lhs, type );
            auto rhsBV = GetAsBitVec( rhs, type );
            return lhsBV ^ rhsBV;
        } );
    }

    bool BuildZ3Op( Builder& b, const Implies& instr )
    {
        return BuildZ3BinLogicExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
        {
            return z3::implies( lhs, rhs );
        } );
    }

    bool BuildZ3Op( Builder& b, const IsPrefixOf& instr )
    {
        return BuildZ3BinSeqExpr< bool >( b, instr, []( auto&& lhs, auto&& rhs )
        {
            return z3::prefixof( lhs, rhs );
        } );
    }

    bool BuildZ3Op( Builder& b, const Shl& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )

        {
            auto lhsBV = GetAsBitVec( lhs, type );
            auto rhsBV = GetAsBitVec( rhs, type );
            return z3::shl( lhsBV, rhsBV );
        } );
    }

    bool BuildZ3Op( Builder& b, const LShr& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )

        {
            auto lhsBV = GetAsBitVec( lhs, type );
            auto rhsBV = GetAsBitVec( rhs, type );
            return z3::lshr( lhsBV, rhsBV );
        } );
    }

    bool BuildZ3Op( Builder& b, const AShr& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )

        {
            auto lhsBV = GetAsBitVec( lhs, type );
            auto rhsBV = GetAsBitVec( rhs, type );
            return z3::ashr( lhsBV, rhsBV );
        } );
    }

    bool BuildZ3Op( Builder& b, const Add& instr )
    {

        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs + rhs; } );
    }

    bool BuildZ3Op( Builder& b, const Sub& instr )
    {

        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs - rhs; } );
    }

    bool BuildZ3Op( Builder& b, const Mul& instr )
    {

        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs * rhs; } );
    }

    bool BuildZ3Op( Builder& b, const UDiv& instr )
    {

        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::udiv( lhs, rhs ); } );
    }

    bool BuildZ3Op( Builder& b, const SDiv& instr )
    {

        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs / rhs; } );
    }

    bool BuildZ3Op( Builder& b, const URem& instr )
    {

        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::urem( lhs, rhs ); } );
    }

    bool BuildZ3Op( Builder& b, const SRem& instr )
    {
        return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )

        {
            if( lhs.is_bv() )
                return z3::srem( lhs, rhs );

            return z3::rem( lhs, rhs );
        } );
    }

    bool BuildZ3Op( Builder& b, const Eq& instr )
    {

        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs == rhs; } );
    }

    bool BuildZ3Op( Builder& b, const Neq& instr )
    {

        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs != rhs; } );
    }

    bool BuildZ3Op( Builder& b, const UGT& instr )
    {

        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::ugt( lhs, rhs ); } );
    }

    bool BuildZ3Op( Builder& b, const UGE& instr )
    {

        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::uge( lhs, rhs ); } );
    }

    bool BuildZ3Op( Builder& b, const ULT& instr )
    {

        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::ult( lhs, rhs ); } );
    }

    bool BuildZ3Op( Builder& b, const ULE& instr )
    {

        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::ule( lhs, rhs ); } );
    }

    bool BuildZ3Op( Builder& b, const SGT& instr )
    {

        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs > rhs; } );
    }

    bool BuildZ3Op( Builder& b, const SGE& instr )
    {

        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs >= rhs; } );
    }

    bool BuildZ3Op( Builder& b, const SLT& instr )
    {

        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs < rhs; } );
    }

    bool BuildZ3Op( Builder& b, const SLE& instr )
    {

        return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs <= rhs; } );
    }

    bool BuildZ3Op( Builder& b, const Assert& instr )
    {
        auto cond = b.pop();
        if( !cond )
            return false;







>
|








|
>
|
|
|

|
|
|




|
>
|
|
|

|
|
|
|




|
>
|
|
|

|
|
|
|




|
>
|
|
|

|
|
|
|




|
<
|
<




|
<
|
<




|
>
|
|
|
|
|




|
>
|
|
|
|
|




|
>
|
|
|
|
|




>
|




>
|




>
|




>
|




>
|




>
|




|
>
|
|
|

|
|




>
|




>
|




>
|




>
|




>
|




>
|




>
|




>
|




>
|




>
|







404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476

477

478
479
480
481
482

483

484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631

    bool BuildZ3Op( Builder& b, const AllocVar& instr )
    {
        auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), instr.type().get() );
        if( !tinfo )
            tinfo = TypeCache::GetInstance().uninterpretedTypeInfo();

        b.setVar( instr.index(),
            Z3Val{ tinfo->undefined( b ), *EIRToValue( instr.type().get() ), instr.locationId() } );
        return true;
    }

    // Implemented in phi.cpp
    bool BuildZ3Op( Builder& b, const Phi& instr );

    bool BuildZ3Op( Builder& b, const Not& instr )
    {
        return BuildZ3UnaryExpr( b, instr,
            []( auto&& operand )
            {
                if( operand.is_bool() )
                    return !operand;

                assert( operand.is_bv() );
                return ~operand;
            } );
    }

    bool BuildZ3Op( Builder& b, const And& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr,
            []( auto&& lhs, auto&& rhs, auto&& type )
            {
                if( lhs.is_bool() && rhs.is_bool() )
                    return lhs && rhs;

                auto lhsBV = GetAsBitVec( lhs, type );
                auto rhsBV = GetAsBitVec( rhs, type );
                return lhsBV & rhsBV;
            } );
    }

    bool BuildZ3Op( Builder& b, const Or& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr,
            []( auto&& lhs, auto&& rhs, auto&& type )
            {
                if( lhs.is_bool() && rhs.is_bool() )
                    return lhs || rhs;

                auto lhsBV = GetAsBitVec( lhs, type );
                auto rhsBV = GetAsBitVec( rhs, type );
                return lhsBV | rhsBV;
            } );
    }

    bool BuildZ3Op( Builder& b, const Xor& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr,
            []( auto&& lhs, auto&& rhs, auto&& type )
            {
                if( lhs.is_bool() && rhs.is_bool() )
                    return lhs ^ rhs;

                auto lhsBV = GetAsBitVec( lhs, type );
                auto rhsBV = GetAsBitVec( rhs, type );
                return lhsBV ^ rhsBV;
            } );
    }

    bool BuildZ3Op( Builder& b, const Implies& instr )
    {
        return BuildZ3BinLogicExpr( b, instr,

            []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::implies( lhs, rhs ); } );

    }

    bool BuildZ3Op( Builder& b, const IsPrefixOf& instr )
    {
        return BuildZ3BinSeqExpr< bool >(

            b, instr, []( auto&& lhs, auto&& rhs ) { return z3::prefixof( lhs, rhs ); } );

    }

    bool BuildZ3Op( Builder& b, const Shl& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr,
            []( auto&& lhs, auto&& rhs, auto&& type )
            {
                auto lhsBV = GetAsBitVec( lhs, type );
                auto rhsBV = GetAsBitVec( rhs, type );
                return z3::shl( lhsBV, rhsBV );
            } );
    }

    bool BuildZ3Op( Builder& b, const LShr& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr,
            []( auto&& lhs, auto&& rhs, auto&& type )
            {
                auto lhsBV = GetAsBitVec( lhs, type );
                auto rhsBV = GetAsBitVec( rhs, type );
                return z3::lshr( lhsBV, rhsBV );
            } );
    }

    bool BuildZ3Op( Builder& b, const AShr& instr )
    {
        return BuildZ3BinBitwiseExpr( b, instr,
            []( auto&& lhs, auto&& rhs, auto&& type )
            {
                auto lhsBV = GetAsBitVec( lhs, type );
                auto rhsBV = GetAsBitVec( rhs, type );
                return z3::ashr( lhsBV, rhsBV );
            } );
    }

    bool BuildZ3Op( Builder& b, const Add& instr )
    {
        return BuildZ3BinExpr(
            b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs + rhs; } );
    }

    bool BuildZ3Op( Builder& b, const Sub& instr )
    {
        return BuildZ3BinExpr(
            b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs - rhs; } );
    }

    bool BuildZ3Op( Builder& b, const Mul& instr )
    {
        return BuildZ3BinExpr(
            b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs * rhs; } );
    }

    bool BuildZ3Op( Builder& b, const UDiv& instr )
    {
        return BuildZ3BinExpr(
            b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::udiv( lhs, rhs ); } );
    }

    bool BuildZ3Op( Builder& b, const SDiv& instr )
    {
        return BuildZ3BinExpr(
            b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs / rhs; } );
    }

    bool BuildZ3Op( Builder& b, const URem& instr )
    {
        return BuildZ3BinExpr(
            b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::urem( lhs, rhs ); } );
    }

    bool BuildZ3Op( Builder& b, const SRem& instr )
    {
        return BuildZ3BinExpr( b, instr,
            []( auto&& lhs, auto&& rhs, auto&& type )
            {
                if( lhs.is_bv() )
                    return z3::srem( lhs, rhs );

                return z3::rem( lhs, rhs );
            } );
    }

    bool BuildZ3Op( Builder& b, const Eq& instr )
    {
        return BuildZ3BinBoolExpr(
            b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs == rhs; } );
    }

    bool BuildZ3Op( Builder& b, const Neq& instr )
    {
        return BuildZ3BinBoolExpr(
            b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs != rhs; } );
    }

    bool BuildZ3Op( Builder& b, const UGT& instr )
    {
        return BuildZ3BinBoolExpr(
            b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::ugt( lhs, rhs ); } );
    }

    bool BuildZ3Op( Builder& b, const UGE& instr )
    {
        return BuildZ3BinBoolExpr(
            b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::uge( lhs, rhs ); } );
    }

    bool BuildZ3Op( Builder& b, const ULT& instr )
    {
        return BuildZ3BinBoolExpr(
            b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::ult( lhs, rhs ); } );
    }

    bool BuildZ3Op( Builder& b, const ULE& instr )
    {
        return BuildZ3BinBoolExpr(
            b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::ule( lhs, rhs ); } );
    }

    bool BuildZ3Op( Builder& b, const SGT& instr )
    {
        return BuildZ3BinBoolExpr(
            b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs > rhs; } );
    }

    bool BuildZ3Op( Builder& b, const SGE& instr )
    {
        return BuildZ3BinBoolExpr(
            b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs >= rhs; } );
    }

    bool BuildZ3Op( Builder& b, const SLT& instr )
    {
        return BuildZ3BinBoolExpr(
            b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs < rhs; } );
    }

    bool BuildZ3Op( Builder& b, const SLE& instr )
    {
        return BuildZ3BinBoolExpr(
            b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs <= rhs; } );
    }

    bool BuildZ3Op( Builder& b, const Assert& instr )
    {
        auto cond = b.pop();
        if( !cond )
            return false;
608
609
610
611
612
613
614

615
616
617
618
619
620
621
622
        const auto* expr = b.retrievePlaceholder( instr.name() );
        if( expr )
        {
            b.push( Z3Val{ *expr, *EIRToValue( instr.type().get() ), instr.locationId() } );
            return true;
        }


        auto result = BuildZ3ConstantFromType( b, instr.type().get(), format( "p{}", instr.name().str() ) );
        b.push( move( result ) );
        return true;
    }

    bool BuildZ3Op( Builder& b, const PHOverrideSet& instr )
    {
        auto phExpr = b.pop();







>
|







639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
        const auto* expr = b.retrievePlaceholder( instr.name() );
        if( expr )
        {
            b.push( Z3Val{ *expr, *EIRToValue( instr.type().get() ), instr.locationId() } );
            return true;
        }

        auto result =
            BuildZ3ConstantFromType( b, instr.type().get(), format( "p{}", instr.name().str() ) );
        b.push( move( result ) );
        return true;
    }

    bool BuildZ3Op( Builder& b, const PHOverrideSet& instr )
    {
        auto phExpr = b.pop();
658
659
660
661
662
663
664

665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
        return !!b.setVar( instr.tempIndex(), move( *initVal ) );
    }

    bool BuildZ3Op( Builder& b, const Select& instr )
    {
        if( auto cstBaseLoc = b.pop< Address >() )
        {

            b.push( Address::Select( move( *cstBaseLoc ), instr.memberIndex(), instr.locationId() ) );
            return true;
        }

        auto symBaseLoc = b.pop();
        if( !symBaseLoc )
            return false;

        const auto& tc = TypeCache::GetInstance();

        auto ltExpr = tc.addrLifetimeGetter()( symBaseLoc->expr );
        auto originExpr = tc.addrOriginGetter()( symBaseLoc->expr );
        auto pathExpr = tc.addrPathGetter()( symBaseLoc->expr );

        b.push( Z3Val
        {
            tc.addrCtor()(
                ltExpr,
                originExpr,
                z3::concat( pathExpr, GetZ3Context().int_val( instr.memberIndex() ).unit() )
            ),

            *EIRToValue( GetValueType< cir::Address >() ),
            instr.locationId()
        } );

        return true;
    }

    bool BuildZ3Op( Builder& b, const GhostCall& instr )
    {
        auto gfunc = b.pop< Value >();







>
|













|
<
|
<
<
|
<

|
<
<







690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712

713


714

715
716


717
718
719
720
721
722
723
        return !!b.setVar( instr.tempIndex(), move( *initVal ) );
    }

    bool BuildZ3Op( Builder& b, const Select& instr )
    {
        if( auto cstBaseLoc = b.pop< Address >() )
        {
            b.push(
                Address::Select( move( *cstBaseLoc ), instr.memberIndex(), instr.locationId() ) );
            return true;
        }

        auto symBaseLoc = b.pop();
        if( !symBaseLoc )
            return false;

        const auto& tc = TypeCache::GetInstance();

        auto ltExpr = tc.addrLifetimeGetter()( symBaseLoc->expr );
        auto originExpr = tc.addrOriginGetter()( symBaseLoc->expr );
        auto pathExpr = tc.addrPathGetter()( symBaseLoc->expr );

        b.push( Z3Val{

            tc.addrCtor()( ltExpr, originExpr,


                z3::concat( pathExpr, GetZ3Context().int_val( instr.memberIndex() ).unit() ) ),


            *EIRToValue( GetValueType< cir::Address >() ), instr.locationId() } );



        return true;
    }

    bool BuildZ3Op( Builder& b, const GhostCall& instr )
    {
        auto gfunc = b.pop< Value >();
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
    {
        G_ERROR( "InlineCall encountered during verification" );
        return false;
    }

    bool BuildZ3Op( Builder& b, const cir::Instruction& instr )
    {
        return visit( [&]( auto&& e )
        {
            return BuildZ3Op( b, e );
        }, instr );
    }

    Z3Val BuildZ3ExprFromValue( Builder& b, const Value& val )
    {
        if( val.isConstant() )
            return BuildZ3ValFromConstant( b, val );








|
<
<
<







794
795
796
797
798
799
800
801



802
803
804
805
806
807
808
    {
        G_ERROR( "InlineCall encountered during verification" );
        return false;
    }

    bool BuildZ3Op( Builder& b, const cir::Instruction& instr )
    {
        return visit( [&]( auto&& e ) { return BuildZ3Op( b, e ); }, instr );



    }

    Z3Val BuildZ3ExprFromValue( Builder& b, const Value& val )
    {
        if( val.isConstant() )
            return BuildZ3ValFromConstant( b, val );

801
802
803
804
805
806
807
808
            return TryBuildZ3ValFromConstant( b, val );

        if( BuildZ3Op( b, *val.cir() ) )
            return b.pop();

        return TryBuildZ3ConstantFromType( b, val.type(), format( "val{}", b.newUniqueId() ) );
    }
}







|
825
826
827
828
829
830
831
832
            return TryBuildZ3ValFromConstant( b, val );

        if( BuildZ3Op( b, *val.cir() ) )
            return b.pop();

        return TryBuildZ3ConstantFromType( b, val.type(), format( "val{}", b.newUniqueId() ) );
    }
} // namespace goose::verify
Changes to bs/verify/value.h.
16
17
18
19
20
21
22
23

24

25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
    extern Z3Val BuildZ3ValFromConstant( Builder& b, const Value& val );
    extern Z3Val BuildZ3ExprFromValue( Builder& b, const Value& val );
    extern Z3Val BuildZ3ConstantFromType( Builder& b, const Value& type, const string& name );
    extern Z3Val BuildZ3ConstantFromType( Builder& b, const Term& type, const string& name );

    extern optional< Z3Val > TryBuildZ3ValFromConstant( Builder& b, const Value& val );
    extern optional< Z3Val > TryBuildZ3ExprFromValue( Builder& b, const Value& val );
    extern optional< Z3Val > TryBuildZ3ConstantFromType( Builder& b, const Value& type, const string& name );

    extern optional< Z3Val > TryBuildZ3ConstantFromType( Builder& b, const Term& type, const string& name );


    extern bool BuildZ3Op( Builder& b, const cir::Instruction& instr );
    extern bool BuildZ3Op( Builder& b, const cir::InstrSeq& is );

    extern z3::expr GetAsBitVec( const z3::expr& expr, const Value& type );
    extern z3::expr GetAsBitVec( const Z3Val& zv );

    extern z3::expr GetAsInt( const z3::expr& expr, const Value& type );
    extern z3::expr GetAsInt( const Z3Val& zv );

    extern z3::expr Coerce( const Z3Val& val, const Z3Val& to );
    extern z3::expr Coerce( const z3::expr& expr, const Z3Val& to );
}

#endif







|
>
|
>












|


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
    extern Z3Val BuildZ3ValFromConstant( Builder& b, const Value& val );
    extern Z3Val BuildZ3ExprFromValue( Builder& b, const Value& val );
    extern Z3Val BuildZ3ConstantFromType( Builder& b, const Value& type, const string& name );
    extern Z3Val BuildZ3ConstantFromType( Builder& b, const Term& type, const string& name );

    extern optional< Z3Val > TryBuildZ3ValFromConstant( Builder& b, const Value& val );
    extern optional< Z3Val > TryBuildZ3ExprFromValue( Builder& b, const Value& val );
    extern optional< Z3Val > TryBuildZ3ConstantFromType(
        Builder& b, const Value& type, const string& name );
    extern optional< Z3Val > TryBuildZ3ConstantFromType(
        Builder& b, const Term& type, const string& name );

    extern bool BuildZ3Op( Builder& b, const cir::Instruction& instr );
    extern bool BuildZ3Op( Builder& b, const cir::InstrSeq& is );

    extern z3::expr GetAsBitVec( const z3::expr& expr, const Value& type );
    extern z3::expr GetAsBitVec( const Z3Val& zv );

    extern z3::expr GetAsInt( const z3::expr& expr, const Value& type );
    extern z3::expr GetAsInt( const Z3Val& zv );

    extern z3::expr Coerce( const Z3Val& val, const Z3Val& to );
    extern z3::expr Coerce( const z3::expr& expr, const Z3Val& to );
} // namespace goose::verify

#endif
Changes to bs/verify/vartracker.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
    const auto* vs = m_varStorage.get( index );
    if( !vs )
        return nullopt;

    if( vs->size() <= bbIndex )
        return nullopt;

    return (*vs)[ bbIndex ];
}

optional< Z3Val > VarTracker::setForBasicBlock( Builder& b, uint32_t bbIndex, uint32_t index, Z3Val&& val )

{
    auto* vs = m_varStorage.get( index );
    if( !vs )
    {
        VarState newVS;
        newVS.resize( bbIndex + 1 );
        auto& vsRef = m_varStorage.set( index, move( newVS ) );
        vs = &vsRef;
    }
    else if( vs->size() <= bbIndex )
        vs->resize( bbIndex + 1 );

    auto& c = GetZ3Context();
    auto predicate = c.bool_const( format( "b{}", bbIndex ).c_str() );

    auto lhsExpr = BuildZ3ConstantFromType( b, val.type, format( "v{}_{}_{}", index, bbIndex, b.newUniqueId() ) );

    b.add( z3::implies( predicate, lhsExpr.expr == Coerce( val, lhsExpr ) ) );

    (*vs)[ bbIndex ] = move( lhsExpr );
    return *(*vs)[ bbIndex ];
}

optional< Z3Val > VarTracker::retrieve( Builder& b, uint32_t bbIndex, uint32_t index )
{
    if( bbIndex == b.currentBBIndex() )
        return nullopt;








|


|
>















|
>


|
|







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
    const auto* vs = m_varStorage.get( index );
    if( !vs )
        return nullopt;

    if( vs->size() <= bbIndex )
        return nullopt;

    return ( *vs )[bbIndex];
}

optional< Z3Val > VarTracker::setForBasicBlock(
    Builder& b, uint32_t bbIndex, uint32_t index, Z3Val&& val )
{
    auto* vs = m_varStorage.get( index );
    if( !vs )
    {
        VarState newVS;
        newVS.resize( bbIndex + 1 );
        auto& vsRef = m_varStorage.set( index, move( newVS ) );
        vs = &vsRef;
    }
    else if( vs->size() <= bbIndex )
        vs->resize( bbIndex + 1 );

    auto& c = GetZ3Context();
    auto predicate = c.bool_const( format( "b{}", bbIndex ).c_str() );

    auto lhsExpr = BuildZ3ConstantFromType(
        b, val.type, format( "v{}_{}_{}", index, bbIndex, b.newUniqueId() ) );
    b.add( z3::implies( predicate, lhsExpr.expr == Coerce( val, lhsExpr ) ) );

    ( *vs )[bbIndex] = move( lhsExpr );
    return *( *vs )[bbIndex];
}

optional< Z3Val > VarTracker::retrieve( Builder& b, uint32_t bbIndex, uint32_t index )
{
    if( bbIndex == b.currentBBIndex() )
        return nullopt;

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

    // Model the ssa phi operation by creating a series of equality assertions, each implied by
    // one of the possible incoming edge condition for the current basic block.
    if( b.remapper() )
    {
        auto& c = GetZ3Context();

        b.remapper()->forEachIncomingEdge( bbIndex, [&]( auto&& predIndex, auto&& expr )

        {
            if( auto predVal = retrieve( b, predIndex, index ) )
            {
                if( !lhsExpr )
                    lhsExpr = BuildZ3ConstantFromType( b, predVal->type, format( "v{}_{}_{}", index, bbIndex, b.newUniqueId() ) );



                b.add( z3::implies( c.bool_const( format( "e{}_{}", predIndex, bbIndex ).c_str() ),
                    lhsExpr->expr == Coerce( *predVal, *lhsExpr ) ) );
            }
        } );
    }

    if( !lhsExpr )
        return nullopt;

    return setForBasicBlock( b, bbIndex, index, move( *lhsExpr ) );
}

optional< Z3Val > VarTracker::set( Builder& b, uint32_t index, Z3Val&& val )
{
    return setForBasicBlock( b, b.currentBBIndex(), index, move( val ) );
}







|
>
|
|
|
|
|
>

>
|
|
|
|












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

    // Model the ssa phi operation by creating a series of equality assertions, each implied by
    // one of the possible incoming edge condition for the current basic block.
    if( b.remapper() )
    {
        auto& c = GetZ3Context();

        b.remapper()->forEachIncomingEdge( bbIndex,
            [&]( auto&& predIndex, auto&& expr )
            {
                if( auto predVal = retrieve( b, predIndex, index ) )
                {
                    if( !lhsExpr )
                        lhsExpr = BuildZ3ConstantFromType( b, predVal->type,
                            format( "v{}_{}_{}", index, bbIndex, b.newUniqueId() ) );

                    b.add(
                        z3::implies( c.bool_const( format( "e{}_{}", predIndex, bbIndex ).c_str() ),
                            lhsExpr->expr == Coerce( *predVal, *lhsExpr ) ) );
                }
            } );
    }

    if( !lhsExpr )
        return nullopt;

    return setForBasicBlock( b, bbIndex, index, move( *lhsExpr ) );
}

optional< Z3Val > VarTracker::set( Builder& b, uint32_t index, Z3Val&& val )
{
    return setForBasicBlock( b, b.currentBBIndex(), index, move( val ) );
}
Changes to bs/verify/vartracker.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
#ifndef GOOSE_VERIFY_VARTRACKER_H
#define GOOSE_VERIFY_VARTRACKER_H

namespace goose::verify
{
    class Builder;

    class VarTracker
    {
        public:
            VarTracker( size_t size ) :
                m_varStorage( size )
            {}



            optional< Z3Val > retrieve( Builder& b, uint32_t bbIndex, uint32_t index );
            optional< Z3Val > set( Builder& b, uint32_t index, Z3Val&& val );

        private:
            optional< Z3Val > getForBasicBlock( uint32_t bbIndex, uint32_t index ) const;
            optional< Z3Val > setForBasicBlock( Builder& b, uint32_t bbIndex, uint32_t index, Z3Val&& val );


            // For each variable, store its last value as a z3 expression.
            using VarState = llvm::SmallVector< optional< Z3Val >, 16 >;
            using VarStorage = cir::TempStorage< VarState >;

            VarStorage m_varStorage;
    };
}


#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
#ifndef GOOSE_VERIFY_VARTRACKER_H
#define GOOSE_VERIFY_VARTRACKER_H

namespace goose::verify
{
    class Builder;

    class VarTracker
    {
      public:
        VarTracker( size_t size ) :
            m_varStorage( size )

        {
        }

        optional< Z3Val > retrieve( Builder& b, uint32_t bbIndex, uint32_t index );
        optional< Z3Val > set( Builder& b, uint32_t index, Z3Val&& val );

      private:
        optional< Z3Val > getForBasicBlock( uint32_t bbIndex, uint32_t index ) const;
        optional< Z3Val > setForBasicBlock(
            Builder& b, uint32_t bbIndex, uint32_t index, Z3Val&& val );

        // For each variable, store its last value as a z3 expression.
        using VarState = llvm::SmallVector< optional< Z3Val >, 16 >;
        using VarStorage = cir::TempStorage< VarState >;

        VarStorage m_varStorage;
    };

} // namespace goose::verify

#endif
Changes to bs/verify/verify.cpp.
1
2
3
4
5
6
7
8
9
10
#include "verify.h"

namespace goose::verify
{
    z3::context& GetZ3Context()
    {
        static z3::context z3Context;
        return z3Context;
    }
}









|
1
2
3
4
5
6
7
8
9
10
#include "verify.h"

namespace goose::verify
{
    z3::context& GetZ3Context()
    {
        static z3::context z3Context;
        return z3Context;
    }
} // namespace goose::verify
Changes to bs/verify/verify.h.
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    {
        Pass,
        Fails,
        Unverifiable
    };

    extern Result VerifyInstrSeq( const sema::Context& c, const InstrSeq& is );
}

#include "helpers.h"
#include "verifyviz.h"

#include "type.h"
#include "value.h"
#include "ghostfunc.h"







|







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    {
        Pass,
        Fails,
        Unverifiable
    };

    extern Result VerifyInstrSeq( const sema::Context& c, const InstrSeq& is );
} // namespace goose::verify

#include "helpers.h"
#include "verifyviz.h"

#include "type.h"
#include "value.h"
#include "ghostfunc.h"
Changes to bs/verify/verifyviz.cpp.
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
    ostream& indent( ostream& out, int indentation )
    {
        while( indentation-- )
            out << '\t';

        return out;
    }
}

VerifyViz::~VerifyViz()
{
#ifdef VERIFY_VIZ_FAIL_ONLY
    if( !m_assertionFailed )
        return;
#endif

    if( m_filename.empty() )
        m_filename = "anon_func.dot";

    ofstream out( m_filename );

    out <<
            "digraph \"goose\"\n"
            "{\n"
            "\tcolor=\"#" << colors::base05 << "\";\n"


            "\tfontcolor=\"#" << colors::base05 << "\";\n"


            "\tbgcolor=\"#" << colors::base00 << "\";\n"


            "\trankdir=TB;\n"
            "\tnode [\n"
            "\t\tcolor=\"#" << colors::base05 << "\";\n"


            "\t\tfontcolor=\"#" << colors::base05 << "\";\n"


            "\t];\n"
            "\tedge [\n"
            "\t\tcolor=\"#" << colors::base03 << "\";\n"


            "\t];\n";

    if( m_assertionFailed )
        indent( out, 1 ) << *m_assertionFailed << "[color=\"#" << colors::base08 << "\"];\n";

    for( auto&& [id,oid] : m_root->m_blocks )
    {
        out << '\n';
        if( id != oid )
            indent( out, 1 ) << id << "[ label=\"" << id << ':' << oid << "\"];";
        else
            indent( out, 1 ) << id << ';';

    }

    for( auto&& x : m_root->m_children )
    {
        out << '\n';
        GenerateSubGraph( out, x, 1 );
    }

    for( auto&& [from,to] : m_edges )
        indent( out, 1 ) << from << "->" << to << ";\n";

    out << "}\n";
}

void VerifyViz::GenerateSubGraph( ostream& out, ptr< SubGraph > sg, int indentation ) const
{
    indent( out, indentation ) << "subgraph cluster_" << sg.get() << endl;
    indent( out, indentation ) << "{\n";
    indent( out, indentation + 1 ) << "color = \"#" << colors::base09 << "\";\n";

    for( auto&& [id,oid] : sg->m_blocks )
    {
        out << '\n';
        if( id != oid )
            indent( out, indentation + 1 ) << id << "[ label=\"" << id << ':' << oid << "\"];";
        else
            indent( out, indentation + 1 ) << id << ';';
    }







|



|


|






<
|
|
|
>
>
|
>
>
|
>
>
|
|
|
>
>
|
>
>
|
|
|
>
>
|




|






<








|











|







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
    ostream& indent( ostream& out, int indentation )
    {
        while( indentation-- )
            out << '\t';

        return out;
    }
} // namespace

VerifyViz::~VerifyViz()
{
    #ifdef VERIFY_VIZ_FAIL_ONLY
    if( !m_assertionFailed )
        return;
    #endif

    if( m_filename.empty() )
        m_filename = "anon_func.dot";

    ofstream out( m_filename );


    out << "digraph \"goose\"\n"
           "{\n"
           "\tcolor=\"#"
        << colors::base05
        << "\";\n"
           "\tfontcolor=\"#"
        << colors::base05
        << "\";\n"
           "\tbgcolor=\"#"
        << colors::base00
        << "\";\n"
           "\trankdir=TB;\n"
           "\tnode [\n"
           "\t\tcolor=\"#"
        << colors::base05
        << "\";\n"
           "\t\tfontcolor=\"#"
        << colors::base05
        << "\";\n"
           "\t];\n"
           "\tedge [\n"
           "\t\tcolor=\"#"
        << colors::base03
        << "\";\n"
           "\t];\n";

    if( m_assertionFailed )
        indent( out, 1 ) << *m_assertionFailed << "[color=\"#" << colors::base08 << "\"];\n";

    for( auto&& [id, oid] : m_root->m_blocks )
    {
        out << '\n';
        if( id != oid )
            indent( out, 1 ) << id << "[ label=\"" << id << ':' << oid << "\"];";
        else
            indent( out, 1 ) << id << ';';

    }

    for( auto&& x : m_root->m_children )
    {
        out << '\n';
        GenerateSubGraph( out, x, 1 );
    }

    for( auto&& [from, to] : m_edges )
        indent( out, 1 ) << from << "->" << to << ";\n";

    out << "}\n";
}

void VerifyViz::GenerateSubGraph( ostream& out, ptr< SubGraph > sg, int indentation ) const
{
    indent( out, indentation ) << "subgraph cluster_" << sg.get() << endl;
    indent( out, indentation ) << "{\n";
    indent( out, indentation + 1 ) << "color = \"#" << colors::base09 << "\";\n";

    for( auto&& [id, oid] : sg->m_blocks )
    {
        out << '\n';
        if( id != oid )
            indent( out, indentation + 1 ) << id << "[ label=\"" << id << ':' << oid << "\"];";
        else
            indent( out, indentation + 1 ) << id << ';';
    }
Changes to bs/verify/verifyviz.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

21

22
23
24
25
26

27
28
29
30
31
32
33

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#ifndef GOOSE_VERIFY_VIZ_H
#define GOOSE_VERIFY_VIZ_H

#include <z3++.h>
#include "cir/cir.h"
#include "sema/sema.h"

// Enable the generation of a dot file of the unrolled CFG generated to
// verify each function, saved as "mangled_func_name.dot"
//#define VERIFY_VIZ

namespace goose::verify
{
#ifndef VERIFY_VIZ
    class VerifyViz
    {
        public:
            void setFunctionIdentity( const Term& id ) {}

            void beginLoopVerification() {}

            void beginLoopInduction() {}

            void endLoopVerification() {}

            void nextLoopIteration() {}

            void addBlock( uint32_t id, uint32_t originalId ) {}

            void addEdge( uint32_t from, uint32_t to ) {}

            void assertionFailed() {}

            struct Guard
            {
                Guard( VerifyViz& vv ) {}

                void commit() {}
            };
    };
#else
    class VerifyViz
    {
        struct SubGraph
        {
            ptr< SubGraph > deepCopy() const;

            vector< pair< uint32_t, uint32_t > > m_blocks;
            list< ptr< SubGraph > > m_children;
            wptr< SubGraph > m_parent;
        };

        public:
            ~VerifyViz();

            void setFunctionIdentity( const Term& id );

            void beginLoopVerification();
            void beginLoopInduction();
            void endLoopVerification();

            void nextLoopIteration();

            void addBlock( uint32_t id, uint32_t originalId );
            void addEdge( uint32_t from, uint32_t to );

            void assertionFailed() { m_assertionFailed = m_lastBlockId; }

            struct Guard
            {
                Guard( VerifyViz& vv );
                ~Guard();

                void commit()
                {
                    m_commit = true;
                }

                ptr< SubGraph > deepCopyGraph( const ptr< SubGraph >& pNode );

                VerifyViz& m_vv;
                ptr< SubGraph > m_savedRoot;
                ptr< SubGraph > m_savedCurrentSubGraph;
                vector< pair< uint32_t, uint32_t > > m_savedEdges;
                uint32_t m_savedLastBlockId = 0;
                bool m_commit = false;
            };

        private:
            void pushSubGraph();
            void popSubGraph();

            void GenerateSubGraph( ostream& out, ptr< SubGraph > sg, int indentation ) const;

            string m_filename;

            ptr< SubGraph > m_root = make_shared< SubGraph >();
            ptr< SubGraph > m_currentSubGraph = m_root;
            vector< pair< uint32_t, uint32_t > > m_edges;

            optional< uint32_t > m_assertionFailed;
            uint32_t m_lastBlockId = 0;
    };
#endif
}

#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
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
#ifndef GOOSE_VERIFY_VIZ_H
#define GOOSE_VERIFY_VIZ_H

#include <z3++.h>
#include "cir/cir.h"
#include "sema/sema.h"

// Enable the generation of a dot file of the unrolled CFG generated to
// verify each function, saved as "mangled_func_name.dot"
// #define VERIFY_VIZ

namespace goose::verify
{
#ifndef VERIFY_VIZ
    class VerifyViz
    {
      public:
        void setFunctionIdentity( const Term& id ) {}

        void beginLoopVerification() {}

        void beginLoopInduction() {}

        void endLoopVerification() {}

        void nextLoopIteration() {}

        void addBlock( uint32_t id, uint32_t originalId ) {}

        void addEdge( uint32_t from, uint32_t to ) {}

        void assertionFailed() {}

        struct Guard
        {
            Guard( VerifyViz& vv ) {}

            void commit() {}
        };
    };
#else
    class VerifyViz
    {
        struct SubGraph
        {
            ptr< SubGraph > deepCopy() const;

            vector< pair< uint32_t, uint32_t > > m_blocks;
            list< ptr< SubGraph > > m_children;
            wptr< SubGraph > m_parent;
        };

      public:
        ~VerifyViz();

        void setFunctionIdentity( const Term& id );

        void beginLoopVerification();
        void beginLoopInduction();
        void endLoopVerification();

        void nextLoopIteration();

        void addBlock( uint32_t id, uint32_t originalId );
        void addEdge( uint32_t from, uint32_t to );

        void assertionFailed() { m_assertionFailed = m_lastBlockId; }

        struct Guard
        {
            Guard( VerifyViz& vv );
            ~Guard();

            void commit() { m_commit = true; }




            ptr< SubGraph > deepCopyGraph( const ptr< SubGraph >& pNode );

            VerifyViz& m_vv;
            ptr< SubGraph > m_savedRoot;
            ptr< SubGraph > m_savedCurrentSubGraph;
            vector< pair< uint32_t, uint32_t > > m_savedEdges;
            uint32_t m_savedLastBlockId = 0;
            bool m_commit = false;
        };

      private:
        void pushSubGraph();
        void popSubGraph();

        void GenerateSubGraph( ostream& out, ptr< SubGraph > sg, int indentation ) const;

        string m_filename;

        ptr< SubGraph > m_root = make_shared< SubGraph >();
        ptr< SubGraph > m_currentSubGraph = m_root;
        vector< pair< uint32_t, uint32_t > > m_edges;

        optional< uint32_t > m_assertionFailed;
        uint32_t m_lastBlockId = 0;
    };
#endif
} // namespace goose::verify

#endif