Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Sema: simplification: the "half-unification" rules don't need to be generators, they can only output one result. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
3bf30e74ace51ec38ac773df6d1a1b36 |
| User & Date: | achavasse 2021-01-13 11:46:18.244 |
Context
|
2021-01-21
| ||
| 21:05 | Removed the vector "typechecking rule", whose existence made no sense. Typechecking rules should operate only on values and pattern of values. Typechecking multiple values against multiple params is now done through a specific function instead. check-in: 7b9f645074 user: achavasse tags: trunk | |
|
2021-01-13
| ||
| 11:46 | Sema: simplification: the "half-unification" rules don't need to be generators, they can only output one result. check-in: 3bf30e74ac user: achavasse tags: trunk | |
|
2021-01-12
| ||
| 21:08 | Implemented missing lowering of reference types to pointer types, added a bunch of runtime/compilation time reference tests. check-in: 90c951f66f user: achavasse tags: trunk | |
Changes
Changes to bs/builtins/types/runtime/typecheck.cpp.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 |
// 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 type checking rule below.
e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,
ValueToEIR( rtIntTypePattern ),
GetValueType< BigInt >(),
| | | > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
// 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 type checking rule below.
e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,
ValueToEIR( rtIntTypePattern ),
GetValueType< BigInt >(),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext c ) -> TCGen
{
if( auto s = HalfUnify( lhs, c ) )
co_yield { *s, 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,
|
| ︙ | ︙ | |||
51 52 53 54 55 56 57 |
ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern(
TSID( constant ),
GetValueType< BigInt >(),
ANYTERM( _ ) ) ),
| | | > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | > | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 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 |
ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern(
TSID( constant ),
GetValueType< BigInt >(),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
// The rhs might not be constant. It can happen even with ct_int if the rhs is a
// fake argument pattern generated to unify a higher-order function param.
// It means that we are unifying a function parameter against any ct_int value,
// which we can't (because we have to know the value to safely convert it),
// so we reject it.
auto rhsVal = *ValueFromEIR( rhs );
if( !rhsVal.isConstant() )
co_return;
auto lhsVal = ValuePatternFromIRExpr( lhs );
if( !lhsVal )
co_return;
auto s = HalfUnify( lhsVal->type(), tcc );
if( !s )
co_return;
// We need to defer the calculation of the init value until after the rest of the type checking 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()
)
);
assert( result );
auto&& [type, rhs] = *result;
auto ct = *FromValue< BigInt >( *ValueFromEIR( rhs ) );
auto rttypeVal = ValueFromEIR( type );
assert( rttypeVal );
auto rttype = FromValue< IntegerType >( *rttypeVal );
assert( rttype );
APSInt valToLoad;
if( rttype->m_signed )
{
if( ct.getMinSignedBits() > rttype->m_numBits )
return nullopt;
valToLoad = ct.sext( rttype->m_numBits );
valToLoad.setIsSigned( true );
}
else
{
if( ct.isNegative() )
return nullopt;
if( ct.getActiveBits() > rttype->m_numBits )
return nullopt;
valToLoad = ct.zext( rttype->m_numBits );
valToLoad.setIsSigned( false );
}
return TERM( valToLoad );
} );
co_yield { ValueToEIR( Value( *s, move( wrapped ) ) ), tcc };
} );
auto rtInt8TypePattern = Value( TypeType(), MkStdType( TSID( integer ),
VEC( TERM( 8U ), ANYTERM( _ ) ) ) );
auto rtInt8PtrTypePattern = Value( TypeType(), MkStdType( TSID( pointer ),
ValueToEIR( rtInt8TypePattern ) ) );
// ct_string type against a char*:
// return the char* type.
e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,
ValueToEIR( rtInt8PtrTypePattern ),
GetValueType< string >(),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext c ) -> TCGen
{
if( auto s = HalfUnify( lhs, c ) )
co_yield { *s, c };
} );
// ct_string constant type checking against a pointer to a integer( 8 ):
// Emit a LoadConstantStr cir instruction.
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ValueToEIR( ValuePattern(
|
| ︙ | ︙ |
Changes to bs/builtins/types/template/tc-tdecl.cpp.
| ︙ | ︙ | |||
41 42 43 44 45 46 47 |
}
void SetupTDeclTypeChecking( Env& e )
{
auto tDeclPat = ValueToEIR( Value( GetValueType< TDecl >(), VEC( ANYTERM( _ ), ANYTERM( _ ) ) ) );
e.typeCheckingRuleSet()->addHalfUnificationRule( TCRINFOS, tDeclPat,
| | | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
}
void SetupTDeclTypeChecking( Env& e )
{
auto tDeclPat = ValueToEIR( Value( GetValueType< TDecl >(), VEC( ANYTERM( _ ), ANYTERM( _ ) ) ) );
e.typeCheckingRuleSet()->addHalfUnificationRule( TCRINFOS, tDeclPat,
[]( const Term& lhs, TypeCheckingContext& c )
{
auto tdecl = FromValue< TDecl >( *ValueFromEIR( lhs ) );
assert( tdecl );
return HalfUnify( tdecl->type(), c );
} );
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS, tDeclPat, ANYTERM( _ ), UnifyTDecl );
|
| ︙ | ︙ |
Changes to bs/sema/tc-ruleset.h.
| ︙ | ︙ | |||
22 23 24 25 26 27 28 |
#define TCRINFOS TCRuleInfo{}
#endif
class TypeCheckingRuleSet
{
public:
using UniFunc = function< TCGen ( const Term& lhs, const Term& rhs, const TypeCheckingContext& ) >;
| | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#define TCRINFOS TCRuleInfo{}
#endif
class TypeCheckingRuleSet
{
public:
using UniFunc = function< TCGen ( const Term& lhs, const Term& rhs, const TypeCheckingContext& ) >;
using HalfUniFunc = function< optional< Term > ( const Term& lhs, TypeCheckingContext& ) >;
struct UniRule
{
UniFunc func;
TCRuleInfo infos;
};
|
| ︙ | ︙ |
Changes to bs/sema/tctrie-typecheck.inl.
| ︙ | ︙ | |||
91 92 93 94 95 96 97 |
for( auto&& [t, payload] : Enumerate( get< 0 >( branch )->m_trie ) )
{
auto newC = tcc;
newC.addComplexity( GetComplexity( t ) );
auto lvec = Vector::MakeAppend( lhsVec, t );
| | | | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
for( auto&& [t, payload] : Enumerate( get< 0 >( branch )->m_trie ) )
{
auto newC = tcc;
newC.addComplexity( GetComplexity( t ) );
auto lvec = Vector::MakeAppend( lhsVec, t );
if( auto s = sema::HalfUnify( t, newC ) )
co_yield HalfUnify< T >( lvec, Vector::MakeAppend( solutionVec, move( *s ) ), payload, newC );
}
}
else
co_yield { lhsVec, solutionVec, *any_cast< T >( &get< 1 >( branch ) ), tcc };
}
template< typename U >
|
| ︙ | ︙ | |||
127 128 129 130 131 132 133 |
for( auto&& [lv, sol, pRptNode, tcc] : HalfUnify< rpt_node >( lhs, sol, branch, tcc ) )
{
for( auto&& [rt, payload] : Enumerate( pRptNode->m_repetition ) )
{
auto newC = tcc;
newC.addComplexity( GetComplexity( rt ) );
| | | | | 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
for( auto&& [lv, sol, pRptNode, tcc] : HalfUnify< rpt_node >( lhs, sol, branch, tcc ) )
{
for( auto&& [rt, payload] : Enumerate( pRptNode->m_repetition ) )
{
auto newC = tcc;
newC.addComplexity( GetComplexity( rt ) );
if( auto s = sema::HalfUnify( rt, newC ) )
{
auto localLhsVec = lv;
localLhsVec.setRepetitionTerm( rt );
auto localSol = sol;
localSol.setRepetitionTerm( move( *s ) );
co_yield { localLhsVec, localSol, payload, newC };
}
}
}
}
}
}
#endif
|
Changes to bs/sema/typecheck.cpp.
| ︙ | ︙ | |||
115 116 117 118 119 120 121 |
if( !bestRule )
co_return;
co_yield bestRule->func( lhs, rhs, context );
}
| | | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
if( !bestRule )
co_return;
co_yield bestRule->func( lhs, rhs, context );
}
optional< Term > HalfUnify( const Term& lhs, TypeCheckingContext& context )
{
const auto& rules = context.rules()->halfUniRules();
MatchSolution bestSol;
optional< TypeCheckingRuleSet::HalfUniRule > bestRule;
bool ambiguous = false;
|
| ︙ | ︙ | |||
144 145 146 147 148 149 150 |
}
// Let's not really worry about how to properly report this for now.
if( ambiguous )
throw runtime_error( "ambiguous half-unification rule" );
if( !bestRule )
| | | | 144 145 146 147 148 149 150 151 152 153 154 155 |
}
// Let's not really worry about how to properly report this for now.
if( ambiguous )
throw runtime_error( "ambiguous half-unification rule" );
if( !bestRule )
return nullopt;
return bestRule->func( lhs, context );
}
}
|
Changes to bs/sema/typecheck.h.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 |
TypeCheckingSolution FindBestTyping( const Term& lhs, const Term& rhs, const Context& context );
using TCGen = Generator< pair< Term, TypeCheckingContext > >;
TCGen TypeCheck( const Term& lhs, const Term& rhs, const TypeCheckingContext& context );
TCGen Unify( const Term& lhs, const Term& rhs, const TypeCheckingContext& context );
| | | 17 18 19 20 21 22 23 24 25 26 27 |
TypeCheckingSolution FindBestTyping( const Term& lhs, const Term& rhs, const Context& context );
using TCGen = Generator< pair< Term, TypeCheckingContext > >;
TCGen TypeCheck( const Term& lhs, const Term& rhs, const TypeCheckingContext& context );
TCGen Unify( const Term& lhs, const Term& rhs, const TypeCheckingContext& context );
optional< Term > HalfUnify( const Term& lhs, TypeCheckingContext& context );
}
#endif
|
Changes to bs/sema/uni-basicrules.cpp.
1 2 3 4 |
#include "sema.h"
namespace goose::sema
{
| < < < < < < < < < < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 |
#include "sema.h"
namespace goose::sema
{
TCGen UnifyVectors( const Vector::container_type< Term >& lTerms, const Vector::container_type< Term >& rTerms, size_t i,
const Vector& solutionVec, TypeCheckingContext tcc )
{
assert( lTerms.size() == rTerms.size() );
assert( i < lTerms.size() );
for( auto&& [u,tcc] : Unify( lTerms[i], rTerms[i], tcc ) )
|
| ︙ | ︙ | |||
50 51 52 53 54 55 56 |
co_yield TypeCheckVectors( lTerms, rTerms, i + 1, vec, tcc );
}
}
void SetupBasicUnificationRules( TypeCheckingRuleSet& ruleSet )
{
// Default half-unification rule
| | | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
co_yield TypeCheckVectors( lTerms, rTerms, i + 1, vec, tcc );
}
}
void SetupBasicUnificationRules( TypeCheckingRuleSet& ruleSet )
{
// Default half-unification rule
ruleSet.addHalfUnificationRule( TCRINFOS, ANYTERM( _ ), []( const Term& lhs, TypeCheckingContext& tcc )
{
// NOTE: the score here is always 0 because:
// Vectors are going to fall into the vector half-unification rule below,
// and holes are going to have their own rules. So we only are here for terminal expressions.
return lhs;
} );
// Identity unification rule
ruleSet.addUnificationRule( TCRINFOS, ANYTERM( T ), []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
assert( lhs == rhs );
|
| ︙ | ︙ | |||
102 103 104 105 106 107 108 |
// in the end if necessary.
auto lpv = get< void* >( lhs );
auto rpv = get< void* >( rhs );
auto result = lpv == rpv ? lpv : nullptr;
co_yield { result, tcc };
} );
| | < < < | > > | > | > | > | < | 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 |
// in the end if necessary.
auto lpv = get< void* >( lhs );
auto rpv = get< void* >( rhs );
auto result = lpv == rpv ? lpv : nullptr;
co_yield { result, tcc };
} );
// Half-Unification rule for vectors
ruleSet.addHalfUnificationRule( TCRINFOS, VECOFLENGTH( _ ), []( const Term& lhs, TypeCheckingContext& tcc )
-> optional< Term >
{
assert( holds_alternative< pvec >( lhs ) );
const auto& lVector = get< pvec >( lhs );
const auto& lTerms = lVector->terms();
Vector result;
for( auto&& t : lTerms )
{
auto s = HalfUnify( t, tcc );
if( !s )
return nullopt;
result.append( *s );
}
return TERM( make_shared< Vector >( move( result ) ) );
} );
// Unification rule for vectors of identical length:
// They are recursed into and each contained members are unified.
// Note: the repetition term, if any, is ignored. The repetition term is only for vector patterns
// for now. We'll have to see how to handle variadic functions later.
ruleSet.addUnificationRule( TCRINFOS, VECOFLENGTH( VL ), []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
|
| ︙ | ︙ |
Changes to bs/sema/uni-holes.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "sema.h"
namespace goose::sema
{
void SetupHoleUnificationRules( TypeCheckingRuleSet& ruleSet )
{
// Anonymous hole half-unification: add 1 to the anon holes count,
// yield the hole as is.
ruleSet.addHalfUnificationRule( TCRINFOS,
HOLE( "_"_sid ),
| | | | | | | | | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
#include "sema.h"
namespace goose::sema
{
void SetupHoleUnificationRules( TypeCheckingRuleSet& ruleSet )
{
// Anonymous hole half-unification: add 1 to the anon holes count,
// yield the hole as is.
ruleSet.addHalfUnificationRule( TCRINFOS,
HOLE( "_"_sid ),
[]( const Term& lhs, TypeCheckingContext& tcc )
{
tcc.addAnonymousHole();
return lhs;
} );
// Anonymous hole versus anything: add 1 to the anon holes count,
// yield the half unification of the rhs
ruleSet.addUnificationRule( TCRINFOS,
HOLE( "_"_sid ),
ANYTERM( _ ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
tcc.addAnonymousHole();
if( auto s = HalfUnify( rhs, tcc.flip() ) )
co_yield { *s, tcc.flip() };
} );
// Hole half-unification: Convert it to a numbered hole,
// If the name wasn't already known, add 1 to the score's unique holes count.
ruleSet.addHalfUnificationRule( TCRINFOS,
HolePattern(),
[]( const Term& lhs, TypeCheckingContext& tcc ) -> optional< Term >
{
auto lh = *HoleFromIRExpr( lhs );
if( !lh.name.isNumerical() )
{
// This is a named hole: look up its name.
auto holeIndex = tcc.getLHSHoleIndex( lh.name );
if( holeIndex != TypeCheckingContext::InvalidIndex )
{
if( !tcc.isHoleLocked( holeIndex ) )
return HOLE( StringId( holeIndex ) );
}
else
{
// This is a new name: create a new value,
// and increment the number of unique holes in the current score.
auto index = tcc.createValue();
tcc.setLHSHoleIndex( lh.name, index );
return HOLE( StringId( index ) );
}
}
else
{
// This is already an indexed hole: yield it as is.
if( !tcc.isHoleLocked( lh.name.id() ) )
return lhs;
}
return nullopt;
} );
// Hole vs anything
ruleSet.addUnificationRule( TCRINFOS,
HolePattern(),
ANYTERM( _ ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
|
| ︙ | ︙ | |||
84 85 86 87 88 89 90 |
if( index == TypeCheckingContext::InvalidIndex )
{
// This is a new name: create a new value.
index = tcc.createValue();
tcc.setLHSHoleIndex( h.name, index );
auto holeExpr = HOLE( StringId( index ) );
| | | | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
if( index == TypeCheckingContext::InvalidIndex )
{
// This is a new name: create a new value.
index = tcc.createValue();
tcc.setLHSHoleIndex( h.name, index );
auto holeExpr = HOLE( StringId( index ) );
if( auto s = HalfUnify( rhs, tcc.flip() ) )
{
tcc.setValue( index, SetComplexity( move( *s ), tcc.complexity() - oldComplexity ) );
co_yield { move( holeExpr ), tcc.flip() };
}
co_return;
}
}
|
| ︙ | ︙ | |||
113 114 115 116 117 118 119 |
tcc.unlockHole( index );
tcc.setValue( index, SetComplexity( move( e ), tcc.complexity() - oldComplexity ) );
co_yield { move( holeExpr ), tcc };
}
}
else
{
| | | | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
tcc.unlockHole( index );
tcc.setValue( index, SetComplexity( move( e ), tcc.complexity() - oldComplexity ) );
co_yield { move( holeExpr ), tcc };
}
}
else
{
if( auto s = HalfUnify( rhs, tcc.flip() ) )
{
tcc.unlockHole( index );
tcc.setValue( index, SetComplexity( move( *s ), tcc.complexity() - oldComplexity ) );
co_yield { move( holeExpr ), tcc.flip() };
}
}
} );
// Hole vs hole
ruleSet.addUnificationRule( TCRINFOS,
|
| ︙ | ︙ | |||
204 205 206 207 208 209 210 |
// stored in the other one. We can't just copy the value over as we would lose the dependency
// relationship between the two holes.
const auto& lval = tcc.getValue( lindex );
const auto& rval = tcc.getValue( rindex );
if( !rval )
{
| | | | 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
// stored in the other one. We can't just copy the value over as we would lose the dependency
// relationship between the two holes.
const auto& lval = tcc.getValue( lindex );
const auto& rval = tcc.getValue( rindex );
if( !rval )
{
if( HalfUnify( *lval, tcc ) )
{
tcc.unlockHole( lindex );
tcc.unlockHole( rindex );
tcc.setValue( rindex, HOLE( StringId( lindex ) ) );
co_yield { HOLE( StringId( lindex ) ), tcc };
}
co_return;
}
if( !lval )
{
if( HalfUnify( *rval, tcc.flip() ) )
{
tcc.unlockHole( lindex );
tcc.unlockHole( rindex );
tcc.setValue( lindex, HOLE( StringId( rindex ) ) );
co_yield { HOLE( StringId( rindex ) ), tcc.flip() };
}
|
| ︙ | ︙ |
Changes to bs/sema/uni-quote.cpp.
| ︙ | ︙ | |||
22 23 24 25 26 27 28 |
auto&& [quoted] = *result;
return quoted;
}
void SetupQuoteUnificationRules( TypeCheckingRuleSet& ruleSet )
{
ruleSet.addHalfUnificationRule( TCRINFOS, VEC( TSID( quote ), ANYTERM( _ ) ),
| | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
auto&& [quoted] = *result;
return quoted;
}
void SetupQuoteUnificationRules( TypeCheckingRuleSet& ruleSet )
{
ruleSet.addHalfUnificationRule( TCRINFOS, VEC( TSID( quote ), ANYTERM( _ ) ),
[]( const Term& lhs, TypeCheckingContext& tcc )
{
return lhs;
} );
ruleSet.addUnificationRule( TCRINFOS, VEC( TSID( quote ), ANYTERM( _ ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
if( lhs == rhs )
co_yield { lhs, tcc };
|
| ︙ | ︙ |