Goose  Check-in [9b8306c3af]

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

Overview
Comment:Fixed passing tuple by value to functions, which involved properly handling type checking of constant tuples containing computed data and some codegen bugs
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 9b8306c3afc8a7f062d7f9c85d488b78e1cd8de57550d2f91c9f7667ddf14568
User & Date: zlodo 2023-01-05 19:44:44.691
Context
2023-01-06
19:13
Remove redundant calls to the ToType extension point when compiling function bodies check-in: 78a358141d user: zlodo tags: trunk
2023-01-05
19:44
Fixed passing tuple by value to functions, which involved properly handling type checking of constant tuples containing computed data and some codegen bugs check-in: 9b8306c3af user: zlodo tags: trunk
2022-11-21
18:59
  • some fixes in #for base implementation
  • g0api: exposed functions to deconstructs decls
check-in: ac0c5a2c6a user: zlodo tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to bs/builtins/operators/assignment.cpp.
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
            {
                DiagnosticsManager::GetInstance().emitErrorMessage( 0, "incompatible tuple sizes." );
                return PoisonValue();
            }

            // Store the source tuple as a temporary, otherwise we're going to recompute rhs
            // for each member assigned (which is bad)
            auto rhsIndex = cfg->getNewTemporaryIndex();
            cfg->currentBB()->append( rhs, CreateTemporary( rhsIndex, rhs.locationId() ) );
            auto srcTup = BuildComputedValue( rhs.type(), GetTemporary( rhs.type(), rhsIndex, rhs.locationId() ) );

            // Load the values from the rhs tuple into temporary vars.
            vector< Value > tempVars;
            tempVars.reserve( tupSize );








|







152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
            {
                DiagnosticsManager::GetInstance().emitErrorMessage( 0, "incompatible tuple sizes." );
                return PoisonValue();
            }

            // Store the source tuple as a temporary, otherwise we're going to recompute rhs
            // for each member assigned (which is bad)
            auto rhsIndex = util::GenerateNewUID();
            cfg->currentBB()->append( rhs, CreateTemporary( rhsIndex, rhs.locationId() ) );
            auto srcTup = BuildComputedValue( rhs.type(), GetTemporary( rhs.type(), rhsIndex, rhs.locationId() ) );

            // Load the values from the rhs tuple into temporary vars.
            vector< Value > tempVars;
            tempVars.reserve( tupSize );

Changes to bs/builtins/operators/logic.cpp.
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
                auto pSuccBB = cfg->createBB();

                // If the lhs is true, skip to the end directly.
                // Otherwise, jump to the BB that computes rhs.
                predBB->append( lhs );
                predBB->setTerminator( CondBranch( pSuccBB, pRhsBB ) );

                auto rhsIndex = cfg->getNewTemporaryIndex();
                pRhsBB->append( rhs, CreateTemporary( rhsIndex, c.locationId() ) );
                pRhsBB->setTerminator( Branch( pSuccBB ) );

                auto resultIndex = cfg->getNewTemporaryIndex();

                // Build the Phi instruction that will collect the final result.
                auto phi = Phi( *EIRToValue( GetValueType< bool >() ), 2,
                    resultIndex, c.locationId() );

                // If coming directly from the lhs BB, we know the result is true.
                phi.setIncoming( predBB, ToValue( true ) );







|



|







147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
                auto pSuccBB = cfg->createBB();

                // If the lhs is true, skip to the end directly.
                // Otherwise, jump to the BB that computes rhs.
                predBB->append( lhs );
                predBB->setTerminator( CondBranch( pSuccBB, pRhsBB ) );

                auto rhsIndex = util::GenerateNewUID();
                pRhsBB->append( rhs, CreateTemporary( rhsIndex, c.locationId() ) );
                pRhsBB->setTerminator( Branch( pSuccBB ) );

                auto resultIndex = util::GenerateNewUID();

                // Build the Phi instruction that will collect the final result.
                auto phi = Phi( *EIRToValue( GetValueType< bool >() ), 2,
                    resultIndex, c.locationId() );

                // If coming directly from the lhs BB, we know the result is true.
                phi.setIncoming( predBB, ToValue( true ) );
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
                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 = cfg->getNewTemporaryIndex();
                pRhsBB->append( rhs, CreateTemporary( rhsIndex, c.locationId() ) );
                pRhsBB->setTerminator( Branch( pSuccBB ) );

                auto resultIndex = cfg->getNewTemporaryIndex();

                // Build the Phi instruction that will collect the final result.
                auto phi = Phi( *EIRToValue( GetValueType< bool >() ), 2,
                    resultIndex, c.locationId() );

                // If coming directly from the lhs BB, we know the result is false.
                phi.setIncoming( predBB, ToValue( false ) );







|



|







247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
                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, c.locationId() ) );
                pRhsBB->setTerminator( Branch( pSuccBB ) );

                auto resultIndex = util::GenerateNewUID();

                // Build the Phi instruction that will collect the final result.
                auto phi = Phi( *EIRToValue( GetValueType< bool >() ), 2,
                    resultIndex, c.locationId() );

                // If coming directly from the lhs BB, we know the result is false.
                phi.setIncoming( predBB, ToValue( false ) );
Changes to bs/builtins/types/func/build.cpp.
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

                // 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::VarAddr( varId++, decl.type(), param.locationId() ),
                        cir::Load( decl.type(), param.locationId() ) ) ) );
            }
            else if( param.isConstant() )
                tv->append( ValueToEIR( param ) );

            return true;
        } );

        if( failed )
            return nullopt;

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







|














|







42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

                // 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::VarAddr( varId++, param.locationId() ),
                        cir::Load( decl.type(), param.locationId() ) ) ) );
            }
            else if( param.isConstant() )
                tv->append( ValueToEIR( param ) );

            return true;
        } );

        if( failed )
            return nullopt;

        // 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( ToType( c, returnType ) );
        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() ) ) ) );
Changes to bs/builtins/types/func/compile.cpp.
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
            if( IsOpenTuple( declType ) )
            {
                auto packId = varId;

                auto pack = EmptyClosedTuple();
                ForEachInTuple( declType, [&]( auto&& type )
                {
                    auto t = ValueToEIR( type );
                    auto val = BuildComputedValue( t,
                        cir::VarAddr( varId++, t, type.locationId() ),
                        cir::Load( t, type.locationId() ) );
                    pack = AppendToTuple( pack, val );
                    return true;
                } );

                c.env()->storeValue( paramIdentity, ANYTERM( _ ),
                    ValueToEIR( pack ) );

                cb->declareValue( pack, packId );
            }
            else
            {
                // Create a locvar to hold the param.

                LocalVar lv( decl.name(), f.type().kind() == FuncType::Kind::Intrinsic ?
                    GetValueType< TypeWrapper< Value > >() : decl.type(), varId++ );







                auto locVar = ToValue( lv ).setLocationId( param.locationId() );

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

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







|

|













>
|
|
>
>
>
>
>
>
>







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
            if( IsOpenTuple( declType ) )
            {
                auto packId = varId;

                auto pack = EmptyClosedTuple();
                ForEachInTuple( declType, [&]( auto&& type )
                {
                    auto t = ValueToEIR( ToType( c, type ) );
                    auto val = BuildComputedValue( t,
                        cir::VarAddr( varId++, type.locationId() ),
                        cir::Load( t, type.locationId() ) );
                    pack = AppendToTuple( pack, val );
                    return true;
                } );

                c.env()->storeValue( paramIdentity, ANYTERM( _ ),
                    ValueToEIR( pack ) );

                cb->declareValue( pack, packId );
            }
            else
            {
                // Create a locvar to hold the param.
                Term type;
                if( f.type().kind() == FuncType::Kind::Intrinsic )
                    type = GetValueType< TypeWrapper< Value > >();
                else
                    // TODO: we are calling the ToType extension point to convert a param that was already converted
                    // when building the function type! We can probably manage to reuse the already converted type
                    // and avoid this redundant call to ToType which is expensive
                    type = ValueToEIR( ToType( c, *EIRToValue( decl.type() ) ) );

                LocalVar lv( decl.name(), move( type ), varId++ );
                auto locVar = ToValue( lv ).setLocationId( param.locationId() );

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

                cb->declareValue( locVar, lv.index() );
            }
222
223
224
225
226
227
228

229
230
231
232
233
            // 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() ) );
        }


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







>





230
231
232
233
234
235
236
237
238
239
240
241
242
            // 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() ) );
        }

        ReindexVars( cfg );
        pFuncCIR->body() = cfg;
        verify::Func fv( localContext, f );
        return fv.verify();
    }
}
Changes to bs/builtins/types/func/functype.cpp.
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
    {
        auto av = make_shared< Vector >();
        av->reserve( VecSize( ft.params() ) );

        bool failed = false;
        ForEachInVectorTerms( ft.params(), unifiedArgs, [&]( auto&& p, auto&& a )
        {
            auto vp = ValuePatternFromEIR( p );
            if( vp->val() == HOLE( "_"_sid ) )
            {
                auto arg = *EIRToValue( a );
                auto convertedArg = InvokeOverloadSet( c, c.env()->extConvertFuncArg(),
                    MakeClosedTuple( arg, *EIRToValue( vp->type() ) ) );
                if( convertedArg.isPoison() )
                {







|







92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
    {
        auto av = make_shared< Vector >();
        av->reserve( VecSize( ft.params() ) );

        bool failed = false;
        ForEachInVectorTerms( ft.params(), unifiedArgs, [&]( auto&& p, auto&& a )
        {
            auto vp = EIRToValuePattern( p );
            if( vp->val() == HOLE( "_"_sid ) )
            {
                auto arg = *EIRToValue( a );
                auto convertedArg = InvokeOverloadSet( c, c.env()->extConvertFuncArg(),
                    MakeClosedTuple( arg, *EIRToValue( vp->type() ) ) );
                if( convertedArg.isPoison() )
                {
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142

        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 = ValuePatternFromEIR( p );
            if( vp->val() == HOLE( "_"_sid ) )
            {
                auto wrappedArg = ToValue( TypeWrapper< Value >( *EIRToValue( a ) ) );
                av->append( ValueToEIR( wrappedArg ) );
            }

            return true;







|







128
129
130
131
132
133
134
135
136
137
138
139
140
141
142

        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 ) )
            {
                auto wrappedArg = ToValue( TypeWrapper< Value >( *EIRToValue( a ) ) );
                av->append( ValueToEIR( wrappedArg ) );
            }

            return true;
Changes to bs/builtins/types/func/invocation/func.cpp.
34
35
36
37
38
39
40









41
42
43
44
45



46
47
48
49
50
51
52
53
54
55
56
57
                auto result = BuildComputedValue( typeCheckedRType, argsInstrSeq, preparedCallee,
                    cir::Call( argCount, loc ) );

                if( result.type() != GetValueType< void >() && !IsExternalFunc( callee ) )
                {
                    if( cir::CanValueBeEagerlyEvaluated( result ) )
                    {









                        if( !verify::VerifyInstrSeq( c, *result.cir() ) )
                            return PoisonValue();

                        execute::VM vm;
                        result = execute::Evaluate( result, vm );



                    }

                    // Register the result for destruction.
                    if( auto cfg = GetCFG( c ) )
                        DeclareValue( c, result, cfg->getNewTemporaryIndex() );
                }

                return result;
            }

            Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
            {







>
>
>
>
>
>
>
>
>





>
>
>




|







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
                auto result = BuildComputedValue( typeCheckedRType, argsInstrSeq, preparedCallee,
                    cir::Call( argCount, loc ) );

                if( result.type() != GetValueType< void >() && !IsExternalFunc( callee ) )
                {
                    if( cir::CanValueBeEagerlyEvaluated( result ) )
                    {
                        // Backup the value's instr seq: we need to reindex it to verify it,
                        // but if the evaluation into a constant doesn't pan out, we need to restore
                        // it so that any contained var uid gets reindexed as part of the whole cfg
                        // later.
                        auto savedInstrSeq = result.cir();
                        result.setCIR( make_shared< cir::InstrSeq >( *savedInstrSeq ) );

                        ReindexVars( *result.cir() );

                        if( !verify::VerifyInstrSeq( c, *result.cir() ) )
                            return PoisonValue();

                        execute::VM vm;
                        result = execute::Evaluate( result, vm );

                        if( !result.isConstant() )
                            result.setCIR( move( savedInstrSeq ) );
                    }

                    // Register the result for destruction.
                    if( auto cfg = GetCFG( c ) )
                        DeclareValue( c, result, util::GenerateNewUID() );
                }

                return result;
            }

            Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
            {
Changes to bs/builtins/types/func/invocation/intrinsic.cpp.
20
21
22
23
24
25
26

27
28
29
30
31
32


33
34
35
36
37
38
39
                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, 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 )







>






>
>







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
                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, argsInstrSeq, preparedCallee, cir::Call( argCount, loc ) );
                ReindexVars( is );

                if( !vm.execute( is ) )
                    return PoisonValue();
                if( ft.returnType() == GetValueType< void >() )
                    return Value( GetValueType< void >(), 0U );

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

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







|







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

            if( vp->val() == HOLE( "_"_sid ) )
Changes to bs/builtins/types/func/typecheck.cpp.
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                ANYTERM( _ ),
                ANYTERM( _ ) ) ),

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

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

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

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

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

            // 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( move( ut ), move( uv ), rhsVal.locationId() );
                    co_yield { ValueToEIR( move( result ) ), tcc };
                }
            }
        } );

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







|



















|
|







|







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
            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( TCRINFOS,

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

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

        []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
        {
            auto lhsVal = *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( TCRINFOS,
Changes to bs/builtins/types/localvar/localvar.cpp.
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

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

        auto index = cfg->getNewTemporaryIndex();

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

        Value typeVal = *EIRToValue( type );
        if( !typeVal.isType() )







|







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

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

        auto index = util::GenerateNewUID();

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

        Value typeVal = *EIRToValue( type );
        if( !typeVal.isType() )
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
        );

        auto&& [type, initializer] = *callDecomp;

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

        auto index = cfg->getNewTemporaryIndex();

        // 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();
        LocalVar lv( name, type, index );








|







174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
        );

        auto&& [type, initializer] = *callDecomp;

        if( !ParseTypePredicates( c, *EIRToValue( type ) ) )
            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.
        auto typeLoc = EIRToValue( typeTExpr )->locationId();
        LocalVar lv( name, type, index );

Changes to bs/builtins/types/localvar/typecheck.cpp.
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                localVarPattern,
                ANYTERM( _ ) ) ),

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

                auto rhsVal = *ValuePatternFromEIR( 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 };
                }







|

|







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
            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 };
                }
Changes to bs/builtins/types/param.h.
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

    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( HOLE( "_"_sid ), 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
    {







|







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

    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
    {
Changes to bs/builtins/types/pretty.cpp.
58
59
60
61
62
63
64













65
66
67
68
69
70
71
                ANYTERM( _ ) ) ),
            [&]( auto&& out, auto&& t )
            {
                auto decl = *FromValue< Decl >( *EIRToValue( t ) );
                out << "decl(" << decl.type() << ", " << decl.name() << ")";
                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; } );








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







58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
                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( 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; } );

Changes to bs/builtins/types/reference/reference.cpp.
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
        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
        return cir::VarAddr( lv.index(), lv.type(), {} );
    }

    Value BuildLocalVarMutRef( const LocalVar& lv )
    {
        // TODO LOC: may need loc in LocalVar
        auto rt = ValueToEIR( ToValue( ReferenceType{ lv.type(), MutAccessSpecifer() } ) );
        return BuildComputedValue( move( rt ), cir::VarAddr( lv.index(), lv.type(), {} ) );
    }

    const Term& MutAccessSpecifer()
    {
        static auto al = ValueToEIR( ToValue( AccessSpecifier( "mut"_sid ) ) );
        return al;
    }







|






|







51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
        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
        return cir::VarAddr( lv.index(), {} );
    }

    Value BuildLocalVarMutRef( const LocalVar& lv )
    {
        // TODO LOC: may need loc in LocalVar
        auto rt = ValueToEIR( ToValue( ReferenceType{ lv.type(), MutAccessSpecifer() } ) );
        return BuildComputedValue( move( rt ), cir::VarAddr( lv.index(), {} ) );
    }

    const Term& MutAccessSpecifer()
    {
        static auto al = ValueToEIR( ToValue( AccessSpecifier( "mut"_sid ) ) );
        return al;
    }
Changes to bs/builtins/types/reference/typecheck.cpp.
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

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

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

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

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

        for( auto&& [s,tcc] : TypeCheck( lhsPat, rhs, tcc ) )







|







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

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

    TCGen TypeCheckingBuildTempRef( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc )
    {
        auto lRefType = *FromValue< ReferenceType >( *EIRToValue( EIRToValuePattern( lhs )->type() ) );

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

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

        for( auto&& [s,tcc] : TypeCheck( lhsPat, rhs, tcc ) )
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

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

                // TODO create an ext point for this
                auto loc = rhsVal.locationId();
                auto tempIndex = cfg->getNewTemporaryIndex();
                return ValueToEIR( BuildComputedValue( ValueToEIR( ToValue( rt ) ), rhsVal,
                    TempAddr( 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







|







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

                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,
                    TempAddr( 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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                refTypePattern,
                ANYTERM( _ ) ) ),

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

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

                // Type check the behaviors
                for( auto&& [b, tcc] : TypeCheck( lRefType.behavior(), rRefType.behavior(), tcc ) )
                {
                    // Check the types
                    for( auto&& [t, tcc] : TypeCheck( lRefType.type(), rRefType.type(), tcc ) )







|

|







105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
            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 behaviors
                for( auto&& [b, tcc] : TypeCheck( lRefType.behavior(), rRefType.behavior(), tcc ) )
                {
                    // Check the types
                    for( auto&& [t, tcc] : TypeCheck( lRefType.type(), rRefType.type(), tcc ) )
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                localVarPattern,
                ANYTERM( _ ) ) ),

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

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

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







|







226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                localVarPattern,
                ANYTERM( _ ) ) ),

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

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

            auto ref = ValueToEIR( ToValue( BuildLocalVarMutRef( *locvar ) )
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                refRefTypePattern,
                ANYTERM( _ ) ) ),

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

            auto rrefVal = *EIRToValue( rhs );
            auto rrType = FromValue< ReferenceType >( *EIRToValue( rrefVal.type() ) );
            if( !rrType )
                co_return;

            auto loc = rrefVal.locationId();







|







252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
            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();
Changes to bs/builtins/types/runtime/typecheck.cpp.
1
2
3
4
5
6
7












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

using namespace goose;
using namespace goose::eir;

namespace goose::builtins
{












    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







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







1
2
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"

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
            // 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 = ValuePatternFromEIR( lhs );
            if( !lhsVal )
                co_return;

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








|







44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
            // 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;

117
118
119
120
121
122
123
124
125
126
127
128
            ValueToEIR( ValuePattern(
                ANYTERM( _ ),
                GetValueType< NullPointer >(),
                ANYTERM( _ ) ) ),

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







|




129
130
131
132
133
134
135
136
137
138
139
140
            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 };
        } );
    }
}
Changes to bs/builtins/types/template/rules/tuple.cpp.
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
                }
                else
                    v->append( t );

                return true;
            } );

            return ValueToEIR( ValuePattern( 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 = ValuePatternFromEIR( 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() );
        }








|



















|







48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
                }
                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() );
        }

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
                }
                else
                    v->append( t );

                return true;
            } );

            return ValueToEIR( ValuePattern( ValueToEIR( MkTupleType( TSID( closed ), HOLE( "_"_sid ) ) ), v ) );
        }

        bool isPackExpr( const Context& c, const Term& val ) const final
        {
            return false;
        }
    };







|







97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
                }
                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;
        }
    };
Changes to bs/builtins/types/template/rules/tvectype.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
#include "builtins/builtins.h"

namespace goose::builtins
{
    class TVecTypeTemplateRule : public TemplateRule
    {
        bool prepare( TemplateContext& c, const Term& t ) const final
        {
            auto tvecType = ValuePatternFromEIR( 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 buildign 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 = ValuePatternFromEIR( 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( 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 = ValuePatternFromEIR( 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 = ValuePatternFromEIR( 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() );
        }

        optional< Term > buildArgPattern( const Context& c, const Term& t ) const final
        {
            auto tvecType = ValuePatternFromEIR( 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() )
            {
                auto s = BuildTemplateArgPattern( c, x );
                if( s )
                    v->append( move( *s ) );
                else
                    v->append( x );
            }

            return ValueToEIR( ValuePattern( TypeType(), v ) );
        }

        bool isPackExpr( const Context& c, const Term& val ) const final
        {
            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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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"

namespace goose::builtins
{
    class TVecTypeTemplateRule : public TemplateRule
    {
        bool prepare( TemplateContext& c, const Term& t ) const final
        {
            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() );
        }

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

            return ValueToEIR( ValuePattern( TSID( constant ), TypeType(), v ) );
        }

        bool isPackExpr( const Context& c, const Term& val ) const final
        {
            return false;
        }
    };
Changes to bs/builtins/types/tuple/tuple.cpp.
81
82
83
84
85
86
87
















88
89
90
91
92
93
94
            ConcatenateVectorTerms( ltup.val(), rtup.val() ) );

        if( ltup.isPoison() || rtup.isPoison() )
            result.setPoison();

        return result;
    }

















    bool IsTuple( const Value& t )
    {
        auto typeVal = EIRToValue( t.type() );

        if( !typeVal || !typeVal->isConstant() )
            return false;







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







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
            ConcatenateVectorTerms( ltup.val(), rtup.val() ) );

        if( ltup.isPoison() || rtup.isPoison() )
            result.setPoison();

        return result;
    }

    Value VectorToTuple( const Term& state, 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( state, TERM( move( types ) ) ) ), vec );
    }

    bool IsTuple( const Value& t )
    {
        auto typeVal = EIRToValue( t.type() );

        if( !typeVal || !typeVal->isConstant() )
            return false;
Changes to bs/builtins/types/tuple/tuple.h.
13
14
15
16
17
18
19


20
21
22
23
24
25
26
    extern const Value& EmptyClosedTuple();

    extern Value AppendToTuple( const Value& tup, const Value& val );
    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 );



    template< typename... V >
    Value MakeClosedTuple( V&&... values );

    template< typename... V >
    Value MakeOpenTuple( V&&... values );








>
>







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    extern const Value& EmptyClosedTuple();

    extern Value AppendToTuple( const Value& tup, const Value& val );
    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& state, const pvec& vec );

    template< typename... V >
    Value MakeClosedTuple( V&&... values );

    template< typename... V >
    Value MakeOpenTuple( V&&... values );

Changes to bs/builtins/types/tuple/typecheck.cpp.
1
2
3
4

5
6
7
8
9
10
11
12
13
14
15
16


17
18
19
20

21






22
23


24

25



26

27
28









29

30

31









32

33
34


35

36
37
38
39
40
41
42
43
44

45
46
47
48
49

50
51

52
53

54

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



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115



116
117
118
119
120
121
122
123
#include "builtins/builtins.h"

using namespace goose;
using namespace goose::eir;


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

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

        auto tupSize = TupleSize( tupArg );



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

            auto newOut = AppendToTuple( out, *val );







            if( index == ( tupSize - 1 ) )


                co_yield { ValueToEIR( newOut ), tcc };

            else



                co_yield TypeCheckConstantTuple( tcc, tupType, tupArg, index + 1, newOut );

        }
    }











    TCGen TypeCheckComputedTuple( const TypeCheckingContext& tcc, const Value& tupType, const Value& tupArg, const Value& tupArgRef, uint32_t index, const Value& out )

    {









        auto param = ParamPat( GetTupleTypeElement( tupType, index ) );


        auto argType = GetTupleElementType( tupArg, index );


        ReferenceType rt( argType, ConstAccessSpecifer() );


        G_VAL_ASSERT( tupArgRef, !tupArgRef.isConstant() );

        auto argRef = ValueToEIR( BuildComputedValue( ValueToEIR( ToValue( rt ) ),
            tupArgRef, cir::Select( index, tupArg.locationId() ) ) );

        auto tupSize = TupleTypeSize( tupType );

        for( auto&& [s,tcc] : TypeCheck( param, argRef, tcc ) )

        {
            auto val = ValuePatternFromEIR( s );
            assert( val );
            auto newOut = AppendToTuple( out, *val );


            if( index == ( tupSize - 1 ) )
                co_yield { ValueToEIR( newOut ), tcc };

            else
                co_yield TypeCheckComputedTuple( tcc, tupType, tupArg, tupArgRef, index + 1, newOut );

        }

    }

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

            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 = ValuePatternFromEIR( lhs );
            auto rtup = EIRToValue( rhs );

            if( !ltup || !rtup )
                co_return;

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



            co_yield TypeCheckConstantTuple( tcc, tupType, *rtup, 0, IsOpenTuple( *rtup ) ? EmptyOpenTuple() : EmptyClosedTuple() );
        } );

        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,

            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 = ValuePatternFromEIR( lhs );
            auto rtup = EIRToValue( rhs );

            if( !ltup || !rtup )
                co_return;

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

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

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

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




            co_yield TypeCheckComputedTuple( tcc, tupType, *rtup, rtupref, 0, IsOpenTuple( *rtup ) ? EmptyOpenTuple() : EmptyClosedTuple() );
        } );

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

            ValueToEIR( ValuePattern(




>



|

|
|
<
<

|

>
>
|

|

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

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


|
|

|

<
>

<
|
<

>
|
|
>
|
<
>
|
>


















|






>
>
>
|
















|










<
<
<
|




>
>
>
|







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


13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

83
84

85

86
87
88
89
90
91

92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#include "builtins/builtins.h"

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

namespace goose::builtins
{
    Value BuildComputedTuple( const pvec& elems, bool closed, LocationId locationId )
    {
        auto types = make_shared< Vector >();
        types->reserve( elems->terms().size() );



        auto tupTempUID = util::GenerateNewUID();

        InstrSeq is;
        uint32_t elemIndex = 0;
        for( auto&& t : elems->terms() )
        {
            auto val = EIRToValue( t );
            assert( val );
            types->append( val->type() );

            AppendToInstrSeq( is,
                VarAddr( tupTempUID, locationId ),
                Select( elemIndex++, val->locationId() ),
                *val,
                Store( move( 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( move( tupType ), tupTempUID, locationId ) );
        AppendToInstrSeq( *tupIS, move( is ) );
        AppendToInstrSeq( *tupIS, GetTemporary( tupTypeTerm, tupTempUID, 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, ConstAccessSpecifer() );


            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( TCRINFOS,

            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( TCRINFOS,

            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(), ConstAccessSpecifer() } ) ),
                *rtup, cir::TempAddr( 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( TCRINFOS,

            ValueToEIR( ValuePattern(
Changes to bs/builtins/types/types.cpp.
105
106
107
108
109
110
111



112
113
114
115
116
117
118
        }

        return *boolRes;
    }

    Value ToType( const Context& c, const Value& v )
    {



        auto result = InvokeOverloadSet( c,
            c.env()->extToType(),
            MakeClosedTuple( v ) );

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







>
>
>







105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
        }

        return *boolRes;
    }

    Value ToType( const Context& c, const Value& v )
    {
        if( v.isType() )
            return v;

        auto result = InvokeOverloadSet( c,
            c.env()->extToType(),
            MakeClosedTuple( v ) );

        if( result.isPoison() )
        {
            PoisonBuilder( c );
Changes to bs/cir/allocvar.h.
11
12
13
14
15
16
17

18
19
20
21
22
23
24
                BaseInstr( loc ),
                m_type( forward< T >( type ) ),
                m_index( index )
            {}

            const auto& type() const { return m_type; }
            const auto& index() const { return m_index; }


            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return true; }

            bool operator<( const AllocVar& rhs ) const
            {







>







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
                BaseInstr( 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
            {
Changes to bs/cir/basicblock.h.
48
49
50
51
52
53
54

55
56
57
58
59
60
61

            auto* llvmBB() { return m_llvmBB; }
            void setLLVMBB( llvm::BasicBlock* pBB ) { m_llvmBB = pBB; }

            auto empty() const { return m_instructions.empty(); }

            const auto& instructions() const { return m_instructions; }


            using RunnableInstructions = llvm::SmallVector< Instruction, 16 >;
            const RunnableInstructions* runnableInstructions() const
            {
                if( m_dirty )
                {
                    m_dirty = false;







>







48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

            auto* llvmBB() { return m_llvmBB; }
            void setLLVMBB( llvm::BasicBlock* pBB ) { m_llvmBB = pBB; }

            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;
81
82
83
84
85
86
87


88
89
90
91
92
93
94
                m_dirty = true;
            }

            template< typename T >
            void setTerminator( T&& terminator );

            const auto& terminator() const { return m_terminator; }



            bool canBeExecuted() const
            {
                return m_canBeExecuted
                    && ( !m_terminator || m_terminator->canBeExecuted() );
            }








>
>







82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
                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() );
            }

Changes to bs/cir/cfg.h.
72
73
74
75
76
77
78
79
80

81
82
83
84
85
86
87

            const auto& getBB( uint32_t index ) const { return m_basicBlocks[index - 1]; }

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

            const ptr< BasicBlock >& createBB();

            auto getNewTemporaryIndex() { return m_temporariesCount++; }
            auto temporariesCount() const { return m_temporariesCount; }


            // Clear the llvm basic block pointers from the entire cfg.
            void unbindFromLLVM();

            bool canBeExecuted() const;
            bool canBeEagerlyEvaluated() const;








<

>







72
73
74
75
76
77
78

79
80
81
82
83
84
85
86
87

            const auto& getBB( uint32_t index ) const { return m_basicBlocks[index - 1]; }

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

            const ptr< BasicBlock >& createBB();


            auto temporariesCount() const { return m_temporariesCount; }
            void setTemporariesCount( uint32_t count ) { m_temporariesCount = count; }

            // Clear the llvm basic block pointers from the entire cfg.
            void unbindFromLLVM();

            bool canBeExecuted() const;
            bool canBeEagerlyEvaluated() const;

166
167
168
169
170
171
172





173
174
175
    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 );





}

#endif







>
>
>
>
>



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
    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
Changes to bs/cir/createtemporary.h.
8
9
10
11
12
13
14

15
16
17
18
19
20
21
        public:
            CreateTemporary( uint32_t index, LocationId loc ) :
                BaseInstr( loc ),
                m_index( index )
            {}

            const auto& index() const { return m_index; }


            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }
            bool haveSideEffects() const { return true; }

            bool operator<( const CreateTemporary& rhs ) const
            {







>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
        public:
            CreateTemporary( uint32_t index, LocationId loc ) :
                BaseInstr( loc ),
                m_index( index )
            {}

            const auto& index() const { return m_index; }
            void setIndex( uint32_t index ) { m_index = index; }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }
            bool haveSideEffects() const { return true; }

            bool operator<( const CreateTemporary& rhs ) const
            {
Changes to bs/cir/gettemporary.h.
11
12
13
14
15
16
17

18
19
20
21
22
23
24
                BaseInstr( loc ),
                m_type( forward< T >( type ) ),
                m_index( index )
            {}

            const auto& type() const { return m_type; }
            const auto& index() const { return m_index; }


            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return true; }
            bool haveSideEffects() const { return false; }

            bool operator<( const GetTemporary& rhs ) const
            {







>







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
                BaseInstr( 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 true; }
            bool haveSideEffects() const { return false; }

            bool operator<( const GetTemporary& rhs ) const
            {
Changes to bs/cir/hash.cpp.
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
    size_t hash< goose::cir::Constant >::operator()( const goose::cir::Constant& x ) const
    {
        return goose::util::ComputeHash( x.value() );
    }

    size_t hash< goose::cir::VarAddr >::operator()( const goose::cir::VarAddr& x ) const
    {
        return llvm::hash_combine( goose::util::ComputeHash( x.varIndex() ), goose::util::ComputeHash( x.type() ) );
    }

    size_t hash< goose::cir::TempAddr >::operator()( const goose::cir::TempAddr& x ) const
    {
        return goose::util::ComputeHash( x.tempIndex() );
    }








|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
    size_t hash< goose::cir::Constant >::operator()( const goose::cir::Constant& x ) const
    {
        return goose::util::ComputeHash( x.value() );
    }

    size_t hash< goose::cir::VarAddr >::operator()( const goose::cir::VarAddr& x ) const
    {
        return goose::util::ComputeHash( x.varIndex() );
    }

    size_t hash< goose::cir::TempAddr >::operator()( const goose::cir::TempAddr& x ) const
    {
        return goose::util::ComputeHash( x.tempIndex() );
    }

Changes to bs/cir/helpers.h.
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
            TempStorage( size_t size ) :
                m_storage( size )
            {}

            template< typename TT >
            T& set( uint32_t index, TT&& x )
            {


                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( index < m_storage.size() )
                    return &m_storage[index];
                return nullptr;
            }

            T* get( uint32_t index )
            {


                if( index < m_storage.size() )
                    return &m_storage[index];
                return nullptr;
            }

        private:
            llvm::SmallVector< T, 16 > m_storage;







>
>








>
>







>
>







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
            TempStorage( size_t size ) :
                m_storage( size )
            {}

            template< typename TT >
            T& set( uint32_t index, TT&& x )
            {
                assert( !IsUid( index ) );

                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
            {
                assert( !IsUid( index ) );

                if( index < m_storage.size() )
                    return &m_storage[index];
                return nullptr;
            }

            T* get( uint32_t index )
            {
                assert( !IsUid( index ) );

                if( index < m_storage.size() )
                    return &m_storage[index];
                return nullptr;
            }

        private:
            llvm::SmallVector< T, 16 > m_storage;
Changes to bs/cir/instruction.h.
225
226
227
228
229
230
231

232
233
234
235
236
237
238
                GhostCall,
                PHOverrideSet,
                PHOverrideClear,
                Placeholder
            >;

            const auto& content() const { return m_content; }


            bool canBeExecuted() const;
            bool canBeEagerlyEvaluated() const;
            bool haveSideEffects() const;

            friend ostream& operator<<( ostream& out, const Instruction& inst );








>







225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
                GhostCall,
                PHOverrideSet,
                PHOverrideClear,
                Placeholder
            >;

            const auto& content() const { return m_content; }
            auto& content() { return m_content; }

            bool canBeExecuted() const;
            bool canBeEagerlyEvaluated() const;
            bool haveSideEffects() const;

            friend ostream& operator<<( ostream& out, const Instruction& inst );

Changes to bs/cir/meson.build.
1
2
3
4
5

6
7
8
9
10
11
12
goose_cir = library( 'goose-cir',
    'cfg.cpp',
    'dominators.cpp',
    'loops.cpp',
    'loopaddrs.cpp',

    'instruction.cpp',
    'terminator.cpp',
    'func.cpp',
    'helpers.cpp',
    'verifinstrfilter.cpp',
    'hash.cpp',
    'decorator.cpp',





>







1
2
3
4
5
6
7
8
9
10
11
12
13
goose_cir = library( 'goose-cir',
    'cfg.cpp',
    'dominators.cpp',
    'loops.cpp',
    'loopaddrs.cpp',
    'reindexvars.cpp',
    'instruction.cpp',
    'terminator.cpp',
    'func.cpp',
    'helpers.cpp',
    'verifinstrfilter.cpp',
    'hash.cpp',
    'decorator.cpp',
Changes to bs/cir/phi.h.
16
17
18
19
20
21
22

23
24
25
26
27
28
29
            {
                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 setIncoming( const ptr< BasicBlock >& bb, const eir::Value& val )
            {
                m_incomings.emplace_back( bb, val );
            }

            template< typename F >







>







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
            {
                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 >
Added 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
#include "cir.h"

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 ReindexVars( InstrSeq& is, F& getOrCreateIndex )
    {
        for( auto& x : is )
        {
            visit( [&]< typename T >( T& instr )
            {
                if constexpr( is_same_v< T, CreateTemporary > )
                {
                    instr.setIndex( getOrCreateIndex( instr.index() ) );
                }
                else if constexpr( is_same_v< T, GetTemporary > )
                {
                    instr.setIndex( getOrCreateIndex( instr.index() ) );
                }
                else if constexpr( is_same_v< T, TempAddr > )
                {
                    instr.setTempIndex( getOrCreateIndex( instr.tempIndex() ) );
                }
                else if constexpr( is_same_v< T, AllocVar > )
                {
                    instr.setIndex( getOrCreateIndex( instr.index() ) );
                }
                else if constexpr( is_same_v< T, VarAddr > )
                {
                    instr.setVarIndex( getOrCreateIndex( instr.varIndex() ) );
                }
                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.content() );
        }
    }

    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 );
    }
}
Changes to bs/cir/tempaddr.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_TEMPADDR_H
#define GOOSE_CIR_TEMPADDR_H

namespace goose::cir
{
    class TempAddr : public BaseInstr< 1, true >
    {
        public:
            TempAddr( uint32_t i, LocationId loc ) :
                BaseInstr( loc ),
                m_tempIndex( i )
            {}

            uint32_t tempIndex() const
            {
                return m_tempIndex;
            }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return false; }

            bool operator<( const TempAddr& rhs ) const
            {













|
<
|
<







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

15

16
17
18
19
20
21
22
#ifndef GOOSE_CIR_TEMPADDR_H
#define GOOSE_CIR_TEMPADDR_H

namespace goose::cir
{
    class TempAddr : public BaseInstr< 1, true >
    {
        public:
            TempAddr( uint32_t i, LocationId loc ) :
                BaseInstr( loc ),
                m_tempIndex( i )
            {}

            uint32_t tempIndex() const{ return m_tempIndex; }

            void setTempIndex( uint32_t index ) { m_tempIndex = index; }


            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return false; }

            bool operator<( const TempAddr& rhs ) const
            {
Changes to bs/cir/varaddr.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_VARADDR_H
#define GOOSE_CIR_VARADDR_H

namespace goose::cir
{
    class VarAddr : public BaseInstr< 0, true >
    {
        public:
            template< typename T >
            VarAddr( uint32_t varIndex, T&& type, LocationId loc ) :
                BaseInstr( loc ),
                m_type( forward< T >( type ) ),
                m_varIndex( varIndex )
            {}

            const auto& type() const { return m_type; }

            uint32_t varIndex() const
            {
                return m_varIndex;
            }

            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return false; }

            bool operator<( const VarAddr& rhs ) const
            {
                if( m_varIndex != rhs.m_varIndex )
                    return m_varIndex < rhs.m_varIndex;

                return m_type < rhs.m_type;
            }

            friend ostream& operator<<( ostream& out, const VarAddr& ins )
            {
                return out << "VARADDR(" << ins.m_varIndex << ", " << ins.m_type << ')';
            }

        private:
            eir::Term m_type;
            uint32_t m_varIndex = 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
#ifndef GOOSE_CIR_VARADDR_H
#define GOOSE_CIR_VARADDR_H

namespace goose::cir
{
    class VarAddr : public BaseInstr< 0, true >
    {
        public:

            VarAddr( uint32_t varIndex, LocationId loc ) :
                BaseInstr( loc ),

                m_varIndex( varIndex )
            {}



            uint32_t varIndex() const { return m_varIndex; }

            void setVarIndex( uint32_t index ) { m_varIndex = index; }


            bool canBeExecuted() const { return true; }
            bool canBeEagerlyEvaluated() const { return false; }
            bool haveSideEffects() const { return false; }

            bool operator<( const VarAddr& rhs ) const
            {

                return m_varIndex < rhs.m_varIndex;


            }

            friend ostream& operator<<( ostream& out, const VarAddr& ins )
            {
                return out << "VARADDR(" << ins.m_varIndex << ')';
            }

        private:

            uint32_t m_varIndex = 0;
    };
}

#endif
Changes to bs/codegen/address.cpp.
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
{
    auto initVal = st.stack.pop( m_llvmBuilder );
    if( !initVal )
        return false;

    auto* ppVal = st.temporaries->get( ta.tempIndex() );

    if( !ppVal )
    {
        st.temporaries->set( ta.tempIndex(), *initVal );
        ppVal = st.temporaries->get( ta.tempIndex() );
    }

    if( llvm::isa< llvm::AllocaInst >( **ppVal ) )
    {







|







35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
{
    auto initVal = st.stack.pop( m_llvmBuilder );
    if( !initVal )
        return false;

    auto* ppVal = st.temporaries->get( ta.tempIndex() );

    if( !ppVal || !*ppVal )
    {
        st.temporaries->set( ta.tempIndex(), *initVal );
        ppVal = st.temporaries->get( ta.tempIndex() );
    }

    if( llvm::isa< llvm::AllocaInst >( **ppVal ) )
    {
Changes to bs/codegen/stack.h.
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
                    return get< codegen::Address >( result );
                }
                else if constexpr( is_same_v< T, llvm::Value* > )
                {
                    if( holds_alternative< codegen::Address >( result ) )
                        return AddressToGEP( builder, get< codegen::Address >( result ) );
                    else
                        return get< llvm::Value* >( result );
                }
                else if( holds_alternative< llvm::Value* >( result ) )
                    return llvm::dyn_cast_or_null< remove_pointer_t< T > >( get< llvm::Value* >( result ) );

                return nullopt;
            }

            optional< Slot > pop()
            {
                if( m_stack.empty() )







|


|







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
                    return get< codegen::Address >( result );
                }
                else if constexpr( is_same_v< T, llvm::Value* > )
                {
                    if( holds_alternative< codegen::Address >( result ) )
                        return AddressToGEP( builder, get< codegen::Address >( result ) );
                    else
                        return InsertLoadIfNeeded( builder, get< llvm::Value* >( result ) );
                }
                else if( holds_alternative< llvm::Value* >( result ) )
                    return llvm::dyn_cast_or_null< remove_pointer_t< T > >( InsertLoadIfNeeded( builder, get< llvm::Value* >( result ) ) );

                return nullopt;
            }

            optional< Slot > pop()
            {
                if( m_stack.empty() )
49
50
51
52
53
54
55







56
57
58
59
60
            template< typename T >
            void push( T&& v )
            {
                m_stack.push( forward< T >( v ) );
            }

        private:







            stack< Slot > m_stack;
    };
}

#endif







>
>
>
>
>
>
>





49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
            template< typename T >
            void push( T&& v )
            {
                m_stack.push( forward< T >( v ) );
            }

        private:
            static llvm::Value* InsertLoadIfNeeded( llvm::IRBuilder<>& builder, llvm::Value* lv )
            {
                if( llvm::isa< llvm::AllocaInst >( lv ) )
                    return builder.CreateLoad( llvm::cast< llvm::AllocaInst >( lv )->getAllocatedType(), lv );
                return lv;
            }

            stack< Slot > m_stack;
    };
}

#endif
Changes to bs/compile/compiler.cpp.
175
176
177
178
179
180
181


182
183
184
185
186
187
188
                    return nullptr;
                }

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



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

        return cfg;
    }







>
>







175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
                    return nullptr;
                }

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

        ReindexVars( cfg );

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

        return cfg;
    }
Changes to bs/eir/value.cpp.
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
    }

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

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







|







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

    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(),
Changes to bs/eir/value.h.
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

    extern const Term& TypeType();

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

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

    class Value
    {
        public:
            Value() = default;

            template< typename T, typename VL >







|







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

    extern const Term& TypeType();

    extern Term ValueToEIR( const Value& v );
    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 >
38
39
40
41
42
43
44






45
46
47
48
49
50
51
            ptr< cir::InstrSeq > cir() const
            {
                const auto* ppCir = get_if< ptr< cir::InstrSeq > >( &m_valOrCIR );
                if( !ppCir )
                    return nullptr;
                return *ppCir;
            }







            auto locationId() const { return m_locationId; }
            auto&& setLocationId( LocationId id )
            {
                if( !m_locationId.isPoison() )
                    m_locationId = id;
                return *this;







>
>
>
>
>
>







38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
            ptr< cir::InstrSeq > cir() const
            {
                const auto* ppCir = get_if< ptr< cir::InstrSeq > >( &m_valOrCIR );
                if( !ppCir )
                    return nullptr;
                return *ppCir;
            }

            template< typename T >
            void setCIR( T&& pInstrSeq )
            {
                m_valOrCIR = forward< T >( pInstrSeq );
            }

            auto locationId() const { return m_locationId; }
            auto&& setLocationId( LocationId id )
            {
                if( !m_locationId.isPoison() )
                    m_locationId = id;
                return *this;
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109

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

            template< typename T, typename V >
            ValuePattern( T&& type, V&& v, LocationId locationId = LocationId() ) :
                m_sort( TSID( constant ) ),
                m_type( forward< T >( type ) ),
                m_val( forward< V >( v ) ),
                m_locationId( locationId )
            {}

            auto locationId() const { return m_locationId; }
            auto&& setLocationId( LocationId id )







<
<
<
<
<
<
<
<







94
95
96
97
98
99
100








101
102
103
104
105
106
107

    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 )
Changes to bs/execute/vm.cpp.
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
{
    push( cst.value() );
    return true;
}

bool VM::execute( const cir::VarAddr& va )
{
    auto stackIndex = m_currentFrameStart + va.varIndex();
    if( stackIndex >= m_stack.size() )
        return false;

    if( !m_stack[stackIndex] )
        return false;

    push( ToValue( m_stack[stackIndex].get() ) );
    return true;
}

bool VM::execute( const cir::TempAddr& ta )
{
    auto initVal = pop();
    if( !initVal )
        return false;

    auto stackIndex = m_currentFrameStart + ta.tempIndex();

    if( stackIndex >= m_stack.size() )
        return false;

    if( !m_stack[stackIndex] )
        m_stack[stackIndex] = make_shared< Term >( ValueToEIR( Evaluate( *initVal, *this ) ) );








|
















|







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
{
    push( cst.value() );
    return true;
}

bool VM::execute( const cir::VarAddr& va )
{
    auto stackIndex = m_currentFrameStart + ( va.varIndex() & 0x7fffffff );
    if( stackIndex >= m_stack.size() )
        return false;

    if( !m_stack[stackIndex] )
        return false;

    push( ToValue( m_stack[stackIndex].get() ) );
    return true;
}

bool VM::execute( const cir::TempAddr& ta )
{
    auto initVal = pop();
    if( !initVal )
        return false;

    auto stackIndex = m_currentFrameStart + ( ta.tempIndex() & 0x7fffffff );

    if( stackIndex >= m_stack.size() )
        return false;

    if( !m_stack[stackIndex] )
        m_stack[stackIndex] = make_shared< Term >( ValueToEIR( Evaluate( *initVal, *this ) ) );

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

bool VM::execute( const cir::CreateTemporary& ct )
{
    auto val = pop();
    if( !val )
        return false;

    auto stackIndex = m_currentFrameStart + ct.index();
    if( m_stack.size() <= stackIndex )
        m_stack.resize( stackIndex + 1 );

    m_stack[stackIndex] = make_shared< Term >( ValueToEIR( Evaluate( *val, *this ) ) );
    return true;
}

bool VM::execute( const cir::GetTemporary& gt )
{
    auto stackIndex = gt.index() + m_currentFrameStart;
    if( stackIndex >= m_stack.size() )
        return false;


    push( *EIRToValue( *m_stack[stackIndex] ) );


    return true;
}

bool VM::execute( const cir::AllocVar& av )
{
    auto stackIndex = m_currentFrameStart + av.index();

    if( m_stack.size() <= stackIndex )
        m_stack.resize( stackIndex + 1 );

    m_stack[stackIndex] = BuildUninitializedValue( av.type() );
    return true;
}







|









|



>
|
>
>





|







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

bool VM::execute( const cir::CreateTemporary& ct )
{
    auto val = pop();
    if( !val )
        return false;

    auto stackIndex = m_currentFrameStart + ( ct.index() & 0x7fffffff );
    if( m_stack.size() <= stackIndex )
        m_stack.resize( stackIndex + 1 );

    m_stack[stackIndex] = make_shared< Term >( ValueToEIR( Evaluate( *val, *this ) ) );
    return true;
}

bool VM::execute( const cir::GetTemporary& gt )
{
    auto stackIndex = ( gt.index() & 0x7fffffff ) + m_currentFrameStart;
    if( stackIndex >= m_stack.size() )
        return false;

    assert( m_stack[stackIndex] );
    auto val = EIRToValue( *m_stack[stackIndex] );
    assert( val );
    push( *val );
    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( av.type() );
    return true;
}
383
384
385
386
387
388
389



390

391
392
393
394
395
396
397
    if( opval.isPoison() )
        return false;
    if( !opval.isConstant() )
        return false;

    auto boolVal = FromValue< bool >( opval );
    if( !boolVal )



        return false;


    push( ToValue( !*boolVal ) );
    return true;
}

ptr< BasicBlock > VM::executeTerminator( const cir::Terminator& terminator )
{







>
>
>

>







386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
    if( opval.isPoison() )
        return false;
    if( !opval.isConstant() )
        return false;

    auto boolVal = FromValue< bool >( opval );
    if( !boolVal )
    {
        G_TRACE_VAL( *operand );
        G_TRACE_VAL( opval );
        return false;
    }

    push( ToValue( !*boolVal ) );
    return true;
}

ptr< BasicBlock > VM::executeTerminator( const cir::Terminator& terminator )
{
Changes to bs/g0api/extensibility/cir.cpp.
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
            } );

        // 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< VarAddr, InstrOpCode::VarAddr >( e, MkInstr, UnpackInstr, &VarAddr::varIndex, &VarAddr::type, &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::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 );







|







207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
            } );

        // 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< 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::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 );
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
            ( e, "CFGCreateBasicBlock"_sid,
            []( const auto& pCFG ) -> TypeWrapper< ptr< BasicBlock > >
            {
                auto& cfg = *pCFG.get();
                return cfg.createBB();
            } );

        RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< CFG > > ) >
            ( e, "CFGGetNewTemporaryIndex"_sid,
            []( const auto& pCFG )
            {
                auto& cfg = *pCFG.get();
                return cfg.getNewTemporaryIndex();
            } );

        ////////////////////////////
        // Decorator
        ////////////////////////////
        RegisterBuiltinFunc< void ( TypeWrapper< ptr< Decorator > >, TypeWrapper< ptr< Instruction > > ) >
            ( e, "DecoratorAddPrologueInstr"_sid,







|
|
|

<
|







432
433
434
435
436
437
438
439
440
441
442

443
444
445
446
447
448
449
450
            ( 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();
            } );

        ////////////////////////////
        // Decorator
        ////////////////////////////
        RegisterBuiltinFunc< void ( TypeWrapper< ptr< Decorator > >, TypeWrapper< ptr< Instruction > > ) >
            ( e, "DecoratorAddPrologueInstr"_sid,
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
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();
}
Changes to bs/sema/typecheck.cpp.
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

        auto expr = VEC( lhs, rhs );
        for( auto&& [s, rule] : Match( expr, rules ) )
        {
            if( rule.exclusive )
            {
                if( exclusiveRule )
                    G_VAL_ERROR( *ValuePatternFromEIR( rhs ), "ambiguous exclusive type checking rule" );

                exclusiveRule = &rule;
            }
            else if( !exclusiveRule )
                candidates.push( { s.score(), &rule } );
        }








|







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

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

137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
            if( c.score < bestScore )
                break;

            auto gen = c.rule->func( lhs, rhs, tcc );
            if( gen.begin() != gen.end() )
            {
        #ifdef TCRULES_DEBUG
                G_VAL_ERROR( *ValuePatternFromEIR( 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( *ValuePatternFromEIR( rhs ), "ambiguous type checking rule" );
        #endif
            }

            candidates.pop();
        }

#ifdef TC_LOG_RULES







|


|







137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
            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
Changes to bs/sema/uni-holes.cpp.
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
                ANYTERM( _ ),
                ForwardingHolePattern(),
                ANYTERM( _ ) ) ),

            ANYTERM( _ ),
            []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
            {
                auto lVal = *ValuePatternFromEIR( lhs );
                auto h = *ForwardingHoleFromIRExpr( lVal.type() );

                if( h.name() == "_"_sid )
                    co_yield UniAnonHoleAny( lVal.type(), rhs, tcc );
                else
                    co_yield UniHoleAny( lVal.type(), rhs, tcc );
            }, true







|







397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
                ANYTERM( _ ),
                ForwardingHolePattern(),
                ANYTERM( _ ) ) ),

            ANYTERM( _ ),
            []( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
            {
                auto lVal = *EIRToValuePattern( lhs );
                auto h = *ForwardingHoleFromIRExpr( lVal.type() );

                if( h.name() == "_"_sid )
                    co_yield UniAnonHoleAny( lVal.type(), rhs, tcc );
                else
                    co_yield UniHoleAny( lVal.type(), rhs, tcc );
            }, true
Changes to bs/util/meson.build.
1

2
3
4
5
6
7
8
goose_util = library( 'goose-util',

    'stringid.cpp',
    'bigint.cpp',
    'graphviz.cpp',

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

>







1
2
3
4
5
6
7
8
9
goose_util = library( 'goose-util',
    'util.cpp',
    'stringid.cpp',
    'bigint.cpp',
    'graphviz.cpp',

    include_directories: bsinc,
    dependencies: [fmt_dep, mimalloc_dep, tracy_dep]
)
Added bs/util/util.cpp.














>
>
>
>
>
>
>
1
2
3
4
5
6
7
#include "util.h"

uint32_t goose::util::GenerateNewUID()
{
    static uint32_t nextUid = 0x80000000;
    return nextUid++;
}
Changes to bs/util/util.h.
229
230
231
232
233
234
235





236
237
238
            {
                m_observers.emplace_back( forward< F >( observer ) );
            }

        private:
            vector< function< void( T... ) > > m_observers;
    };





}

#endif







>
>
>
>
>



229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
            {
                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
Changes to bs/verify/call.cpp.
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
                )
            );

            if( !result )
                return true;

            auto&& [type, val, locId] = *result;
            auto paramVal = BuildComputedValue( type, VarAddr( varId++, type, 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 );
            } );







|







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

            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 );
            } );
Changes to bs/verify/func.cpp.
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
                        )
                    );

                    if( !result )
                        return true;

                    auto&& [type, val, locId] = *result;
                    auto paramVal = BuildComputedValue( type, VarAddr( varId, type, locId ), Load( type, locId ) )
                        .setLocationId( locId );

                    // Initialize every parameter containing variable with a freshly named constant of the right type.
                    auto paramInit = BuildZ3ConstantFromType( m_builder, type, format( "p{}", varId ) );
                    m_builder.setVar( varId, move( paramInit ) );

                    ++varId;







|







68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
                        )
                    );

                    if( !result )
                        return true;

                    auto&& [type, val, locId] = *result;
                    auto paramVal = BuildComputedValue( type, VarAddr( varId, locId ), Load( type, locId ) )
                        .setLocationId( locId );

                    // Initialize every parameter containing variable with a freshly named constant of the right type.
                    auto paramInit = BuildZ3ConstantFromType( m_builder, type, format( "p{}", varId ) );
                    m_builder.setVar( varId, move( paramInit ) );

                    ++varId;
Added tests/g0/function/e-template-tuple-vararg-2.g0.




































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
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( "tests/g0/helpers.g0" )

bool testVarArgFunc1( ( $T... ) gg )
{
    if !check( GetTupleSize( $T ) == 3, "vararg test 1" )
        return false

    if !check( GetTupleSize( gg ) == 3, "vararg test 2" )
        return false

    if !check( $T.0 == ct_string, "vararg test 3" )
        return false

    if !check( $T.1 == bool, "vararg test 4" )
        return false

    if !check( $T.2 == ct_int, "vararg test 5" )
        return false

    if !check( gg.0 == "blah", "vararg test 6" )
        return false

    if !check( gg.1 == false, "vararg test 7" )
        return false

    if !check( gg.2 == 219, "vararg test 8" )
        return false

    return true
}

var gg = ( "blah", false, 219 )
if !testVarArgFunc1( gg )
    return 1
Added tests/g0/function/e-template-vararg-2.g0.


































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#Include( "tests/g0/helpers.g0" )

bool foo( ( ct_string, bool, ct_int ) tup )
{
    if tup.0 == "blah" & tup.1 == false & tup.2 == 219
        return true

    return false
}

bool testVarArgFunc2( $T... gg )
{
    return foo( gg );
}

if !testVarArgFunc2( "blah", false, 219 )
    return 1
Changes to tests/g0/function/meson.build.
1
2
3
4
5
6
7
8

9

10
11
12
13
14
15
16
e_tests = [
    'e-overloading',
    'e-templates',
    'e-func',
    'e-higher-func',
    'e-higher-template',
    'e-higher-poly',
    'e-template-vararg',

    'e-template-tuple-vararg'

]

c_fail_tests = [
    'c-fail-calling-compiletime-func',
    'c-fail-func-type-mismatch',
]









>
|
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
e_tests = [
    'e-overloading',
    'e-templates',
    'e-func',
    'e-higher-func',
    'e-higher-template',
    'e-higher-poly',
    'e-template-vararg',
    'e-template-vararg-2',
    'e-template-tuple-vararg',
    'e-template-tuple-vararg-2'
]

c_fail_tests = [
    'c-fail-calling-compiletime-func',
    'c-fail-func-type-mismatch',
]

Changes to tests/g0/tuple/ce-tuple.g0.
49
50
51
52
53
54
55












56
57
58
59
60
61
62
    return 1

tup2.0, tup2.2, tup2.3 = tup2.3, tup2.0, tup2.2

if !check( tup2.0 == 420 & tup2.1 & tup2.2 == 219 & tup2.3 == 654654, "tuple test 4" )
    return 1













using fflush = ExternalFunction( uint(32) ( pointer(void) stream ), "fflush" )

#if IsCompiling
{
    fflush( nullptr )
}
#else







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







49
50
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 1

tup2.0, tup2.2, tup2.3 = tup2.3, tup2.0, tup2.2

if !check( tup2.0 == 420 & tup2.1 & tup2.2 == 219 & tup2.3 == 654654, "tuple test 4" )
    return 1


// Passing a tuple by value
uint(32), uint(32) baz( ( uint(32), bool, uint(32), uint(32) ) tup )
{
    return tup.0, tup.2
}

( uint(32), bool, uint(32), uint(32) ) tup3 = ( 123, false, 456, 789 )
var a, var b = baz( tup3 )
if !check( a == 123 & b == 456, "tuple test 5" )
    return 1

using fflush = ExternalFunction( uint(32) ( pointer(void) stream ), "fflush" )

#if IsCompiling
{
    fflush( nullptr )
}
#else
Changes to tests/g0/tuple/ce-tuple.txt.
1
2
3
4

tuple test 1: OK
tuple test 2: OK
tuple test 3: OK
tuple test 4: OK





>
1
2
3
4
5
tuple test 1: OK
tuple test 2: OK
tuple test 3: OK
tuple test 4: OK
tuple test 5: OK
Changes to tests/g0/verification/z3gen/e-z3gen-test-2.txt.
1
2
3
4


5
6
7
8
9
10
11
12
13
14
15
16

17
18
19
20
21
22

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
=== Begin function verification trace ===
(=> b1 (= v0_1_0 p0))
(=> b1 (= v1_1_1 p1))
assume (distinct v0_1_0 v1_1_1)


(=> b1 (= v3_1_3 u2))
(=> b1 (= v3_1_4 v0_1_0))
(=> b1 (= v4_1_6 u5))
(=> b1 (= v4_1_7 v1_1_1))
(=> b1 (= v5_1_9 u8))
(=> b1 (= v5_1_10 v3_1_4))
(=> b1 (= v6_1_12 u11))
(=> b1 (= v6_1_13 v4_1_7))
(= e1_2 (and b1 (< v5_1_10 v6_1_13)))
(= b2 e1_2)
(=> b2 (= v8_2_15 u14))
(=> e1_2 (= v6_2_16 v6_1_13))

(=> b2 (= v6_2_17 v6_2_16))
(=> b2 (= v8_2_18 v6_2_17))
(=> b2 (= v9_2_20 u19))
(=> e1_2 (= v5_2_21 v5_1_10))
(=> b2 (= v5_2_22 v5_2_21))
(=> b2 (= v9_2_23 v5_2_22))

(=> b2 (= v5_2_24 v8_2_18))
(=> b2 (= v6_2_25 v9_2_23))
(= e1_3 (and b1 (not (< v5_1_10 v6_1_13))))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(= e3_4 (and b3 b3))
(= b4 e3_4)
(=> e1_3 (= v5_3_26 v5_1_10))
(=> e2_3 (= v5_3_26 v5_2_24))
(=> b3 (= v5_3_27 v5_3_26))
(=> e3_4 (= v5_4_28 v5_3_27))
(=> b4 (= v5_4_29 v5_4_28))
(=> e1_3 (= v6_3_30 v6_1_13))
(=> e2_3 (= v6_3_30 v6_2_25))
(=> b3 (= v6_3_31 v6_3_30))
(=> e3_4 (= v6_4_32 v6_3_31))
(=> b4 (= v6_4_33 v6_4_32))
check_unsat (and b4 (not (> v5_4_29 v6_4_33)))
(= e4_5 (and b4 b4))
(= b5 e4_5)
=== End function verification trace ===

=== Begin function verification trace ===
(=> b1 (= v1_1_0 1))
(=> b1 (= v0_1_1 2))




>
>
|
|
|
|
|
|
<
<
|

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




|
|
|
|
|
|
|
|
|
|
|







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


13
14
15
16
17
18

19
20
21
22
23
24

25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
=== Begin function verification trace ===
(=> b1 (= v0_1_0 p0))
(=> b1 (= v1_1_1 p1))
assume (distinct v0_1_0 v1_1_1)
(=> b1 (= v2_1_3 u2))
(=> b1 (= v2_1_4 v0_1_0))
(=> b1 (= v3_1_6 u5))
(=> b1 (= v3_1_7 v1_1_1))
(=> b1 (= v4_1_9 u8))
(=> b1 (= v4_1_10 v2_1_4))
(=> b1 (= v5_1_12 u11))
(=> b1 (= v5_1_13 v3_1_7))


(= e1_2 (and b1 (< v4_1_10 v5_1_13)))
(= b2 e1_2)
(=> b2 (= v6_2_15 u14))
(=> e1_2 (= v5_2_16 v5_1_13))
(=> b2 (= v5_2_17 v5_2_16))
(=> b2 (= v6_2_18 v5_2_17))

(=> b2 (= v7_2_20 u19))
(=> e1_2 (= v4_2_21 v4_1_10))
(=> b2 (= v4_2_22 v4_2_21))
(=> b2 (= v7_2_23 v4_2_22))
(=> b2 (= v4_2_24 v6_2_18))
(=> b2 (= v5_2_25 v7_2_23))

(= e1_3 (and b1 (not (< v4_1_10 v5_1_13))))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(= e3_4 (and b3 b3))
(= b4 e3_4)
(=> e1_3 (= v4_3_26 v4_1_10))
(=> e2_3 (= v4_3_26 v4_2_24))
(=> b3 (= v4_3_27 v4_3_26))
(=> e3_4 (= v4_4_28 v4_3_27))
(=> b4 (= v4_4_29 v4_4_28))
(=> e1_3 (= v5_3_30 v5_1_13))
(=> e2_3 (= v5_3_30 v5_2_25))
(=> b3 (= v5_3_31 v5_3_30))
(=> e3_4 (= v5_4_32 v5_3_31))
(=> b4 (= v5_4_33 v5_4_32))
check_unsat (and b4 (not (> v4_4_29 v5_4_33)))
(= e4_5 (and b4 b4))
(= b5 e4_5)
=== End function verification trace ===

=== Begin function verification trace ===
(=> b1 (= v1_1_0 1))
(=> b1 (= v0_1_1 2))
Changes to tests/g0/verification/z3gen/e-z3gen-test-3.txt.
1
2
3
4


5
6
7
8
9
10
11
12
13
14
15
16

17
18
19
20
21
22

23
24
25
26
27



28
29
30
31
32
33
34
35
36
37
38
39
40
41
=== Begin function verification trace ===
(=> b1 (= v0_1_0 p0))
(=> b1 (= v1_1_1 p1))
assume (distinct v0_1_0 v1_1_1)


(=> b1 (= v3_1_3 u2))
(=> b1 (= v3_1_4 v0_1_0))
(=> b1 (= v4_1_6 u5))
(=> b1 (= v4_1_7 v1_1_1))
(=> b1 (= v5_1_9 u8))
(=> b1 (= v5_1_10 v3_1_4))
(=> b1 (= v6_1_12 u11))
(=> b1 (= v6_1_13 v4_1_7))
(= e1_2 (and b1 (< v5_1_10 v6_1_13)))
(= b2 e1_2)
(=> b2 (= v8_2_15 u14))
(=> e1_2 (= v6_2_16 v6_1_13))

(=> b2 (= v6_2_17 v6_2_16))
(=> b2 (= v8_2_18 v6_2_17))
(=> b2 (= v9_2_20 u19))
(=> e1_2 (= v5_2_21 v5_1_10))
(=> b2 (= v5_2_22 v5_2_21))
(=> b2 (= v9_2_23 v5_2_22))

(=> b2 (= v5_2_24 v8_2_18))
(=> b2 (= v6_2_25 v9_2_23))
(= e1_3 (and b1 (not (< v5_1_10 v6_1_13))))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))



(=> e1_3 (= v5_3_26 v5_1_10))
(=> e2_3 (= v5_3_26 v5_2_24))
(=> b3 (= v5_3_27 v5_3_26))
(=> e1_3 (= v6_3_28 v6_1_13))
(=> e2_3 (= v6_3_28 v6_2_25))
(=> b3 (= v6_3_29 v6_3_28))
check_unsat (and b3 (not (> (- v5_3_27 v6_3_29) 0)))
=== End function verification trace ===

=== Checking instr seq ===
(=> b1 (= v1_1_1 1))
(=> b1 (= v0_1_2 2))
check_unsat (not (distinct v0_1_2 v1_1_1))
assume (> r0 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
=== Begin function verification trace ===
(=> b1 (= v0_1_0 p0))
(=> b1 (= v1_1_1 p1))
assume (distinct v0_1_0 v1_1_1)
(=> b1 (= v2_1_3 u2))
(=> b1 (= v2_1_4 v0_1_0))
(=> b1 (= v3_1_6 u5))
(=> b1 (= v3_1_7 v1_1_1))
(=> b1 (= v4_1_9 u8))
(=> b1 (= v4_1_10 v2_1_4))
(=> b1 (= v5_1_12 u11))
(=> b1 (= v5_1_13 v3_1_7))


(= e1_2 (and b1 (< v4_1_10 v5_1_13)))
(= b2 e1_2)
(=> b2 (= v6_2_15 u14))
(=> e1_2 (= v5_2_16 v5_1_13))
(=> b2 (= v5_2_17 v5_2_16))
(=> b2 (= v6_2_18 v5_2_17))

(=> b2 (= v7_2_20 u19))
(=> e1_2 (= v4_2_21 v4_1_10))
(=> b2 (= v4_2_22 v4_2_21))
(=> b2 (= v7_2_23 v4_2_22))
(=> b2 (= v4_2_24 v6_2_18))
(=> b2 (= v5_2_25 v7_2_23))

(= e1_3 (and b1 (not (< v4_1_10 v5_1_13))))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(=> e1_3 (= v4_3_26 v4_1_10))
(=> e2_3 (= v4_3_26 v4_2_24))
(=> b3 (= v4_3_27 v4_3_26))
(=> e1_3 (= v5_3_28 v5_1_13))
(=> e2_3 (= v5_3_28 v5_2_25))
(=> b3 (= v5_3_29 v5_3_28))



check_unsat (and b3 (not (> (- v4_3_27 v5_3_29) 0)))
=== End function verification trace ===

=== Checking instr seq ===
(=> b1 (= v1_1_1 1))
(=> b1 (= v0_1_2 2))
check_unsat (not (distinct v0_1_2 v1_1_1))
assume (> r0 0)
Changes to tests/g0/verification/z3gen/e-z3gen-test-4.txt.
1
2
3
4


5
6
7
8
9
10
11
12
13
14
15
16

17
18
19
20
21
22

23
24
25
26
27



28
29
30
31
32
33
34
35
36
37
38
39
40
41
=== Begin function verification trace ===
(=> b1 (= v0_1_0 p0))
(=> b1 (= v1_1_1 p1))
assume (distinct v0_1_0 v1_1_1)


(=> b1 (= v3_1_3 u2))
(=> b1 (= v3_1_4 v0_1_0))
(=> b1 (= v4_1_6 u5))
(=> b1 (= v4_1_7 v1_1_1))
(=> b1 (= v5_1_9 u8))
(=> b1 (= v5_1_10 v3_1_4))
(=> b1 (= v6_1_12 u11))
(=> b1 (= v6_1_13 v4_1_7))
(= e1_2 (and b1 (< v5_1_10 v6_1_13)))
(= b2 e1_2)
(=> b2 (= v8_2_15 u14))
(=> e1_2 (= v6_2_16 v6_1_13))

(=> b2 (= v6_2_17 v6_2_16))
(=> b2 (= v8_2_18 v6_2_17))
(=> b2 (= v9_2_20 u19))
(=> e1_2 (= v5_2_21 v5_1_10))
(=> b2 (= v5_2_22 v5_2_21))
(=> b2 (= v9_2_23 v5_2_22))

(=> b2 (= v5_2_24 v8_2_18))
(=> b2 (= v6_2_25 v9_2_23))
(= e1_3 (and b1 (not (< v5_1_10 v6_1_13))))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))



(=> e1_3 (= v5_3_26 v5_1_10))
(=> e2_3 (= v5_3_26 v5_2_24))
(=> b3 (= v5_3_27 v5_3_26))
(=> e1_3 (= v6_3_28 v6_1_13))
(=> e2_3 (= v6_3_28 v6_2_25))
(=> b3 (= v6_3_29 v6_3_28))
check_unsat (and b3 (not (> (- v5_3_27 v6_3_29) 0)))
=== End function verification trace ===

=== Begin function verification trace ===
(=> b1 (= v0_1_0 p0))
assume (distinct v0_1_0 0)
(=> b1 (= v1_1_2 (* 2 v0_1_0)))
(=> b1 (= v0_1_3 v0_1_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
=== Begin function verification trace ===
(=> b1 (= v0_1_0 p0))
(=> b1 (= v1_1_1 p1))
assume (distinct v0_1_0 v1_1_1)
(=> b1 (= v2_1_3 u2))
(=> b1 (= v2_1_4 v0_1_0))
(=> b1 (= v3_1_6 u5))
(=> b1 (= v3_1_7 v1_1_1))
(=> b1 (= v4_1_9 u8))
(=> b1 (= v4_1_10 v2_1_4))
(=> b1 (= v5_1_12 u11))
(=> b1 (= v5_1_13 v3_1_7))


(= e1_2 (and b1 (< v4_1_10 v5_1_13)))
(= b2 e1_2)
(=> b2 (= v6_2_15 u14))
(=> e1_2 (= v5_2_16 v5_1_13))
(=> b2 (= v5_2_17 v5_2_16))
(=> b2 (= v6_2_18 v5_2_17))

(=> b2 (= v7_2_20 u19))
(=> e1_2 (= v4_2_21 v4_1_10))
(=> b2 (= v4_2_22 v4_2_21))
(=> b2 (= v7_2_23 v4_2_22))
(=> b2 (= v4_2_24 v6_2_18))
(=> b2 (= v5_2_25 v7_2_23))

(= e1_3 (and b1 (not (< v4_1_10 v5_1_13))))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(=> e1_3 (= v4_3_26 v4_1_10))
(=> e2_3 (= v4_3_26 v4_2_24))
(=> b3 (= v4_3_27 v4_3_26))
(=> e1_3 (= v5_3_28 v5_1_13))
(=> e2_3 (= v5_3_28 v5_2_25))
(=> b3 (= v5_3_29 v5_3_28))



check_unsat (and b3 (not (> (- v4_3_27 v5_3_29) 0)))
=== End function verification trace ===

=== Begin function verification trace ===
(=> b1 (= v0_1_0 p0))
assume (distinct v0_1_0 0)
(=> b1 (= v1_1_2 (* 2 v0_1_0)))
(=> b1 (= v0_1_3 v0_1_0))
Changes to tests/g0/verification/z3gen/e-z3gen-test-5.txt.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
=== Begin function verification trace ===
(=> b1 (= v0_1_0 p0))
(=> b1 (= v1_1_1 p1))
assume (distinct v0_1_0 v1_1_1)
(=> b1 (= v2_1_4 (tupsort0 u2 u3)))
(=> b1 (= v2_1_5 (tupsort0 v0_1_0 (tupsort0_1 v2_1_4))))
(=> b1 (= v2_1_6 (tupsort0 (tupsort0_0 v2_1_5) v1_1_1)))
(= e1_2 (and b1 (< (tupsort0_0 v2_1_6) (tupsort0_1 v2_1_6))))
(= b2 e1_2)
(=> b2 (= v4_2_8 u7))
(=> e1_2 (= v2_2_9 v2_1_6))
(=> b2 (= v2_2_10 v2_2_9))
(=> b2 (= v4_2_11 (tupsort0_1 v2_2_10)))
(=> b2 (= v5_2_13 u12))
(=> b2 (= v5_2_14 (tupsort0_0 v2_2_10)))
(=> b2 (= v2_2_15 (tupsort0 v4_2_11 (tupsort0_1 v2_2_10))))
(=> b2 (= v2_2_16 (tupsort0 (tupsort0_0 v2_2_15) v5_2_14)))
(let ((a!1 (and b1 (not (< (tupsort0_0 v2_1_6) (tupsort0_1 v2_1_6))))))
  (= e1_3 a!1))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(= e3_4 (and b3 b3))
(= b4 e3_4)
(=> e1_3 (= v2_3_17 v2_1_6))









|


|
|
|
|
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
=== Begin function verification trace ===
(=> b1 (= v0_1_0 p0))
(=> b1 (= v1_1_1 p1))
assume (distinct v0_1_0 v1_1_1)
(=> b1 (= v2_1_4 (tupsort0 u2 u3)))
(=> b1 (= v2_1_5 (tupsort0 v0_1_0 (tupsort0_1 v2_1_4))))
(=> b1 (= v2_1_6 (tupsort0 (tupsort0_0 v2_1_5) v1_1_1)))
(= e1_2 (and b1 (< (tupsort0_0 v2_1_6) (tupsort0_1 v2_1_6))))
(= b2 e1_2)
(=> b2 (= v3_2_8 u7))
(=> e1_2 (= v2_2_9 v2_1_6))
(=> b2 (= v2_2_10 v2_2_9))
(=> b2 (= v3_2_11 (tupsort0_1 v2_2_10)))
(=> b2 (= v4_2_13 u12))
(=> b2 (= v4_2_14 (tupsort0_0 v2_2_10)))
(=> b2 (= v2_2_15 (tupsort0 v3_2_11 (tupsort0_1 v2_2_10))))
(=> b2 (= v2_2_16 (tupsort0 (tupsort0_0 v2_2_15) v4_2_14)))
(let ((a!1 (and b1 (not (< (tupsort0_0 v2_1_6) (tupsort0_1 v2_1_6))))))
  (= e1_3 a!1))
(= e2_3 (and b2 b2))
(= b3 (or e1_3 e2_3))
(= e3_4 (and b3 b3))
(= b4 e3_4)
(=> e1_3 (= v2_3_17 v2_1_6))