Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | 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. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
b64ea47f6b7383e3e195fa34c3f43612 |
| User & Date: | achavasse 2020-06-27 22:05:05.101 |
Context
|
2020-07-02
| ||
| 00:47 |
| |
|
2020-06-27
| ||
| 22:05 | 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. check-in: b64ea47f6b user: achavasse tags: trunk | |
|
2020-06-26
| ||
| 23:34 |
Cleanup:
| |
Changes
Changes to bs/builtins/helpers.cpp.
| ︙ | ︙ | |||
44 45 46 47 48 49 50 |
}
variant< Value, ValUnifyError > ConvertValueToType( const Context& c, const Value& val, const Term& type )
{
auto valTerm = ValueToIRExpr( val );
auto paramPat = ParamPat( type );
| | | | | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
}
variant< Value, ValUnifyError > ConvertValueToType( const Context& c, const Value& val, const Term& type )
{
auto valTerm = ValueToIRExpr( val );
auto paramPat = ParamPat( type );
auto us = FindBestTyping( paramPat, valTerm, c );
if( holds_alternative< NoUnification >( us ) )
return ValUnifyError::NoSolution;
if( holds_alternative< AmbiguousTypeCheck >( us ) )
return ValUnifyError::Ambiguous;
auto&& [s,tcc] = get< TCSol >( us );
auto finalVal = ValueFromIRExpr( s );
assert( finalVal );
return *finalVal;
}
pvec ParseExpressionList( Parser& p, uint32_t precedence, const pvec& pVec )
|
| ︙ | ︙ |
Changes to bs/builtins/meson.build.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
'types/lower.cpp',
'types/runtime/runtime.cpp',
'types/runtime/basic.cpp',
'types/runtime/pointer.cpp',
'types/runtime/array.cpp',
'types/runtime/record.cpp',
| | | | | | | | | | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
'types/lower.cpp',
'types/runtime/runtime.cpp',
'types/runtime/basic.cpp',
'types/runtime/pointer.cpp',
'types/runtime/array.cpp',
'types/runtime/record.cpp',
'types/runtime/typecheck.cpp',
'types/runtime/init.cpp',
'types/tuple/tuple.cpp',
'types/tuple/typecheck.cpp',
'types/tuple/init.cpp',
'types/tuple/destroy.cpp',
'types/tuple/drop.cpp',
'types/tuple/lower.cpp',
'types/func/bfunc.cpp',
'types/func/func.cpp',
'types/func/build.cpp',
'types/func/compile.cpp',
'types/func/invoke.cpp',
'types/func/typecheck.cpp',
'types/func/lower.cpp',
'types/intrinsic/intrinsic.cpp',
'types/template/tvec.cpp',
'types/template/tvar.cpp',
'types/template/tdecl.cpp',
'types/template/tnameddecl.cpp',
'types/template/tfunctype.cpp',
'types/template/tfunc.cpp',
'types/template/build.cpp',
'types/template/rules.cpp',
'types/template/invoke.cpp',
'types/template/instantiate.cpp',
'types/template/typecheck.cpp',
'types/template/tc-tdecl.cpp',
'types/overloadset/overloadset.cpp',
'types/overloadset/invoke.cpp',
'types/overloadset/typecheck.cpp',
'types/overloadset/helpers.cpp',
'types/constrainedfunc/constrainedfunc.cpp',
'types/constrainedfunc/invoke.cpp',
'types/constrainedfunc/typecheck.cpp',
'types/localvar/localvar.cpp',
'types/localvar/typecheck.cpp',
'types/localvar/drop.cpp',
'types/localvar/invoke.cpp',
'types/reference/reference.cpp',
'types/reference/typecheck.cpp',
'operators/semicolon.cpp',
'operators/comma.cpp',
'operators/dollar.cpp',
'operators/logic.cpp',
'operators/arith.cpp',
'operators/comparison.cpp',
|
| ︙ | ︙ |
Changes to bs/builtins/types/constrainedfunc/constrainedfunc.cpp.
1 2 3 4 | #include "builtins/builtins.h" using namespace goose::builtins; | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include "builtins/builtins.h"
using namespace goose::builtins;
bool goose::builtins::IsConstrainedFunc( const Value& tcc )
{
return tcc.type() == GetValueType< ConstrainedFunc >();
}
namespace goose::ir
{
const Term& Bridge< builtins::ConstrainedFunc >::Type()
{
static auto type = ValueToIRExpr( Value( TypeType(), VEC( TSID( ct_type ), TSID( constrainedfunc ) ) ) );
|
| ︙ | ︙ |
Changes to bs/builtins/types/constrainedfunc/constrainedfunc.h.
1 2 3 4 5 6 |
#ifndef GOOSE_BUILTINS_TYPES_CONSTRAINEDFUNC_H
#define GOOSE_BUILTINS_TYPES_CONSTRAINEDFUNC_H
namespace goose::builtins
{
extern void SetupConstrainedFuncInvocationRule( Env& e );
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#ifndef GOOSE_BUILTINS_TYPES_CONSTRAINEDFUNC_H
#define GOOSE_BUILTINS_TYPES_CONSTRAINEDFUNC_H
namespace goose::builtins
{
extern void SetupConstrainedFuncInvocationRule( Env& e );
extern void SetupConstrainedFuncTypeChecking( Env& e );
class ConstrainedFunc
{
public:
template< typename P, typename IR, typename F >
ConstrainedFunc( P&& constraintPat, IR&& pInvRule, F&& func ) :
m_constraintPat( forward< P >( constraintPat ) ),
|
| ︙ | ︙ | |||
22 23 24 25 26 27 28 |
private:
Term m_constraintPat;
ptr< InvocationRule > m_pInvRule;
Value m_func;
};
| | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
private:
Term m_constraintPat;
ptr< InvocationRule > m_pInvRule;
Value m_func;
};
extern bool IsConstrainedFunc( const Value& tcc );
}
namespace goose::ir
{
template<>
struct Bridge< builtins::ConstrainedFunc >
{
|
| ︙ | ︙ |
Changes to bs/builtins/types/constrainedfunc/invoke.cpp.
| ︙ | ︙ | |||
13 14 15 16 17 18 19 |
// Unify with the constraint pattern. We only care that there is at least one
// solution, and if so, we defer the actual call resolution to the contained
// function.
auto cfunc = FromValue< ConstrainedFunc >( callee );
assert( cfunc );
| | | | | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// Unify with the constraint pattern. We only care that there is at least one
// solution, and if so, we defer the actual call resolution to the contained
// function.
auto cfunc = FromValue< ConstrainedFunc >( callee );
assert( cfunc );
TypeCheckingContext tcc( c );
tcc.setComplexity( GetComplexity( cfunc->constraintPat() ) + GetComplexity( callPat ) );
for( auto&& [s, tcc] : TypeCheck( cfunc->constraintPat(), callPat, tcc ) )
{
if( Postprocess( s, tcc ) )
return cfunc->invRule()->resolveInvocation( c, loc, cfunc->func(), args );
}
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"function arguments mismatch." );
return PoisonValue();
|
| ︙ | ︙ |
Name change from bs/builtins/types/constrainedfunc/unify.cpp to bs/builtins/types/constrainedfunc/typecheck.cpp.
1 2 3 4 5 6 7 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::ir;
namespace goose::builtins
{
| | | | | | | | | | | | | < > | | | | > > | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 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 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::ir;
namespace goose::builtins
{
void SetupConstrainedFuncTypeChecking( Env& e )
{
auto funcTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( func ),
ANYTERM( _ ), ANYTERM( _ ) ) ) );
auto tFuncTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( texpr ), TSID( tfunc ),
ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );
// func type param / constrainedfunc arg
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ParamPat( move( funcTypePat ) ),
ValueToIRExpr( ValuePattern(
TSID( constant ),
GetValueType< builtins::ConstrainedFunc >(),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto ldecomp = Decompose( lhs,
Vec(
Lit( "value"_sid ),
SubTerm(),
SubTerm(),
SubTerm(),
Val< LocationId >()
)
);
assert( ldecomp );
auto&& [sort, type, val, locId] = *ldecomp;
auto callPat = BuildCallPatternFromFuncType( *ValueFromIRExpr( type ) );
auto rhsVal = *ValueFromIRExpr( rhs );
auto cfunc = FromValue< ConstrainedFunc >( rhsVal );
assert( cfunc );
auto localC = tcc;
for( auto&& [s, tcc] : TypeCheck( callPat, cfunc->constraintPat(), localC ) )
{
if( Postprocess( s, tcc ) )
{
auto func = ValueToIRExpr( cfunc->func() );
co_yield TypeCheck( lhs, func, tcc );
co_return;
}
}
} );
// tfunc type param / constrainedfunc arg
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ValueToIRExpr( ValuePattern( HOLE( "_"_sid ), move( tFuncTypePat ), HOLE( "_"_sid ) ) ),
ValueToIRExpr( ValuePattern(
TSID( constant ),
GetValueType< builtins::ConstrainedFunc >(),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto ldecomp = Decompose( lhs,
Vec(
Lit( "value"_sid ),
SubTerm(),
SubTerm(),
SubTerm(),
Val< LocationId >()
)
);
assert( ldecomp );
auto&& [sort, type, val, locId] = *ldecomp;
auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *ValueFromIRExpr( type ) );
assert( callPat );
auto rhsVal = *ValueFromIRExpr( rhs );
auto cfunc = FromValue< ConstrainedFunc >( rhsVal );
assert( cfunc );
auto localC = tcc;
for( auto&& [s, tcc] : TypeCheck( *callPat, cfunc->constraintPat(), localC ) )
{
if( Postprocess( s, tcc ) )
{
auto func = ValueToIRExpr( cfunc->func() );
co_yield TypeCheck( lhs, func, tcc );
co_return;
}
}
} );
// constrainedfunc param / any arg:
// Just yield the constrained func.
//
// This is because when we monomorphize a function that takes
// a polymorphic function type, we turn the later into a
// compile time constant. So it isn't really a parameter
// in the resulting monomorphic function, and we just want
// to ignore whatever parameter is being passed there
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ValueToIRExpr( ValuePattern(
TSID( constant ),
GetValueType< builtins::ConstrainedFunc >(),
ANYTERM( _ ) ) ),
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
ANYTERM( _ ),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
co_yield { lhs, tcc };
} );
}
}
|
Changes to bs/builtins/types/func/bfunc.inl.
| ︙ | ︙ | |||
64 65 66 67 68 69 70 |
};
template< typename T >
struct BuildBuiltinFuncParamPat< builtins::TypeParam< T > >
{
static const auto& GetParamPattern()
{
| | | | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
};
template< typename T >
struct BuildBuiltinFuncParamPat< builtins::TypeParam< T > >
{
static const auto& GetParamPattern()
{
static auto pat = GetValueType< T >();
return pat;
}
};
template< typename PP >
struct BuildBuiltinFuncParamPat< builtins::TypePatternParam< PP > >
{
static const auto& GetParamPattern()
{
static auto pat = PP::GetPattern();
return pat;
}
};
template< typename... T >
Term BuildBuiltinFuncParamTypeList()
{
|
| ︙ | ︙ |
Changes to bs/builtins/types/func/build.cpp.
| ︙ | ︙ | |||
23 24 25 26 27 28 29 |
TERM( decl.name() ) );
c.env()->storeValue( paramVerificationIdentity, ANYTERM( _ ),
ValueToIRExpr( BuildComputedValue( decl.type(),
llr::Load( llr::VarAddress( varId++ ), decl.type() ) ) ) );
}
else if( param.isConstant() )
| | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
TERM( decl.name() ) );
c.env()->storeValue( paramVerificationIdentity, ANYTERM( _ ),
ValueToIRExpr( BuildComputedValue( decl.type(),
llr::Load( llr::VarAddress( varId++ ), decl.type() ) ) ) );
}
else if( param.isConstant() )
tv->append( ValueToIRExpr( param ) );
return true;
} );
// 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 llr instruction. This will allow verification
// expressions to refer to the current function's return value as a value of the correct type.
|
| ︙ | ︙ |
Changes to bs/builtins/types/func/func.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_BUILTINS_TYPES_FUNC_H
#define GOOSE_BUILTINS_TYPES_FUNC_H
namespace goose::builtins
{
extern ptr< InvocationRule >& GetFuncInvocationRule();
extern void SetupFunctionInvocationRule( Env& e );
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#ifndef GOOSE_BUILTINS_TYPES_FUNC_H
#define GOOSE_BUILTINS_TYPES_FUNC_H
namespace goose::builtins
{
extern ptr< InvocationRule >& GetFuncInvocationRule();
extern void SetupFunctionInvocationRule( Env& e );
extern void SetupFunctionTypeChecking( Env& e );
extern void SetupFunctionLowering( Env& e );
extern optional< FuncType > LowerFunctionTypeForRuntime( const Context& c, const FuncType& ft );
// Helper to provide generic param patterns for functions.
struct FuncPattern
{
|
| ︙ | ︙ |
Changes to bs/builtins/types/func/invoke.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "builtins/builtins.h"
using namespace goose::sema;
namespace goose::builtins
{
class FunctionInvocationRule : public InvocationRule
{
public:
Value resolveInvocation( const Context& c, uint32_t loc, const Value& callee, const Term& args ) const final
{
| | < > | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
#include "builtins/builtins.h"
using namespace goose::sema;
namespace goose::builtins
{
class FunctionInvocationRule : public InvocationRule
{
public:
Value resolveInvocation( const Context& c, uint32_t loc, const Value& callee, const Term& args ) const final
{
optional< TypeCheckingContext > bestTCC;
optional< Term > bestSol;
bool ambiguous = false;
auto sig = GetFuncSig( callee );
auto callPat = VEC( args, HOLE( "_"_sid ) );
auto us = FindBestTyping( sig, callPat, c );
if( holds_alternative< NoUnification >( us ) )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"function arguments mismatch." );
return PoisonValue();
}
if( holds_alternative< AmbiguousTypeCheck >( us ) )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"ambiguous function call." );
return PoisonValue();
}
auto&& [s,tcc] = get< TCSol >( us );
return invoke( c, loc, callee, args, s, tcc );
}
Value invoke( const Context& c, uint32_t loc, const Value& callee, const Term& args, const Term& unifiedCallPat, TypeCheckingContext& tcc ) const final
{
auto newCallee = prepareFunc( c, 0, callee, unifiedCallPat, tcc );
if( newCallee.isPoison() )
return PoisonValue();
auto callDecomp = Decompose( unifiedCallPat,
Vec(
SubTerm(), // args
SubTerm() // return type
|
| ︙ | ︙ | |||
69 70 71 72 73 74 75 |
}
optional< Term > getSignature( const Value& callee ) const final
{
return GetFuncSig( callee );
}
| | | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
}
optional< Term > getSignature( const Value& callee ) const final
{
return GetFuncSig( callee );
}
Value prepareFunc( const Context& c, uint32_t funcValLocation, const Value& callee, const Term& unifiedCallPat, TypeCheckingContext& tcc ) const final
{
if( IsBuiltinFunc( callee ) || IsIntrinsicFunc( callee ) )
return callee;
// TODO better description with the function's name if possible (we may need to explicitely store it in the func)
DiagnosticsContext dc( 0, true );
VerbosityContext vc( Verbosity::Normal, true );
|
| ︙ | ︙ |
Name change from bs/builtins/types/func/unify.cpp to bs/builtins/types/func/typecheck.cpp.
1 2 3 4 5 6 7 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::ir;
namespace goose::builtins
{
| | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | < | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 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 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::ir;
namespace goose::builtins
{
void SetupFunctionTypeChecking( Env& e )
{
// Default param rule: we basically treat it like a regular value.
// Things that need a more specific rule for params can override this with
// more specific pattern.
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ValueToIRExpr( ValuePattern(
TSID( param ),
ANYTERM( _ ),
ANYTERM( _ ) ) ),
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
ANYTERM( _ ),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto lhsVal = *ValuePatternFromIRExpr( lhs );
lhsVal.sort() = HOLE( "_"_sid );
co_yield Unify( ValueToIRExpr( lhsVal ), rhs, tcc );
} );
// Constant param rule
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ValueToIRExpr( ValuePattern(
TSID( constant ),
ANYTERM( _ ),
ANYTERM( _ ) ) ),
ValueToIRExpr( ValuePattern(
TSID( constant ),
ANYTERM( _ ),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto lhsVal = *ValuePatternFromIRExpr( lhs );
auto rhsVal = *ValuePatternFromIRExpr( rhs );
// Unify the types
for( auto&& [ut,tcc] : Unify( lhsVal.type(), rhsVal.type(), tcc ) )
{
// Unify the contents
for( auto&& [uv,tcc] : Unify( lhsVal.val(), rhsVal.val(), tcc ) )
{
ValuePattern result( move( ut ), move( uv ), rhsVal.locationId() );
co_yield { ValueToIRExpr( move( result ) ), tcc };
}
}
} );
auto funcTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( func ),
ANYTERM( _ ), ANYTERM( RT ), ANYTERM( P ), ANYTERM( _ ), ANYTERM( VA ) ) ) );
auto tFuncTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( texpr ), TSID( tfunc ),
ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );
// func type param / func arg
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ParamPat( funcTypePat ),
ValueToIRExpr( ValuePattern(
TSID( constant ),
funcTypePat,
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto rhsVal = *ValueFromIRExpr( rhs );
if( IsBuiltinFunc( rhsVal ) )
{
co_yield { rhs, tcc };
co_return;
}
auto wrapped = WrapWithPostprocFunc( rhs, [rhsVal]( const Term& t, const TypeCheckingContext& tcc )
-> optional< Term >
{
DiagnosticsContext dc( 0, true );
VerbosityContext vc( Verbosity::Normal, true );
auto func = CompileFunc( tcc.context(), rhsVal );
if( func.isPoison() )
return nullopt;
return ValueToIRExpr( func );
} );
co_yield { move( wrapped ), tcc };
} );
// tfunc type param / func arg
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ParamPat( move( tFuncTypePat ) ),
ValueToIRExpr( ValuePattern(
TSID( constant ),
move( funcTypePat ),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto ldecomp = Decompose( lhs,
Vec(
Lit( "value"_sid ),
SubTerm(),
SubTerm(),
SubTerm(),
Val< LocationId >()
)
);
assert( ldecomp );
auto&& [sort, type, val, locId] = *ldecomp;
auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *ValueFromIRExpr( type ) );
assert( callPat );
auto rhsVal = *ValueFromIRExpr( rhs );
auto sig = GetFuncSig( rhsVal );
for( auto&& [s, tcc] : TypeCheck( sig, *callPat, tcc ) )
{
if( IsBuiltinFunc( rhsVal ) )
{
co_yield { ValueToIRExpr( rhsVal ), tcc };
continue;
}
auto wrapped = WrapWithPostprocFunc( s, [rhsVal]( const Term& t, const TypeCheckingContext& tcc )
-> optional< Term >
{
DiagnosticsContext dc( 0, true );
VerbosityContext vc( Verbosity::Normal, true );
auto func = CompileFunc( tcc.context(), rhsVal );
if( func.isPoison() )
return nullopt;
return ValueToIRExpr( func );
} );
co_yield { move( wrapped ), tcc };
}
} );
}
}
|
Changes to bs/builtins/types/localvar/localvar.cpp.
| ︙ | ︙ | |||
123 124 125 126 127 128 129 |
// Create our arg list pattern.
auto args = VEC(
*BuildTemplateArgPattern( c, typeTExpr ),
ValueToIRExpr( ToValue( c.env()->extInitialize() ) )
);
| | | | | | 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 |
// Create our arg list pattern.
auto args = VEC(
*BuildTemplateArgPattern( c, typeTExpr ),
ValueToIRExpr( ToValue( c.env()->extInitialize() ) )
);
auto us = FindBestTyping( paramPat, args, c );
if( holds_alternative< NoUnification >( us ) )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( 0,
"variable initialization type mismatch." );
return PoisonValue();
}
if( holds_alternative< AmbiguousTypeCheck >( us ) )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( 0,
"ambiguous variable type inference." );
return PoisonValue();
}
auto&& [s,tcc] = get< TCSol >( us );
// Perform the setup of the type template expression. This will create local bindings
// for tvars ($whatever), if any, making them available for further use.
// We need to create a temporary context and adjust the identity so that the tvar
// bindings will be injected to our parent scope, rather than in our local statement scope.
auto parentIdentity = make_shared< Vector >( *get< pvec >( c.identity() ) );
parentIdentity->terms().resize( parentIdentity->terms().size() - 1 );
Context ctxt( c.env(), parentIdentity );
TemplateSetup( ctxt, tcc, typeTExpr );
auto callDecomp = Decompose( s,
Vec(
SubTerm(), // locvar
SubTerm() // initializer
)
);
|
| ︙ | ︙ |
Changes to bs/builtins/types/localvar/localvar.h.
1 2 3 4 5 6 7 8 9 10 11 |
#ifndef GOOSE_BUILTINS_TYPES_LOCALVAR_H
#define GOOSE_BUILTINS_TYPES_LOCALVAR_H
namespace goose::parse
{
class Parser;
}
namespace goose::builtins
{
extern void SetupLocalVarInvocationRule( Env& e );
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#ifndef GOOSE_BUILTINS_TYPES_LOCALVAR_H
#define GOOSE_BUILTINS_TYPES_LOCALVAR_H
namespace goose::parse
{
class Parser;
}
namespace goose::builtins
{
extern void SetupLocalVarInvocationRule( Env& e );
extern void SetupLocalVarTypeChecking( Env& e );
extern void SetupLocalVarInitialize( Env& e );
extern void SetupLocalVarDropValue( Env& e );
Value DeclareLocalVar( const Context& c, const Term& type, StringId name, const optional< Value >& initializer );
Value DeclareLocalVarWithTypeInference( Context& c, const Term& typeTExpr, StringId name, const Value& initVal );
class LocalVarType
|
| ︙ | ︙ |
Name change from bs/builtins/types/localvar/unify.cpp to bs/builtins/types/localvar/typecheck.cpp.
1 2 3 4 5 6 7 8 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::ir;
using namespace goose::llr;
namespace goose::builtins
{
| | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::ir;
using namespace goose::llr;
namespace goose::builtins
{
void SetupLocalVarTypeChecking( Env& e )
{
auto localVarPattern = GetValueType< LocalVar >( ANYTERM( _ ) );
auto refTypePattern = ValueToIRExpr(
Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), ANYTERM( _ ), ANYTERM( _ ) ) ) );
// LocalVar unification against another LocalVar: unify their types.
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ParamPat( localVarPattern ),
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
localVarPattern,
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto lvarType = *FromValue< LocalVarType >( *ValueFromIRExpr( ValuePatternFromIRExpr( lhs )->type() ) );
auto rhsVal = *ValuePatternFromIRExpr( rhs );
auto rvarType = *FromValue< LocalVarType >( *ValueFromIRExpr( rhsVal.type() ) );
for( auto&& [s, tcc] : Unify( lvarType.type(), rvarType.type(), tcc ) )
{
co_yield { ValueToIRExpr( Value( ValueToIRExpr( ToValue( LocalVarType( s ) ) ),
rhsVal.val() ) ), tcc };
}
} );
}
}
|
Changes to bs/builtins/types/overloadset/invoke.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include "builtins/builtins.h"
using namespace goose::sema;
namespace goose::builtins
{
class OverloadSetInvocationRule : public InvocationRule
{
public:
Value resolveInvocation( const Context& c, uint32_t loc, const Value& callee, const Term& args ) const final
{
auto pOvlSet = *FromValue< ptr< OverloadSet > >( callee );
| | < | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#include "builtins/builtins.h"
using namespace goose::sema;
namespace goose::builtins
{
class OverloadSetInvocationRule : public InvocationRule
{
public:
Value resolveInvocation( const Context& c, uint32_t loc, const Value& callee, const Term& args ) const final
{
auto pOvlSet = *FromValue< ptr< OverloadSet > >( callee );
optional< TypeCheckingContext > bestTCC;
optional< Term > bestSol;
const OverloadSet::Overload* bestOvl = nullptr;
bool ambiguous = false;
auto rtPat = HOLE( "_"_sid );
TypeCheckingContext tcc( c );
for( auto&& [s,ovl,tcc] : pOvlSet->typeCheck( args, rtPat, tcc ) )
{
if( tcc.numUnknownValues() )
continue;
if( bestTCC && tcc.score() < bestTCC->score() )
continue;
auto pps = Postprocess( s, tcc );
if( !pps )
continue;
if( bestTCC && tcc.score() == bestTCC->score() )
{
ambiguous = true;
continue;
}
bestTCC = tcc;
bestSol = move( *pps );
bestOvl = &ovl;
ambiguous = false;
}
if( ambiguous )
{
|
| ︙ | ︙ | |||
55 56 57 58 59 60 61 |
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"function arguments mismatch." );
return PoisonValue();
}
| | | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"function arguments mismatch." );
return PoisonValue();
}
return bestOvl->pInvRule->invoke( c, loc, *bestOvl->callee, args, *bestSol, *bestTCC );
}
};
ptr< InvocationRule >& GetOverloadSetInvocationRule()
{
static ptr< InvocationRule > pRule = make_shared< OverloadSetInvocationRule >();
return pRule;
|
| ︙ | ︙ |
Changes to bs/builtins/types/overloadset/overloadset.h.
1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_BUILTINS_TYPES_OVERLOADSET_H
#define GOOSE_BUILTINS_TYPES_OVERLOADSET_H
#include "helpers.h"
namespace goose::builtins
{
extern ptr< InvocationRule >& GetOverloadSetInvocationRule();
extern void SetupOverloadSetInvocationRule( Env& e );
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#ifndef GOOSE_BUILTINS_TYPES_OVERLOADSET_H
#define GOOSE_BUILTINS_TYPES_OVERLOADSET_H
#include "helpers.h"
namespace goose::builtins
{
extern ptr< InvocationRule >& GetOverloadSetInvocationRule();
extern void SetupOverloadSetInvocationRule( Env& e );
extern void SetupOverloadSetTypeChecking( Env& e );
extern bool IsOverloadSet( const Value& os );
}
namespace goose::ir
{
template<>
|
| ︙ | ︙ |
Name change from bs/builtins/types/overloadset/unify.cpp to bs/builtins/types/overloadset/typecheck.cpp.
1 2 3 4 5 6 7 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::ir;
namespace goose::builtins
{
| | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::ir;
namespace goose::builtins
{
void SetupOverloadSetTypeChecking( Env& e )
{
auto funcTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( func ),
ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( VA ) ) ) );
auto tFuncTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( texpr ), TSID( tfunc ),
ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );
// func type param / overloadset arg
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ParamPat( move( funcTypePat ) ),
ValueToIRExpr( ValuePattern(
TSID( constant ),
GetValueType< ptr< builtins::OverloadSet > >(),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
auto ldecomp = Decompose( lhs,
Vec(
Lit( "value"_sid ),
SubTerm(),
SubTerm(),
SubTerm(),
|
| ︙ | ︙ | |||
52 53 54 55 56 57 58 |
auto rhsVal = *ValueFromIRExpr( rhs );
auto os = *FromValue< ptr< OverloadSet > >( rhsVal );
auto rhsLocId = rhsVal.locationId();
// Create a new named hole namespace to isolate holes from the passed function from those in
// the called function.
| | | | | | | | | | | | | | | | | | | | | | | | | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 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 |
auto rhsVal = *ValueFromIRExpr( rhs );
auto os = *FromValue< ptr< OverloadSet > >( rhsVal );
auto rhsLocId = rhsVal.locationId();
// Create a new named hole namespace to isolate holes from the passed function from those in
// the called function.
auto savedRHSNamespaceIndex = tcc.RHSNamespaceIndex();
auto localNSId = tcc.newNamespaceIndex();
tcc.setRHSNamespaceIndex( localNSId );
for( auto&& [s,ovl,tcc] : os->typeCheck( argPat, rtPat, tcc.flip() ) )
{
if( !ovl.callee )
continue;
// Restore the namespace
tcc.flip();
tcc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
auto overload = ovl;
auto wrapped = WrapWithPostprocFunc( s,
[overload,localNSId,rhsLocId]( const Term& t, TypeCheckingContext tcc ) -> optional< Term >
{
tcc.setRHSNamespaceIndex( localNSId );
auto func = overload.pInvRule->prepareFunc( tcc.context(), rhsLocId, *overload.callee, t, tcc.flip() );
if( func.isPoison() )
return nullopt;
return ValueToIRExpr( move( func ) );
} );
co_yield { move( wrapped ), tcc };
}
} );
// tfunc type param / overloadset arg
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
move( tFuncTypePat ),
ANYTERM( _ ) ) ),
ValueToIRExpr( ValuePattern(
TSID( constant ),
GetValueType< ptr< builtins::OverloadSet > >(),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
auto ldecomp = Decompose( lhs,
Vec(
Lit( "value"_sid ),
SubTerm(),
SubTerm(),
SubTerm(),
Val< LocationId >()
)
);
assert( ldecomp );
auto&& [sort, type, val, locId] = *ldecomp;
auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *ValueFromIRExpr( type ) );
assert( callPat );
auto cpdecomp = Decompose( *callPat,
Vec(
SubTerm(),
SubTerm()
)
);
assert( cpdecomp );
auto&& [argPat, rtPat] = *cpdecomp;
auto rhsVal = *ValueFromIRExpr( rhs );
auto os = *FromValue< ptr< OverloadSet > >( rhsVal );
auto rhsLocId = rhsVal.locationId();
// Create a new named hole namespace to isolate holes from the called functions from those
// in the call expression.
auto savedRHSNamespaceIndex = tcc.RHSNamespaceIndex();
auto localNSId = tcc.newNamespaceIndex();
tcc.setRHSNamespaceIndex( localNSId );
for( auto&& [s,ovl,tcc] : os->typeCheck( argPat, rtPat, tcc.flip() ) )
{
if( !ovl.callee )
continue;
tcc.flip();
tcc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
auto overload = ovl;
auto wrapped = WrapWithPostprocFunc( s,
[overload,localNSId,rhsLocId]( const Term& t, TypeCheckingContext tcc ) -> optional< Term >
{
tcc.setRHSNamespaceIndex( localNSId );
auto func = overload.pInvRule->prepareFunc( tcc.context(), rhsLocId, *overload.callee, t, tcc.flip() );
if( func.isPoison() )
return nullopt;
return ValueToIRExpr( move( func ) );
} );
co_yield { move( wrapped ), tcc };
}
} );
}
}
|
Changes to bs/builtins/types/param.h.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 |
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 = ValueFromIRExpr( type ); typeVal && IsTuple( *typeVal ) )
| | < < < < < < | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
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 = ValueFromIRExpr( type ); typeVal && IsTuple( *typeVal ) )
return ValueToIRExpr( ValuePattern( HOLE( "_"_sid ), ValueToIRExpr( TupleOfTypesToTupleType( *typeVal ) ), HOLE( "_"_sid ) ) );
return ValueToIRExpr( ValuePattern( TSID( param ), forward< T >( type ), HOLE( "_"_sid ) ) );
}
}
#endif
|
Changes to bs/builtins/types/reference/reference.h.
1 2 3 4 5 |
#ifndef GOOSE_BUILTINS_TYPES_REFERENCE_H
#define GOOSE_BUILTINS_TYPES_REFERENCE_H
namespace goose::builtins
{
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 |
#ifndef GOOSE_BUILTINS_TYPES_REFERENCE_H
#define GOOSE_BUILTINS_TYPES_REFERENCE_H
namespace goose::builtins
{
extern void SetupReferenceTypeChecking( Env& e );
class ReferenceType
{
public:
template< typename T, typename B >
ReferenceType( T&& type, B&& bhv ) :
m_type( forward< T >( type ) ),
|
| ︙ | ︙ |
Name change from bs/builtins/types/reference/unify.cpp to bs/builtins/types/reference/typecheck.cpp.
1 2 3 4 5 6 7 8 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::ir;
using namespace goose::llr;
namespace goose::builtins
{
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 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::ir;
using namespace goose::llr;
namespace goose::builtins
{
void SetupReferenceTypeChecking( Env& e )
{
auto localVarPattern = GetValueType< LocalVar >( ANYTERM( _ ) );
auto refTypePattern = ValueToIRExpr(
Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), ANYTERM( _ ), ANYTERM( _ ) ) ) );
auto refTypePatternConstant = ValueToIRExpr(
Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TSID( const ), ANYTERM( _ ) ) ) );
auto refTypePatternMutable = ValueToIRExpr(
Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TSID( mut ), ANYTERM( _ ) ) ) );
auto refTypePatternTemporary = ValueToIRExpr(
Value( GetValueType< ReferenceType >(), TVEC( TSID( reference ), TSID( temporary ), ANYTERM( _ ) ) ) );
// Reference unification rule.
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ParamPat( refTypePattern ),
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
refTypePattern,
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto lRefType = *FromValue< ReferenceType >( *ValueFromIRExpr( ValuePatternFromIRExpr( lhs )->type() ) );
auto rhsVal = *ValueFromIRExpr( rhs );
auto rRefType = *FromValue< ReferenceType >( *ValueFromIRExpr( rhsVal.type() ) );
// Unify the behaviors
for( auto&& [b, tcc] : Unify( lRefType.behavior(), rRefType.behavior(), tcc ) )
{
// Unify the types
for( auto&& [t, tcc] : Unify( lRefType.type(), rRefType.type(), tcc ) )
{
co_yield { ValueToIRExpr( Value( ValueToIRExpr( ToValue( ReferenceType( t, b ) ) ),
rhsVal.llr() ) ), tcc };
}
}
}
);
// mut -> const reference unification rule.
e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,
TSID( const ),
TSID( mut ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
co_yield { TSID( const ), tcc };
}
);
// temp -> const reference unification rule.
e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,
TSID( const ),
TSID( temp ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
co_yield { TSID( const ), tcc };
}
);
// temp -> mut reference unification rule.
e.typeCheckingRuleSet()->addUnificationRule( TCRINFOS,
TSID( mut ),
TSID( temp ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
co_yield { TSID( mut ), tcc };
}
);
// Reference unification against a param (implicit dereferencing):
// Unify the referenced value with the param.
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ParamPat( ANYTERM( _ ) ),
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
refTypePattern,
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto refval = *ValueFromIRExpr( rhs );
auto ref = FromValue< Reference >( refval );
if( !ref )
co_return;
auto content = ValueToIRExpr( BuildComputedValue( ref->type().type(),
llr::Load( ref->address(), ref->type().type() ) )
.setLocationId( refval.locationId() ) );
// TypeCheck the param with the ref's content
co_yield TypeCheck( lhs, content, tcc );
} );
// LocalVar unification against a param (implicit referencing):
// Build a mutable ref value.
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ParamPat( ANYTERM( _ ) ),
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
localVarPattern,
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto ltype = ValuePatternFromIRExpr( lhs )->type();
auto lvval = *ValueFromIRExpr( rhs );
auto locvar = FromValue< LocalVar >( lvval );
if( !locvar )
co_return;
ReferenceType rt( locvar->type(), TSID( mut ) );
auto ref = ValueToIRExpr( BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),
Load( VarAddress( locvar->index() ), rt.type() ) )
.setLocationId( lvval.locationId() ) );
co_yield TypeCheck( lhs, ref, tcc );
} );
// Implicit referencing of non-variables: build a tempref
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ParamPat( refTypePattern ),
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
ANYTERM( _ ),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto rval = *ValueFromIRExpr( rhs );
ReferenceType rt( rval.type(), TSID( temp ) );
auto tempIndex = tcc.context().codeBuilder()->cfg()->getNewTemporaryIndex();
auto ref = ValueToIRExpr( BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),
Load( TemporaryAddress( tempIndex, rval ), rt.type() ) )
.setLocationId( rval.locationId() ) );
// TypeCheck the param with the ref
co_yield TypeCheck( lhs, ref, tcc );
} );
}
}
|
Name change from bs/builtins/types/runtime/unify.cpp to bs/builtins/types/runtime/typecheck.cpp.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 |
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.
| | | | | | | | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 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 |
valToLoad = ct.zext( rttype->m_numBits );
valToLoad.setIsSigned( false );
}
return TERM( valToLoad );
} );
| | | | | < | < | < < < < < < < < < < < < | | | | | | | | 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 };
} );
}
}
|
Changes to bs/builtins/types/template/instantiate.cpp.
1 2 3 4 5 6 7 8 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::sema;
using namespace goose::parse;
namespace goose::builtins
{
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::sema;
using namespace goose::parse;
namespace goose::builtins
{
Value InstantiateTFunc( const Context& c, const Value& callee, const Term& unifiedCallPat, const TypeCheckingContext& tcc )
{
auto tf = FromValue< TFunc >( callee );
assert( tf );
auto callDecomp = Decompose( unifiedCallPat,
Vec(
SubTerm(), // args
|
| ︙ | ︙ | |||
56 57 58 59 60 61 62 |
auto verifContext = c;
verifContext.setIdentity( instanceType.verifInfos()->identity() );
// Setup the template TVars binding in the verification context.
ForEachInVectorTerm( tf->type().params(), [&]( auto&& param )
{
| | | | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
auto verifContext = c;
verifContext.setIdentity( instanceType.verifInfos()->identity() );
// Setup the template TVars binding in the verification context.
ForEachInVectorTerm( tf->type().params(), [&]( auto&& param )
{
TemplateSetup( verifContext, tcc, param );
return true;
} );
auto bodyContext = c;
auto func = BuildFunc( c, instanceType, instanceIdentity, instanceParams, tf->toks(), bodyContext );
ForEachInVectorTerm( tf->type().params(), [&]( auto&& param )
{
TemplateSetup( bodyContext, tcc, param );
return true;
} );
instanceFunc = ToValue( func );
// TODO: better description including the function's name
DiagnosticsContext dc( callee.locationId(), "In the template function declared here.", false );
|
| ︙ | ︙ |
Changes to bs/builtins/types/template/invoke.cpp.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 |
{
public:
Value resolveInvocation( const Context& c, uint32_t loc, const Value& callee, const Term& args ) const final
{
auto tf = FromValue< TFunc >( callee );
assert( tf );
| | | | | | | | | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
{
public:
Value resolveInvocation( const Context& c, uint32_t loc, const Value& callee, const Term& args ) const final
{
auto tf = FromValue< TFunc >( callee );
assert( tf );
optional< TypeCheckingContext > bestTCC;
optional< Term > bestSol;
bool ambiguous = false;
auto callPat = VEC( args, HOLE( "_"_sid ) );
auto us = FindBestTyping( tf->signature(), callPat, c );
if( holds_alternative< NoUnification >( us ) )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"template function arguments mismatch." );
return PoisonValue();
}
if( holds_alternative< AmbiguousTypeCheck >( us ) )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"ambiguous function call." );
return PoisonValue();
}
auto&& [s,tcc] = get< TCSol >( us );
return invoke( c, loc, callee, args, s, tcc );
}
Value invoke( const Context& c, uint32_t loc, const Value& callee, const Term& args, const Term& unifiedCallPat, TypeCheckingContext& tcc ) const final
{
auto callDecomp = Decompose( unifiedCallPat,
Vec(
SubTerm(), // args
SubTerm() // return type
)
);
auto&& [unifiedArgs, unifiedRType] = *callDecomp;
DiagnosticsContext dc( loc, loc ? "Called here." : "", false );
auto instanceFunc = InstantiateTFunc( c, callee, unifiedCallPat, tcc );
if( instanceFunc.isPoison() )
return PoisonValue();
return GetFuncInvocationRule()->resolveInvocation( c, loc, instanceFunc, args );
}
optional< Term > getSignature( const Value& callee ) const final
{
auto tfuncVal = FromValue< TFunc >( callee );
assert( tfuncVal );
return tfuncVal->signature();
}
Value prepareFunc( const Context& c, uint32_t funcValLocation, const Value& callee, const Term& unifiedCallPat, TypeCheckingContext& tcc ) const final
{
DiagnosticsContext dc( funcValLocation, funcValLocation ? "Instantiated here." : "", false );
return InstantiateTFunc( c, callee, unifiedCallPat, tcc );
}
};
ptr< InvocationRule >& GetTFuncInvocationRule()
{
static ptr< InvocationRule > pRule = make_shared< TemplateFunctionInvocationRule >();
return pRule;
|
| ︙ | ︙ |
Changes to bs/builtins/types/template/rules.cpp.
1 2 3 4 |
#include "builtins/builtins.h"
namespace goose::builtins
{
| | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#include "builtins/builtins.h"
namespace goose::builtins
{
static void setupTVar( const Context& c, TypeCheckingContext& tcc, const StringId& name )
{
// By convention, the callee's pattern will always be on the lhs of the unification,
// which means that the captures TVars will always be in the "left hand side" namespace
// of the unification context.
auto varIndex = tcc.getLHSHoleIndex( name );
if( varIndex == TypeCheckingContext::InvalidIndex )
return;
// The captured value may be missing: unresolved TVars make sense when second order polymorphism
// is involved.
// For instance, if we have a function whose signature is this:
// void( void( $T foo ) $F ), $T can't be resolved, as the function that we pass
// may be polymorphic over this parameter. In this case, it doesn't make sense to use $T inside of the
// function anyway.
// TODO: in this case setup a special value provider for $T that will emit a specific diagnostic
// to explain why $T doesn't have a value.
auto capturedValue = tcc.getValue( varIndex );
if( !capturedValue )
return;
auto valueToStore = Substitute( *capturedValue, tcc );
auto captureIdentity = AppendToVectorTerm( c.identity(),
TERM( "$$"_sid ), TERM( name ) );
c.env()->storeValue( captureIdentity, ANYTERM( _ ), valueToStore );
// If the same TVar was present multiple time in the
// function's signature, its setup will be called multiple time, so
// to make sure we only store it once, erase the variable's name from
// the unification context.
tcc.eraseLHSName( name );
}
class DeclTemplateRule : public TemplateRule
{
optional< Term > buildSignature( const Context& c, const Value& val ) const final
{
auto decl = FromValue< Decl >( val );
|
| ︙ | ︙ | |||
76 77 78 79 80 81 82 |
{
auto tnd = FromValue< TNamedDecl >( param );
assert( tnd );
return ToValue( Decl( arg.type(), tnd->name() ) );
}
| | | | | | | | | | | 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 |
{
auto tnd = FromValue< TNamedDecl >( param );
assert( tnd );
return ToValue( Decl( arg.type(), tnd->name() ) );
}
void setup( const Context& c, TypeCheckingContext& tcc, const Value& val ) const final
{
auto tnd = FromValue< TNamedDecl >( val );
assert( tnd );
TemplateSetup( c, tcc, tnd->type() );
}
optional< Term > buildArgPattern( const Context& c, const Value& val ) const final
{
auto tnd = FromValue< TNamedDecl >( val );
assert( tnd );
auto typeSig = BuildTemplateArgPattern( c, tnd->type() );
if( !typeSig )
return nullopt;
return ValueToIRExpr( ValuePattern( TSID( computed ), *typeSig, TERM( ptr< void >() ) ) );
}
};
class TDeclTemplateRule : public TemplateRule
{
optional< Term > buildSignature( const Context& c, const Value& val ) const final
{
return ValueToIRExpr( val );
}
Value buildParamDecl( const Context& c, const Value& param, const Value& arg ) const final
{
return arg;
}
void setup( const Context& c, TypeCheckingContext& tcc, const Value& val ) const final
{
auto tdecl = FromValue< TDecl >( val );
assert( tdecl );
TemplateSetup( c, tcc, tdecl->type() );
setupTVar( c, tcc, tdecl->name() );
}
optional< Term > buildArgPattern( const Context& c, const Value& val ) const final
{
return buildSignature( c, val );
}
};
class TVarTemplateRule : public TemplateRule
{
optional< Term > buildSignature( const Context& c, const Value& val ) const final
{
auto tvar = FromValue< TVar >( val );
assert( tvar );
return HOLE( tvar->name() );
}
Value buildParamDecl( const Context& c, const Value& param, const Value& arg ) const final
{
return arg;
}
void setup( const Context& c, TypeCheckingContext& tcc, const Value& val ) const final
{
auto tvar = FromValue< TVar >( val );
assert( tvar );
setupTVar( c, tcc, tvar->name() );
}
optional< Term > buildArgPattern( const Context& c, const Value& val ) const final
{
return buildSignature( c, val );
}
};
|
| ︙ | ︙ | |||
168 169 170 171 172 173 174 |
{
if( auto s = BuildTemplateSignature( c, x ) )
v->append( move( *s ) );
else
v->append( x );
}
| | | | | 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 |
{
if( auto s = BuildTemplateSignature( c, x ) )
v->append( move( *s ) );
else
v->append( x );
}
return v;
}
Value buildParamDecl( const Context& c, const Value& param, const Value& arg ) const final
{
return arg;
}
void setup( const Context& c, TypeCheckingContext& tcc, const Value& val ) const final
{
auto tvec = FromValue< TVec >( val );
assert( tvec );
for( auto&& x : tvec->content()->terms() )
TemplateSetup( c, tcc, x );
}
optional< Term > buildArgPattern( const Context& c, const Value& val ) const final
{
auto tvec = FromValue< TVec >( val );
assert( tvec );
|
| ︙ | ︙ | |||
222 223 224 225 226 227 228 |
}
Value buildParamDecl( const Context& c, const Value& val, const Value& arg ) const final
{
return val;
}
| | | | | | | | | 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
}
Value buildParamDecl( const Context& c, const Value& val, const Value& arg ) const final
{
return val;
}
void setup( const Context& c, TypeCheckingContext& tcc, const Value& val ) const final
{
TemplateSetup( c, tcc, val.type() );
TemplateSetup( c, tcc, val.val() );
}
optional< Term > buildArgPattern( const Context& c, const Value& val ) const final
{
return buildSignature( c, val );
}
};
class TFuncTypeTemplateRule : public TemplateRule
{
optional< Term > buildSignature( const Context& c, const Value& val ) const final
{
return ValueToIRExpr( val );
}
Value buildParamDecl( const Context& c, const Value& param, const Value& arg ) const final
{
return arg;
}
void setup( const Context& c, TypeCheckingContext& tcc, const Value& val ) const final
{
auto tft = FromValue< TFuncType >( val );
assert( tft );
ForEachInVectorTerm( tft->params(), [&]( auto&& param )
{
TemplateSetup( c, tcc, param );
return true;
} );
TemplateSetup( c, tcc, tft->returnType() );
}
optional< Term > buildArgPattern( const Context& c, const Value& val ) const final
{
return buildSignature( c, val );
}
};
|
| ︙ | ︙ |
Name change from bs/builtins/types/template/uni-tdecl.cpp to bs/builtins/types/template/tc-tdecl.cpp.
1 2 3 4 5 6 7 8 9 |
#include "builtins/builtins.h"
namespace goose::builtins
{
Term BuildArgPatternFromTDecl( const TDecl& td )
{
return ValueToIRExpr( ValuePattern( HOLE( "_"_sid ), td.type(), 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 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 |
#include "builtins/builtins.h"
namespace goose::builtins
{
Term BuildArgPatternFromTDecl( const TDecl& td )
{
return ValueToIRExpr( ValuePattern( HOLE( "_"_sid ), td.type(), HOLE( "_"_sid ) ) );
}
TCGen UnifyTDecl( const Term& lhs, const Term& rhs, TypeCheckingContext tcc )
{
auto tdecl = FromValue< TDecl >( *ValueFromIRExpr( lhs ) );
assert( tdecl );
auto tdeclHole = HOLE( tdecl->name() );
auto pat = ValueToIRExpr( Value( tdecl->type(), HOLE( "_"_sid ) ) );
// We are replacing lhs with a different terms and re-unifying,
// so update the complexity accordingly. The structure of the tdecl
// shouldn't count, only its pattern.
tcc.subComplexity( GetComplexity( lhs ) );
tcc.addComplexity( GetComplexity( pat ) );
for( auto&& [s,tcc] : Unify( pat, rhs, tcc ) )
{
// We need to unify the result with a hole named after the decl. However, since both sides of
// this unification orignally appeared on the LHS, we need to setup RHS to alias the LHS namespace for this.
auto savedRHSNamespaceIndex = tcc.RHSNamespaceIndex();
tcc.setRHSNamespaceIndex( tcc.LHSNamespaceIndex() );
tcc.subComplexity( GetComplexity( pat ) + GetComplexity( rhs ) );
tcc.addComplexity( GetComplexity( s ) );
for( auto&& [s,tcc] : TypeCheck( s, tdeclHole, tcc ) )
{
tcc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
co_yield { s, tcc };
}
}
}
void SetupTDeclTypeChecking( Env& e )
{
auto tDeclPat = ValueToIRExpr( Value( GetValueType< TDecl >(), VEC( ANYTERM( _ ), ANYTERM( _ ) ) ) );
e.typeCheckingRuleSet()->addHalfUnificationRule( TCRINFOS, tDeclPat,
[]( const Term& lhs, const TypeCheckingContext& c ) -> TCGen
{
auto tdecl = FromValue< TDecl >( *ValueFromIRExpr( lhs ) );
assert( tdecl );
return HalfUnify( tdecl->type(), c );
} );
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS, tDeclPat, ANYTERM( _ ), UnifyTDecl );
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS, tDeclPat, tDeclPat, UnifyTDecl );
// tfunc tdecl param / tfunc arg
auto tFuncTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( texpr ), TSID( tfunc ),
ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );
auto tDeclTFuncPat = ParamPat( GetValueType< TDecl >(), VEC( tFuncTypePat, ANYTERM( _ ) ) );
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
tDeclTFuncPat,
ValueToIRExpr( ValuePattern(
TSID( constant ),
move( tFuncTypePat ),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
auto tdecl = FromValue< TDecl >( *ValueFromIRExpr( lhs ) );
assert( tdecl );
auto tfuncType = FromValue< TFuncType >( *ValueFromIRExpr( tdecl->type() ) );
assert( tfuncType );
auto callPat = BuildArgPatternFromTDecl( *tdecl );
auto tdeclHole = HOLE( tdecl->name() );
auto rhsVal = *ValueFromIRExpr( rhs );
auto constraintPat = BuildTFuncSignature( tcc.context(), *tfuncType );
assert( constraintPat );
ConstrainedFunc cfunc( *constraintPat, GetTFuncInvocationRule(), rhsVal );
auto cFuncTerm = ValueToIRExpr( ToValue( move( cfunc ) ) );
// Create a new named hole namespace to isolate holes from the passed function from those in
// the called function.
auto savedRHSNamespaceIndex = tcc.RHSNamespaceIndex();
tcc.setRHSNamespaceIndex( tcc.newNamespaceIndex() );
auto oldValueRequired = tcc.isValueResolutionRequired();
tcc.setValueResolutionRequired( false );
// We are replacing lhs with a different terms and re-unifying,
// so update the complexity accordingly. The structure of the tdecl
// shouldn't count, only its pattern.
tcc.subComplexity( GetComplexity( lhs ) );
tcc.addComplexity( GetComplexity( callPat ) );
for( auto&& [s, tcc] : TypeCheck( callPat, rhs, tcc ) )
{
// Restore the namespace
tcc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
tcc.setValueResolutionRequired( oldValueRequired );
// We need to unify the result with a hole named after the decl. However, since both sides of
// this unification orignally appeared on the LHS, we need to setup RHS to alias the LHS namespace for this.
tcc.setRHSNamespaceIndex( tcc.LHSNamespaceIndex() );
for( auto&& [s,tcc] : Unify( cFuncTerm, tdeclHole, tcc ) )
{
tcc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
co_yield { s, tcc };
}
}
} );
// tfunc tdecl param / overloadset arg
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
move( tDeclTFuncPat ),
ValueToIRExpr( ValuePattern(
TSID( constant ),
GetValueType< ptr< builtins::OverloadSet > >(),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
auto tdecl = FromValue< TDecl >( *ValueFromIRExpr( lhs ) );
assert( tdecl );
auto tfuncType = FromValue< TFuncType >( *ValueFromIRExpr( tdecl->type() ) );
assert( tfuncType );
auto callPat = BuildArgPatternFromTDecl( *tdecl );
auto tdeclHole = HOLE( tdecl->name() );
auto rhsVal = *ValueFromIRExpr( rhs );
auto constraintPat = BuildTFuncSignature( tcc.context(), *tfuncType );
assert( constraintPat );
ConstrainedFunc cfunc( *constraintPat, GetOverloadSetInvocationRule(), rhsVal );
auto cFuncTerm = ValueToIRExpr( ToValue( move( cfunc ) ) );
// Create a new named hole namespace to isolate holes from the passed function from those in
// the called function.
auto savedRHSNamespaceIndex = tcc.RHSNamespaceIndex();
tcc.setRHSNamespaceIndex( tcc.newNamespaceIndex() );
auto oldValueRequired = tcc.isValueResolutionRequired();
tcc.setValueResolutionRequired( false );
// We are replacing lhs with a different terms and re-unifying,
// so update the complexity accordingly. The structure of the tdecl
// shouldn't count, only its pattern.
tcc.subComplexity( GetComplexity( lhs ) );
tcc.addComplexity( GetComplexity( callPat ) );
for( auto&& [s, tcc] : TypeCheck( callPat, rhs, tcc ) )
{
// Restore the namespace
tcc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
tcc.setValueResolutionRequired( oldValueRequired );
// We need to unify the result with a hole named after the decl. However, since both sides of
// this unification orignally appeared on the LHS, we need to setup RHS to alias the LHS namespace for this.
tcc.setRHSNamespaceIndex( tcc.LHSNamespaceIndex() );
for( auto&& [s,tcc] : Unify( cFuncTerm, tdeclHole, tcc ) )
{
tcc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
co_yield { s, tcc };
}
}
} );
}
}
|
Changes to bs/builtins/types/template/tdecl.h.
1 2 3 4 5 |
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TDECL_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TDECL_H
namespace goose::builtins
{
| | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TDECL_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TDECL_H
namespace goose::builtins
{
extern void SetupTDeclTypeChecking( Env& e );
extern TCGen UnifyTDecl( const Term& lhs, const Term& rhs, TypeCheckingContext c );
extern Term BuildArgPatternFromTDecl( const Term& td );
class TDecl
{
public:
template< typename T >
TDecl( T&& type, const StringId& name ) :
|
| ︙ | ︙ |
Changes to bs/builtins/types/template/tfunc.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TFUNC_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TFUNC_H
namespace goose::builtins
{
extern ptr< InvocationRule >& GetTFuncInvocationRule();
extern void SetupTemplateFunctionInvocationRule( Env& e );
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TFUNC_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TFUNC_H
namespace goose::builtins
{
extern ptr< InvocationRule >& GetTFuncInvocationRule();
extern void SetupTemplateFunctionInvocationRule( Env& e );
extern void SetupTemplateFunctionTypeChecking( Env& e );
class TFunc
{
public:
template< typename T, typename S, typename I, typename V >
TFunc( T&& type, S&& signature, I&& identity, V&& toks ) :
m_type( forward< T >( type ) ),
|
| ︙ | ︙ | |||
26 27 28 29 30 31 32 |
private:
TFuncType m_type;
Term m_signature;
Term m_identity;
ptr< void > m_toks;
};
| | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
private:
TFuncType m_type;
Term m_signature;
Term m_identity;
ptr< void > m_toks;
};
extern Value InstantiateTFunc( const Context& c, const Value& callee, const Term& unifiedCallPat, const TypeCheckingContext& tcc );
}
namespace goose::ir
{
template<>
struct Bridge< builtins::TFunc >
{
|
| ︙ | ︙ |
Name change from bs/builtins/types/template/unify.cpp to bs/builtins/types/template/typecheck.cpp.
1 2 3 4 5 6 7 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::ir;
namespace goose::builtins
{
| | | | | | | | | | | | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 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 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::ir;
namespace goose::builtins
{
void SetupTemplateFunctionTypeChecking( Env& e )
{
auto funcTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( func ),
ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( VA ) ) ) );
auto tFuncTypePat = ValueToIRExpr( Value( TypeType(), VEC( TSID( texpr ), TSID( tfunc ),
ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );
// func type param / tfunc arg
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ParamPat( move( funcTypePat ) ),
ValueToIRExpr( ValuePattern(
TSID( constant ),
tFuncTypePat,
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
auto ldecomp = Decompose( lhs,
Vec(
Lit( "value"_sid ),
SubTerm(),
SubTerm(),
SubTerm(),
Val< LocationId >()
)
);
assert( ldecomp );
auto&& [sort, type, val, locId] = *ldecomp;
auto callPat = BuildCallPatternFromFuncType( *ValueFromIRExpr( type ) );
auto rhsVal = *ValueFromIRExpr( rhs );
auto tf = *FromValue< TFunc >( rhsVal );
// Create a new named hole namespace to isolate holes from the passed function from those in
// the called function.
auto savedRHSNamespaceIndex = tcc.RHSNamespaceIndex();
auto localNSId = tcc.newNamespaceIndex();
tcc.setRHSNamespaceIndex( localNSId );
for( auto&& [s, tcc] : TypeCheck( tf.signature(), callPat, tcc ) )
{
// Restore the namespace
tcc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
auto wrapped = WrapWithPostprocFunc( s, [rhsVal,localNSId]( const Term& t, TypeCheckingContext tcc )
-> optional< Term >
{
tcc.setRHSNamespaceIndex( localNSId );
DiagnosticsContext dc( rhsVal.locationId(), rhsVal.locationId() ? "Instantiated here." : "", false );
auto ifunc = InstantiateTFunc( tcc.context(), rhsVal, t, tcc.flip() );
if( ifunc.isPoison() )
return nullopt;
return ValueToIRExpr( ifunc );
} );
co_yield { move( wrapped ), tcc };
}
} );
// tfunc type param / tfunc arg
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ParamPat( tFuncTypePat ),
ValueToIRExpr( ValuePattern(
TSID( constant ),
move( tFuncTypePat ),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
auto ldecomp = Decompose( lhs,
Vec(
Lit( "value"_sid ),
SubTerm(),
SubTerm(),
SubTerm(),
Val< LocationId >()
)
);
assert( ldecomp );
auto&& [sort, type, val, locId] = *ldecomp;
auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *ValueFromIRExpr( type ) );
assert( callPat );
auto rhsVal = *ValueFromIRExpr( rhs );
auto tf = *FromValue< TFunc >( rhsVal );
// Create a new named hole namespace to isolate holes from the passed function from those in
// the called function.
auto savedRHSNamespaceIndex = tcc.RHSNamespaceIndex();
auto localNSId = tcc.newNamespaceIndex();
tcc.setRHSNamespaceIndex( localNSId );
for( auto&& [s, tcc] : TypeCheck( tf.signature(), *callPat, tcc ) )
{
// Restore the namespace
tcc.setRHSNamespaceIndex( savedRHSNamespaceIndex );
auto wrapped = WrapWithPostprocFunc( s, [rhsVal,localNSId]( const Term& t, TypeCheckingContext tcc )
-> optional< Term >
{
tcc.setRHSNamespaceIndex( localNSId );
DiagnosticsContext dc( rhsVal.locationId(), rhsVal.locationId() ? "Instantiated here." : "", false );
auto ifunc = InstantiateTFunc( tcc.context(), rhsVal, t, tcc.flip() );
if( ifunc.isPoison() )
return nullopt;
return ValueToIRExpr( ifunc );
} );
co_yield { move( wrapped ), tcc };
}
} );
}
}
|
Changes to bs/builtins/types/tuple/tuple.h.
1 2 3 4 5 |
#ifndef GOOSE_BUILTINS_TYPES_TUPLE_H
#define GOOSE_BUILTINS_TYPES_TUPLE_H
namespace goose::builtins
{
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 |
#ifndef GOOSE_BUILTINS_TYPES_TUPLE_H
#define GOOSE_BUILTINS_TYPES_TUPLE_H
namespace goose::builtins
{
extern void SetupTupleTypeChecking( Env& e );
extern void SetupTupleInitialize( Env& e );
extern void SetupTupleDestroyValue( Env& e );
extern void SetupTupleDropValue( Env& e );
extern void SetupTupleLowering( Env& e );
extern Value MkTupleType( const Term& state, const Term& types );
extern Value TupleOfTypesToTupleType( const Value& tup );
|
| ︙ | ︙ |
Name change from bs/builtins/types/tuple/unify.cpp to bs/builtins/types/tuple/typecheck.cpp.
1 2 3 4 5 6 7 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::ir;
namespace goose::builtins
{
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 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 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::ir;
namespace goose::builtins
{
TCGen UnifyConstantTuple( 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 = ValuePatternFromIRExpr( s );
assert( val );
auto newOut = AppendToTuple( out, *val );
if( index == ( tupSize - 1 ) )
co_yield { ValueToIRExpr( newOut ), tcc };
else
co_yield UnifyConstantTuple( tcc, tupType, tupArg, index + 1, newOut );
}
}
TCGen UnifyComputedTuple( const TypeCheckingContext& tcc, const Value& tupType, const Value& tupArg, const llr::Address& tupAddr, uint32_t index, const Value& out )
{
auto param = ParamPat( GetTupleTypeElement( tupType, index ) );
auto argType = GetTupleElementType( tupArg, index );
ReferenceType rt( argType, TSID( const ) );
auto argAddr = tupAddr;
argAddr.appendToPath( index );
auto argRef = ValueToIRExpr( BuildComputedValue( ValueToIRExpr( ToValue( rt ) ),
llr::Load( move( argAddr ), argType ) ) );
auto tupSize = TupleTypeSize( tupType );
for( auto&& [s,tcc] : TypeCheck( param, argRef, tcc ) )
{
auto val = ValuePatternFromIRExpr( s );
assert( val );
auto newOut = AppendToTuple( out, *val );
if( index == ( tupSize - 1 ) )
co_yield { ValueToIRExpr( newOut ), tcc };
else
co_yield UnifyComputedTuple( tcc, tupType, tupArg, tupAddr, index + 1, newOut );
}
}
void SetupTupleTypeChecking( Env& e )
{
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
ValueToIRExpr( MkTupleType( ANYTERM( _ ), VECOFLENGTH( L ) ) ),
ANYTERM( _ ) ) ),
ValueToIRExpr( ValuePattern(
TSID( constant ),
ValueToIRExpr( MkTupleType( ANYTERM( _ ), VECOFLENGTH( L ) ) ),
VECOFLENGTH( L ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto ltup = ValuePatternFromIRExpr( lhs );
auto rtup = ValueFromIRExpr( rhs );
if( !ltup || !rtup )
co_return;
auto tupType = *ValueFromIRExpr( ltup->type() );
assert( TupleTypeSize( tupType ) == TupleSize( *rtup ) );
co_yield UnifyConstantTuple( tcc, tupType, *rtup, 0, EmptyTuple() );
} );
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
ValueToIRExpr( MkTupleType( ANYTERM( _ ), VECOFLENGTH( L ) ) ),
ANYTERM( _ ) ) ),
ValueToIRExpr( ValuePattern(
TSID( computed ),
ValueToIRExpr( MkTupleType( ANYTERM( _ ), VECOFLENGTH( L ) ) ),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto ltup = ValuePatternFromIRExpr( lhs );
auto rtup = ValueFromIRExpr( rhs );
if( !ltup || !rtup || !tcc.context().codeBuilder() )
co_return;
auto tupType = *ValueFromIRExpr( ltup->type() );
assert( TupleTypeSize( tupType ) == TupleTypeSize( *ValueFromIRExpr( rtup->type() ) ) );
auto tempIndex = tcc.context().codeBuilder()->cfg()->getNewTemporaryIndex();
llr::Address addr( llr::TemporaryAddress( tempIndex, *rtup ) );
co_yield UnifyComputedTuple( tcc, tupType, *rtup, addr, 0, EmptyTuple() );
} );
// 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,
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
ANYTERM( _ ),
ANYTERM( _ ) ) ),
ValueToIRExpr( ValuePattern(
TSID( constant ),
ValueToIRExpr( MkTupleType( ANYTERM( _ ), VEC( ANYTERM( _ ) ) ) ),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto tup = ValueFromIRExpr( rhs );
if( !tup )
co_return;
const auto& vec1 = *get< pvec >( tup->val() );
co_yield TypeCheck( lhs, vec1[0], tcc );
} );
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
ValueToIRExpr( MkTupleType( ANYTERM( _ ), VEC( ANYTERM( _ ) ) ) ),
ANYTERM( _ ) ) ),
ValueToIRExpr( ValuePattern(
TSID( constant ),
ANYTERM( _ ),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto tup = ValueFromIRExpr( lhs );
if( !tup )
co_return;
const auto& vec1 = *get< pvec >( tup->val() );
// In some cases, the tuple may not be an actual tuple value but merely
// a pattern for a tuple value, with a hole instead of its content vector.
// If it happens, since that hole is represented as a vector, we'll have a longer than
// 1 vector here. But this means we can't extract the first element anyway (since it
// doesn't really exist yet), so just yield the tuple as is.
if( vec1.length().minLength() != 1 )
{
co_yield { lhs, tcc };
co_return;
}
co_yield TypeCheck( vec1[0], rhs, tcc );
} );
// When unifying a tuple against a value whose type is a hole, we don't want
// the above rule to apply (ie we don't want 1 element tuples bound to generic
// template parameters to be peeled off). So here's a rule for this case which
// leaves the tuple unchanged. It will take priority over the above rule in
// those cases because it matches a more specific pattern.
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
ValueToIRExpr( ValuePattern(
ANYTERM( _ ),
ValueToIRExpr( MkTupleType( ANYTERM( _ ), VEC( ANYTERM( _ ) ) ) ),
ANYTERM( _ ) ) ),
ValueToIRExpr( ValuePattern( ANYTERM( _ ), HolePattern(), ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
co_yield { lhs, tcc };
} );
}
}
|
Changes to bs/builtins/types/types.cpp.
1 2 3 4 5 6 7 8 |
#include "builtins/builtins.h"
namespace goose::builtins
{
void SetupBuiltinTypes( Env& e )
{
SetupBasicTypes( e );
| | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include "builtins/builtins.h"
namespace goose::builtins
{
void SetupBuiltinTypes( Env& e )
{
SetupBasicTypes( e );
SetupTupleTypeChecking( e );
SetupFunctionInvocationRule( e );
SetupFunctionTypeChecking( e );
SetupFunctionLowering( e );
SetupTemplateRules( e );
SetupTemplateFunctionInvocationRule( e );
SetupTemplateFunctionTypeChecking( e );
SetupTDeclTypeChecking( e );
SetupOverloadSetInvocationRule( e );
SetupOverloadSetTypeChecking( e );
SetupConstrainedFuncInvocationRule( e );
SetupConstrainedFuncTypeChecking( e );
SetupLocalVarDropValue( e );
SetupLocalVarTypeChecking( e );
SetupLocalVarInvocationRule( e );
SetupReferenceTypeChecking( e );
SetupRuntimeTypes( e );
SetupInitialize( e );
SetupDestroyValue( e );
SetupDropValue( e );
SetupLower( e );
|
| ︙ | ︙ |
Changes to bs/sema/env.cpp.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
uint32_t Env::ms_nextUniqueId = 0;
// Env just instantiates the builtin unification rule set for now. The rule set will
// need to be extendable by compile time code later on.
Env::Env() :
m_templateRuleSet( make_shared< TemplateRuleSet >() ),
m_invocationRuleSet( make_shared< InvocationRuleSet >() ),
| | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
uint32_t Env::ms_nextUniqueId = 0;
// Env just instantiates the builtin unification rule set for now. The rule set will
// need to be extendable by compile time code later on.
Env::Env() :
m_templateRuleSet( make_shared< TemplateRuleSet >() ),
m_invocationRuleSet( make_shared< InvocationRuleSet >() ),
m_unificationRuleSet( make_shared< TypeCheckingRuleSet >() ),
m_memManager( make_shared< CTMemoryManager >() )
{}
Env::~Env() {}
void Env::storeValue( const Term& idPat, const Term& contextIdPat, const Term& val )
{
|
| ︙ | ︙ |
Changes to bs/sema/env.h.
1 2 3 4 5 6 7 8 9 10 11 12 |
#ifndef GOOSE_SEMA_ENV_H
#define GOOSE_SEMA_ENV_H
namespace llvm
{
class LLVMContext;
}
namespace goose::sema
{
class TemplateRuleSet;
class InvocationRuleSet;
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#ifndef GOOSE_SEMA_ENV_H
#define GOOSE_SEMA_ENV_H
namespace llvm
{
class LLVMContext;
}
namespace goose::sema
{
class TemplateRuleSet;
class InvocationRuleSet;
class TypeCheckingRuleSet;
class OverloadSet;
class Env : public enable_shared_from_this< Env >
{
public:
Env();
~Env();
|
| ︙ | ︙ | |||
34 35 36 37 38 39 40 |
auto valueStoreVersion() const { return m_valueStoreVersion; }
void addVisibilityRule( const Term& toImport, const Term& destination, bool transitive = true );
const auto& templateRuleSet() const { return m_templateRuleSet; }
const auto& invocationRuleSet() const { return m_invocationRuleSet; }
| | | | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
auto valueStoreVersion() const { return m_valueStoreVersion; }
void addVisibilityRule( const Term& toImport, const Term& destination, bool transitive = true );
const auto& templateRuleSet() const { return m_templateRuleSet; }
const auto& invocationRuleSet() const { return m_invocationRuleSet; }
const auto& typeCheckingRuleSet() const { return m_unificationRuleSet; }
auto& templateRuleSet() { return m_templateRuleSet; }
auto& invocationRuleSet() { return m_invocationRuleSet; }
auto& typeCheckingRuleSet() { return m_unificationRuleSet; }
static auto NewUniqueId() { return ms_nextUniqueId++; }
auto& extDropValue() { return m_extDropValue; }
const auto& extDropValue() const { return m_extDropValue; }
auto& extDestroyValue() { return m_extDestroyValue; }
|
| ︙ | ︙ | |||
74 75 76 77 78 79 80 |
return ms_llrFuncs.back();
}
private:
Trie< ptr< ValueProvider > > m_valueStore;
ptr< TemplateRuleSet > m_templateRuleSet;
ptr< InvocationRuleSet > m_invocationRuleSet;
| | | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
return ms_llrFuncs.back();
}
private:
Trie< ptr< ValueProvider > > m_valueStore;
ptr< TemplateRuleSet > m_templateRuleSet;
ptr< InvocationRuleSet > m_invocationRuleSet;
ptr< TypeCheckingRuleSet > m_unificationRuleSet;
ptr< OverloadSet > m_extDropValue;
ptr< OverloadSet > m_extDestroyValue;
ptr< OverloadSet > m_extInitialize;
ptr< OverloadSet > m_extLowerTypeForRuntime;
ptr< OverloadSet > m_extLowerConstantForRuntime;
|
| ︙ | ︙ |
Changes to bs/sema/inv-ruleset.h.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 |
virtual bool canBeInvoked( const Context& c, const Value& callee ) const
{
return true;
}
virtual Value resolveInvocation( const Context& c, uint32_t locationId, const Value& callee, const Term& args ) const = 0;
| | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
virtual bool canBeInvoked( const Context& c, const Value& callee ) const
{
return true;
}
virtual Value resolveInvocation( const Context& c, uint32_t locationId, const Value& callee, const Term& args ) const = 0;
virtual Value invoke( const Context& c, uint32_t locationId, const Value& callee, const Term& args, const Term& unifiedCallPat, TypeCheckingContext& tcc ) const
{
return PoisonValue();
}
virtual optional< Term > getSignature( const Value& callee ) const
{
return nullopt;
}
virtual Value prepareFunc( const Context& c, uint32_t funcValLocation, const Value& callee, const Term& unifiedCallPat, TypeCheckingContext& tcc ) const
{
return PoisonValue();
}
};
class InvocationRuleSet
{
|
| ︙ | ︙ |
Changes to bs/sema/meson.build.
1 2 3 4 |
goose_sema = library( 'goose-sema',
'env.cpp',
'hole.cpp',
| | | > < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
goose_sema = library( 'goose-sema',
'env.cpp',
'hole.cpp',
'tc-context.cpp',
'tc-ruleset.cpp',
'tc-postproc.cpp',
'uni-basicrules.cpp',
'uni-holes.cpp',
'uni-quote.cpp',
'typecheck.cpp',
'substitute.cpp',
'postprocess.cpp',
'inv-ruleset.cpp',
'invocation.cpp',
'tpl-ruleset.cpp',
|
| ︙ | ︙ |
Changes to bs/sema/overloadset.cpp.
| ︙ | ︙ | |||
39 40 41 42 43 44 45 |
return { pInvRule, callee };
} );
} );
return success;
}
| | | | | | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
return { pInvRule, callee };
} );
} );
return success;
}
OverloadSet::TCGen OverloadSet::typeCheck( const Term& argsPat, const Term& rtPat, const TypeCheckingContext& tcc ) const
{
auto argDecomp = Decompose( argsPat,
Val< pvec >()
);
if( !argDecomp )
co_return;
for( auto&& [paramsVec,uniParamsVec,rtTrie,tcc] : m_trie->typeCheck( *argDecomp->get(), tcc ) )
{
auto params = TERM( make_shared< Vector >( paramsVec ) );
auto uniParams = TERM( make_shared< Vector >( uniParamsVec ) );
for( auto&& [rt,ovl] : Enumerate( rtTrie ) )
{
auto localC = tcc;
localC.addComplexity( GetComplexity( rt ) + GetComplexity( rtPat ) );
for( auto&& [uniRt,tcc] : Unify( rt, rtPat, localC ) )
{
auto uniCall = TERM( Vector::Make( uniParams, uniRt ) );
co_yield { move( uniCall ), ovl, tcc };
}
}
}
}
|
Changes to bs/sema/overloadset.h.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 |
{}
const auto& identity() const { return m_identity; }
bool add( const Env& e, const Value& callee );
bool add( const Env& e, const Value& callee, const ptr< InvocationRule >& pInvRule );
| | | | | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
{}
const auto& identity() const { return m_identity; }
bool add( const Env& e, const Value& callee );
bool add( const Env& e, const Value& callee, const ptr< InvocationRule >& pInvRule );
using TCGen = Generator< tuple<
Term,
const Overload&,
TypeCheckingContext
> >;
TCGen typeCheck( const Term& argsPat, const Term& rtPat, const TypeCheckingContext& tcc ) const;
private:
Term m_identity;
using trie_type = TCTrie< Trie< Overload > >;
ptr< trie_type > m_trie = make_shared< trie_type >();
};
}
#endif
|
Changes to bs/sema/postprocess.cpp.
| ︙ | ︙ | |||
29 30 31 32 33 34 35 |
if( !result )
return nullopt;
auto&& [pPP, t] = *result;
return make_pair( t, static_pointer_cast< PostProcFunc >( pPP ) );
}
| | | | | | | | | | | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 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 |
if( !result )
return nullopt;
auto&& [pPP, t] = *result;
return make_pair( t, static_pointer_cast< PostProcFunc >( pPP ) );
}
optional< Term > Postprocess( const Term& src, TypeCheckingContext& tcc )
{
if( auto optHole = HoleFromIRExpr( src ) )
{
const auto& hole = *optHole;
if( !hole.name.isNumerical() )
{
// If the name is not found, output it as is.
auto index = tcc.getLHSHoleIndex( hole.name );
if( index == TypeCheckingContext::InvalidIndex )
return src;
const auto& val = tcc.getValue( index );
if( !val )
return src;
return Postprocess( *val, tcc );
}
else
{
const auto& optVal = tcc.getValue( hole.name.id() );
if( !optVal )
return HOLE( "_"_sid );
return Postprocess( *optVal, tcc );
}
}
if( !holds_alternative< pvec >( src ) )
return src;
if( auto optPP = UnwrapPostprocFunc( src ) )
{
auto val = Postprocess( optPP->first, tcc );
if( !val )
return nullopt;
return ( *optPP->second )( *val, tcc );
}
const auto& vec = *get< pvec >( src );
auto outputTerms = make_shared< Vector >();
outputTerms->reserve( vec.terms().size() );
for( auto&& t : vec.terms() )
{
auto newT = Postprocess( t, tcc );
if( !newT )
return nullopt;
outputTerms->append( move( *newT ) );
}
return outputTerms;
|
| ︙ | ︙ |
Changes to bs/sema/postprocess.h.
1 2 3 4 5 |
#ifndef GOOSE_SEMA_POSTPROCESS_H
#define GOOSE_SEMA_POSTPROCESS_H
namespace goose::sema
{
| | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#ifndef GOOSE_SEMA_POSTPROCESS_H
#define GOOSE_SEMA_POSTPROCESS_H
namespace goose::sema
{
using PostProcFunc = function< optional< Term > ( const Term& t, TypeCheckingContext& tcc ) >;
extern Term WrapWithPostprocFunc( const Term& t, const ptr< PostProcFunc >& pp );
extern Term WrapWithPostprocFunc( const Term& t, PostProcFunc&& pp );
extern optional< pair< Term, ptr< PostProcFunc > > > UnwrapPostprocFunc( const Term& ppt );
extern void SetupPostProcUnificationRules( TypeCheckingRuleSet& ruleSet );
extern optional< Term > Postprocess( const Term& src, TypeCheckingContext& tcc );
}
#endif
|
Changes to bs/sema/sema.h.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 | } #include "hole.h" #include "context.h" #include "ctmm.h" #include "env.h" | | | | | | | | | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | } #include "hole.h" #include "context.h" #include "ctmm.h" #include "env.h" #include "tc-score.h" #include "tc-context.h" #include "typecheck.h" #include "tc-ruleset.h" #include "uni-basicrules.h" #include "uni-holes.h" #include "uni-quote.h" #include "substitute.h" #include "postprocess.h" #include "tctrie.h" #include "inv-ruleset.h" #include "invocation.h" #include "tpl-ruleset.h" #include "template.h" #include "overloadset.h" #include "codebuilder.h" #include "tctrie.inl" #include "tctrie-typecheck.inl" #endif |
Changes to bs/sema/substitute.cpp.
1 2 3 4 |
#include "sema.h"
namespace goose::sema
{
| | | 1 2 3 4 5 6 7 8 9 10 11 12 |
#include "sema.h"
namespace goose::sema
{
Term Substitute( const Term& src, const TypeCheckingContext& context )
{
if( auto optHole = HoleFromIRExpr( src ) )
{
const auto& hole = *optHole;
// We only substitute indexed holes. If we encounter a named hole,
// output it as is.
|
| ︙ | ︙ |
Changes to bs/sema/substitute.h.
1 2 3 4 5 |
#ifndef GOOSE_SEMA_SUBSTITUTE_H
#define GOOSE_SEMA_SUBSTITUTE_H
namespace goose::sema
{
| | | 1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_SEMA_SUBSTITUTE_H
#define GOOSE_SEMA_SUBSTITUTE_H
namespace goose::sema
{
extern Term Substitute( const Term& src, const TypeCheckingContext& context );
}
#endif
|
Name change from bs/sema/uni-context.cpp to bs/sema/tc-context.cpp.
1 2 3 4 5 | #include "sema.h" using namespace goose; using namespace goose::sema; | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 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 |
#include "sema.h"
using namespace goose;
using namespace goose::sema;
TypeCheckingContext::TypeCheckingContext( const Context& c ) :
m_context( c )
{}
TypeCheckingContext::TypeCheckingContext( Context&& c ) :
m_context( move( c ) )
{}
uint32_t TypeCheckingContext::getLHSHoleIndex( const StringId& name ) const
{
if( name == "_"_sid )
return InvalidIndex;
HoleName holeName( name, m_currentLHSNamespaceIndex );
auto it = m_pCow->holeDict.find( holeName );
if( it == m_pCow->holeDict.end() )
return InvalidIndex;
return it->second;
}
uint32_t TypeCheckingContext::getRHSHoleIndex( const StringId& name ) const
{
if( name == "_"_sid )
return InvalidIndex;
HoleName holeName( name, m_currentRHSNamespaceIndex );
auto it = m_pCow->holeDict.find( holeName );
if( it == m_pCow->holeDict.end() )
return InvalidIndex;
return it->second;
}
uint32_t TypeCheckingContext::createValue()
{
CoW( m_pCow )->values.push_back( StoredValue() );
uint32_t index = m_pCow->values.size() - 1;
return index;
}
void TypeCheckingContext::setValueRequired( uint32_t index )
{
assert( m_pCow->values.size() > index );
if( m_pCow->values[index].m_required )
return;
CoW( m_pCow )->values[index] = { m_pCow->values[index].m_term, true };
if( !m_pCow->values[index].m_term )
++m_numUnknownValues;
}
void TypeCheckingContext::setLHSHoleIndex( const StringId& name, uint32_t index )
{
if( name == "_"_sid )
{
++m_numAnonymousHoles;
return;
}
HoleName holeName( name, m_currentLHSNamespaceIndex );
CoW( m_pCow )->holeDict.emplace( holeName, index );
if( m_valuesAreRequired )
setValueRequired( index );
}
void TypeCheckingContext::setRHSHoleIndex( const StringId& name, uint32_t index )
{
if( name == "_"_sid )
{
++m_numAnonymousHoles;
return;
}
HoleName holeName( name, m_currentRHSNamespaceIndex );
CoW( m_pCow )->holeDict.emplace( holeName, index );
if( m_valuesAreRequired )
setValueRequired( index );
}
void TypeCheckingContext::eraseLHSName( const StringId& name )
{
HoleName holeName( name, m_currentLHSNamespaceIndex );
CoW( m_pCow )->holeDict.erase( holeName );
}
void TypeCheckingContext::eraseRHSName( const StringId& name )
{
HoleName holeName( name, m_currentRHSNamespaceIndex );
CoW( m_pCow )->holeDict.erase( holeName );
}
bool TypeCheckingContext::isHoleLocked( uint32_t index ) const
{
return m_pCow->lockedHoles.find( index ) != m_pCow->lockedHoles.end();
}
void TypeCheckingContext::lockHole( uint32_t index )
{
CoW( m_pCow )->lockedHoles.emplace( index );
}
void TypeCheckingContext::unlockHole( uint32_t index )
{
CoW( m_pCow )->lockedHoles.erase( index );
}
|
Name change from bs/sema/uni-context.h to bs/sema/tc-context.h.
|
| | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#ifndef GOOSE_SEMA_TC_CONTEXT_H
#define GOOSE_SEMA_TC_CONTEXT_H
namespace goose::sema
{
class TypeCheckingContext
{
public:
static constexpr uint32_t InvalidIndex = numeric_limits< uint32_t >::max();
TypeCheckingContext( const Context& c );
TypeCheckingContext( Context&& c );
const auto& context() const { return m_context; }
const auto& env() const { return m_context.env(); }
const auto& rules() const { return env()->typeCheckingRuleSet(); }
uint32_t getLHSHoleIndex( const StringId& name ) const;
uint32_t getRHSHoleIndex( const StringId& name ) const;
uint32_t createValue();
void setLHSHoleIndex( const StringId& name, uint32_t index );
|
| ︙ | ︙ | |||
67 68 69 70 71 72 73 |
m_complexity -= GetComplexity( *m_pCow->values[index].m_term );
CoW( m_pCow )->values[index] = { forward< T >( val ), true };
m_complexity += GetComplexity( *m_pCow->values[index].m_term );
}
| | | | 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 |
m_complexity -= GetComplexity( *m_pCow->values[index].m_term );
CoW( m_pCow )->values[index] = { forward< T >( val ), true };
m_complexity += GetComplexity( *m_pCow->values[index].m_term );
}
TypeCheckingContext& flip()
{
swap( m_currentLHSNamespaceIndex, m_currentRHSNamespaceIndex );
return *this;
}
uint32_t numUnknownValues() const { return m_numUnknownValues; }
uint32_t complexity() const { return m_complexity; }
void addComplexity( uint32_t c ) { m_complexity +=c; }
void subComplexity( uint32_t c ) { m_complexity -=c; }
void setComplexity( uint32_t complexity ) { m_complexity = complexity; }
void addAnonymousHole() { ++m_numAnonymousHoles; }
auto score() const { return TypeCheckingScore( m_complexity, m_pCow->holeDict.size() + m_numAnonymousHoles ); }
// Used to detect and reject recursive hole nesting.
bool isHoleLocked( uint32_t index ) const;
void lockHole( uint32_t index );
void unlockHole( uint32_t index );
private:
|
| ︙ | ︙ |
Name change from bs/sema/uni-postproc.cpp to bs/sema/tc-postproc.cpp.
1 2 3 4 5 6 7 |
#include "sema.h"
namespace goose::sema
{
// When encountering a postprocess wrapper during unification, strip it away
// and unify the content. The wrapper will be put back by the inner unification
// rule.
| | | | | | | | < | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include "sema.h"
namespace goose::sema
{
// When encountering a postprocess wrapper during unification, strip it away
// and unify the content. The wrapper will be put back by the inner unification
// rule.
void SetupPostProcUnificationRules( TypeCheckingRuleSet& ruleSet )
{
ruleSet.addUnificationRule( TCRINFOS, VEC( TSID( postproc ), ANYTERM( _ ), ANYTERM( _ ) ), ANYTERM( _ ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto unwrap = UnwrapPostprocFunc( lhs );
if( !unwrap )
co_return;
auto&& [t,pp] = *unwrap;
co_yield Unify( t, rhs, tcc );
for( auto&& [s,tcc] : Unify( t, rhs, tcc ) )
co_yield { t, tcc };
} );
ruleSet.addUnificationRule( TCRINFOS,
VEC( TSID( postproc ), ANYTERM( _ ), ANYTERM( _ ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto unwrap = UnwrapPostprocFunc( lhs );
if( !unwrap )
co_return;
auto&& [t,pp] = *unwrap;
for( auto&& [s,c] : Unify( t, rhs, tcc ) )
co_yield { t, tcc };
} );
}
}
|
Name change from bs/sema/uni-ruleset.cpp to bs/sema/tc-ruleset.cpp.
1 2 3 4 5 | #include "sema.h" using namespace goose; using namespace goose::sema; | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
#include "sema.h"
using namespace goose;
using namespace goose::sema;
TypeCheckingRuleSet::TypeCheckingRuleSet()
{
SetupBasicUnificationRules( *this );
SetupPostProcUnificationRules( *this );
SetupHoleUnificationRules( *this );
SetupQuoteUnificationRules( *this );
}
void TypeCheckingRuleSet::addUnificationRule( TCRuleInfo&& infos, const Term& pat, UniFunc f )
{
m_uniRules = Merge( m_uniRules, VEC( pat, pat ), [&]( auto&& ){ return UniRule{ f, infos }; } );
}
TCGen FlippedContextAdapter( const Term& lhs, const Term& rhs, TypeCheckingContext tcc, TypeCheckingRuleSet::UniFunc f )
{
for( auto&& [e,tcc] : f( rhs, lhs, tcc.flip() ) )
co_yield { move( e ), tcc.flip() };
}
void TypeCheckingRuleSet::addUnificationRule( TCRuleInfo&& infos, const Term& pat1, const Term& pat2, UniFunc f )
{
m_uniRules = Merge( m_uniRules, VEC( pat1, pat2 ), [&]( auto&& ){ return UniRule{ f, infos }; } );
m_uniRules = Merge( m_uniRules, VEC( pat2, pat1 ),
[&]( auto&& ){ return UniRule{ UniFunc( [&,f]( auto&& lhs, auto&& rhs, auto&& c ) -> TCGen
{
return FlippedContextAdapter( lhs, rhs, c, f );
} ), infos }; }
);
}
void TypeCheckingRuleSet::addTypeCheckingRule( TCRuleInfo&& infos, const Term& pat1, const Term& pat2, UniFunc f )
{
m_typeCheckingRules = Merge( m_typeCheckingRules, VEC( pat1, pat2 ), [&]( auto&& ){ return UniRule{ f, infos }; } );
}
void TypeCheckingRuleSet::addHalfUnificationRule( TCRuleInfo&& infos, const Term& pat, HalfUniFunc f )
{
m_halfUniRules = Merge( m_halfUniRules, pat, [&]( auto&& ){ return HalfUniRule{ f, infos }; } );
}
|
Name change from bs/sema/uni-ruleset.h to bs/sema/tc-ruleset.h.
|
| | | | | | | | | | | | | | | | | > | | | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
#ifndef GOOSE_SEMA_TC_RULESET_H
#define GOOSE_SEMA_TC_RULESET_H
//#define TCRULES_DEBUG
namespace goose::sema
{
class TypeCheckingContext;
#ifdef TCRULES_DEBUG
struct TCRuleInfo
{
const char* pFilename = nullptr;
uint32_t line = 0;
};
#define TCRINFOS TCRuleInfo{ __FILE__, __LINE__ }
#else
struct TCRuleInfo
{};
#define TCRINFOS TCRuleInfo{}
#endif
class TypeCheckingRuleSet
{
public:
using UniFunc = function< TCGen ( const Term& lhs, const Term& rhs, const TypeCheckingContext& ) >;
using HalfUniFunc = function< TCGen ( const Term& lhs, const TypeCheckingContext& ) >;
struct UniRule
{
UniFunc func;
TCRuleInfo infos;
};
struct HalfUniRule
{
HalfUniFunc func;
TCRuleInfo infos;
};
TypeCheckingRuleSet();
void addTypeCheckingRule( TCRuleInfo&& infos, const Term& pat1, const Term& pat2, UniFunc f );
void addUnificationRule( TCRuleInfo&& infos, const Term& pat, UniFunc f );
void addUnificationRule( TCRuleInfo&& infos, const Term& pat1, const Term& pat2, UniFunc f );
void addHalfUnificationRule( TCRuleInfo&& infos, const Term& pat, HalfUniFunc f );
const auto& typeCheckingRules() const { return m_typeCheckingRules; }
const auto& uniRules() const { return m_uniRules; }
const auto& halfUniRules() const { return m_halfUniRules; }
private:
Trie< UniRule > m_typeCheckingRules;
Trie< UniRule > m_uniRules;
Trie< HalfUniRule > m_halfUniRules;
};
}
#endif
|
Name change from bs/sema/uni-score.h to bs/sema/tc-score.h.
|
| | | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
#ifndef GOOSE_SEMA_TC_SCORE_H
#define GOOSE_SEMA_TC_SCORE_H
namespace goose::sema
{
class TypeCheckingScore
{
public:
TypeCheckingScore() {}
TypeCheckingScore( uint32_t complexity, uint32_t uniqueHoles ) :
m_complexity( complexity ),
m_uniqueHoles( uniqueHoles )
{}
auto& operator+=( const TypeCheckingScore& rhs )
{
m_complexity += rhs.m_complexity;
m_uniqueHoles += rhs.m_uniqueHoles;
return *this;
}
auto operator+( const TypeCheckingScore& rhs ) const
{
return TypeCheckingScore( m_complexity + rhs.m_complexity, m_uniqueHoles + rhs.m_uniqueHoles );
}
bool operator==( const TypeCheckingScore& rhs ) const
{
return m_complexity == rhs.m_complexity
&& m_uniqueHoles == rhs.m_uniqueHoles;
}
bool operator!=( const TypeCheckingScore& rhs ) const
{
return !operator==( rhs );
}
bool operator<( const TypeCheckingScore& rhs ) const
{
if( m_complexity != rhs.m_complexity )
return m_complexity < rhs.m_complexity ;
return m_uniqueHoles > rhs.m_uniqueHoles;
}
bool operator>( const TypeCheckingScore& rhs ) const
{
if( m_complexity != rhs.m_complexity )
return m_complexity > rhs.m_complexity ;
return m_uniqueHoles < rhs.m_uniqueHoles;
}
bool operator<=( const TypeCheckingScore& rhs ) const
{
return !operator>( rhs );
}
bool operator>=( const TypeCheckingScore& rhs ) const
{
return !operator<( rhs );
}
friend ostream& operator<<( ostream& out, const TypeCheckingScore& s )
{
return out << '(' << s.m_complexity << ',' << s.m_uniqueHoles << ')';
}
private:
uint32_t m_complexity = 0;
uint32_t m_uniqueHoles = 0;
|
| ︙ | ︙ |
Name change from bs/sema/utrie-uni.inl to bs/sema/tctrie-typecheck.inl.
|
| | | | | | | | | | | | | | < | | | | | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 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 |
#ifndef GOOSE_SEMA_TCTRIE_TYPECHECK_INL
#define GOOSE_SEMA_TCTRIE_TYPECHECK_INL
namespace goose::sema
{
template< typename U > typename TCTrie< U >::template TCGen< U >
TCTrie< U >::TypeCheckRepetition( VecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec,
const RepetitionNode& rptNode, const TypeCheckingContext& tcc )
{
if( vgen.finished() )
co_yield { lhsVec, solutionVec, rptNode.m_next, tcc };
else
{
const auto& vt = vgen();
for( auto&& rt : Enumerate( rptNode.m_repetition ) )
{
auto newC = tcc;
newC.addComplexity( GetComplexity( rt ) + GetComplexity( vt ) );
auto localRhsVec = lhsVec;
localRhsVec.setRepetitionTerm( rt );
for( auto&& [s,tcc] : sema::TypeCheck( rt, vt, newC ) )
co_yield TypeCheckRepetition( vgen, localRhsVec, Vector::MakeAppend( solutionVec, move( s ) ), rptNode, tcc );
}
}
}
template< typename U > template< typename T > Generator< tuple< Vector, Vector, const T&, TypeCheckingContext, VecGenerator > >
TCTrie< U >::TypeCheck( VecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec, const branch_t& branch, const TypeCheckingContext& tcc )
{
if( branch.index() == 0 )
{
const auto& vt = vgen();
for( auto&& [t, payload] : Enumerate( get< 0 >( branch )->m_trie ) )
{
auto newC = tcc;
newC.addComplexity( GetComplexity( t ) + GetComplexity( vt ) );
auto lvec = Vector::MakeAppend( lhsVec, t );
for( auto&& [s, tcc] : sema::TypeCheck( t, vt, newC ) )
co_yield TypeCheck< T >( vgen, lvec, Vector::MakeAppend( solutionVec, move( s ) ), payload, tcc );
}
}
else
co_yield { lhsVec, solutionVec, *any_cast< T >( &get< 1 >( branch ) ), tcc, vgen };
}
template< typename U >
typename TCTrie< U >::template TCGen< U > TCTrie< U >::typeCheck( const Vector& rhs, TypeCheckingContext tcc ) const
{
auto exprLen = rhs.length();
tcc.addComplexity( 2 );
auto it = m_fixedLengthBranches.find( exprLen.minLength() );
if( it != m_fixedLengthBranches.end() )
{
VecGenerator vgen( rhs );
Vector lhs;
Vector sol;
for( auto&& [lv, s, content, c, g] : TypeCheck< U >( vgen, lhs, sol, it->second, tcc ) )
co_yield { lv, s, content, c };
}
using rpt_node = ptr< RepetitionNode >;
it = m_variableLengthBranches.begin();
auto end = m_variableLengthBranches.upper_bound( exprLen.minLength() );
while( it != end )
{
VecGenerator vgen( rhs );
Vector lhs;
Vector sol;
for( auto&& [lv, s, pRptNode, c, g] : TypeCheck< rpt_node >( vgen, lhs, sol, it->second, tcc ) )
co_yield TypeCheckRepetition( g, lv, sol, *pRptNode, c );
++it;
}
}
template< typename U > template< typename T > typename TCTrie< U >::template TCGen< T >
TCTrie< U >::HalfUnify( const Vector& lhsVec, const Vector& solutionVec, const branch_t& branch, const TypeCheckingContext& tcc )
{
if( branch.index() == 0 )
{
for( auto&& [t, payload] : Enumerate( get< 0 >( branch )->m_trie ) )
{
auto newC = tcc;
newC.addComplexity( GetComplexity( t ) );
auto lvec = Vector::MakeAppend( lhsVec, t );
for( auto&& [s, tcc] : sema::HalfUnify( t, newC ) )
co_yield HalfUnify< T >( lvec, Vector::MakeAppend( solutionVec, move( s ) ), payload, tcc );
}
}
else
co_yield { lhsVec, solutionVec, *any_cast< T >( &get< 1 >( branch ) ), tcc };
}
template< typename U >
typename TCTrie< U >::template TCGen< U > TCTrie< U >::halfUnify( TypeCheckingContext tcc ) const
{
tcc.addComplexity( 1 );
for( auto&& [len, branch] : m_fixedLengthBranches )
{
Vector lhs;
Vector sol;
for( auto&& [lv, s, content, c] : HalfUnify< U >( lhs, sol, branch, tcc ) )
co_yield { lv, s, content, c };
}
using rpt_node = ptr< TrieContainerRepetitionNode< U > >;
for( auto&& [minLen, branch] : m_variableLengthBranches )
{
Vector lhs;
Vector sol;
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 ) );
for( auto&& [s, c] : sema::HalfUnify( rt, newC ) )
{
auto localLhsVec = lv;
localLhsVec.setRepetitionTerm( rt );
|
| ︙ | ︙ |
Name change from bs/sema/utrie.h to bs/sema/tctrie.h.
|
| | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
#ifndef GOOSE_SEMA_TCTRIE_H
#define GOOSE_SEMA_TCTRIE_H
namespace goose::sema
{
// Similar to an ir trie, but instead of being used to find the best pattern among a set of patterns,
// it is used to find the best typing among a set of vector terms.
// This is used to construct overload sets and to efficiently resolve overloads.
template< typename U >
class TCTrie
{
public:
template< typename F >
ptr< TCTrie > merge( const Vector& v, F&& func ) const;
template< typename T >
using TCGen = Generator< tuple< Vector, Vector, const T&, TypeCheckingContext > >;
TCGen< U > typeCheck( const Vector& rhs, TypeCheckingContext c ) const;
TCGen< U > halfUnify( TypeCheckingContext c ) const;
private:
struct Node;
struct RepetitionNode;
using branch_t = variant< ptr< Node >, any >;
template< typename T, typename IT, typename F >
static branch_t Merge( const branch_t& b, const IT& it, const IT& end, F&& next );
template< typename F >
static ptr< RepetitionNode > Merge( const ptr< RepetitionNode >& lhs, const Term& rhs, F&& next );
static TCGen< U > TypeCheckRepetition( VecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec,
const RepetitionNode& rptNode, const TypeCheckingContext& c );
template< typename T > static Generator< tuple< Vector, Vector, const T&, TypeCheckingContext, VecGenerator > >
TypeCheck( VecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec, const branch_t& branch, const TypeCheckingContext& c );
template< typename T > static TCGen< T >
HalfUnify( const Vector& lhsVec, const Vector& solutionVec, const branch_t& branch, const TypeCheckingContext& c );
struct Node
{
Trie< branch_t > m_trie;
};
struct RepetitionNode
|
| ︙ | ︙ |
Name change from bs/sema/utrie.inl to bs/sema/tctrie.inl.
|
| | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#ifndef GOOSE_SEMA_TCTRIE_INL
#define GOOSE_SEMA_TCTRIE_INL
namespace goose::sema
{
template< typename U > template< typename T, typename IT, typename F >
typename TCTrie< U >::branch_t TCTrie< U >::Merge( const branch_t& b, const IT& it, const IT& end, F&& next )
{
if( it == end )
{
if( b.index() == 1 )
return any( next( *any_cast< T >( &get< 1 >( b ) ) ) );
return any( next( T() ) );
|
| ︙ | ︙ | |||
33 34 35 36 37 38 39 |
} );
pNewNode->m_trie = ir::Merge( pNewNode->m_trie, *it, move( f ) );
return pNewNode;
}
template< typename U > template< typename F >
| | | | | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
} );
pNewNode->m_trie = ir::Merge( pNewNode->m_trie, *it, move( f ) );
return pNewNode;
}
template< typename U > template< typename F >
ptr< typename TCTrie< U >::RepetitionNode > TCTrie< U >::Merge( const ptr< RepetitionNode >& lhs, const Term& rhs, F&& next )
{
auto pNewNode = make_shared< RepetitionNode >();
pNewNode->m_next = next( lhs ? lhs->m_next : U() );
if( lhs )
pNewNode->m_repetition = lhs->m_repetition;
pNewNode->m_repetition = ir::Merge( pNewNode->m_repetition, rhs );
return pNewNode;
}
template< typename U > template< typename F >
ptr< TCTrie< U > > TCTrie< U >::merge( const Vector& v, F&& next ) const
{
auto pNewTrie = make_shared< TCTrie< U > >( *this );
auto length = v.length();
if( length.isVariable() )
{
using rpt_node = ptr< RepetitionNode >;
auto f = [&]( auto&& payload )
|
| ︙ | ︙ |
Changes to bs/sema/template.cpp.
| ︙ | ︙ | |||
47 48 49 50 51 52 53 |
const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
if( !pTemplateRuleSet )
return PoisonValue();
return pTemplateRuleSet->buildParamDecl( c, *ValueFromIRExpr( tpl ), *ValueFromIRExpr( arg ) );
}
| | | | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
if( !pTemplateRuleSet )
return PoisonValue();
return pTemplateRuleSet->buildParamDecl( c, *ValueFromIRExpr( tpl ), *ValueFromIRExpr( arg ) );
}
void TemplateSetup( const Context& c, TypeCheckingContext tcc, const Term& tpl )
{
const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
if( !pTemplateRuleSet )
return;
pTemplateRuleSet->setup( c, tcc, *ValueFromIRExpr( tpl ) );
}
extern optional< Term > BuildTemplateArgPattern( const Context& c, const Term& tpl )
{
const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
if( !pTemplateRuleSet )
return nullopt;
|
| ︙ | ︙ |
Changes to bs/sema/template.h.
1 2 3 4 5 6 7 8 |
#ifndef GOOSE_SEMA_TEMPLATE_H
#define GOOSE_SEMA_TEMPLATE_H
namespace goose::sema
{
extern ptr< TemplateRule > GetTemplateRuleSet( const Context& c, const Term& tpl );
extern optional< Term > BuildTemplateSignature( const Context& c, const Term& tpl );
extern Value BuildTemplateParam( const Context& c, const Term& tpl, const Term& arg );
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#ifndef GOOSE_SEMA_TEMPLATE_H
#define GOOSE_SEMA_TEMPLATE_H
namespace goose::sema
{
extern ptr< TemplateRule > GetTemplateRuleSet( const Context& c, const Term& tpl );
extern optional< Term > BuildTemplateSignature( const Context& c, const Term& tpl );
extern Value BuildTemplateParam( const Context& c, const Term& tpl, const Term& arg );
extern void TemplateSetup( const Context& c, TypeCheckingContext tcc, const Term& tpl );
extern optional< Term > BuildTemplateArgPattern( const Context& c, const Term& tpl );
}
#endif
|
Changes to bs/sema/tests/meson.build.
1 2 |
tests = [
'unify-holes',
| | | | 1 2 3 4 5 6 7 8 9 10 11 |
tests = [
'unify-holes',
'tctrie-merge',
'tctrie-typecheck'
]
foreach t : tests
exe = executable( 'sema-' + t, t + '.cpp',
link_with:
[
goose_util,
|
| ︙ | ︙ |
Name change from bs/sema/tests/utrie-merge.cpp to bs/sema/tests/tctrie-merge.cpp.
1 2 3 4 5 6 7 8 9 10 11 | #define CATCH_CONFIG_MAIN #include "catch2/catch.hpp" #include "sema/sema.h" #include "builtins/builtins.h" using namespace std; using namespace goose; using namespace goose::ir; using namespace goose::sema; using namespace goose::builtins; | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "sema/sema.h"
#include "builtins/builtins.h"
using namespace std;
using namespace goose;
using namespace goose::ir;
using namespace goose::sema;
using namespace goose::builtins;
SCENARIO( "TCTrie merge works", "[utrie-merge]" )
{
WHEN( "Merging various expressions into an utrie" )
{
auto vec1 = Vector::Make( TSTR( "foo" ), TSTR( "bar" ) );
auto vec2 = Vector::Make( HOLE( "a"_sid ), HOLE( "a"_sid ) );
auto vec3 = Vector::Make( TSTR( "meh" ), TSTR( "meh" ) );
|
| ︙ | ︙ | |||
29 30 31 32 33 34 35 |
auto vec10 = Vector::Make( HOLE( "x"_sid ), TERM( vec4 ), HOLE( "z"_sid ), HOLE( "z"_sid ) );
auto vec11 = Vector::Make( TERM( vec5 ), HOLE( "y"_sid ), HOLE( "y"_sid ), TERM( vec5 ) );
auto vec12 = Vector::Make( VEC( HOLE( "a"_sid ), TSTR( "foo" ) ), HOLE( "a"_sid ) );
auto vec13 = Vector::Make( HOLE( "b"_sid ), HOLE( "b"_sid ) );
| | | | | | | | | | | | | | | | | | | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
auto vec10 = Vector::Make( HOLE( "x"_sid ), TERM( vec4 ), HOLE( "z"_sid ), HOLE( "z"_sid ) );
auto vec11 = Vector::Make( TERM( vec5 ), HOLE( "y"_sid ), HOLE( "y"_sid ), TERM( vec5 ) );
auto vec12 = Vector::Make( VEC( HOLE( "a"_sid ), TSTR( "foo" ) ), HOLE( "a"_sid ) );
auto vec13 = Vector::Make( HOLE( "b"_sid ), HOLE( "b"_sid ) );
auto trie = TCTrie< string >().merge( *vec1, []( auto&& ){ return "vec1"s; } );
trie = trie->merge( *vec2, []( auto&& ){ return "vec2"s; } );
trie = trie->merge( *vec3, []( auto&& ){ return "vec3"s; } );
trie = trie->merge( *vec4, []( auto&& ){ return "vec4"s; } );
trie = trie->merge( *vec5, []( auto&& ){ return "vec5"s; } );
trie = trie->merge( *vec6, []( auto&& ){ return "vec6"s; } );
trie = trie->merge( *vec7, []( auto&& ){ return "vec7"s; } );
trie = trie->merge( *vec8, []( auto&& ){ return "vec8"s; } );
trie = trie->merge( *vec9, []( auto&& ){ return "vec9"s; } );
trie = trie->merge( *vec10, []( auto&& ){ return "vec10"s; } );
trie = trie->merge( *vec11, []( auto&& ){ return "vec11"s; } );
trie = trie->merge( *vec12, []( auto&& ){ return "vec12"s; } );
trie = trie->merge( *vec13, []( auto&& ){ return "vec13"s; } );
THEN( "Half-unifications yields the expected solutions" )
{
Context ctxt( make_shared< Env >(), VEC( TSID( g0 ) ) );
TypeCheckingContext context( ctxt );
vector< pair< string, TypeCheckingScore > > results;
for( auto&& [lv, s,content,tcc] : trie->halfUnify( context ) )
results.emplace_back( content, tcc.score() );
sort( results.begin(), results.end(), []( auto&& a, auto&& b )
{
return a.first < b.first;
} );
REQUIRE( results.size() == 13 );
REQUIRE( results[0] == make_pair( "vec1"s, TypeCheckingScore( 1, 0 ) ) );
REQUIRE( results[1] == make_pair( "vec10"s, TypeCheckingScore( 2, 3 ) ) );
REQUIRE( results[2] == make_pair( "vec11"s, TypeCheckingScore( 3, 2 ) ) );
REQUIRE( results[3] == make_pair( "vec12"s, TypeCheckingScore( 2, 1 ) ) );
REQUIRE( results[4] == make_pair( "vec13"s, TypeCheckingScore( 1, 1 ) ) );
REQUIRE( results[5] == make_pair( "vec2"s, TypeCheckingScore( 1, 1 ) ) );
REQUIRE( results[6] == make_pair( "vec3"s, TypeCheckingScore( 1, 0 ) ) );
REQUIRE( results[7] == make_pair( "vec4"s, TypeCheckingScore( 1, 1 ) ) );
REQUIRE( results[8] == make_pair( "vec5"s, TypeCheckingScore( 1, 1 ) ) );
REQUIRE( results[9] == make_pair( "vec6"s, TypeCheckingScore( 1, 2 ) ) );
REQUIRE( results[10] == make_pair( "vec7"s, TypeCheckingScore( 1, 1 ) ) );
REQUIRE( results[11] == make_pair( "vec8"s, TypeCheckingScore( 1, 2 ) ) );
REQUIRE( results[12] == make_pair( "vec9"s, TypeCheckingScore( 3, 1 ) ) );
}
}
}
|
Name change from bs/sema/tests/utrie-unify.cpp to bs/sema/tests/tctrie-typecheck.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "sema/sema.h"
#include "builtins/builtins.h"
using namespace std;
using namespace goose;
using namespace goose::ir;
using namespace goose::sema;
using namespace goose::builtins;
namespace
{
| | | | | | | | > > > > > > > > > > | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 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 |
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "sema/sema.h"
#include "builtins/builtins.h"
using namespace std;
using namespace goose;
using namespace goose::ir;
using namespace goose::sema;
using namespace goose::builtins;
namespace
{
auto GetSortedSolutions( const ptr< Env >& e, ptr< TCTrie< string > >& trie, const Vector& vec )
{
Context ctxt( e, VEC( TSID( g0 ) ) );
TypeCheckingContext context( ctxt );
vector< tuple< Term, string, TypeCheckingScore > > solutions;
for( auto&& [lv, v,content,c] : trie->typeCheck( vec, context ) )
{
if( c.numUnknownValues() )
continue;
auto sol = Postprocess( TERM( make_shared< Vector >( v ) ), c );
solutions.emplace_back( *sol, content, c.score() );
}
sort( solutions.begin(), solutions.end(), []( auto&& a, auto&& b )
{
return get< 1 >( a ) < get< 1 >( b );
} );
return solutions;
}
}
SCENARIO( "TCTrie type checking works", "[typecheck]" )
{
WHEN( "Type checking a tctrie with various expressions" )
{
// We don't have all the builtin type check rules here but we do need some kind
// of default rule to "typecheck" two identical strings for this test, so we just create it here.
auto e = make_shared< Env >();
e->typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS, ANYTERM( T ), ANYTERM( T ), []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
assert( lhs == rhs );
co_yield { lhs, tcc };
} );
auto vec1 = Vector::Make( TSTR( "foo" ), TSTR( "bar" ) );
auto vec2 = Vector::Make( HOLE( "a"_sid ), HOLE( "a"_sid ) );
auto vec3 = Vector::Make( TSTR( "meh" ), TSTR( "meh" ) );
auto vec4 = Vector::Make( HOLE( "a"_sid ), TSTR( "bar" ) );
auto vec5 = Vector::Make( TSTR( "foo" ), HOLE( "b"_sid ) );
auto vec9 = Vector::Make( TERM( vec5 ), TERM( vec1 ) );
auto vec10 = Vector::Make( HOLE( "x"_sid ), TERM( vec4 ), HOLE( "z"_sid ), HOLE( "z"_sid ) );
auto vec11 = Vector::Make( TERM( vec5 ), HOLE( "y"_sid ), HOLE( "y"_sid ), TERM( vec5 ) );
auto vec12 = Vector::Make( VEC( HOLE( "a"_sid ), TSTR( "foo" ) ), HOLE( "a"_sid ) );
auto vec13 = Vector::Make( HOLE( "b"_sid ), HOLE( "b"_sid ) );
auto trie = TCTrie< string >().merge( *vec2, []( auto&& ){ return "vec2"s; } );
trie = trie->merge( *vec4, []( auto&& ){ return "vec4"s; } );
trie = trie->merge( *vec10, []( auto&& ){ return "vec10"s; } );
trie = trie->merge( *vec12, []( auto&& ){ return "vec12"s; } );
THEN( "We obtain the expected solutions" )
{
auto sols1 = GetSortedSolutions( e, trie, *vec1 );
auto sols3 = GetSortedSolutions( e, trie, *vec3 );
auto sols5 = GetSortedSolutions( e, trie, *vec5 );
auto sols9 = GetSortedSolutions( e, trie, *vec9 );
auto sols11 = GetSortedSolutions( e, trie, *vec11 );
auto sols13 = GetSortedSolutions( e, trie, *vec13 );
REQUIRE( sols1.size() == 1 );
REQUIRE( sols1[0] == make_tuple( VEC( TSTR( "foo" ), TSTR( "bar" ) ), "vec4"s, TypeCheckingScore( 2, 1 ) ) );
REQUIRE( sols3.size() == 1 );
REQUIRE( sols3[0] == make_tuple( VEC( TSTR( "meh" ), TSTR( "meh" ) ), "vec2"s, TypeCheckingScore( 2, 1 ) ) );
REQUIRE( sols5.size() == 2 );
REQUIRE( sols5[0] == make_tuple( VEC( TSTR( "foo" ), TSTR( "foo" ) ), "vec2"s, TypeCheckingScore( 2, 2 ) ) );
REQUIRE( sols5[1] == make_tuple( VEC( TSTR( "foo" ), TSTR( "bar" ) ), "vec4"s, TypeCheckingScore( 2, 2 ) ) );
REQUIRE( sols9.size() == 1 );
REQUIRE( sols9[0] == make_tuple(
VEC(
VEC( TSTR( "foo" ), TSTR( "bar" ) ),
VEC( TSTR( "foo" ), TSTR( "bar" ) )
), "vec2"s, TypeCheckingScore( 4, 2 ) )
);
REQUIRE( sols11.size() == 1 );
REQUIRE( sols11[0] == make_tuple(
VEC(
VEC( TSTR( "foo" ), TSTR( "bar" ) ),
VEC( TSTR( "foo" ), TSTR( "bar" ) ),
VEC( TSTR( "foo" ), TSTR( "bar" ) ),
VEC( TSTR( "foo" ), TSTR( "bar" ) )
), "vec10"s, TypeCheckingScore( 5, 5 ) )
);
REQUIRE( sols13.size() == 1 );
REQUIRE( sols13[0] == make_tuple( VEC( TSTR( "bar" ), TSTR( "bar" ) ), "vec4"s, TypeCheckingScore( 2, 2 ) ) );
}
}
}
|
Changes to bs/sema/tests/unify-holes.cpp.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
using namespace goose::sema;
using namespace goose::builtins;
namespace
{
// Verifies that the unification of lhs and rhs yields only one solution, that it is complete,
// and that this solution is the expected one.
| | | | | | | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
using namespace goose::sema;
using namespace goose::builtins;
namespace
{
// Verifies that the unification of lhs and rhs yields only one solution, that it is complete,
// and that this solution is the expected one.
void CheckForUniqueSolution( const Term& lhs, const Term& rhs, const Term& expectedSolution, const TypeCheckingScore& expectedScore )
{
Context ctxt( make_shared< Env >(), VEC( TSID( g0 ) ) );
TypeCheckingContext tcc( ctxt );
tcc.setComplexity( GetComplexity( lhs ) + GetComplexity( rhs ) );
auto g = Unify( lhs, rhs, tcc );
auto it = g.begin();
REQUIRE( it != g.end() );
auto&& [e,c] = *it;
REQUIRE( c.numUnknownValues() == 0 );
REQUIRE( c.score() == expectedScore );
auto sol = Postprocess( e, c );
REQUIRE( sol == expectedSolution );
++it;
REQUIRE( it == g.end() );
}
void CheckForNoSolution( const Term& lhs, const Term& rhs )
{
Context ctxt( make_shared< Env >(), VEC( TSID( g0 ) ) );
TypeCheckingContext tcc( ctxt );
tcc.setComplexity( GetComplexity( lhs ) + GetComplexity( rhs ) );
auto g = Unify( lhs, rhs, tcc );
auto it = g.begin();
REQUIRE( it == g.end() );
}
}
SCENARIO( "Unify works", "[unify]" )
|
| ︙ | ︙ |
Changes to bs/sema/tpl-ruleset.h.
1 2 3 4 5 6 7 8 9 10 11 12 |
#ifndef GOOSE_SEMA_TPL_RULESET_H
#define GOOSE_SEMA_TPL_RULESET_H
namespace goose::sema
{
class TemplateRule
{
public:
virtual ~TemplateRule() {}
virtual optional< Term > buildSignature( const Context& c, const Value& tpl ) const = 0;
virtual Value buildParamDecl( const Context& c, const Value& param, const Value& arg ) const = 0;
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#ifndef GOOSE_SEMA_TPL_RULESET_H
#define GOOSE_SEMA_TPL_RULESET_H
namespace goose::sema
{
class TemplateRule
{
public:
virtual ~TemplateRule() {}
virtual optional< Term > buildSignature( const Context& c, const Value& tpl ) const = 0;
virtual Value buildParamDecl( const Context& c, const Value& param, const Value& arg ) const = 0;
virtual void setup( const Context& c, TypeCheckingContext& tcc, const Value& tpl ) const {};
virtual optional< Term > buildArgPattern( const Context& c, const Value& tpl ) const = 0;
};
class TemplateRuleSet
{
public:
TemplateRuleSet() {}
|
| ︙ | ︙ |
Name change from bs/sema/unify.cpp to bs/sema/typecheck.cpp.
1 2 3 4 |
#include "sema.h"
namespace goose::sema
{
| | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 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 |
#include "sema.h"
namespace goose::sema
{
TypeCheckingSolution FindBestTyping( const Term& lhs, const Term& rhs, const Context& context )
{
optional< TypeCheckingContext > bestTCC;
optional< Term > bestSol;
bool ambiguous = false;
TypeCheckingContext tcc( context );
tcc.setComplexity( GetComplexity( lhs ) + GetComplexity( rhs ) );
for( auto&& [s, tcc] : TypeCheck( lhs, rhs, tcc ) )
{
if( tcc.numUnknownValues() )
continue;
if( bestTCC && tcc.score() < bestTCC->score() )
continue;
auto pps = Postprocess( s, tcc );
if( !pps )
continue;
if( bestTCC && tcc.score() == bestTCC->score() )
{
ambiguous = true;
continue;
}
bestTCC = tcc;
bestSol = move( *pps );
ambiguous = false;
}
if( ambiguous )
return AmbiguousTypeCheck{};
if( !bestSol )
return {};
return make_pair( move( *bestSol ), move( *bestTCC ) );
}
TCGen TypeCheck( const Term& lhs, const Term& rhs, const TypeCheckingContext& context )
{
const auto& rules = context.rules()->typeCheckingRules();
MatchSolution bestSol;
optional< TypeCheckingRuleSet::UniRule > bestRule;
bool ambiguous = false;
auto expr = VEC( lhs, rhs );
for( auto&& [s, rule] : Match( expr, rules ) )
{
if( !bestRule || s > bestSol )
{
bestSol = s;
bestRule = rule;
ambiguous = false;
continue;
}
if( s < bestSol )
continue;
ambiguous = true;
}
// Let's not really worry about how to properly report this for now.
if( ambiguous )
throw runtime_error( "ambiguous unification rule" );
if( !bestRule )
co_return;
co_yield bestRule->func( lhs, rhs, context );
}
TCGen Unify( const Term& lhs, const Term& rhs, const TypeCheckingContext& context )
{
const auto& rules = context.rules()->uniRules();
MatchSolution bestSol;
optional< TypeCheckingRuleSet::UniRule > bestRule;
bool ambiguous = false;
auto expr = VEC( lhs, rhs );
for( auto&& [s, rule] : Match( expr, rules ) )
{
if( !bestRule || s > bestSol )
{
|
| ︙ | ︙ | |||
74 75 76 77 78 79 80 |
if( !bestRule )
co_return;
co_yield bestRule->func( lhs, rhs, context );
}
| | | | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
if( !bestRule )
co_return;
co_yield bestRule->func( lhs, rhs, context );
}
TCGen HalfUnify( const Term& lhs, const TypeCheckingContext& context )
{
const auto& rules = context.rules()->halfUniRules();
MatchSolution bestSol;
optional< TypeCheckingRuleSet::HalfUniRule > bestRule;
bool ambiguous = false;
for( auto&& [s, rule] : Match( lhs, rules ) )
{
if( !bestRule || s > bestSol )
{
bestSol = s;
|
| ︙ | ︙ |
Name change from bs/sema/unify.h to bs/sema/typecheck.h.
|
| | | | | | | | | | > > | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#ifndef GOOSE_SEMA_TYPECHECK_H
#define GOOSE_SEMA_TYPECHECK_H
namespace goose::sema
{
struct NoUnification {};
struct AmbiguousTypeCheck {};
using TCSol = pair< Term, TypeCheckingContext >;
using TypeCheckingSolution = variant
<
NoUnification,
AmbiguousTypeCheck,
TCSol
>;
TypeCheckingSolution FindBestTyping( const Term& lhs, const Term& rhs, const Context& context );
using TCGen = Generator< pair< Term, TypeCheckingContext > >;
TCGen TypeCheck( const Term& lhs, const Term& rhs, const TypeCheckingContext& context );
TCGen Unify( const Term& lhs, const Term& rhs, const TypeCheckingContext& context );
TCGen HalfUnify( const Term& lhs, const 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 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 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 |
#include "sema.h"
namespace goose::sema
{
TCGen HalfUnifyVectors( const Vector::container_type< Term >& lTerms, size_t i, const Vector& solutionVec, TypeCheckingContext tcc )
{
assert( i < lTerms.size() );
for( auto&& [u,tcc] : HalfUnify( lTerms[i], tcc ) )
{
auto vec = Vector::MakeAppend( solutionVec, move( u ) );
if( i == ( lTerms.size() - 1 ) )
co_yield { TERM( make_shared< Vector >( move( vec ) ) ), tcc };
else
co_yield HalfUnifyVectors( lTerms, i + 1, vec, tcc );
}
}
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 ) )
{
auto vec = Vector::MakeAppend( solutionVec, move( u ) );
if( i == ( lTerms.size() - 1 ) )
co_yield { TERM( make_shared< Vector >( move( vec ) ) ), tcc };
else
co_yield UnifyVectors( lTerms, rTerms, i + 1, vec, tcc );
}
}
TCGen TypeCheckVectors( 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] : TypeCheck( lTerms[i], rTerms[i], tcc ) )
{
auto vec = Vector::MakeAppend( solutionVec, move( u ) );
if( i == ( lTerms.size() - 1 ) )
co_yield { TERM( make_shared< Vector >( move( vec ) ) ), tcc };
else
co_yield TypeCheckVectors( lTerms, rTerms, i + 1, vec, tcc );
}
}
void SetupBasicUnificationRules( TypeCheckingRuleSet& ruleSet )
{
// Default half-unification rule
ruleSet.addHalfUnificationRule( TCRINFOS, ANYTERM( _ ), []( const Term& lhs, const TypeCheckingContext& tcc ) -> TCGen
{
// 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.
co_yield { lhs, tcc };
} );
// Identity unification rule
ruleSet.addUnificationRule( TCRINFOS, ANYTERM( T ), []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
assert( lhs == rhs );
// NOTE: the score here is always 0 because:
// Vectors of identical size are going to fall into the vector unification rule below,
// and holes are going to have their own rules. So we only are here for literal matches
// of terminal expressions.
co_yield { lhs, tcc };
} );
// LocationId unification rule
ruleSet.addUnificationRule( TCRINFOS, TERM( LocationId( 0 ) ), []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
// This rule doesn't have any semantic impact, it's just here to try and preserve locationIds
// and poisoning accross unification as much as possible. This only impacts the quality of
// error messages.
auto lloc = static_cast< uint32_t >( get< LocationId >( lhs ) );
auto rloc = static_cast< uint32_t >( get< LocationId >( rhs ) );
// We keep the max location index of the two:
// - if either loc is invalid (0), this will take the other one.
// - if either loc is poisoned (~0), the result will be poisoned.
// - otherwise it will pick the most recently created location of the two,
// which is as good an heuristic as any.
uint32_t result = max( lloc, rloc );
co_yield { static_cast< LocationId >( result ), tcc };
} );
// void* unification rule
ruleSet.addUnificationRule( TCRINFOS, TERM( nullptr ), []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
// Only preserve void* accross unification when they are equal. Otherwise,
// just clear it: it's mostly an optimisation to try and keep llvm types
// around as much as possible, but if we can't, codegen will recompute them
// in the end if necessary.
auto lpv = get< void* >( lhs );
auto rpv = get< void* >( rhs );
auto result = lpv == rpv ? lpv : nullptr;
co_yield { result, tcc };
} );
// Half-Unification rule for vectors:
// They are recursed into and each contained members are half-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.addHalfUnificationRule( TCRINFOS, VECOFLENGTH( _ ), []( const Term& lhs, const TypeCheckingContext& tcc ) -> TCGen
{
assert( holds_alternative< pvec >( lhs ) );
const auto& lVector = get< pvec >( lhs );
const auto& lTerms = lVector->terms();
if( lTerms.empty() )
{
co_yield { lhs, tcc };
co_return;
}
Vector sol;
co_yield HalfUnifyVectors( lTerms, 0, sol, tcc );
} );
// 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
{
assert( holds_alternative< pvec >( lhs ) );
assert( holds_alternative< pvec >( rhs ) );
const auto& lVector = get< pvec >( lhs );
const auto& rVector = get< pvec >( rhs );
const auto& lTerms = lVector->terms();
const auto& rTerms = rVector->terms();
if( lTerms.empty() )
{
co_yield { lhs, tcc };
co_return;
}
Vector sol;
co_yield UnifyVectors( lTerms, rTerms, 0, sol, tcc );
} );
ruleSet.addTypeCheckingRule( TCRINFOS, VECOFLENGTH( VL ), VECOFLENGTH( VL ), []( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
assert( holds_alternative< pvec >( lhs ) );
assert( holds_alternative< pvec >( rhs ) );
const auto& lVector = get< pvec >( lhs );
const auto& rVector = get< pvec >( rhs );
const auto& lTerms = lVector->terms();
const auto& rTerms = rVector->terms();
if( lTerms.empty() )
{
co_yield { lhs, tcc };
co_return;
}
Vector sol;
co_yield TypeCheckVectors( lTerms, rTerms, 0, sol, tcc );
} );
}
}
|
Changes to bs/sema/uni-basicrules.h.
1 2 3 4 5 |
#ifndef GOOSE_SEMA_UNI_BASICRULES_H
#define GOOSE_SEMA_UNI_BASICRULES_H
namespace goose::sema
{
| | | 1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_SEMA_UNI_BASICRULES_H
#define GOOSE_SEMA_UNI_BASICRULES_H
namespace goose::sema
{
extern void SetupBasicUnificationRules( TypeCheckingRuleSet& ruleSet );
}
#endif
|
Changes to bs/sema/uni-holes.cpp.
1 2 3 4 |
#include "sema.h"
namespace goose::sema
{
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > | > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
#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 ) -> TCGen
{
tcc.addAnonymousHole();
co_yield { lhs, tcc };
} );
// 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();
for( auto&& [s,tcc] : 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 ) -> TCGen
{
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 ) )
co_yield { HOLE( StringId( holeIndex ) ), tcc };
}
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 );
co_yield { HOLE( StringId( index ) ), tcc };
}
}
else
{
// This is already an indexed hole: yield it as is.
if( !tcc.isHoleLocked( lh.name.id() ) )
co_yield { lhs, tcc };
}
} );
// Hole vs anything
ruleSet.addUnificationRule( TCRINFOS,
HolePattern(),
ANYTERM( _ ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
auto h = *HoleFromIRExpr( lhs );
uint32_t index = 0;
// Remember the previous complexity count so we know how much complexity
// is added by this particular sub-term. This is because we need
// to be able to subtract it when updating the hole's value with a new solution.
uint32_t oldComplexity = tcc.complexity();
if( h.name.isNumerical() )
index = h.name.id();
else
{
// This is a named hole: look up its name.
index = tcc.getLHSHoleIndex( h.name );
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 ) );
for( auto&& [e,tcc] : HalfUnify( rhs, tcc.flip() ) )
{
tcc.setValue( index, SetComplexity( move( e ), tcc.complexity() - oldComplexity ) );
co_yield { move( holeExpr ), tcc.flip() };
}
co_return;
}
}
// Reject recursive hole nesting.
if( tcc.isHoleLocked( index ) )
co_return;
tcc.lockHole( index );
auto holeExpr = HOLE( StringId( index ) );
auto& maybeVal = tcc.getValue( index );
if( maybeVal )
{
for( auto&& [e,tcc] : Unify( *maybeVal, rhs, tcc ) )
{
tcc.unlockHole( index );
tcc.setValue( index, SetComplexity( move( e ), tcc.complexity() - oldComplexity ) );
co_yield { move( holeExpr ), tcc };
}
}
else
{
for( auto&& [e,tcc] : HalfUnify( rhs, tcc.flip() ) )
{
tcc.unlockHole( index );
tcc.setValue( index, SetComplexity( move( e ), tcc.complexity() - oldComplexity ) );
co_yield { move( holeExpr ), tcc.flip() };
}
}
} );
// Hole vs hole
ruleSet.addUnificationRule( TCRINFOS,
HolePattern(),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
auto lh = *HoleFromIRExpr( lhs );
auto rh = *HoleFromIRExpr( rhs );
StringId lname;
StringId rname;
uint32_t lindex = 0;
uint32_t rindex = 0;
if( !lh.name.isNumerical() )
{
// L is a named hole: look up its name.
lname = lh.name;
lindex = tcc.getLHSHoleIndex( lname );
}
else
lindex = lh.name.id();
if( !rh.name.isNumerical() )
{
// R is a named hole: look up its name.
rname = rh.name;
rindex = tcc.getRHSHoleIndex( rname );
}
else
rindex = rh.name.id();
// If neither hole currently have a value, create a new one.
if( lindex == TypeCheckingContext::InvalidIndex && rindex == TypeCheckingContext::InvalidIndex )
{
auto index = tcc.createValue();
tcc.setLHSHoleIndex( lname, index );
tcc.setRHSHoleIndex( rname, index );
co_yield { HOLE( StringId( index ) ), tcc };
co_return;
}
// If both holes actually point to the same value, just yield it as the solution.
if( lindex == rindex )
{
co_yield { HOLE( StringId( lindex ) ), tcc };
co_return;
}
// If either hole doesn't have a value yet, assign it the other one's value.
if( lindex == TypeCheckingContext::InvalidIndex )
{
tcc.setLHSHoleIndex( lname, rindex );
co_yield { HOLE( StringId( rindex ) ), tcc };
co_return;
}
if( rindex == TypeCheckingContext::InvalidIndex )
{
tcc.setRHSHoleIndex( rname, lindex );
co_yield { HOLE( StringId( lindex ) ), tcc };
co_return;
}
// Reject recursive hole nesting.
if( tcc.isHoleLocked( lindex ) )
co_return;
if( tcc.isHoleLocked( rindex ) )
co_return;
tcc.lockHole( lindex );
tcc.lockHole( rindex );
// If either hole have an empty value, set it to a hole expression with the id of the value
// stored in the other one. We can't just copy the value over as we would lose the dependency
// relationship between the two holes.
const auto& lval = tcc.getValue( lindex );
const auto& rval = tcc.getValue( rindex );
if( !rval )
{
for( auto&& [e,tcc] : 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 )
{
for( auto&& [e,tcc] : HalfUnify( *rval, tcc.flip() ) )
{
tcc.unlockHole( lindex );
tcc.unlockHole( rindex );
tcc.setValue( lindex, HOLE( StringId( rindex ) ) );
co_yield { HOLE( StringId( rindex ) ), tcc.flip() };
}
co_return;
}
// Both L and R have a value: unify them, store the result in lhs,
// replace rhs with a hole expression pointing to lhs's value.
// Remember the previous complexity count so we know how much complexity
// is added by this particular sub-term. This is because we need
// to be able to subtract it when updating the hole's value with a new solution.
uint32_t oldComplexity = tcc.complexity();
for( auto&& [e,tcc] : Unify( *lval, *rval, tcc ) )
{
tcc.unlockHole( lindex );
tcc.unlockHole( rindex );
tcc.setValue( lindex, SetComplexity( move( e ), tcc.complexity() - oldComplexity ) );
tcc.setValue( rindex, HOLE( StringId( lindex ) ) );
co_yield { HOLE( StringId( lindex ) ), tcc };
}
} );
// Any typecheck involving a hole on one side
// becomes an unification
ruleSet.addTypeCheckingRule( TCRINFOS,
HolePattern(),
ANYTERM( _ ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
co_yield Unify( lhs, rhs, tcc );
} );
ruleSet.addTypeCheckingRule( TCRINFOS,
ANYTERM( _ ),
HolePattern(),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
co_yield Unify( lhs, rhs, tcc );
} );
ruleSet.addTypeCheckingRule( TCRINFOS,
HolePattern(),
HolePattern(),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
co_yield Unify( lhs, rhs, tcc );
} );
}
}
|
Changes to bs/sema/uni-holes.h.
1 2 3 4 5 |
#ifndef GOOSE_SEMA_UNI_HOLES_H
#define GOOSE_SEMA_UNI_HOLES_H
namespace goose::sema
{
| | | 1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_SEMA_UNI_HOLES_H
#define GOOSE_SEMA_UNI_HOLES_H
namespace goose::sema
{
extern void SetupHoleUnificationRules( TypeCheckingRuleSet& ruleSet );
}
#endif
|
Changes to bs/sema/uni-quote.cpp.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 |
if( !result )
return nullopt;
auto&& [quoted] = *result;
return quoted;
}
| | | | | | | | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
if( !result )
return nullopt;
auto&& [quoted] = *result;
return quoted;
}
void SetupQuoteUnificationRules( TypeCheckingRuleSet& ruleSet )
{
ruleSet.addHalfUnificationRule( TCRINFOS, VEC( TSID( quote ), ANYTERM( _ ) ),
[]( const Term& lhs, const TypeCheckingContext& tcc ) -> TCGen
{
co_yield { lhs, tcc };
} );
ruleSet.addUnificationRule( TCRINFOS, VEC( TSID( quote ), ANYTERM( _ ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
if( lhs == rhs )
co_yield { lhs, tcc };
} );
}
}
|
Changes to bs/sema/uni-quote.h.
1 2 3 4 5 6 7 8 9 10 11 12 |
#ifndef GOOSE_SEMA_UNI_QUOTE_H
#define GOOSE_SEMA_UNI_QUOTE_H
namespace goose::sema
{
// Expressions can be quoted so that they aren't recursed into during
// unification. This allows to carry unification patterns around (like function
// signatures) without them causing an unification failure because their holes remain
// unresolved.
extern Term Quote( const Term& t );
extern optional< Term > Unquote( const Term& t );
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#ifndef GOOSE_SEMA_UNI_QUOTE_H
#define GOOSE_SEMA_UNI_QUOTE_H
namespace goose::sema
{
// Expressions can be quoted so that they aren't recursed into during
// unification. This allows to carry unification patterns around (like function
// signatures) without them causing an unification failure because their holes remain
// unresolved.
extern Term Quote( const Term& t );
extern optional< Term > Unquote( const Term& t );
extern void SetupQuoteUnificationRules( TypeCheckingRuleSet& ruleSet );
}
#endif
|