Goose  Diff

Differences From Artifact [a27a4d237c]:

  • File bs/builtins/types/runtime/unify.cpp — part of check-in [5a80915ee6] at 2019-08-04 00:04:57 on branch trunk — Apply the ct_int unification fix to ct_string unification. (user: achavasse size: 8991)

To Artifact [ce9cd387f8]:

  • File bs/builtins/types/runtime/unify.cpp — part of check-in [43e22af793] at 2019-08-05 02:45:01 on branch trunk —
    • Unification now works in two passes. The second pass gives unification rules a chance to match again after all the holes have been resolved and substituted.
    • Fixed many horrible bugs in various unification rules that managed to go by unnoticed until the above change, after which they made everything catch on fire.
    • Simplified the ct_int and ct_string unification rules to take advantage of the new unification behavior. Everything finally works as intended wrt to ct_int versus RT integers.
    • Removed unification callbacks. It was a system to provide a way to perform unification work post hole substitution, so it is now obsolete.
    (user: achavasse size: 6050)

12
13
14
15
16
17
18

19

















20


21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64


65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99


100
101


102
103

104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155

156

















157


158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175





176
177
178
179
180
181
182
183
184


185
186
187
188

189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
            TSID( rt_integer ), ANYTERM( _ ), ANYTERM( _ ) ) );

        // ct_int type against a RTInteger type:
        // return the RTInteger type. We don't care if the
        // ct_int fits at this point, this will be dealt with by
        // the ct_int value unification rule below.
        e.unificationRuleSet()->addSymRule(

            GetValueType< APSInt >(),

















            ValueToIRExpr( rtIntTypePattern ),


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

        // ct_int constant unification against any other value:
        // Unify the value's type with ct_int, and if successful
        // (as per the rule of ct_int versus rtint above), wrap
        // the result in a callback.
        // That callback will check that the final unified value is
        // of an RTInt type of suitable
        // size. If it is, it wil emit a load const int instruction.
        // Otherwise, it will fail the unification.
        //
        // The reason we do all this is because we have to resolve the
        // type unification first. It is only after this is done (and any
        // hole is replaced with its infered value) that we know the actual
        // wanted type for the value. We need to know that type to know if
        // our constant value can be converted, and to emit the conversion.
        //
        // So we do this in a callback, which lets us perform a check and
        // substitution of the final value after any contained hole has been
        // replaced, and unification callback has been applied.
        //
        // An example of situation where this is needed is as follow:
        //
        // void lomarf( $T a, $T b ) {}
        // RTUInt( 8 ) someInt;
        // lomarf( someInt, 123 );
        //
        // $T will resolve to RTUInt( 8 ), but if we had a simple ct_int against
        // RTUInt param unification rule, the rule wouldn't work, because before we
        // apply substitutions, the second parameter is still expressed as $T b,
        // which wouldn't match the rule.
        // However, the ct_int against "value with placeholder type and content" will
        // match against $T b, and wrap it with a callback. Substitute will then
        // turn $T b into RTUInt( 8 ) b and then invoke the callback on that, which
        // can do its final checks and emit the load const int instruction wrapped as
        // a value, or return nullopt to indicate that the unification failed (in case
        // the constant is too big to fit in the RTUInt( 8 )).
        e.unificationRuleSet()->addAsymRule(

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



            ValueToIRExpr( Value(
                GetValueType< APSInt >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, UnificationContext& c ) -> UniGen
        {
            // Unify the type
            auto valPat = ValuePatternFromIRExpr( lhs );

            for( auto&& [s, uc] : Unify( valPat->type(), GetValueType< APSInt >(), c ) )
            {
                // When we match through this rule rather than through the default rule,
                // we don't count the vector that contains the value itself, so add its complexity manually
                // here.
                // Otherwise it can yield incorrect results and incorrect overloads ordering.
                //
                // For instance, in this case, given
                //   bool equals( $a, $a ) { return true }
                //   bool equals( $a, $b ) { return false }
                // equals( 219, 219 ) would return false, because the unification of $b against $b
                // invokes this rule, whereas the two separate unifications of 219 against a different
                // hole would each yield a higher result. The complexity score being higher, the
                // additional unique hole for the second overload would be ignored in the ranking.
                uc.incComplexity( 2 );

                // Yield a value of the unified type, wrapped into a callback
                // that will perform the size check and emit the load const once
                // we know the final unified solution for the value's type.
                // Also, the callback should just return the final valus as is if
                // is happens to be a ct_int.
                valPat->type() = s;

                auto wrapped = WrapWithCallback( ValueToIRExpr( *valPat ),
                    [rhs]( auto&& t ) -> optional< Term >


                    {
                        auto lhsVal = *ValuePatternFromIRExpr( t );



                        if( lhsVal.type() == GetValueType< APSInt >() )

                            return rhs;

                        auto ct = *FromValue< APSInt >( *ValueFromIRExpr( rhs ) );
                        auto rttypeVal = *ValueFromIRExpr( lhsVal.type() );
                        auto rttype = FromValue< RTInteger >( rttypeVal );
                        if( !rttype )
                            return nullopt;

                        APSInt valToLoad;

                        if( rttype->m_signed )
                        {
                            if( ct.getMinSignedBits() > rttype->m_numBits )
                                return nullopt;

                            if( ct.isNegative() )
                                valToLoad = ct.sext( rttype->m_numBits );
                            else
                                valToLoad = ct.zext( rttype->m_numBits );
                        }
                        else
                        {
                            if( ct.isNegative() )
                                return nullopt;

                            if( ct.getActiveBits() > rttype->m_numBits )
                                return nullopt;

                            valToLoad = ct.zext( rttype->m_numBits );
                        }

                        auto* llvmType = static_cast< llvm::IntegerType* >( GetLLVMType( rttypeVal ) );
                        return ValueToIRExpr(
                            BuildComputedValue( lhsVal.type(), llr::LoadConstInt( llvmType, move( valToLoad ) ) ) );
                    }
                );

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

        auto rtInt8TypePattern = Value( TypeType(), TVEC( TSID( rt_type ),
            ANYTERM( _ ),
            TSID( rt_integer ), TERM( 8ULL ), ANYTERM( _ ) ) );

        auto rtInt8PtrTypePattern = Value( TypeType(), TVEC( TSID( rt_type ),
            ANYTERM( _ ),
            TSID( rt_pointer ), ValueToIRExpr( rtInt8TypePattern ) ) );

        // ct_int type against a against a rt_pointer to a rt_integer( 8 ):
        // return the later.
        e.unificationRuleSet()->addSymRule(

            GetValueType< string >(),

















            ValueToIRExpr( rtInt8PtrTypePattern ),


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

        // ct_string constant unification against a rt_pointer to a rt_integer( 8 ):
        // Emit a LoadConstantStr llr instruction from an unification callback.
        // This works the same way as the ct_int value unification rule, see
        // above for the grisly details.
        e.unificationRuleSet()->addAsymRule(

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

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






        []( const Term& lhs, const Term& rhs, UnificationContext& c ) -> UniGen
        {
            // Unify the type
            auto valPat = ValuePatternFromIRExpr( lhs );

            for( auto&& [s, uc] : Unify( valPat->type(), GetValueType< string >(), c ) )
            {
                uc.incComplexity( 2 );
                valPat->type() = s;



                auto wrapped = WrapWithCallback( ValueToIRExpr( *valPat ),
                    [rhs]( auto&& t ) -> optional< Term >
                    {

                        auto lhsVal = *ValuePatternFromIRExpr( t );

                        if( lhsVal.type() == GetValueType< string >() )
                            return rhs;

                        const auto* str = FromValue< string >( *ValueFromIRExpr( rhs ) );
                        return ValueToIRExpr(
                            BuildComputedValue( lhsVal.type(), llr::LoadConstStr( *str ) ) );
                    }
                );

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







>

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


|


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



|
>
>







|
|
|
<

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

|
<
>
>
|
|
>
>

|
>
|

<
<
|
|
|

|

|
|
|
|

|
|
|
|
|
|
|
|
|

|
|

|
|

|
|
|
<
<
<
<
<










|
|

>

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


|



|
<
<
|
<
<
<





>
>
>
>
>


|
<
|
<

<
<
>
>
|
<
<
|
>
|

<
<
<
<
|
|
<
<
<
<
<



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46




47


























48


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

65



66


67






68






69
70

71
72
73
74
75
76
77
78
79
80
81


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





112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153


154



155
156
157
158
159
160
161
162
163
164
165
166
167

168

169


170
171
172


173
174
175
176




177
178





179
180
181
            TSID( rt_integer ), ANYTERM( _ ), ANYTERM( _ ) ) );

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

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

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

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

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

        // ct_integer constant unification against a RTInteger:




        // Check if the RTInteger is big enough for the constant,


























        // and emit a LoadConstantInt llr instruction if so.


        e.unificationRuleSet()->addAsymRule(

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

            ValueToIRExpr( Value(
                GetValueType< APSInt >(),
                ANYTERM( _ ) ) ),

        []( const Term& lhs, const Term& rhs, UnificationContext& c ) -> UniGen
        {
            // Don't bother during the first pass, the rt integer type pattern
            // is likely to contain unresolved holes.
            if( !c.secondPass() )

            {



                co_yield { lhs, c };


                co_return;






            }







            auto ct = FromValue< APSInt >( *ValueFromIRExpr( rhs ) );

            if( !ct )
                co_return;

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

            auto rttypeVal = ValueFromIRExpr( lhsVal->type() );
            if( !rttypeVal )
                co_return;



            auto rttype = FromValue< RTInteger >( *rttypeVal );
            if( !rttype )
                co_return;

            APSInt valToLoad;

            if( rttype->m_signed )
            {
                if( ct->getMinSignedBits() > rttype->m_numBits )
                    co_return;

                if( ct->isNegative() )
                    valToLoad = ct->sext( rttype->m_numBits );
                else
                    valToLoad = ct->zext( rttype->m_numBits );
            }
            else
            {
                if( ct->isNegative() )
                    co_return;

                if( ct->getActiveBits() > rttype->m_numBits )
                    co_return;

                valToLoad = ct->zext( rttype->m_numBits );
            }

            auto* llvmType = static_cast< llvm::IntegerType* >( GetLLVMType( *rttypeVal ) );
            co_yield { ValueToIRExpr(
                BuildComputedValue( lhsVal->type(), llr::LoadConstInt( llvmType, move( valToLoad ) ) ) ), c };





        } );

        auto rtInt8TypePattern = Value( TypeType(), TVEC( TSID( rt_type ),
            ANYTERM( _ ),
            TSID( rt_integer ), TERM( 8ULL ), ANYTERM( _ ) ) );

        auto rtInt8PtrTypePattern = Value( TypeType(), TVEC( TSID( rt_type ),
            ANYTERM( _ ),
            TSID( rt_pointer ), ValueToIRExpr( rtInt8TypePattern ) ) );

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

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

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

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

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

        // ct_string constant unification against a rt_pointer to a rt_integer( 8 ):
        // Emit a LoadConstantStr llr instruction.


        e.unificationRuleSet()->addSymRule(




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

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

        []( const Term& lhs, const Term& rhs, UnificationContext& c ) -> UniGen
        {
            // Don't bother during the first pass.

            if( !c.secondPass() )

            {


                co_yield { lhs, c };
                co_return;
            }



            auto str = *FromValue< string >( *ValueFromIRExpr( lhs ) );
            auto rhsVal = *ValuePatternFromIRExpr( rhs );





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





        } );
    }
}