Goose  Diff

Differences From Artifact [863866eacd]:

  • File bs/builtins/types/runtime/unify.cpp — part of check-in [ac4e681af3] at 2020-06-15 22:09:26 on branch trunk — Refactored the integer/ct_int unification to defer the size/sign check to a postprocessing callback. (user: achavasse size: 7920)

To Artifact [65254ece1e]:

  • File bs/builtins/types/runtime/typecheck.cpp — part of check-in [b64ea47f6b] at 2020-06-27 22:05:05 on branch trunk — Clearly separate the type checking rules and the unification rules, instead of lumping them all together in a single set of patterns which is increasingly confusing. (user: achavasse size: 7507)

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
        auto rtIntTypePattern = Value( TypeType(), MkStdType( TSID( integer ),
            VEC( ANYTERM( _ ), ANYTERM( _ ) ) ) );

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

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

            ParamPat( GetValueType< BigInt >() ),

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

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

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

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

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

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

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

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







|


|







|








|







|











|














|







|







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
        auto rtIntTypePattern = Value( TypeType(), MkStdType( TSID( integer ),
            VEC( ANYTERM( _ ), ANYTERM( _ ) ) ) );

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

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

            ParamPat( GetValueType< BigInt >() ),

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

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

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

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

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

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

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

            for( auto&& [s,tcc] : HalfUnify( lhsVal->type(), tcc ) )
            {
                // We need to defer the calculation of the init value until after the rest of the unification is complete,
                // because we need to make sure that any hole present in the integer type is resolved before we can do
                // the sign/bitsize check. However, since we still need to be able to manipulate the result as a value in
                // the mean time, we construct a value with the correct (but possibly not complete yet type), and a payload
                // that contains the type + the ct_int value wrapped with a post process callback that finalizes the job.
                auto wrapped = WrapWithPostprocFunc( VEC( lhsVal->type(), rhs ),
                []( const Term& t, TypeCheckingContext tcc ) -> optional< Term >
                {
                    auto result = Decompose( t,
                        Vec(
                            SubTerm(),
                            SubTerm()
                        )
                    );
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
                        valToLoad = ct.zext( rttype->m_numBits );
                        valToLoad.setIsSigned( false );
                    }

                    return TERM( valToLoad );
                } );

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

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

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

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

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

            ParamPat( GetValueType< string >() ),

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

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

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

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

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

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

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

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

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

            ParamPat( ValueToIRExpr( ptrTypePattern ) ),

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

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







|











|


|




|
<
|
<
|
<






<
<
<
<
<
<
<
<
<
<
<


|


|

|
|


|








|








|






119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146

147

148

149
150
151
152
153
154











155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
                        valToLoad = ct.zext( rttype->m_numBits );
                        valToLoad.setIsSigned( false );
                    }

                    return TERM( valToLoad );
                } );

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

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

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

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

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

        // Emit a LoadConstantStr llr instruction.

        e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,


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












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

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

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

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

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

            ParamPat( ValueToIRExpr( ptrTypePattern ) ),

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

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