Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Add clang format settings, reformat everything |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | cir-ssa-refactor |
| Files: | files | file ages | folders |
| SHA3-256: |
0db147f1175506e2c569635cad3031e7 |
| User & Date: | achavasse 2024-09-15 20:24:31.168 |
| Original Comment: | Add clanf format settings, reformat everything |
Context
|
2024-09-15
| ||
| 21:15 |
| |
| 20:24 | Add clang format settings, reformat everything check-in: 0db147f117 user: achavasse tags: cir-ssa-refactor | |
|
2024-09-13
| ||
| 21:05 | Drop in the most apocalyptic changes: new CIR instruction structure, new sequence builder, new compile time address representation, do almost just the bare minimum to compile again. A few simple things are still working somehow check-in: 57d4d0c79e user: achavasse tags: cir-ssa-refactor | |
Changes
Added .clang-format.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | AccessModifierOffset: -2 AlignAfterOpenBracket: false AlignArrayOfStructures: Right AlignConsecutiveAssignments: Enabled: false AcrossEmptyLines: true AcrossComments: false AlignConsecutiveBitFields: Enabled: false AcrossEmptyLines: true AcrossComments: false AlignConsecutiveDeclarations: Enabled: false AcrossEmptyLines: true AcrossComments: false AlignConsecutiveMacros: Enabled: false AcrossEmptyLines: true AcrossComments: false AlignConsecutiveShortCaseStatements: Enabled: false AcrossEmptyLines: true AcrossComments: true AlignCaseColons: false AlignEscapedNewlines: DontAlign AlignOperands: DontAlign AlignTrailingComments: Kind: Never OverEmptyLines: 2 AllowAllArgumentsOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowBreakBeforeNoexceptSpecifier: OnlyWithParen AllowShortBlocksOnASingleLine: Empty AllowShortCaseLabelsOnASingleLine: false AllowShortCompoundRequirementOnASingleLine: true AllowShortEnumsOnASingleLine: false AllowShortFunctionsOnASingleLine: Inline AllowShortIfStatementsOnASingleLine: false AllowShortLambdasOnASingleLine: true AllowShortLoopsOnASingleLine: false AlwaysBreakBeforeMultilineStrings: true BinPackArguments: true BinPackParameters: true BitFieldColonSpacing: After BracedInitializerIndentWidth: 4 BreakAdjacentStringLiterals: true BreakAfterAttributes: Always BreakAfterReturnType: Automatic BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Allman BreakBeforeConceptDeclarations: Always BreakBeforeTernaryOperators: false BreakBinaryOperations: Never BreakConstructorInitializers: AfterColon BreakFunctionDefinitionParameters: false BreakInheritanceList: AfterComma BreakStringLiterals: true BreakTemplateDeclarations: Yes CompactNamespaces: true ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 ColumnLimit: 100 Cpp11BracedListStyle: false DerivePointerAlignment: false EmptyLineAfterAccessModifier: Never EmptyLineBeforeAccessModifier: Always ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true IncludeBlocks: Regroup IndentAccessModifiers: false IndentCaseBlocks: false IndentCaseLabels: true IndentExternBlock: true IndentGotoLabels: false IndentPPDirectives: BeforeHash IndentRequiresClause: true IndentWidth: 4 IndentWrappedFunctionNames: false InsertBraces: false InsertNewlineAtEOF: true InsertTrailingCommas: None IntegerLiteralSeparator: Binary: 4 Decimal: 3 DecimalMinDigits: 5 Hex: 4 HexMinDigits: 6 KeepEmptyLines: AtEndOfFile: false AtStartOfBlock: false AtStartOfFile: false LambdaBodyIndentation: Signature MainIncludeChar: Any MaxEmptyLinesToKeep: 1 NamespaceIndentation: All PPIndentWidth: 4 PackConstructorInitializers: Never PointerAlignment: Left QualifierAlignment: Left QualifierOrder: [static, inline, const, type, volatile] ReferenceAlignment: Left ReflowComments: true RemoveBracesLLVM: true RemoveParentheses: ReturnStatement RemoveSemicolon: true RequiresClausePosition: WithPreceding RequiresExpressionIndentation: OuterScope SeparateDefinitionBlocks: Always ShortNamespaceLines: 2 SkipMacroDefinitionBody: false SortIncludes: Never SortUsingDeclarations: LexicographicNumeric SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: false SpaceAroundPointerQualifiers: After SpaceBeforeAssignmentOperators: true SpaceBeforeCaseColon: false SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: false SpaceBeforeRangeBasedForLoopColon: true SpaceBeforeSquareBrackets: false SpaceInEmptyBlock: false SpacesBeforeTrailingComments: 1 SpacesInAngles: Always SpacesInContainerLiterals: false SpacesInLineCommentPrefix: Minimum: 1 Maximum: -1 SpacesInParens: Custom SpacesInParensOptions: ExceptDoubleParentheses: false InConditionalStatements: true InEmptyParentheses: false Other: true SpacesInSquareBrackets: false Standard: c++20 TabWidth: 4 UseTab: Never |
Added .vscode/launch.json.
> > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Launch",
"program": "${workspaceFolder}/../dbuild/goose",
"args": [],
"cwd": "${workspaceFolder}/../dbuild/"
}
]
}
|
Changes to bs/builtins/builders/cb_interface.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupCodeBuilderInterface( Env& 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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupCodeBuilderInterface( Env& e )
{
RegisterBuiltinFunc< Eager< void >( TypeWrapper< ptr< CodeBuilder > > ) >( e,
e.extPoisonBuilder(),
[]( const TypeWrapper< ptr< CodeBuilder > >& cb ) { cb->poison(); } );
RegisterBuiltinFunc< Eager< bool >( TypeWrapper< ptr< CodeBuilder > > ) >( e,
e.extBuilderAllowsOverloading(),
[]( const TypeWrapper< ptr< CodeBuilder > >& cb ) { return true; } );
RegisterBuiltinFunc< Eager< TypeWrapper< ptr< cir::CFG > > >(
TypeWrapper< ptr< CodeBuilder > > ) >( e, e.extGetCFG(),
[]( const TypeWrapper< ptr< CodeBuilder > >& cb ) { return cb->cfg(); } );
RegisterBuiltinFunc< Eager< uint32_t >( TypeWrapper< ptr< CodeBuilder > > ) >( e,
e.extGetBreakableScopeLevels(), []( const TypeWrapper< ptr< CodeBuilder > >& cb )
{ return cb->breakableScopeLevels(); } );
RegisterBuiltinFunc< Eager< uint32_t >( TypeWrapper< ptr< CodeBuilder > > ) >( e,
e.extGetContinuableScopeLevels(), []( const TypeWrapper< ptr< CodeBuilder > >& cb )
{ return cb->continuableScopeLevels(); } );
RegisterBuiltinFunc< Eager< void >( TypeWrapper< ptr< CodeBuilder > > ) >( e,
e.extBeginLifetimeScope(),
[]( const TypeWrapper< ptr< CodeBuilder > >& cb ) { cb->beginLifetimeScope(); } );
RegisterBuiltinFunc< Intrinsic< void( TypeWrapper< ptr< CodeBuilder > > ) > >( e,
e.extEndLifetimeScope(),
[]( auto&& c, const Value& b )
{
auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( b );
cb->endLifetimeScope( c );
} );
RegisterBuiltinFunc< Eager< void >( TypeWrapper< ptr< CodeBuilder > > ) >( e,
e.extBeginBreakableScope(),
[]( const TypeWrapper< ptr< CodeBuilder > >& cb ) { cb->beginBreakableScope(); } );
RegisterBuiltinFunc< Eager< void >( TypeWrapper< ptr< CodeBuilder > > ) >( e,
e.extEndBreakableScope(),
[]( const TypeWrapper< ptr< CodeBuilder > >& cb ) { cb->endBreakableScope(); } );
RegisterBuiltinFunc< Eager< void >( TypeWrapper< ptr< CodeBuilder > > ) >( e,
e.extBeginContinuableScope(),
[]( const TypeWrapper< ptr< CodeBuilder > >& cb ) { cb->beginContinuableScope(); } );
RegisterBuiltinFunc< Eager< void >( TypeWrapper< ptr< CodeBuilder > > ) >( e,
e.extEndContinuableScope(),
[]( const TypeWrapper< ptr< CodeBuilder > >& cb ) { cb->endContinuableScope(); } );
RegisterBuiltinFunc<
Intrinsic< void( TypeWrapper< ptr< CodeBuilder > >, Value, uint32_t ) > >( e,
e.extDeclareValue(),
[]( auto&& c, const Value& cbv, const Value& v, const Value& index )
{
auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( cbv );
cb->declareValue( c, v, *FromValue< uint32_t >( index ) );
} );
RegisterBuiltinFunc< Intrinsic< bool( TypeWrapper< ptr< CodeBuilder > >, Value ) > >( e,
e.extDestroyLiveValue(),
[]( auto&& c, const Value& cbv, const Value& val )
{
auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( cbv );
return ToValue( cb->destroyLiveValue( c, val ) );
} );
RegisterBuiltinFunc< Intrinsic< bool( TypeWrapper< ptr< CodeBuilder > > ) > >( e,
e.extDestroyAllLiveValues(),
[]( auto&& c, const Value& cbv )
{
auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( cbv );
return ToValue( cb->destroyAllLiveValues( c ) );
} );
RegisterBuiltinFunc< Intrinsic< bool( TypeWrapper< ptr< CodeBuilder > >, uint32_t ) > >( e,
e.extDestroyAllLiveValuesFromBreakScope(),
[]( auto&& c, const Value& cbv, const Value& index )
{
auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( cbv );
return ToValue(
cb->destroyAllLiveValuesFromBreakScope( c, *FromValue< uint32_t >( index ) ) );
} );
RegisterBuiltinFunc< Intrinsic< bool( TypeWrapper< ptr< CodeBuilder > >, uint32_t ) > >( e,
e.extDestroyAllLiveValuesFromContinueScope(),
[]( auto&& c, const Value& cbv, const Value& index )
{
auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( cbv );
return ToValue( cb->destroyAllLiveValuesFromContinueScope(
c, *FromValue< uint32_t >( index ) ) );
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/builders/codebuilder.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_BUILTINS_BUILDERS_CODEBUILDER_H
#define GOOSE_BUILTINS_BUILDERS_CODEBUILDER_H
namespace goose::builtins
{
class CodeBuilder : public LifeCycleManager< CodeBuilder >
{
| | | | | < | > > | | | | | | | < | | | | | | | 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 |
#ifndef GOOSE_BUILTINS_BUILDERS_CODEBUILDER_H
#define GOOSE_BUILTINS_BUILDERS_CODEBUILDER_H
namespace goose::builtins
{
class CodeBuilder : public LifeCycleManager< CodeBuilder >
{
public:
template< typename C >
CodeBuilder( C&& cfg ) :
m_cfg( forward< C >( cfg ) )
{
}
const auto& cfg() const { return m_cfg; }
void poison()
{
if( m_cfg )
m_cfg->poison();
}
template< typename V > void append( V&& v )
{
cfg()->currentBB()->append( forward< V >( v ) );
}
private:
ptr< cir::CFG > m_cfg;
};
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/builders/default_interface.cpp.
1 2 3 4 5 6 7 8 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
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 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
// Default builder behavior, for cases where we don't want a builder at all, or for simple
// builders that need to overload only a few builder operations.
void SetupDefaultBuilderInterface( Env& e )
{
RegisterBuiltinFunc< Eager< void >( Value ) >(
e, e.extPoisonBuilder(), []( const Value& b ) {} );
RegisterBuiltinFunc< Eager< bool >( Value ) >(
e, e.extBuilderAllowsOverloading(), []( const Value& b ) { return false; } );
RegisterBuiltinFunc< Eager< TypeWrapper< ptr< cir::CFG > > >( Value ) >(
e, e.extGetCFG(), []( const Value& b ) { return nullptr; } );
RegisterBuiltinFunc< Eager< uint32_t >( Value ) >(
e, e.extGetBreakableScopeLevels(), []( const Value& b ) { return 0; } );
RegisterBuiltinFunc< Eager< uint32_t >( Value ) >(
e, e.extGetContinuableScopeLevels(), []( const Value& b ) { return 0; } );
RegisterBuiltinFunc< Intrinsic< void( Value ) > >( e, e.extBeginVisibilityScope(),
[]( auto&& c, const Value& b ) { c.beginVisibilityScope(); } );
RegisterBuiltinFunc< Eager< void >( Value ) >(
e, e.extBeginLifetimeScope(), []( const Value& b ) {} );
RegisterBuiltinFunc< Eager< void >( Value ) >(
e, e.extEndLifetimeScope(), []( const Value& b ) {} );
RegisterBuiltinFunc< Eager< void >( Value ) >(
e, e.extBeginBreakableScope(), []( const Value& b ) {} );
RegisterBuiltinFunc< Eager< void >( Value ) >(
e, e.extEndBreakableScope(), []( const Value& b ) {} );
RegisterBuiltinFunc< Eager< void >( Value ) >(
e, e.extBeginContinuableScope(), []( const Value& b ) {} );
RegisterBuiltinFunc< Eager< void >( Value ) >(
e, e.extEndContinuableScope(), []( const Value& b ) {} );
RegisterBuiltinFunc< Intrinsic< void( Value, Value, uint32_t ) > >( e, e.extDeclareValue(),
[]( auto&& c, const Value& b, const Value& v, const Value& index ) {} );
RegisterBuiltinFunc< Intrinsic< void( Value, Value, uint32_t ) > >( e,
e.extOnValueDeclared(),
[]( auto&& c, const Value& b, const Value& v, const Value& index ) {} );
RegisterBuiltinFunc< Eager< bool >( Value ) >(
e, e.extDestroyLiveValue(), []( const Value& b ) { return true; } );
RegisterBuiltinFunc< Eager< bool >( Value ) >(
e, e.extDestroyAllLiveValues(), []( const Value& b ) { return true; } );
RegisterBuiltinFunc< Eager< bool >( Value, uint32_t ) >( e,
e.extDestroyAllLiveValuesFromBreakScope(),
[]( const Value& b, uint32_t index ) { return true; } );
RegisterBuiltinFunc< Eager< bool >( Value, uint32_t ) >( e,
e.extDestroyAllLiveValuesFromContinueScope(),
[]( const Value& b, uint32_t index ) { return true; } );
RegisterBuiltinFunc< Eager< bool >( Value ) >(
e, e.extInVerificationCode(), []( const Value& b ) { return false; } );
RegisterBuiltinFunc< Eager< bool >( Value ) >(
e, e.extIsBranchingAllowed(), []( const Value& b ) { return true; } );
}
} // namespace goose::builtins
|
Changes to bs/builtins/builders/ghostcode_interface.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupGhostCodeBuilderInterface( Env& 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 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 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupGhostCodeBuilderInterface( Env& e )
{
RegisterBuiltinFunc< Eager< void >( TypeWrapper< ptr< GhostCode > > ) >( e,
e.extPoisonBuilder(),
[]( const TypeWrapper< ptr< GhostCode > >& gcb ) { gcb->poison(); } );
RegisterBuiltinFunc< Eager< bool >( TypeWrapper< ptr< GhostCode > > ) >( e,
e.extInVerificationCode(),
[]( const TypeWrapper< ptr< GhostCode > >& gcb ) { return true; } );
RegisterBuiltinFunc< Eager< bool >( TypeWrapper< ptr< GhostCode > > ) >( e,
e.extIsBranchingAllowed(),
[]( const TypeWrapper< ptr< GhostCode > >& gcb ) { return gcb->branchingAllowed(); } );
RegisterBuiltinFunc< Eager< TypeWrapper< ptr< cir::CFG > > >(
TypeWrapper< ptr< GhostCode > > ) >( e, e.extGetCFG(),
[]( const TypeWrapper< ptr< GhostCode > >& gcb ) { return gcb->cfg(); } );
RegisterBuiltinFunc< Eager< void >( TypeWrapper< ptr< GhostCode > > ) >( e,
e.extBeginLifetimeScope(),
[]( const TypeWrapper< ptr< GhostCode > >& gcb ) { gcb->beginLifetimeScope(); } );
RegisterBuiltinFunc< Intrinsic< void( TypeWrapper< ptr< GhostCode > > ) > >( e,
e.extEndLifetimeScope(),
[]( auto&& c, const Value& b )
{
auto gcb = *FromValue< TypeWrapper< ptr< GhostCode > > >( b );
gcb->endLifetimeScope( c );
} );
RegisterBuiltinFunc<
Intrinsic< void( TypeWrapper< ptr< GhostCode > >, Value, uint32_t ) > >( e,
e.extDeclareValue(),
[]( auto&& c, const Value& cbv, const Value& v, const Value& index )
{
auto gcb = *FromValue< TypeWrapper< ptr< GhostCode > > >( cbv );
gcb->declareValue( c, v, *FromValue< uint32_t >( index ) );
} );
RegisterBuiltinFunc< Intrinsic< bool( TypeWrapper< ptr< GhostCode > >, Value ) > >( e,
e.extDestroyLiveValue(),
[]( auto&& c, const Value& cbv, const Value& val )
{
auto gcb = *FromValue< TypeWrapper< ptr< GhostCode > > >( cbv );
return ToValue( gcb->destroyLiveValue( c, val ) );
} );
RegisterBuiltinFunc< Intrinsic< bool( TypeWrapper< ptr< GhostCode > > ) > >( e,
e.extDestroyAllLiveValues(),
[]( auto&& c, const Value& cbv )
{
auto gcb = *FromValue< TypeWrapper< ptr< GhostCode > > >( cbv );
return ToValue( gcb->destroyAllLiveValues( c ) );
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/builders/lifecycle_manager.h.
1 2 3 4 5 |
#ifndef GOOSE_BUILTINS_BUILDERS_LIFECYCLE_MANAGER_H
#define GOOSE_BUILTINS_BUILDERS_LIFECYCLE_MANAGER_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 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 |
#ifndef GOOSE_BUILTINS_BUILDERS_LIFECYCLE_MANAGER_H
#define GOOSE_BUILTINS_BUILDERS_LIFECYCLE_MANAGER_H
namespace goose::builtins
{
template< class C > class LifeCycleManager
{
public:
void beginLifetimeScope() { ++m_lifetimeScopeLevels; }
void endLifetimeScope( Context& c );
void beginBreakableScope() { ++m_breakableScopeLevels; }
void endBreakableScope() { --m_breakableScopeLevels; }
void beginContinuableScope() { ++m_continuableScopeLevels; }
void endContinuableScope() { --m_continuableScopeLevels; }
uint32_t lifetimeScopeLevels() const { return m_lifetimeScopeLevels; }
uint32_t breakableScopeLevels() const { return m_breakableScopeLevels; }
uint32_t continuableScopeLevels() const { return m_continuableScopeLevels; }
template< typename T > void declareValue( const Context& c, T&& v, uint32_t index )
{
OnValueDeclared( c, v, index );
m_liveValuesList.emplace_front( forward< T >( v ), index, m_lifetimeScopeLevels,
m_breakableScopeLevels, m_continuableScopeLevels );
}
// Extend the life duration of a live value to the current lifetime scope.
void extendValueLifetime( uint32_t index );
// Destruction
// Destroys an individual value by invoking _DestroyValue, appends any resulting cir
// to the current basic block if necessary.
bool destroyLiveValue( const Context& c, const Value& v, CURRENT_LOC );
// Invoke the destroy extension point for every live variable, but leave them on the
// stack. To be called before emitting a return terminator.
bool destroyAllLiveValues( const Context& c );
// Invoke the destroy extension point for every live variable that belong in the
// specified break scope and every child scope, but leave them on the stack. To be
// called before emitting a break terminator.
bool destroyAllLiveValuesFromBreakScope( const Context& c, uint32_t breakScopeLevel );
// Invoke the destroy extension point for every live variable that belong in the
// specified continue scope and every child scope, but leave them on the stack. To be
// called before emitting a continue terminator.
bool destroyAllLiveValuesFromContinueScope( const Context& c, uint32_t continueScopeLevel );
private:
C& crtp() { return static_cast< C& >( *this ); }
// Invoke the destroy extension point for every live variable from the current
// lifetime scope, and pops off the stack. To be called when exiting a lifetime scope.
// Returns false if something goes wrong, in which case the cfg will also be marked
// as poisoned.
// Note: it is called automatically by LifetimeScopeGuard.
void destroyCurrentLifetimeScopeValues( const Context& c );
// The number of nested lifetime scopes.
uint32_t m_lifetimeScopeLevels = 0;
// The number of nested breakable scopes.
uint32_t m_breakableScopeLevels = 0;
// The number of nested continuable scopes.
uint32_t m_continuableScopeLevels = 0;
// The live values stack, used to track when
// to destroy values (such as local variables going out of scope)
struct LiveValue
{
LiveValue() {}
template< typename T >
LiveValue( T&& v, uint32_t index, uint32_t lt, uint32_t b, uint32_t c ) :
m_value( forward< T >( v ) ),
m_index( index ),
m_lifetimeScopeLevel( lt ),
m_breakableScopeLevel( b ),
m_continuableScopeLevel( c )
{
}
Value m_value;
uint32_t m_index = 0;
// We keep track of those to determine which values to
// destroy when exiting a scope, emitting a break terminator,
// and emitting a continue terminator, respectively.
uint32_t m_lifetimeScopeLevel = 0;
uint32_t m_breakableScopeLevel = 0;
uint32_t m_continuableScopeLevel = 0;
};
list< LiveValue > m_liveValuesList;
};
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/builders/lifecycle_manager.inl.
1 2 3 4 5 |
#ifndef GOOSE_BUILTINS_BUILDERS_LIFECYCLE_MANAGER_INL
#define GOOSE_BUILTINS_BUILDERS_LIFECYCLE_MANAGER_INL
namespace goose::builtins
{
| < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 |
#ifndef GOOSE_BUILTINS_BUILDERS_LIFECYCLE_MANAGER_INL
#define GOOSE_BUILTINS_BUILDERS_LIFECYCLE_MANAGER_INL
namespace goose::builtins
{
template< class C > void LifeCycleManager< C >::endLifetimeScope( Context& c )
{
destroyCurrentLifetimeScopeValues( c );
--m_lifetimeScopeLevels;
}
template< class C >
void LifeCycleManager< C >::destroyCurrentLifetimeScopeValues( const Context& c )
|
| ︙ | ︙ | |||
26 27 28 29 30 31 32 |
return;
}
it = m_liveValuesList.erase( it );
}
}
| < | | > | > | > | | < | | < < < < < | < | | 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 |
return;
}
it = m_liveValuesList.erase( it );
}
}
template< class C > bool LifeCycleManager< C >::destroyAllLiveValues( const Context& c )
{
for( auto it = m_liveValuesList.begin(); it != m_liveValuesList.end(); ++it )
{
if( !destroyLiveValue( c, it->m_value ) )
{
m_liveValuesList.clear();
crtp().poison();
return false;
}
}
return true;
}
template< class C >
bool LifeCycleManager< C >::destroyAllLiveValuesFromBreakScope(
const Context& c, uint32_t breakScopeLevel )
{
for( auto it = m_liveValuesList.begin(); it != m_liveValuesList.end(); ++it )
{
if( it->m_breakableScopeLevel < breakScopeLevel )
return true;
if( !destroyLiveValue( c, it->m_value ) )
{
m_liveValuesList.clear();
crtp().poison();
return false;
}
}
return true;
}
template< class C >
bool LifeCycleManager< C >::destroyAllLiveValuesFromContinueScope(
const Context& c, uint32_t continueScopeLevel )
{
for( auto it = m_liveValuesList.begin(); it != m_liveValuesList.end(); ++it )
{
if( it->m_continuableScopeLevel < continueScopeLevel )
return true;
if( !destroyLiveValue( c, it->m_value ) )
{
m_liveValuesList.clear();
crtp().poison();
return false;
}
}
return true;
}
template< class C >
bool LifeCycleManager< C >::destroyLiveValue(
const Context& c, const Value& v, source_location sloc )
{
if( v.isPoison() )
{
crtp().poison();
return false;
}
DiagnosticsContext dc( v.locationId(), "When invoking _DestroyValue." );
auto result = InvokeOverloadSet(
c, c.env()->extDestroyValue(), MakeClosedTuple( v ), Location::Create( sloc ) );
if( result.isPoison() )
{
crtp().poison();
return false;
}
if( !result.isConstant() )
crtp().append( move( result ) );
return true;
}
template< class C > void LifeCycleManager< C >::extendValueLifetime( uint32_t index )
{
// Find the live value.
auto it = find_if( m_liveValuesList.begin(), m_liveValuesList.end(),
[&]( auto&& lv ) { return lv.m_index == index; } );
if( it == m_liveValuesList.end() )
return;
--it->m_lifetimeScopeLevel;
// Look for the last value that belongs to the parent lifetime scope, if any.
auto last = find_if( it, m_liveValuesList.end(),
[&]( auto&& lv ) { return lv.m_lifetimeScopeLevel < it->m_lifetimeScopeLevel; } );
// Reinsert the value just after the last value of the parent scope (the one we
// just found), like it would be if it was inserted in that lifetime scope in the first
// place.
auto lvToExtend = move( *it );
m_liveValuesList.erase( it );
m_liveValuesList.emplace( last, move( lvToExtend ) );
}
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/builders/props_interface.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupPropositionsBuilderInterface( Env& 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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupPropositionsBuilderInterface( Env& e )
{
RegisterBuiltinFunc< Eager< void >( TypeWrapper< ptr< Propositions > > ) >( e,
e.extPoisonBuilder(),
[]( const TypeWrapper< ptr< Propositions > >& pb ) { pb->poison(); } );
RegisterBuiltinFunc< Eager< bool >( TypeWrapper< ptr< Propositions > > ) >( e,
e.extInVerificationCode(),
[]( const TypeWrapper< ptr< Propositions > >& pb ) { return true; } );
RegisterBuiltinFunc< Eager< void >( TypeWrapper< ptr< Propositions > > ) >( e,
e.extBeginLifetimeScope(),
[]( const TypeWrapper< ptr< Propositions > >& pb ) { pb->beginLifetimeScope(); } );
RegisterBuiltinFunc< Intrinsic< void( TypeWrapper< ptr< Propositions > > ) > >( e,
e.extEndLifetimeScope(),
[]( auto&& c, const Value& b )
{
auto pb = *FromValue< TypeWrapper< ptr< Propositions > > >( b );
pb->endLifetimeScope( c );
} );
RegisterBuiltinFunc<
Intrinsic< void( TypeWrapper< ptr< Propositions > >, Value, uint32_t ) > >( e,
e.extDeclareValue(),
[]( auto&& c, const Value& cbv, const Value& v, const Value& index )
{
auto pb = *FromValue< TypeWrapper< ptr< Propositions > > >( cbv );
pb->declareValue( c, v, *FromValue< uint32_t >( index ) );
} );
RegisterBuiltinFunc< Intrinsic< bool( TypeWrapper< ptr< Propositions > >, Value ) > >( e,
e.extDestroyLiveValue(),
[]( auto&& c, const Value& cbv, const Value& val )
{
auto pb = *FromValue< TypeWrapper< ptr< Propositions > > >( cbv );
return ToValue( pb->destroyLiveValue( c, val ) );
} );
RegisterBuiltinFunc< Intrinsic< bool( TypeWrapper< ptr< Propositions > > ) > >( e,
e.extDestroyAllLiveValues(),
[]( auto&& c, const Value& cbv )
{
auto pb = *FromValue< TypeWrapper< ptr< Propositions > > >( cbv );
return ToValue( pb->destroyAllLiveValues( c ) );
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/builders/struct_interface.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupStructBuilderInterface( Env& 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 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 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupStructBuilderInterface( Env& e )
{
RegisterBuiltinFunc< Eager< TypeWrapper< ptr< cir::CFG > > >(
TypeWrapper< ptr< StructBuilder > > ) >( e, e.extGetCFG(),
[]( const TypeWrapper< ptr< StructBuilder > >& sb ) { return sb->defaultCtorCFG(); } );
// Default implementation for dropvalue inside a struct builder:
// Append side effects of computed void values to the current CFG (needed by member
// initialisers), otherwise error out. The things that we handle differently inside of
// structs (such as decls) will have struct specific implementations of dropValue().
RegisterBuiltinFunc< Intrinsic< void( TypeWrapper< ptr< StructBuilder > >, Value ) > >( e,
e.extDropValue(),
[]( const Context& c, const Value& b, const Value& v )
{
if( v.isConstant() || v.type() != GetValueType< void >()
|| !DoesInstrSeqHaveSideEffects( *v.cir() ) )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
v.locationId(), "this is not allowed here.", 0 );
PoisonBuilder( c );
return;
}
auto cfg = GetCFG( c );
if( !cfg )
return;
auto bb = cfg->currentBB();
bb->append( move( *v.cir() ) );
} );
using AnyDeclType = CustomPattern< Decl, Decl::Pattern >;
using AnyDeclWithInitType = CustomPattern< DeclWithInit, DeclWithInit::Pattern >;
// DropValue for Decl inside a struct: declare a member value.
RegisterBuiltinFunc<
Intrinsic< void( TypeWrapper< ptr< StructBuilder > >, AnyDeclType ) > >( e,
e.extDropValue(),
[]( const Context& c, const Value& b, const Value& v )
{
auto sb = *FromValue< TypeWrapper< ptr< StructBuilder > > >( b );
auto decl = *FromValue< Decl >( v );
sb->declareMemberVar( c, decl.name(), move( decl.type() ), v.locationId() );
} );
// DropValue for DeclWithInit inside a struct: declare a member value with initialization.
// TODO: TNamedDecls
RegisterBuiltinFunc<
Intrinsic< void( TypeWrapper< ptr< StructBuilder > >, AnyDeclWithInitType ) > >( e,
e.extDropValue(),
[]( const Context& c, const Value& b, const Value& v )
{
auto sb = *FromValue< TypeWrapper< ptr< StructBuilder > > >( b );
auto decl = *FromValue< DeclWithInit >( v );
sb->declareMemberVar(
c, decl.name(), move( decl.type() ), v.locationId(), move( decl.init() ) );
} );
// Finalize for structs: call the builder's finalize method, which will use all the
// collected members to actually create the struct
RegisterBuiltinFunc< Intrinsic< bool( TypeWrapper< ptr< StructBuilder > >,
TypePatternParam< StructType, StructType::Pattern > ) > >( e, e.extFinalize(),
[]( const Context& c, const Value& b, const Value& st )
{
auto sb = *FromValue< TypeWrapper< ptr< StructBuilder > > >( b );
return ToValue( sb->finalize( c, st ) );
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/builtins.cpp.
| ︙ | ︙ | |||
40 41 42 43 44 45 46 |
e.extEndContinuableScope() = CreateOverloadSet( e, "_EndContinuableScope"_sid );
e.extDeclareValue() = CreateOverloadSet( e, "_DeclareValue"_sid );
e.extOnValueDeclared() = CreateOverloadSet( e, "_OnValueDeclared"_sid );
e.extDestroyLiveValue() = CreateOverloadSet( e, "_DestroyLiveValue"_sid );
e.extDestroyAllLiveValues() = CreateOverloadSet( e, "_DestroyAllLiveValues"_sid );
| | > | > | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
e.extEndContinuableScope() = CreateOverloadSet( e, "_EndContinuableScope"_sid );
e.extDeclareValue() = CreateOverloadSet( e, "_DeclareValue"_sid );
e.extOnValueDeclared() = CreateOverloadSet( e, "_OnValueDeclared"_sid );
e.extDestroyLiveValue() = CreateOverloadSet( e, "_DestroyLiveValue"_sid );
e.extDestroyAllLiveValues() = CreateOverloadSet( e, "_DestroyAllLiveValues"_sid );
e.extDestroyAllLiveValuesFromBreakScope() =
CreateOverloadSet( e, "_DestroyAllLiveValuesFromBreakScope"_sid );
e.extDestroyAllLiveValuesFromContinueScope() =
CreateOverloadSet( e, "_DestroyAllLiveValuesFromContinueScope"_sid );
e.extInVerificationCode() = CreateOverloadSet( e, "_InVerificationCode"_sid );
e.extIsBranchingAllowed() = CreateOverloadSet( e, "_IsBranchingAllowed"_sid );
e.extCTForEach() = CreateOverloadSet( e, "_CTForEach"_sid );
SetupBuiltinTypes( e );
|
| ︙ | ︙ | |||
65 66 67 68 69 70 71 |
}
const Term& RootG0Identity()
{
static auto identity = VEC( TSID( g0 ) );
return identity;
}
| | | 67 68 69 70 71 72 73 74 |
}
const Term& RootG0Identity()
{
static auto identity = VEC( TSID( g0 ) );
return identity;
}
} // namespace goose::builtins
|
Changes to bs/builtins/builtins.h.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
namespace goose::builtins
{
using namespace eir;
using namespace sema;
using namespace diagnostics;
extern const Term& RootG0Identity();
| | | | 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 |
namespace goose::builtins
{
using namespace eir;
using namespace sema;
using namespace diagnostics;
extern const Term& RootG0Identity();
} // namespace goose::builtins
#include "codegen/codegen.h"
#include "helpers.h"
#include "builders/builders.h"
#include "types/types.h"
#include "operators/operators.h"
#include "statements/statements.h"
#include "exprhelpers.h"
#include "builders/lifecycle_manager.inl"
namespace goose::builtins
{
extern void SetupDefaultBuilderInterface( Env& e );
extern void SetupCodeBuilderInterface( Env& e );
extern void SetupPropositionsBuilderInterface( Env& e );
extern void SetupGhostCodeBuilderInterface( Env& e );
extern void SetupStructBuilderInterface( Env& e );
extern void SetupBuiltins( Env& e );
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/exprhelpers.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_BUILTINS_EXPRHELPERS_H
#define GOOSE_BUILTINS_EXPRHELPERS_H
#include "parse/parse.h"
namespace goose::builtins::exprhelpers
{
| | < | | | < | | > | < | | > | < | | > | < | | > | < | | > | < | | > | < | | > | < | | > | < | > | | < | | > | < | | > | < | | > | | 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 |
#ifndef GOOSE_BUILTINS_EXPRHELPERS_H
#define GOOSE_BUILTINS_EXPRHELPERS_H
#include "parse/parse.h"
namespace goose::builtins::exprhelpers
{
template< typename V > Value Not( V&& val )
{
auto locId = val.locationId();
return BuildComputedValue( GetValueType< bool >(), forward< V >( val ), cir::Not( locId ) )
.setLocationId( locId );
}
template< typename L, typename R > Value Or( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue(
GetValueType< bool >(), forward< L >( lhs ), forward< R >( rhs ), cir::Or( locId ) )
.setLocationId( locId );
}
template< typename L, typename R > Value And( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue(
GetValueType< bool >(), forward< L >( lhs ), forward< R >( rhs ), cir::And( locId ) )
.setLocationId( locId );
}
template< typename L, typename R > Value Eq( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue(
GetValueType< bool >(), forward< L >( lhs ), forward< R >( rhs ), cir::Eq( locId ) )
.setLocationId( locId );
}
template< typename L, typename R > Value Neq( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue(
GetValueType< bool >(), forward< L >( lhs ), forward< R >( rhs ), cir::Neq( locId ) )
.setLocationId( locId );
}
template< typename L, typename R > Value UGT( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue(
GetValueType< bool >(), forward< L >( lhs ), forward< R >( rhs ), cir::UGT( locId ) )
.setLocationId( locId );
}
template< typename L, typename R > Value UGE( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue(
GetValueType< bool >(), forward< L >( lhs ), forward< R >( rhs ), cir::UGE( locId ) )
.setLocationId( locId );
}
template< typename L, typename R > Value ULT( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue(
GetValueType< bool >(), forward< L >( lhs ), forward< R >( rhs ), cir::ULT( locId ) )
.setLocationId( locId );
}
template< typename L, typename R > Value ULE( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue(
GetValueType< bool >(), forward< L >( lhs ), forward< R >( rhs ), cir::ULE( locId ) )
.setLocationId( locId );
}
template< typename L, typename R > Value SGT( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue(
GetValueType< bool >(), forward< L >( lhs ), forward< R >( rhs ), cir::SGT( locId ) )
.setLocationId( locId );
}
template< typename L, typename R > Value SGE( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue(
GetValueType< bool >(), forward< L >( lhs ), forward< R >( rhs ), cir::SGE( locId ) )
.setLocationId( locId );
}
template< typename L, typename R > Value SLT( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue(
GetValueType< bool >(), forward< L >( lhs ), forward< R >( rhs ), cir::SLT( locId ) )
.setLocationId( locId );
}
template< typename L, typename R > Value SLE( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue(
GetValueType< bool >(), forward< L >( lhs ), forward< R >( rhs ), cir::SLE( locId ) )
.setLocationId( locId );
}
} // namespace goose::builtins::exprhelpers
#endif
|
Changes to bs/builtins/helpers.cpp.
1 2 3 4 5 6 7 8 |
#include "builtins/builtins.h"
using namespace goose::parse;
namespace goose::builtins
{
void RegisterRule( sema::Env& env, StringId name, parse::Rule&& rule )
{
| > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include "builtins/builtins.h"
using namespace goose::parse;
namespace goose::builtins
{
void RegisterRule( sema::Env& env, StringId name, parse::Rule&& rule )
{
parse::RegisterRule(
env, AppendToVectorTerm( RootG0Identity(), TERM( name ) ), move( rule ) );
}
ptr< cir::BasicBlock > ParseSubStatement( Parser& p, uint32_t precedence )
{
auto next = p.resolver()->lookAheadUnresolved();
if( !next )
return nullptr;
|
| ︙ | ︙ | |||
39 40 41 42 43 44 45 |
else if( !np.parseExpression( precedence ) )
return nullptr;
np.flushValue();
return bb;
}
| | > | | < | | | | | | | | | | | | | < | | | | | | | | | | | | | > | > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < | | | | | | < < < < < < < < < < < < < < < < < | 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 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 |
else if( !np.parseExpression( precedence ) )
return nullptr;
np.flushValue();
return bb;
}
variant< Value, ValUnifyError > ConvertValueToType(
const Context& c, const Value& val, const Term& type )
{
auto valTerm = ValueToEIR( 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, _] = get< TCSol >( us );
return *EIRToValue( s );
}
void PoisonBuilder( const Context& c, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _PoisonBuilder." );
InvokeOverloadSet( c, c.env()->extPoisonBuilder(), MakeClosedTuple( c.builder() ), loc );
}
bool BuilderAllowsOverloading( const Context& c, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _BuilderAllowsOverloading." );
auto result = InvokeOverloadSet(
c, c.env()->extBuilderAllowsOverloading(), MakeClosedTuple( c.builder() ), loc );
if( result.isPoison() )
return false;
auto success = FromValue< bool >( result );
if( !success )
return false;
return *success;
}
ptr< cir::CFG > GetCFG( const Context& c, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _GetCFG." );
auto result =
InvokeOverloadSet( c, c.env()->extGetCFG(), MakeClosedTuple( c.builder() ), loc );
if( result.isPoison() )
return {};
auto cfg = FromValue< TypeWrapper< ptr< cir::CFG > > >( result );
if( !cfg )
return {};
return *cfg;
}
uint32_t GetBreakableScopeLevels( const Context& c, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _GetBreakableScopeLevels." );
auto result = InvokeOverloadSet(
c, c.env()->extGetBreakableScopeLevels(), MakeClosedTuple( c.builder() ), loc );
if( result.isPoison() )
return 0;
auto levels = FromValue< uint32_t >( result );
if( !levels )
return 0;
return *levels;
}
uint32_t GetContinuableScopeLevels( const Context& c, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _GetContinuableScopeLevels." );
auto result = InvokeOverloadSet(
c, c.env()->extGetContinuableScopeLevels(), MakeClosedTuple( c.builder() ), loc );
if( result.isPoison() )
return 0;
auto levels = FromValue< uint32_t >( result );
if( !levels )
return 0;
return *levels;
}
void BeginVisibilityScope( Context& c, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _BeginVisibilityScope." );
InvokeOverloadSet(
c, c.env()->extBeginVisibilityScope(), MakeClosedTuple( c.builder() ), loc );
}
void BeginLifetimeScope( const Context& c, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _BeginLifetimeScope." );
InvokeOverloadSet(
c, c.env()->extBeginLifetimeScope(), MakeClosedTuple( c.builder() ), loc );
}
void EndLifetimeScope( const Context& c, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _EndLifetimeScope." );
InvokeOverloadSet( c, c.env()->extEndLifetimeScope(), MakeClosedTuple( c.builder() ), loc );
}
void BeginBreakableScope( const Context& c, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _BeginBreakableScope." );
InvokeOverloadSet(
c, c.env()->extBeginBreakableScope(), MakeClosedTuple( c.builder() ), loc );
}
void EndBreakableScope( const Context& c, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _EndBreakableScope." );
InvokeOverloadSet(
c, c.env()->extEndBreakableScope(), MakeClosedTuple( c.builder() ), loc );
}
void BeginContinuableScope( const Context& c, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _BeginContinuableScope." );
InvokeOverloadSet(
c, c.env()->extBeginContinuableScope(), MakeClosedTuple( c.builder() ), loc );
}
void EndContinuableScope( const Context& c, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _EndContinuableScope." );
InvokeOverloadSet(
c, c.env()->extEndContinuableScope(), MakeClosedTuple( c.builder() ), loc );
}
void DeclareValue( const Context& c, const Value& val, uint32_t index, source_location sloc )
{
DiagnosticsContext dc( val.locationId(), "When invoking _DeclareValue." );
InvokeOverloadSet( c, c.env()->extDeclareValue(),
MakeClosedTuple( c.builder(), val, ToValue( index ) ), Location::Create( sloc ) );
}
void OnValueDeclared( const Context& c, const Value& val, uint32_t index, source_location sloc )
{
DiagnosticsContext dc( val.locationId(), "When invoking _OnValueDeclared." );
InvokeOverloadSet( c, c.env()->extOnValueDeclared(),
MakeClosedTuple( c.builder(), val, ToValue( index ) ), Location::Create( sloc ) );
}
bool DestroyLiveValue( const Context& c, const Value& val, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _DestroyLiveValue." );
auto result = InvokeOverloadSet(
c, c.env()->extDestroyLiveValue(), MakeClosedTuple( c.builder(), val ), loc );
if( result.isPoison() )
return false;
auto success = FromValue< bool >( result );
if( !success )
return false;
return *success;
}
bool DestroyAllLiveValues( const Context& c, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _DestroyAllLiveValues." );
auto result = InvokeOverloadSet(
c, c.env()->extDestroyAllLiveValues(), MakeClosedTuple( c.builder() ), loc );
if( result.isPoison() )
return false;
auto success = FromValue< bool >( result );
if( !success )
return false;
return *success;
}
bool DestroyAllLiveValuesFromBreakScope(
const Context& c, uint32_t breakScopeLevel, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _DestroyAllLiveValuesFromBreakScope." );
auto result = InvokeOverloadSet( c, c.env()->extDestroyAllLiveValuesFromBreakScope(),
MakeClosedTuple( c.builder(), ToValue( breakScopeLevel ) ), loc );
if( result.isPoison() )
return false;
auto success = FromValue< bool >( result );
if( !success )
return false;
return *success;
}
bool DestroyAllLiveValuesFromContinueScope(
const Context& c, uint32_t continueScopeLevel, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _DestroyAllLiveValuesFromContinueScope." );
auto result = InvokeOverloadSet( c, c.env()->extDestroyAllLiveValuesFromContinueScope(),
MakeClosedTuple( c.builder(), ToValue( continueScopeLevel ) ), loc );
if( result.isPoison() )
return false;
auto success = FromValue< bool >( result );
if( !success )
return false;
return *success;
}
bool InVerificationCode( const Context& c, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _InVerificationCode." );
auto result = InvokeOverloadSet(
c, c.env()->extInVerificationCode(), MakeClosedTuple( c.builder() ), loc );
if( result.isPoison() )
return false;
auto success = FromValue< bool >( result );
if( !success )
return false;
return *success;
}
bool IsBranchingAllowed( const Context& c, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _IsBranchingAllowed." );
auto result = InvokeOverloadSet(
c, c.env()->extIsBranchingAllowed(), MakeClosedTuple( c.builder() ), loc );
if( result.isPoison() )
return false;
auto success = FromValue< bool >( result );
if( !success )
return false;
return *success;
}
void CTForEach( const Context& c, const Value& decl, const Value& container,
const ptr< vector< TermLoc > >& body, source_location sloc )
{
DiagnosticsContext dc( decl.locationId(), "When invoking _CTForEach." );
InvokeOverloadSet( c, c.env()->extCTForEach(),
MakeClosedTuple( decl, container, Wrap( body ) ), Location::Create( sloc ) );
}
bool IsTrivialInitialization(
const Context& c, const Value& lhsType, const Value& rhsType, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _IsTrivialInitialization." );
auto result = InvokeOverloadSet(
c, c.env()->extIsTrivialInitialization(), MakeClosedTuple( lhsType, rhsType ), loc );
if( result.isPoison() )
return false;
auto success = FromValue< bool >( result );
if( !success )
return false;
return *success;
}
bool IsTrivialAssignment(
const Context& c, const Value& lhsType, const Value& rhsType, source_location sloc )
{
auto loc = Location::Create( sloc );
DiagnosticsContext dc( loc, "When invoking _IsTrivialAssignment." );
auto result = InvokeOverloadSet(
c, c.env()->extIsTrivialAssignment(), MakeClosedTuple( lhsType, rhsType ), loc );
if( result.isPoison() )
return false;
auto success = FromValue< bool >( result );
if( !success )
return false;
return *success;
}
} // namespace goose::builtins
|
Changes to bs/builtins/helpers.h.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
namespace goose::builtins
{
class Propositions;
extern const Term& EmptyPredicates();
| < | | > | > | | | < | > | < | > < | | 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 |
namespace goose::builtins
{
class Propositions;
extern const Term& EmptyPredicates();
template< typename T > void DefineConstant( sema::Env& env, StringId name, T&& val )
{
env.storeValue( AppendToVectorTerm( RootG0Identity(), TERM( name ) ), ANYTERM( _ ),
forward< T >( val ) );
}
extern void RegisterRule( sema::Env& env, StringId name, parse::Rule&& rule );
// Utility function used to parse flow control statements, such as if and loops.
// This parses a sub statement, which can be enclosed in a brace block or not.
// It will get its own scope, with visibility rules setup to see the current
// scope.
// It returns a pointer to the final basic block generated by the statement.
ptr< cir::BasicBlock > ParseSubStatement( parse::Parser& p, uint32_t precedence );
enum class ValUnifyError
{
NoSolution,
Ambiguous
};
// Typecheck the provided value with a value placeholder of the specified type,
// and return the result or an error code.
variant< Value, ValUnifyError > ConvertValueToType(
const Context& c, const Value& val, const Term& type );
// Helpers to create a standard type construction, as a vector starting with an identity,
// followed by an optional pointer to predicates and an optional pointer to a llvm type.
template< typename I, typename... T > auto MkStdType( I&& identity, T&&... extra )
{
return VEC( forward< I >( identity ), EmptyPredicates(), TERM( (void*)nullptr ),
forward< T >( extra )... );
}
template< typename I > auto MkStdType( I&& identity )
{
return VEC( forward< I >( identity ), EmptyPredicates(), TERM( (void*)nullptr ) );
}
// Same as above, but directly specify the llvm type pointer.
template< typename I, typename... T >
auto MkStdRTType( I&& identity, const void* cgType, T&&... extra )
{
return VEC( forward< I >( identity ), EmptyPredicates(), const_cast< void* >( cgType ),
forward< T >( extra )... );
}
template< typename I > auto MkStdRTType( I&& identity, const void* cgType )
{
return VEC( forward< I >( identity ), EmptyPredicates(), const_cast< void* >( cgType ) );
}
extern void PoisonBuilder( const Context& c, CURRENT_LOC );
extern bool BuilderAllowsOverloading( const Context& c, CURRENT_LOC );
|
| ︙ | ︙ | |||
84 85 86 87 88 89 90 |
extern void EndContinuableScope( const Context& c, CURRENT_LOC );
extern void DeclareValue( const Context& c, const Value& val, uint32_t index, CURRENT_LOC );
extern void OnValueDeclared( const Context& c, const Value& val, uint32_t index, CURRENT_LOC );
extern bool DestroyLiveValue( const Context& c, const Value& val, CURRENT_LOC );
extern bool DestroyAllLiveValues( const Context& c, CURRENT_LOC );
| | > | > | > > | > | > | > | | | 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 |
extern void EndContinuableScope( const Context& c, CURRENT_LOC );
extern void DeclareValue( const Context& c, const Value& val, uint32_t index, CURRENT_LOC );
extern void OnValueDeclared( const Context& c, const Value& val, uint32_t index, CURRENT_LOC );
extern bool DestroyLiveValue( const Context& c, const Value& val, CURRENT_LOC );
extern bool DestroyAllLiveValues( const Context& c, CURRENT_LOC );
extern bool DestroyAllLiveValuesFromBreakScope(
const Context& c, uint32_t breakScopeLevel, CURRENT_LOC );
extern bool DestroyAllLiveValuesFromContinueScope(
const Context& c, uint32_t continueScopeLevel, CURRENT_LOC );
extern bool InVerificationCode( const Context& c, CURRENT_LOC );
extern bool IsBranchingAllowed( const Context& c, CURRENT_LOC );
extern void CTForEach( const Context& c, const Value& decl, const Value& container,
const ptr< vector< TermLoc > >& body, CURRENT_LOC );
extern bool IsTrivialInitialization(
const Context& c, const Value& lhsType, const Value& rhsType, CURRENT_LOC );
extern bool IsTrivialAssignment(
const Context& c, const Value& lhsType, const Value& rhsType, CURRENT_LOC );
struct TypeTypePattern
{
static const Term& GetPattern()
{
static auto pat = TypeType();
return pat;
}
};
struct CTTypePattern
{
static const Term& GetPattern()
{
static auto pat = ValueToEIR(
Value( TypeType(), VEC( TSID( ct_type ), REPEAT( HOLE( "_"_sid ) ) ) ) );
return pat;
}
};
struct RTTypePattern
{
static const Term& GetPattern()
{
static auto pat = ValueToEIR(
Value( TypeType(), VEC( TSID( rt_type ), REPEAT( HOLE( "_"_sid ) ) ) ) );
return pat;
}
};
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/operators/apostrophe.cpp.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
namespace goose::builtins
{
void SetupApostropheOp( Env& e )
{
Rule r;
| | > | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 |
namespace goose::builtins
{
void SetupApostropheOp( Env& e )
{
Rule r;
MakeLeftAssInfixOp( e, r, "'"_sid, precedence::ApostropheOp,
[]( auto&& p, auto&& lhs, auto&& rhs )
{
bool isTVarAS = lhs.type() != GetValueType< AccessSpecifier >();
bool isTVarLT = rhs.type() != GetValueType< Lifetime >();
if( isTVarAS && !IsTVar( lhs ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage( lhs.locationId(),
"expected an access specifier or a template variable before the ' "
"operator." );
return PoisonValue();
}
if( isTVarLT && !IsTVar( rhs ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage( rhs.locationId(),
"expected an lifetime or a template variable after the ' operator." );
return PoisonValue();
}
// If the lifetime is a TVar, wrap it into a TDecl constrained to the lifetime type.
if( isTVarLT )
{
auto tv = *FromValue< TVar >( rhs );
rhs = ToValue( TDecl( GetValueType< Lifetime >(), tv.name() ) )
.setLocationId( rhs.locationId() );
}
// Likewise for the access specifier.
if( isTVarAS )
{
auto tv = *FromValue< TVar >( lhs );
lhs = ToValue( TDecl( GetValueType< AccessSpecifier >(), tv.name() ) )
.setLocationId( lhs.locationId() );
return ToValue( AccessSpecifier{ ValueToEIR( lhs ), ValueToEIR( rhs ) } );
}
auto as = *FromValue< AccessSpecifier >( lhs );
if( as.lifetime() != Lifetime::Unspecified() )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( lhs.locationId(),
"a lifetime has already been attached to this access specifier." );
return PoisonValue();
}
as.lifetime() = ValueToEIR( rhs );
return ToValue( as );
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/operators/arith.cpp.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
using namespace goose::parse;
namespace goose::builtins
{
void SetupArithOps( Env& e )
{
BuildParseRule( e, "+"_sid,
| | < < | < | < | < < | < | < | < | | < < | < > | | | | | | | | | | | | | < | < | < | < | < < | < | < | < | < < | < | | | | | | | | | | | < | < | < | | | | | | | | | | | | < | < | < | < < | < | | | | | | | | | | | < | < | < | | | | | | | | | | | | < | < | < | < < < > | 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 |
using namespace goose::parse;
namespace goose::builtins
{
void SetupArithOps( Env& e )
{
BuildParseRule( e, "+"_sid,
PrefixOp( "operator_unary_plus"_sid, precedence::UnaryOps, BuildGenericTupleOperator(),
ForType< BigInt >( []< typename O >( auto&& c, O&& operand ) -> Value
{ return forward< O >( operand ); } ),
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]< typename O >( auto&& c, O&& operand ) -> Value
{ return forward< O >( operand ); } ) ),
LeftAssInfixOp( "operator_add"_sid, precedence::AddSubOp, BuildGenericTupleOperator(),
ForType< BigInt, Add >(),
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value {
return BuildComputedValue( lhs.type(), lhs, rhs, Add( c.locationId() ) );
} ) ) );
BuildParseRule( e, "-"_sid,
PrefixOp( "operator_unary_minus"_sid, precedence::UnaryOps, BuildGenericTupleOperator(),
ForType< BigInt >(
[]( auto&& c, auto&& operand ) -> Value
{
return BuildComputedValue( GetValueType< BigInt >(), ToValue( BigInt() ),
operand, Sub( c.locationId() ) );
} ),
ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
[]( auto&& c, auto&& operand ) -> Value
{
auto opTypeVal = *EIRToValue( operand.type() );
auto opType = *FromValue< IntegerType >( opTypeVal );
return BuildComputedValue( operand.type(),
Value( operand.type(), APSInt( opType.m_numBits, false ) ), operand,
Sub( c.locationId() ) );
} ) ),
LeftAssInfixOp( "operator_sub"_sid, precedence::AddSubOp, BuildGenericTupleOperator(),
ForType< BigInt, Sub >(),
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value {
return BuildComputedValue( lhs.type(), lhs, rhs, Sub( c.locationId() ) );
} ) ) );
BuildParseRule( e, "*"_sid,
LeftAssInfixOp( "operator_multiply"_sid, precedence::MulDivOp,
BuildGenericTupleOperator(), ForType< BigInt, Mul >(),
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value {
return BuildComputedValue( lhs.type(), lhs, rhs, Mul( c.locationId() ) );
} ) ) );
BuildParseRule( e, "/"_sid,
LeftAssInfixOp( "operator_divide"_sid, precedence::MulDivOp,
BuildGenericTupleOperator(), ForType< BigInt, SDiv >(),
ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
using namespace goose::builtins::exprhelpers;
auto cfg = GetCFG( c );
assert( cfg );
// Build a zero constant of the same type as the denominator
// to construct the assertion expression.
auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
auto cond = Neq( rhs, zeroValue );
DiagnosticsManager::GetInstance().defineCustomDiagnostic(
cond.locationId(), "assert"_sid, "the divisor may be 0." );
cfg->currentBB()->append( move( cond ), cir::Assert( rhs.locationId() ) );
return BuildComputedValue( lhs.type(), lhs, rhs, SDiv( c.locationId() ) );
} ),
ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
using namespace goose::builtins::exprhelpers;
auto cfg = GetCFG( c );
assert( cfg );
// Build a zero constant of the same type as the denominator
// to construct the assertion expression.
auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
auto cond = Neq( rhs, zeroValue );
DiagnosticsManager::GetInstance().defineCustomDiagnostic(
cond.locationId(), "assert"_sid, "the divisor may be 0." );
cfg->currentBB()->append( move( cond ), cir::Assert( rhs.locationId() ) );
return BuildComputedValue( lhs.type(), lhs, rhs, UDiv( c.locationId() ) );
} ) ) );
BuildParseRule( e, "%"_sid,
LeftAssInfixOp( "operator_modulo"_sid, precedence::MulDivOp,
BuildGenericTupleOperator(), ForType< BigInt, SRem >(),
ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
using namespace goose::builtins::exprhelpers;
auto cfg = GetCFG( c );
assert( cfg );
// Build a zero constant of the same type as the denominator
// to construct the assertion expression.
auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
auto cond = Neq( rhs, zeroValue );
DiagnosticsManager::GetInstance().defineCustomDiagnostic(
cond.locationId(), "assert"_sid, "the divisor may be 0." );
cfg->currentBB()->append( move( cond ), cir::Assert( rhs.locationId() ) );
return BuildComputedValue( lhs.type(), lhs, rhs, SRem( c.locationId() ) );
} ),
ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
using namespace goose::builtins::exprhelpers;
auto cfg = GetCFG( c );
assert( cfg );
// Build a zero constant of the same type as the denominator
// to construct the assertion expression.
auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
auto cond = Neq( rhs, zeroValue );
DiagnosticsManager::GetInstance().defineCustomDiagnostic(
cond.locationId(), "assert"_sid, "the divisor may be 0." );
cfg->currentBB()->append( move( cond ), cir::Assert( rhs.locationId() ) );
return BuildComputedValue( lhs.type(), lhs, rhs, URem( c.locationId() ) );
} ) ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/operators/assignment.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include "builtins/builtins.h"
#include "precedence.h"
#include "helpers.h"
#include "tuple.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::cir;
using namespace goose::parse;
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 |
#include "builtins/builtins.h"
#include "precedence.h"
#include "helpers.h"
#include "tuple.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::cir;
using namespace goose::parse;
namespace goose::builtins
{
extern Value NonRefTupleAssignment(
const ptr< OverloadSet >& assOp, Context& c, const Value& lhs, const Value& rhs );
extern Value MutRefTupleAssignment(
const ptr< OverloadSet >& assOp, Context& c, const Value& lhs, const Value& rhs );
void SetupAssignmentOps( Env& e )
{
auto assOp = GetOrCreateOverloadSet( e, "operator_assign"_sid );
// Generic function to perform the assignation of a builtin type
// to a mutable reference of that same type.
auto BuiltinTypeAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
G_VAL_ASSERT( lhs, !lhs.isConstant() );
auto refType = *FromValue< ReferenceType >( *EIRToValue( lhs.type() ) );
if( !ParseTypePredicates( c, *EIRToValue( refType.type() ) ) )
return PoisonValue();
auto cfg = GetCFG( c );
if( !cfg )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
0, "assignments are not allowed here." );
return PoisonValue();
}
else if( auto bb = cfg->currentBB() )
bb->append( rhs, lhs, Store( refType.type(), rhs.locationId(), lhs.locationId() ) );
// Return the ref to support chained assignments, like in c++.
return Value( lhs );
};
// Generic function to assign a value to a decl: create a DeclWithInit
auto DeclAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
auto decl = *FromValue< Decl >( lhs );
return ToValue( DeclWithInit( decl.type(), decl.name(), rhs, lhs.locationId() ) );
};
// Generic function to assign a value to a template named decl (local variable declaration
// with local type inference)
auto TNamedDeclAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
auto tndecl = *FromValue< TNamedDecl >( lhs );
return ToValue(
TNamedDeclWithInit( tndecl.type(), tndecl.name(), rhs, lhs.locationId() ) );
};
BuildParseRule( e, "="_sid,
RightAssInfixOp( "operator_assign"_sid, precedence::AssignmentOp,
// Assignment with a decl of type $T to the left and a value of type $T to the
// right: Local variable declaration and initialization.
ForTypes< CustomPattern< Decl, Decl::Pattern >,
CustomPattern< Value, ForwarderPattern > >( DeclAssignment ),
// Assignment with a tnameddecl to the left and a value to the right:
// Local variable declaration and initialization with type inference.
ForTypes< CustomPattern< TNamedDecl, TNamedDecl::Pattern >,
CustomPattern< Value, ForwarderPattern > >( TNamedDeclAssignment ),
// Compilation time tuple from compilation time tuple
ForTypes< CustomConstantPattern< Value, TuplePattern >,
CustomConstantPattern< Value, TuplePattern > >(
[assOp]( auto&& c, auto&& lhs, auto&& rhs )
{ return NonRefTupleAssignment( assOp, c, lhs, rhs ); } ),
// Compilation time tuple from tuple reference
ForTypes< CustomConstantPattern< Value, TuplePattern >,
CustomPattern< Value, ReferenceType::PatternAnyOf< TuplePattern > > >(
[assOp]( auto&& c, auto&& lhs, auto&& rhs )
{ return NonRefTupleAssignment( assOp, c, lhs, rhs ); } ),
// Mutable tuple ref from compilation time tuple
ForTypes< CustomPattern< Value, ReferenceType::PatternMutableOf< TuplePattern > >,
CustomConstantPattern< Value, TuplePattern > >(
[assOp]( auto&& c, auto&& lhs, auto&& rhs )
{ return MutRefTupleAssignment( assOp, c, lhs, rhs ); } ),
// Mutable tuple ref from tuple reference
ForTypes< CustomPattern< Value, ReferenceType::PatternMutableOf< TuplePattern > >,
CustomPattern< Value, ReferenceType::PatternAnyOf< TuplePattern > > >(
[assOp]( auto&& c, auto&& lhs, auto&& rhs )
{ return MutRefTupleAssignment( assOp, c, lhs, rhs ); } ),
// Mutable ref assignment for compile time builtin types
ForTypes< CustomPattern< Value, ReferenceType::PatternMutableOf< CTTypePattern > >,
CustomPattern< Value, ValuePatternT > >( BuiltinTypeAssignment ),
// Mutable ref assignment for run time builtin types
ForTypes< CustomPattern< Value, ReferenceType::PatternMutableOf< RTTypePattern > >,
CustomPattern< Value, ValuePatternT > >( BuiltinTypeAssignment ),
// Explicit overload for assigning an rt_int to an rt_int reference:
// We need this to be able to assign a ct_int to a rt_int: the generic version
// above can't perform the necessary implicit conversion.
ForTypes<
CustomPattern< Value, ReferenceType::PatternMutableOf< IntegerType::Pattern > >,
CustomPattern< Value, IntegerType::Pattern > >( BuiltinTypeAssignment ) ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/operators/comma.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include "builtins/builtins.h"
#include "precedence.h"
#include "helpers.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::parse;
namespace goose::builtins
{
void SetupCommaOp( Env& 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 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 |
#include "builtins/builtins.h"
#include "precedence.h"
#include "helpers.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::parse;
namespace goose::builtins
{
void SetupCommaOp( Env& e )
{
using RefOfAnyType = CustomPattern< Value, ReferenceType::PatternAny >;
BuildParseRule( e, ","_sid,
LeftAssInfixOp( "operator_comma"_sid, precedence::CommaOp,
// Overload: open tuple, open tuple
ForTypes< CustomPattern< Value, TupleOpenPattern >,
CustomConstantPattern< Value, TupleOpenPattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{ return ConcatenateTuples( lhs, rhs ); } ),
// Overload: open tuple, anything
ForTypes< CustomConstantPattern< Value, TupleOpenPattern >, Value >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{ return AppendToTuple( lhs, rhs ); } ),
// Overload: anything, open tuple
ForTypes< Value, CustomConstantPattern< Value, TupleOpenPattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{ return PrependToTuple( lhs, rhs ); } ),
// Overloads for computed tuples
ForTypes< CustomPattern< Value, TupleOpenPattern >,
CustomPattern< Value, TupleOpenPattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
G_ERROR( "concatenation of computed tuples is not yet implemented." );
return PoisonValue();
} ),
// Overload: open tuple, anything
ForTypes< CustomPattern< Value, TupleOpenPattern >, Value >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
G_ERROR( "appending to a computed tuple is not yet implemented." );
return PoisonValue();
} ),
// Overload: anything, open tuple
ForTypes< Value, CustomPattern< Value, TupleOpenPattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
G_ERROR( "prepending to a computed tuple is not yet implemented." );
return PoisonValue();
} ),
// Overload: anything, anything
ForType< Value >( []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{ return AppendToTuple( AppendToTuple( EmptyOpenTuple(), lhs ), rhs ); } ),
// We want to be able to put references in tuples without them getting
// automatically dereferenced, so we have specific overloads that treat
// them explicitely.
ForType< RefOfAnyType >( []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{ return AppendToTuple( AppendToTuple( EmptyOpenTuple(), lhs ), rhs ); } ),
ForTypes< Value, RefOfAnyType >( []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{ return AppendToTuple( AppendToTuple( EmptyOpenTuple(), lhs ), rhs ); } ),
ForTypes< RefOfAnyType, Value >( []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{ return AppendToTuple( AppendToTuple( EmptyOpenTuple(), lhs ), rhs ); } ),
ForTypes< CustomConstantPattern< Value, TupleOpenPattern >, RefOfAnyType >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{ return AppendToTuple( lhs, rhs ); } ),
ForTypes< RefOfAnyType, CustomConstantPattern< Value, TupleOpenPattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{ return PrependToTuple( lhs, rhs ); } ) ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/operators/comparison.cpp.
1 2 3 4 5 6 7 8 9 10 | #include "builtins/builtins.h" #include "precedence.h" #include "helpers.h" #include "tuple.h" using namespace goose; using namespace goose::eir; using namespace goose::cir; using namespace goose::parse; | | > | < | < | < | | | | < | < < < | < | < | < | | | | < | < < < | < | | | | < | | | < < | < | | | | < | | | < < | < | | | | < | | | < < | < | | | | < | | | < < < > | 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 |
#include "builtins/builtins.h"
#include "precedence.h"
#include "helpers.h"
#include "tuple.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::cir;
using namespace goose::parse;
// TODO: tuple comparisons. Bit of a pita to implement and cba right now. Maybe something to
// implement in the lib.
namespace goose::builtins
{
void SetupComparisonOps( Env& e )
{
BuildParseRule( e, "=="_sid,
LeftAssInfixOp( "operator_equals"_sid, precedence::EqualityOp,
ForType< bool, Eq, bool >(), ForType< BigInt, Eq, bool >(),
ForType< char32_t, Eq, bool >(), ForType< string, Eq, bool >(),
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value {
return BuildComputedValue(
GetValueType< bool >(), lhs, rhs, Eq( c.locationId() ) );
} ),
ForType< CustomPattern< Value, TypeTypePattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{ return ToValue( lhs == rhs ); } ) ) );
BuildParseRule( e, "!="_sid,
LeftAssInfixOp( "operator_not_equals"_sid, precedence::EqualityOp,
ForType< bool, Neq, bool >(), ForType< BigInt, Neq, bool >(),
ForType< char32_t, Neq, bool >(), ForType< string, Neq, bool >(),
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value {
return BuildComputedValue(
GetValueType< bool >(), lhs, rhs, Neq( c.locationId() ) );
} ),
ForType< CustomPattern< Value, TypeTypePattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{ return ToValue( lhs != rhs ); } ) ) );
BuildParseRule( e, ">"_sid,
LeftAssInfixOp( "operator_greater"_sid, precedence::GreaterLesserOp,
ForType< BigInt, SGT, bool >(),
ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value {
return BuildComputedValue(
GetValueType< bool >(), lhs, rhs, SGT( c.locationId() ) );
} ),
ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value {
return BuildComputedValue(
GetValueType< bool >(), lhs, rhs, UGT( c.locationId() ) );
} ) ) );
BuildParseRule( e, ">="_sid,
LeftAssInfixOp( "operator_greater_or_equals"_sid, precedence::GreaterLesserOp,
ForType< BigInt, SGE, bool >(),
ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value {
return BuildComputedValue(
GetValueType< bool >(), lhs, rhs, SGE( c.locationId() ) );
} ),
ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value {
return BuildComputedValue(
GetValueType< bool >(), lhs, rhs, UGE( c.locationId() ) );
} ) ) );
BuildParseRule( e, "<"_sid,
LeftAssInfixOp( "operator_lesser"_sid, precedence::GreaterLesserOp,
ForType< BigInt, SLT, bool >(),
ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value {
return BuildComputedValue(
GetValueType< bool >(), lhs, rhs, SLT( c.locationId() ) );
} ),
ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value {
return BuildComputedValue(
GetValueType< bool >(), lhs, rhs, ULT( c.locationId() ) );
} ) ) );
BuildParseRule( e, "<="_sid,
LeftAssInfixOp( "operator_lesser_or_equals"_sid, precedence::GreaterLesserOp,
ForType< BigInt, SLE, bool >(),
ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value {
return BuildComputedValue(
GetValueType< bool >(), lhs, rhs, SLE( c.locationId() ) );
} ),
ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value {
return BuildComputedValue(
GetValueType< bool >(), lhs, rhs, ULE( c.locationId() ) );
} ) ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/operators/compoundass.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "builtins/builtins.h"
#include "precedence.h"
#include "helpers.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::cir;
using namespace goose::parse;
namespace goose::builtins
{
// Helper to build a compound assignment operator
| | < | | | | | | | < | | | | | | < | < > | | | | | | < | | | < | < | 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 |
#include "builtins/builtins.h"
#include "precedence.h"
#include "helpers.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::cir;
using namespace goose::parse;
namespace goose::builtins
{
// Helper to build a compound assignment operator
template< typename L, typename R > auto MakeCompoundAssOpFunc( Env& e, StringId opName )
{
auto assOp = GetOverloadSet( e, "operator_assign"_sid );
auto opOvlSet = GetOverloadSet( e, opName );
return ForTypes< L, R >(
[=]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
// Resolve the wanted operator call.
auto newVal = PoisonValue();
{
DiagnosticsContext dc( 0, format( "When invoking {}.", opName.str() ) );
newVal = InvokeOverloadSet( c, opOvlSet, MakeClosedTuple( lhs, rhs ) );
}
if( newVal.isPoison() )
return newVal;
DiagnosticsContext dc( 0, "When invoking operator_assign." );
// Resolve the assignment operator call.
return InvokeOverloadSet( c, assOp, MakeClosedTuple( lhs, newVal ) );
} );
}
void SetupCompoundAssOps( Env& e )
{
auto CompoundAssOp = [&]( StringId opName )
{
auto opOvlSet = GetOverloadSet( e, opName );
return MakeCompoundAssOpFunc< CustomPattern< Value, ReferenceType::PatternAnyMutable >,
Value >( e, opName );
};
// The reason we don't use BuildGenericTupleOperator() for the tuple version is to that we
// go through the tuple assignment operator, which will take care of building the result in
// temporaries before performong the assignment, avoiding tuple values clobbering each other
// before we are done with the original values.
auto TupleCompoundAssOp = [&]( StringId opName )
{
auto opOvlSet = GetOverloadSet( e, opName );
return MakeCompoundAssOpFunc< CustomPattern< Value, TuplePattern >,
CustomPattern< Value, TuplePattern > >( e, opName );
};
BuildParseRule( e, "+="_sid,
RightAssInfixOp( "operator_compound_add"_sid, precedence::AssignmentOp,
TupleCompoundAssOp( "operator_add"_sid ), CompoundAssOp( "operator_add"_sid ) ) );
BuildParseRule( e, "-="_sid,
RightAssInfixOp( "operator_compound_sub"_sid, precedence::AssignmentOp,
TupleCompoundAssOp( "operator_sub"_sid ), CompoundAssOp( "operator_sub"_sid ) ) );
BuildParseRule( e, "*="_sid,
RightAssInfixOp( "operator_compound_multiply"_sid, precedence::AssignmentOp,
TupleCompoundAssOp( "operator_multiply"_sid ),
CompoundAssOp( "operator_multiply"_sid ) ) );
BuildParseRule( e, "/="_sid,
|
| ︙ | ︙ | |||
93 94 95 96 97 98 99 |
BuildParseRule( e, ">>="_sid,
RightAssInfixOp( "operator_compound_shift_right"_sid, precedence::AssignmentOp,
TupleCompoundAssOp( "operator_shift_right"_sid ),
CompoundAssOp( "operator_shift_right"_sid ) ) );
BuildParseRule( e, "&="_sid,
RightAssInfixOp( "operator_compound_and"_sid, precedence::AssignmentOp,
| | < | < | < < > | 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
BuildParseRule( e, ">>="_sid,
RightAssInfixOp( "operator_compound_shift_right"_sid, precedence::AssignmentOp,
TupleCompoundAssOp( "operator_shift_right"_sid ),
CompoundAssOp( "operator_shift_right"_sid ) ) );
BuildParseRule( e, "&="_sid,
RightAssInfixOp( "operator_compound_and"_sid, precedence::AssignmentOp,
TupleCompoundAssOp( "operator_and"_sid ), CompoundAssOp( "operator_and"_sid ) ) );
BuildParseRule( e, "|="_sid,
RightAssInfixOp( "operator_compound_or"_sid, precedence::AssignmentOp,
TupleCompoundAssOp( "operator_or"_sid ), CompoundAssOp( "operator_or"_sid ) ) );
BuildParseRule( e, "^="_sid,
RightAssInfixOp( "operator_compound_xor"_sid, precedence::AssignmentOp,
TupleCompoundAssOp( "operator_xor"_sid ), CompoundAssOp( "operator_xor"_sid ) ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/operators/comptime.cpp.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 |
namespace goose::builtins
{
void SetupComptimeOp( Env& e )
{
// TODO error out if function is not regular or already set as comptime
BuildParseRule( e, "comptime"_sid,
PrefixOp( "operator_comptime"_sid, precedence::FuncQualifier,
| | > | | | | | | > | | | | | > | | | | | | > | | | | | | < < < > | 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 |
namespace goose::builtins
{
void SetupComptimeOp( Env& e )
{
// TODO error out if function is not regular or already set as comptime
BuildParseRule( e, "comptime"_sid,
PrefixOp( "operator_comptime"_sid, precedence::FuncQualifier,
ForType< TypePatternParam< FuncType, FuncPattern > >(
[]< typename O >( auto&& c, O&& operand ) -> Value
{
auto ft = *FromValue< FuncType >( operand );
ft.setKind( FuncType::Kind::Comptime );
return ToValue( ft );
} ),
ForType< TypePatternParam< TFuncType, TFuncPattern > >(
[]< typename O >( auto&& c, O&& operand ) -> Value
{
auto tft = *FromValue< TFuncType >( operand );
tft.setKind( FuncType::Kind::Comptime );
return ToValue( tft );
} ),
ForType< FuncDecl >(
[]< typename O >( auto&& c, O&& operand ) -> Value
{
auto fd = *FromValue< FuncDecl >( operand );
fd.func().type().setKind( FuncType::Kind::Comptime );
return ToValue( fd );
} ),
ForType< TFuncDecl >(
[]< typename O >( auto&& c, O&& operand ) -> Value
{
auto tfd = *FromValue< TFuncDecl >( operand );
tfd.tFunc().type().setKind( FuncType::Kind::Comptime );
return ToValue( tfd );
} ) ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/operators/contract.cpp.
| ︙ | ︙ | |||
28 29 30 31 32 33 34 |
auto type = p.peekLastValue();
if( !type )
return false;
auto delim = p.resolver()->lookAheadUnresolved();
if( !delim )
{
| > | > | | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
auto type = p.peekLastValue();
if( !type )
return false;
auto delim = p.resolver()->lookAheadUnresolved();
if( !delim )
{
dm.emitSyntaxErrorMessage(
p.resolver()->currentLocation(), format( "'[' expected after '{}'.", name ), 0 );
return false;
}
auto decomp = Decompose( delim->first, Val< Delimiter >() );
if( !decomp || *decomp != Delimiter::OpenBracket )
{
dm.emitSyntaxErrorMessage(
p.resolver()->currentLocation(), format( "'[' expected after '{}'.", name ), 0 );
return false;
}
UnparsedPropositions* unparsedProps = nullptr;
if( auto ft = FromValue< FuncType >( *type ) )
unparsedProps = func( *ft );
|
| ︙ | ︙ | |||
69 70 71 72 73 74 75 |
toks.resize( toks.size() - 1 );
return true;
}
bool ParseRequiresOp( Parser& p, LocationId locationId, uint32_t prec )
{
| | > | | < | < | < | < | | > | | < | < | < | < | | | | 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 |
toks.resize( toks.size() - 1 );
return true;
}
bool ParseRequiresOp( Parser& p, LocationId locationId, uint32_t prec )
{
return ParseVerifOp( p, locationId, prec, "requires",
[&]< typename FT >( FT& ft )
{
if constexpr( is_same_v< FT, FuncType > )
return &ft.verifInfos()->preConds()->unparsed();
else
return &ft.preConds()->unparsed();
} );
}
bool ParseEnsuresOp( Parser& p, LocationId locationId, uint32_t prec )
{
return ParseVerifOp( p, locationId, prec, "ensures",
[&]< typename FT >( FT& ft )
{
if constexpr( is_same_v< FT, FuncType > )
return &ft.verifInfos()->postConds()->unparsed();
else
return &ft.postConds()->unparsed();
} );
}
} // namespace
namespace goose::builtins
{
void SetupContractOps( Env& e )
{
RegisterRule( e, "requires"_sid, Rule( ContractOpPrecedence, ParseRequiresOp ) );
RegisterRule( e, "ensures"_sid, Rule( ContractOpPrecedence, ParseEnsuresOp ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/operators/dollar.cpp.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
//
// Also, it works both as a prefix operator to construct a TVar,
// and as an infix operator (with a decl or TEXpr as the left value) to
// construct a Decl of the form "<type> $FOO" or a TDecl of the form
// "<TExpr> $FOO" (for instance $T $FOO)
namespace
{
| > | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
//
// Also, it works both as a prefix operator to construct a TVar,
// and as an infix operator (with a decl or TEXpr as the left value) to
// construct a Decl of the form "<type> $FOO" or a TDecl of the form
// "<TExpr> $FOO" (for instance $T $FOO)
namespace
{
Value BuildOrRetrieveTVar(
const Context& c, LocationId locationId, StringId name, bool forwarding )
{
if( name == "_"_sid )
return forwarding ? ToValue( TTVar( "_"_sid ) ) : ToValue( TVar( "_"_sid ) );
// Template name bindings are stored with a "$$" prefix so they don't
// collide with regular names.
auto captureIdentity = AppendToVectorTerm( c.identity(),
|
| ︙ | ︙ | |||
50 51 52 53 54 55 56 |
}
bool ParsePrefixDollarOperator( Parser& p, LocationId locationId, uint32_t prec )
{
auto nameTerm = p.resolver()->consumeUnresolved();
if( !nameTerm )
{
| | | | | | | | | | 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 |
}
bool ParsePrefixDollarOperator( Parser& p, LocationId locationId, uint32_t prec )
{
auto nameTerm = p.resolver()->consumeUnresolved();
if( !nameTerm )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "expected an identifier after '$'.", 0 );
return false;
}
const auto* name = get_if< StringId >( &nameTerm->first );
if( !name )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "expected an identifier after '$'.", 0 );
return false;
}
const auto& c = p.context();
auto val = BuildOrRetrieveTVar( c, nameTerm->second, *name, false );
auto loc = Location::CreateSpanningLocation( locationId, nameTerm->second );
val.setLocationId( loc );
p.pushValue( move( val ) );
return true;
}
bool ParsePrefixDollarDollarOperator( Parser& p, LocationId locationId, uint32_t prec )
{
auto nameTerm = p.resolver()->consumeUnresolved();
if( !nameTerm )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "expected an identifier after '$$'.", 0 );
return false;
}
const auto* name = get_if< StringId >( &nameTerm->first );
if( !name )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "expected an identifier after '$$'.", 0 );
return false;
}
const auto& c = p.context();
auto val = BuildOrRetrieveTVar( c, nameTerm->second, *name, true );
p.pushValue( move( val ) );
|
| ︙ | ︙ | |||
122 123 124 125 126 127 128 |
bool ParseInfixDollarOperator( Parser& p, LocationId locationId, uint32_t prec )
{
const auto& leftVal = p.peekLastValue();
if( !leftVal )
return false;
| | | | | | | 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 |
bool ParseInfixDollarOperator( Parser& p, LocationId locationId, uint32_t prec )
{
const auto& leftVal = p.peekLastValue();
if( !leftVal )
return false;
if( !builtins::IsType( p.context(), *leftVal ) && !IsTExpr( *leftVal ) )
return false;
auto nameTerm = p.resolver()->consumeUnresolved();
const auto* name = get_if< StringId >( &nameTerm->first );
const auto& c = p.context();
auto val = BuildOrRetrieveTVar( c, nameTerm->second, *name, false );
// If the TVar was already bound, we may end up with something in val
// that isn't a TVar, which is an error.
if( !IsTVar( val ) )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
nameTerm->second, "expected an unbound identifier after '$'." );
return false;
}
auto loc = Location::CreateSpanningLocation( leftVal->locationId(), nameTerm->second );
auto typeOrTExpr = ValueToEIR( *p.popValue() );
auto tdecl = TDecl( move( typeOrTExpr ), *name );
p.pushValue( ToValue( move( tdecl ) ).setLocationId( loc ) );
return true;
}
} // namespace
namespace goose::builtins
{
void SetupDollarOp( Env& e )
{
Rule r( ParsePrefixDollarOperator, InfixDollarPrecedence, ParseInfixDollarOperator );
RegisterRule( e, "$"_sid, move( r ) );
Rule r2( ParsePrefixDollarDollarOperator );
RegisterRule( e, "$$"_sid, move( r2 ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/operators/dot.cpp.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 |
BuildParseRule( e, "."_sid,
LeftAssInfixOp( "operator_dot"_sid, precedence::DotOp,
// dot operator for a tuple reference:
// returns a reference to the specified member.
ForTypes< CustomPattern< Value, ReferenceType::PatternAnyOf< TuplePattern > >,
CustomPattern< IntegerType, IntegerType::PatternUnsigned32 > >(
| | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | > | | | | | | | | | | | | | | | | > | | > | | > | | < < < > | 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 |
BuildParseRule( e, "."_sid,
LeftAssInfixOp( "operator_dot"_sid, precedence::DotOp,
// dot operator for a tuple reference:
// returns a reference to the specified member.
ForTypes< CustomPattern< Value, ReferenceType::PatternAnyOf< TuplePattern > >,
CustomPattern< IntegerType, IntegerType::PatternUnsigned32 > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
G_VAL_ASSERT( lhs, !lhs.isConstant() );
if( !rhs.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage( rhs.locationId(),
"the right operand for the dot operator needs to be a constant." );
return PoisonValue();
}
auto refType = *FromValue< ReferenceType >( *EIRToValue( lhs.type() ) );
auto tupType = *EIRToValue( refType.type() );
uint32_t index = *FromValue< uint32_t >( rhs );
if( index >= TupleTypeSize( tupType ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
rhs.locationId(), "the index is out of range." );
return PoisonValue();
}
auto rt = ValueToEIR( ToValue( ReferenceType(
GetTupleTypeElement( tupType, index ), refType.accessSpec() ) ) );
return BuildComputedValue( rt, lhs, Select( index, c.locationId() ) );
} ),
// Overload for constant tuples: directly return the wanted element
ForTypes< CustomConstantPattern< Value, TuplePattern >,
CustomPattern< IntegerType, IntegerType::PatternUnsigned32 > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
G_VAL_ASSERT( lhs, lhs.isConstant() );
if( !rhs.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage( rhs.locationId(),
"the right operand for the dot operator needs to be a constant." );
return PoisonValue();
}
uint32_t index = *FromValue< uint32_t >( rhs );
if( index >= TupleSize( lhs ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
rhs.locationId(), "the index is out of range." );
return PoisonValue();
}
return *EIRToValue( GetConstantTupleElement( lhs, index ) );
} ),
// For structs: look up the member name, get its index, and call the dot operator on
// the underlying tuple
ForTypes<
CustomPattern< Value, ReferenceType::PatternAnyOf< StructType::Pattern > >,
StringId >(
[dotOp]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
// Extract the struct type form the ref type
auto refType = *FromValue< ReferenceType >( *EIRToValue( lhs.type() ) );
auto st = *EIRToValue( refType.type() );
auto structType = *FromValue< StructType >( st );
if( !structType.parse( c.env() ) )
return PoisonValue();
auto memberName = *FromValue< StringId >( rhs );
auto memberIdentity =
AppendToVectorTerm( structType.identity(), TERM( memberName ) );
Term result;
switch( c.env()->retrieveValue( memberIdentity, c.identity(), result ) )
{
case sema::Env::Status::Success:
break;
case sema::Env::Status::AmbiguousMatch:
DiagnosticsManager::GetInstance().emitErrorMessage( st.locationId(),
"unexpected ambiguous match when resolving the member name." );
return PoisonValue();
default:
DiagnosticsManager::GetInstance().emitErrorMessage(
st.locationId(), "unknown member name." );
return PoisonValue();
}
auto tupType = c.env()->retrieveValue( TSID( underlying_type ),
AppendToVectorTerm( structType.identity(), TERM( 0U ) ) );
G_VAL_ASSERT( lhs, tupType );
refType.type() = move( *tupType );
Value newRef(
ValueToEIR( ToValue( refType ) ), lhs.cir(), lhs.locationId() );
return InvokeOverloadSet( c, dotOp,
MakeClosedTuple(
newRef, EIRToValue( result )->setLocationId( rhs.locationId() ) ) );
} ) ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/operators/ellipsis.cpp.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 |
namespace goose::builtins
{
void SetupEllipsisOp( Env& e )
{
BuildParseRule( e, "..."_sid,
PostfixOp( "operator_ellipsis"_sid, precedence::EllipsisOp,
| | | | > | | | | | > | > | | | | < < < > | 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 |
namespace goose::builtins
{
void SetupEllipsisOp( Env& e )
{
BuildParseRule( e, "..."_sid,
PostfixOp( "operator_ellipsis"_sid, precedence::EllipsisOp,
// default ellipsis operator: wrap whatever was given as parameter into a pack
// TExpr. Complain if the operand isn't a constant.
//
// Unpack overloads will be more specific so they will take precedence where
// applicable.
ForType< Value >(
[]( auto&& c, auto&& pattern ) -> Value
{
if( !pattern.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
pattern.locationId(),
"pack expressions must be types, constants or template "
"expressions." );
return PoisonValue();
}
return ToValue( TPack( ValueToEIR( pattern ) ) );
} ) ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/operators/ghost.cpp.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 |
namespace goose::builtins
{
void SetupGhostOp( Env& e )
{
// TODO error out if function isn't a regular function or if its already ghost
BuildParseRule( e, "ghost"_sid,
PrefixOp( "operator_ghost"_sid, precedence::FuncQualifier,
| | > | | | | | | > | | | | | > | | | | | | > | | | | | | < < < > | 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 |
namespace goose::builtins
{
void SetupGhostOp( Env& e )
{
// TODO error out if function isn't a regular function or if its already ghost
BuildParseRule( e, "ghost"_sid,
PrefixOp( "operator_ghost"_sid, precedence::FuncQualifier,
ForType< TypePatternParam< FuncType, FuncPattern > >(
[]< typename O >( auto&& c, O&& operand ) -> Value
{
auto ft = *FromValue< FuncType >( operand );
ft.setKind( FuncType::Kind::Ghost );
return ToValue( ft );
} ),
ForType< TypePatternParam< TFuncType, TFuncPattern > >(
[]< typename O >( auto&& c, O&& operand ) -> Value
{
auto tft = *FromValue< TFuncType >( operand );
tft.setKind( FuncType::Kind::Ghost );
return ToValue( tft );
} ),
ForType< FuncDecl >(
[]< typename O >( auto&& c, O&& operand ) -> Value
{
auto fd = *FromValue< FuncDecl >( operand );
fd.func().type().setKind( FuncType::Kind::Ghost );
return ToValue( fd );
} ),
ForType< TFuncDecl >(
[]< typename O >( auto&& c, O&& operand ) -> Value
{
auto tfd = *FromValue< TFuncDecl >( operand );
tfd.tFunc().type().setKind( FuncType::Kind::Ghost );
return ToValue( tfd );
} ) ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/operators/helpers.h.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
template< typename... R >
void BuildParseRule( sema::Env& env, StringId name, R&&... ruleBuilders )
{
parse::BuildParseRule( env, name, AppendToVectorTerm( RootG0Identity(), TERM( name ) ),
forward< R >( ruleBuilders )... );
}
| | > > > | | > | | | < | | | | | | | < | < | < | < | | > | | > | | < | | | 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 |
template< typename... R >
void BuildParseRule( sema::Env& env, StringId name, R&&... ruleBuilders )
{
parse::BuildParseRule( env, name, AppendToVectorTerm( RootG0Identity(), TERM( name ) ),
forward< R >( ruleBuilders )... );
}
struct UnaryOpTag
{
};
struct BinaryOpTag
{
};
template< typename... R > auto PrefixOp( StringId funcName, uint32_t precedence, R&&... rules )
{
return [&, precedence, funcName]( auto&& e, auto&& r, auto&& name )
{
MakePrefixOp( e, r, name, precedence,
BuildOpFunc< UnaryOpTag >( e, funcName, forward< R >( rules )... ) );
};
}
template< typename... R > auto PostfixOp( StringId funcName, uint32_t precedence, R&&... rules )
{
return [&, precedence, funcName]( auto&& e, auto&& r, auto&& name )
{
MakePostfixOp( e, r, name, precedence,
BuildOpFunc< UnaryOpTag >( e, funcName, forward< R >( rules )... ) );
};
}
template< typename... R >
auto LeftAssInfixOp( StringId funcName, uint32_t precedence, R&&... rules )
{
return [&, precedence, funcName]( auto&& e, auto&& r, auto&& name )
{
MakeLeftAssInfixOp( e, r, name, precedence,
BuildOpFunc< BinaryOpTag >( e, funcName, forward< R >( rules )... ) );
};
}
template< typename... R >
auto RightAssInfixOp( StringId funcName, uint32_t precedence, R&&... rules )
{
return [&, precedence, funcName]( auto&& e, auto&& r, auto&& name )
{
MakeRightAssInfixOp( e, r, name, precedence,
BuildOpFunc< BinaryOpTag >( e, funcName, forward< R >( rules )... ) );
};
}
template< typename tag, typename... R >
auto BuildOpFunc( Env& e, StringId funcName, R&&... ruleBuilders )
{
auto pOvlSet = GetOrCreateOverloadSet( e, funcName );
( ( ruleBuilders( e, pOvlSet, tag() ) ), ... );
return [pOvlSet, funcName]< typename... O >( Parser& p, O&&... operands )
{
DiagnosticsContext dc( 0, format( "When invoking {}.", funcName.str() ) );
return InvokeOverloadSet( p.context(), pOvlSet,
MakeClosedTuple( forward< O >( operands )... ), p.context().locationId() );
};
}
template< typename T, typename I, typename RT = T > auto ForType( CURRENT_LOC )
{
return [sloc]< typename tag >( auto&& e, auto&& pOvlSet, tag t )
{
using intrinsicType = Intrinsic< Value( T, T ) >;
auto intrinsicFunc = []( const Context& c, const Value& lhs, const Value& rhs )
{ return BuildComputedValue( GetValueType< RT >(), lhs, rhs, I( c.locationId() ) ); };
auto loc = Location::Create( sloc );
pOvlSet->add( e, ToValue< intrinsicType >( move( intrinsicFunc ) ).setLocationId( loc ),
GetBuiltinIntrinsicFuncInvocationRule() );
};
}
template< typename T, typename F > auto ForType( F&& func, CURRENT_LOC )
{
return [&, sloc]< typename tag >( auto&& e, auto&& pOvlSet, tag t )
{
auto loc = Location::Create( sloc );
if constexpr( is_same_v< tag, UnaryOpTag > )
{
using intrinsicType = Intrinsic< Value( T ) >;
pOvlSet->add( e,
ToValue< intrinsicType >( forward< F >( func ) ).setLocationId( loc ),
GetBuiltinIntrinsicFuncInvocationRule() );
}
else
{
using intrinsicType = Intrinsic< Value( T, T ) >;
pOvlSet->add( e,
ToValue< intrinsicType >( forward< F >( func ) ).setLocationId( loc ),
GetBuiltinIntrinsicFuncInvocationRule() );
}
};
}
template< typename T1, typename T2, typename F > auto ForTypes( F&& func, CURRENT_LOC )
{
return [=]< typename tag >( auto&& e, auto&& pOvlSet, tag t )
{
using intrinsicType = Intrinsic< Value( T1, T2 ) >;
auto loc = Location::Create( sloc );
pOvlSet->add( e, ToValue< intrinsicType >( func ).setLocationId( loc ),
GetBuiltinIntrinsicFuncInvocationRule() );
};
}
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/operators/inline.cpp.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 |
namespace goose::builtins
{
void SetupInlineOp( Env& e )
{
// TODO error out if function is not regular or already set as inline
BuildParseRule( e, "inline"_sid,
PrefixOp( "operator_inline"_sid, precedence::FuncQualifier,
| | > | | | | | | > | | | | | > | | | | | | > | | | | | | < < < > | 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 |
namespace goose::builtins
{
void SetupInlineOp( Env& e )
{
// TODO error out if function is not regular or already set as inline
BuildParseRule( e, "inline"_sid,
PrefixOp( "operator_inline"_sid, precedence::FuncQualifier,
ForType< TypePatternParam< FuncType, FuncPattern > >(
[]< typename O >( auto&& c, O&& operand ) -> Value
{
auto ft = *FromValue< FuncType >( operand );
ft.setKind( FuncType::Kind::Inline );
return ToValue( ft );
} ),
ForType< TypePatternParam< TFuncType, TFuncPattern > >(
[]< typename O >( auto&& c, O&& operand ) -> Value
{
auto tft = *FromValue< TFuncType >( operand );
tft.setKind( FuncType::Kind::Inline );
return ToValue( tft );
} ),
ForType< FuncDecl >(
[]< typename O >( auto&& c, O&& operand ) -> Value
{
auto fd = *FromValue< FuncDecl >( operand );
fd.func().type().setKind( FuncType::Kind::Inline );
return ToValue( fd );
} ),
ForType< TFuncDecl >(
[]< typename O >( auto&& c, O&& operand ) -> Value
{
auto tfd = *FromValue< TFuncDecl >( operand );
tfd.tFunc().type().setKind( FuncType::Kind::Inline );
return ToValue( tfd );
} ) ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/operators/intrinsic.cpp.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 |
namespace goose::builtins
{
void SetupIntrinsicOp( Env& e )
{
// TODO error out if function is not regular or already set as intrinsic
BuildParseRule( e, "intrinsic"_sid,
PrefixOp( "operator_intrinsic"_sid, precedence::FuncQualifier,
| | > | | | | | | > | | | | | > | | | | | | > | | | | | | < < < > | 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 |
namespace goose::builtins
{
void SetupIntrinsicOp( Env& e )
{
// TODO error out if function is not regular or already set as intrinsic
BuildParseRule( e, "intrinsic"_sid,
PrefixOp( "operator_intrinsic"_sid, precedence::FuncQualifier,
ForType< TypePatternParam< FuncType, FuncPattern > >(
[]< typename O >( auto&& c, O&& operand ) -> Value
{
auto ft = *FromValue< FuncType >( operand );
ft.setKind( FuncType::Kind::Intrinsic );
return ToValue( ft );
} ),
ForType< TypePatternParam< TFuncType, TFuncPattern > >(
[]< typename O >( auto&& c, O&& operand ) -> Value
{
auto tft = *FromValue< TFuncType >( operand );
tft.setKind( FuncType::Kind::Intrinsic );
return ToValue( tft );
} ),
ForType< FuncDecl >(
[]< typename O >( auto&& c, O&& operand ) -> Value
{
auto fd = *FromValue< FuncDecl >( operand );
fd.func().type().setKind( FuncType::Kind::Intrinsic );
return ToValue( fd );
} ),
ForType< TFuncDecl >(
[]< typename O >( auto&& c, O&& operand ) -> Value
{
auto tfd = *FromValue< TFuncDecl >( operand );
tfd.tFunc().type().setKind( FuncType::Kind::Intrinsic );
return ToValue( tfd );
} ) ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/operators/logic.cpp.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
{
void SetupLogicOps( Env& e )
{
auto orOp = GetOrCreateOverloadSet( e, "operator_or"_sid );
auto andOp = GetOrCreateOverloadSet( e, "operator_and"_sid );
BuildParseRule( e, "!"_sid,
| | < > | < | | | | < < | < | > | | | | | | < | | < < | < | | | | | > > > | | | < | | < | < | | < < | < | | | | | > | > > | | | < | | < | < < > | | | | | | | | | | | | | | | | > | > | | | < < | | < | > | 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 |
{
void SetupLogicOps( Env& e )
{
auto orOp = GetOrCreateOverloadSet( e, "operator_or"_sid );
auto andOp = GetOrCreateOverloadSet( e, "operator_and"_sid );
BuildParseRule( e, "!"_sid,
PrefixOp( "operator_logical_not"_sid, precedence::UnaryOps, BuildGenericTupleOperator(),
ForType< bool >(
[]( auto&& c, auto&& operand ) -> Value {
return BuildComputedValue(
GetValueType< bool >(), operand, cir::Not( c.locationId() ) );
} ) ) );
BuildParseRule( e, "~"_sid,
PrefixOp( "operator_bitwise_not"_sid, precedence::UnaryOps, BuildGenericTupleOperator(),
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& operand ) -> Value
{
auto opTypeVal = *EIRToValue( operand.type() );
auto opType = *FromValue< IntegerType >( opTypeVal );
return BuildComputedValue( operand.type(), operand,
Value( operand.type(), APSInt::getMaxValue( opType.m_numBits, true ) ),
Xor( c.locationId() ) );
} ) ) );
BuildParseRule( e, "^"_sid,
LeftAssInfixOp( "operator_xor"_sid, precedence::OrOp, BuildGenericTupleOperator(),
// Logical xor
ForType< bool, Xor >(),
// ct_int xor
ForType< BigInt >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
if( !lhs.isConstant() || !rhs.isConstant() )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
c.locationId(),
"bitwise operations between ct_int values are only allowed on "
"constants." );
return PoisonValue();
}
return BuildComputedValue( lhs.type(), lhs, rhs, Xor( c.locationId() ) );
} ),
// runtime integer xor, defined to work for any two integers of same
// bit size and signedness.
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value {
return BuildComputedValue( lhs.type(), lhs, rhs, Xor( c.locationId() ) );
} ) ) );
BuildParseRule( e, "|"_sid,
LeftAssInfixOp( "operator_or"_sid, precedence::OrOp, BuildGenericTupleOperator(),
// ct_int or
ForType< BigInt >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
if( !lhs.isConstant() || !rhs.isConstant() )
{
auto loc = Location::CreateSpanningLocation(
lhs.locationId(), rhs.locationId() );
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( loc,
"bitwise operations between ct_int values are only allowed on "
"constants." );
return PoisonValue();
}
return BuildComputedValue( lhs.type(), lhs, rhs, Or( c.locationId() ) );
} ),
// runtime integer or, defined to work for any two integers of same
// bit size and signedness.
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{ return BuildComputedValue( lhs.type(), lhs, rhs, Or( c.locationId() ) ); } ),
// bool or
ForType< bool >(
[orOp]< typename L, typename R >( auto&& c, L&& lhs, R&& rhs ) -> Value
{
// Handle the case where lhs is constant, so that the result gets properly
// eagerly evaluated (in case we are using the expression as a compile time
// constant)
if( lhs.isConstant() )
{
if( *FromValue< bool >( lhs ) )
return forward< L >( lhs );
return forward< R >( rhs );
}
// This operator have different behaviors depending on the context: in
// normal code, we want to generate shortcut evaluation. But in propositions
// and ghost code, we want to simply generate a Or instruction with both
// operands always evaluated. So we delegate the work to another overload of
// operator_or that takes the builder as its first param and is overloaded
// according to it.
return InvokeOverloadSet( c, orOp,
MakeClosedTuple(
c.builder(), forward< L >( lhs ), forward< R >( rhs ) ) );
} ) ) );
RegisterBuiltinFunc< Intrinsic< bool( Value, bool, bool ) > >( e, orOp,
[]( auto&& c, auto&& b, auto&& lhs, auto&& rhs ) {
return BuildComputedValue( GetValueType< bool >(), lhs, rhs, Or( c.locationId() ) );
} );
// TODO_SSA: revise this
/*RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< CodeBuilder > >, bool, bool ) >
>( e, orOp,
[]( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
{
auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( b );
const auto& cfg = cb->cfg();
// Build the control flow for shortcut evaluation.
const auto& predBB = cfg->currentBB();
|
| ︙ | ︙ | |||
174 175 176 177 178 179 180 |
// Build the result val which pulls the temporary created above.
return BuildComputedValue( GetValueType< bool >(),
GetTemporary( GetValueType< bool >(), resultIndex, c.locationId() ) );
} );*/
BuildParseRule( e, "&"_sid,
| | < | | | | | > > > | | | < | | < | < < > | | | | | | | | | | | | | | | | > | > | | | < < | | < > | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | > > | | | < | | | | | | | | | | | | | | > | < | < | < | < < | | | | | > > > | | | < | | | | | | | | | | | | | | > | < | < | < | | | | | | | | | | | | | | | | > | < | < | < | < < < > | 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 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 |
// Build the result val which pulls the temporary created above.
return BuildComputedValue( GetValueType< bool >(),
GetTemporary( GetValueType< bool >(), resultIndex, c.locationId() ) );
} );*/
BuildParseRule( e, "&"_sid,
LeftAssInfixOp( "operator_and"_sid, precedence::AndOp, BuildGenericTupleOperator(),
// ct_int and
ForType< BigInt >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
if( !lhs.isConstant() || !rhs.isConstant() )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
c.locationId(),
"bitwise operations between ct_int values are only allowed on "
"constants." );
return PoisonValue();
}
return BuildComputedValue( lhs.type(), lhs, rhs, And( c.locationId() ) );
} ),
// runtime integer and, defined to work for any two integers of same
// bit size and signedness.
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{ return BuildComputedValue( lhs.type(), lhs, rhs, And( c.locationId() ) ); } ),
// bool and
ForType< bool >(
[andOp]< typename L, typename R >( auto&& c, L&& lhs, R&& rhs ) -> Value
{
// Handle the case where lhs is constant, so that
// the result gets properly eagerly evaluated (in case
// we are using the expression as a compile time constant)
if( lhs.isConstant() )
{
if( *FromValue< bool >( lhs ) )
return forward< R >( rhs );
return forward< L >( lhs );
}
// This operator have different behaviors depending on the context: in
// normal code, we want to generate shortcut evaluation. But in propositions
// and ghost code, we want to simply generate a And instruction with both
// operands always evaluated. So we delegate the work to another overload of
// operator_and that takes the builder as its first param and is overloaded
// according to it.
return InvokeOverloadSet( c, andOp,
MakeClosedTuple(
c.builder(), forward< L >( lhs ), forward< R >( rhs ) ) );
} ) ) );
RegisterBuiltinFunc< Intrinsic< bool( Value, bool, bool ) > >( e, andOp,
[]( auto&& c, auto&& b, auto&& lhs, auto&& rhs ) {
return BuildComputedValue(
GetValueType< bool >(), lhs, rhs, And( c.locationId() ) );
} );
// TODO_SSA revise this
/* RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< CodeBuilder > >, bool,
bool ) > >( e, andOp,
[]( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
{
auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( b );
const auto& cfg = cb->cfg();
// Build the control flow for shortcut evaluation.
const auto& predBB = cfg->currentBB();
auto pRhsBB = cfg->createBB();
auto pSuccBB = cfg->createBB();
// If the lhs is false, skip to the end directly.
// Otherwise, jump to the BB that computes rhs.
predBB->append( lhs );
predBB->setTerminator( CondBranch( pRhsBB, pSuccBB ) );
auto rhsIndex = util::GenerateNewUID();
pRhsBB->append( rhs, CreateTemporary( rhsIndex, false, c.locationId() ) );
pRhsBB->setTerminator( Branch( pSuccBB ) );
auto resultIndex = util::GenerateNewUID();
// Build the Phi instruction that will collect the final result.
auto phi = Phi( GetValueType< bool >(), 2,
resultIndex, c.locationId() );
// If coming directly from the lhs BB, we know the result is false.
phi.setIncoming( predBB, ToValue( false ) );
// Otherwise, the result is whatever was computed by the rhs block.
phi.setIncoming( pRhsBB, BuildComputedValue( GetValueType< bool >(),
GetTemporary( GetValueType< bool >(), rhsIndex, c.locationId() ) ) );
pSuccBB->append( move( phi ) );
cfg->setCurrentBB( pSuccBB );
// Build the result val which pulls the temporary created above.
return BuildComputedValue( GetValueType< bool >(),
GetTemporary( GetValueType< bool >(), resultIndex, c.locationId() ) );
} );*/
BuildParseRule( e, "<<"_sid,
LeftAssInfixOp( "operator_shift_left"_sid, precedence::BitShiftOp,
BuildGenericTupleOperator(),
// ct_int left shift
ForTypes< BigInt, uint32_t >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
if( !lhs.isConstant() || !rhs.isConstant() )
{
auto loc = Location::CreateSpanningLocation(
lhs.locationId(), rhs.locationId() );
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( loc,
"bitwise operations between ct_int values are only allowed on "
"constants." );
return PoisonValue();
}
return BuildComputedValue( lhs.type(), lhs, rhs, Shl( c.locationId() ) );
} ),
// runtime integer left shift.
ForTypes< CustomPattern< IntegerType, IntegerType::Pattern >,
CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
using namespace goose::builtins::exprhelpers;
auto cfg = GetCFG( c );
assert( cfg );
// Shifting for a number of bits equal or larger than the bitsize
// of lhs is an undefined behavior, so we require verification that
// it won't happen.
// Extract the integer type of lhs to retrieve its bit size.
auto lhsType = *FromValue< IntegerType >( *EIRToValue( lhs.type() ) );
auto bitSizeValue = Value( rhs.type(), APSInt::get( lhsType.m_numBits ) );
auto cond = ULT( rhs, bitSizeValue );
DiagnosticsManager::GetInstance().defineCustomDiagnostic( cond.locationId(),
"assert"_sid,
"the shift amount may be equal or greater than the bitsize." );
cfg->currentBB()->append( move( cond ), cir::Assert( rhs.locationId() ) );
return BuildComputedValue( lhs.type(), lhs, rhs, Shl( c.locationId() ) );
} ) ) );
BuildParseRule( e, ">>"_sid,
LeftAssInfixOp( "operator_shift_right"_sid, precedence::BitShiftOp,
BuildGenericTupleOperator(),
// ct_int right shift
ForTypes< BigInt, uint32_t >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
if( !lhs.isConstant() || !rhs.isConstant() )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
c.locationId(),
"bitwise operations between ct_int values are only allowed on "
"constants." );
return PoisonValue();
}
return BuildComputedValue( lhs.type(), lhs, rhs, AShr( c.locationId() ) );
} ),
// runtime signed integer right shift, defined to work for any two integers of same
// bit size.
ForTypes< CustomPattern< IntegerType, IntegerType::PatternSigned >,
CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
using namespace goose::builtins::exprhelpers;
auto cfg = GetCFG( c );
assert( cfg );
// Shifting for a number of bits equal or larger than the bitsize
// of lhs is an undefined behavior, so we require verification that
// it won't happen.
// Extract the integer type of lhs to retreieve its bit size.
auto lhsType = *FromValue< IntegerType >( *EIRToValue( lhs.type() ) );
auto bitSizeValue = Value( rhs.type(), APSInt::get( lhsType.m_numBits ) );
auto cond = ULT( rhs, bitSizeValue );
DiagnosticsManager::GetInstance().defineCustomDiagnostic( cond.locationId(),
"assert"_sid,
"the shift amount may be equal or greater than the bitsize." );
cfg->currentBB()->append( move( cond ), cir::Assert( rhs.locationId() ) );
return BuildComputedValue( lhs.type(), lhs, rhs, AShr( c.locationId() ) );
} ),
// runtime unsigned integer right shift, defined to work for any two integers of
// same bit size.
ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
using namespace goose::builtins::exprhelpers;
auto cfg = GetCFG( c );
assert( cfg );
// Shifting for a number of bits equal or larger than the bitsize
// of lhs is an undefined behavior, so we require verification that
// it won't happen.
// Extract the integer type of lhs to retreieve its bit size.
auto lhsType = *FromValue< IntegerType >( *EIRToValue( lhs.type() ) );
auto bitSizeValue = Value( rhs.type(), APSInt::get( lhsType.m_numBits ) );
auto cond = ULT( rhs, bitSizeValue );
DiagnosticsManager::GetInstance().defineCustomDiagnostic( cond.locationId(),
"assert"_sid,
"the shift amount may be equal or greater than the bitsize." );
cfg->currentBB()->append( move( cond ), cir::Assert( rhs.locationId() ) );
return BuildComputedValue( lhs.type(), lhs, rhs, LShr( c.locationId() ) );
} ) ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/operators/operators.h.
| ︙ | ︙ | |||
27 28 29 30 31 32 33 |
SetupDollarOp( e );
SetupApostropheOp( e );
SetupLogicOps( e );
SetupArithOps( e );
SetupComparisonOps( e );
SetupAssignmentOps( e );
| | < | | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
SetupDollarOp( e );
SetupApostropheOp( e );
SetupLogicOps( e );
SetupArithOps( e );
SetupComparisonOps( e );
SetupAssignmentOps( e );
// Must be last since it needs to retrieve the overload sets of some previously defined
// operators
SetupCompoundAssOps( e );
SetupDotOp( e );
SetupIntrinsicOp( e );
SetupInlineOp( e );
SetupComptimeOp( e );
SetupGhostOp( e );
SetupContractOps( e );
SetupEllipsisOp( e );
}
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/operators/semicolon.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include "builtins/builtins.h"
#include "precedence.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::parse;
namespace
{
bool parseSemicolon( Parser& p, LocationId locationId, uint32_t prec )
{
return p.parseExpression( prec );
| < | > | | > < | | | 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 |
#include "builtins/builtins.h"
#include "precedence.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::parse;
namespace
{
bool parseSemicolon( Parser& p, LocationId locationId, uint32_t prec )
{
return p.parseExpression( prec );
}
} // namespace
namespace goose::builtins
{
void SetupSemicolonOp( Env& e )
{
// Semicolon is not a real operator: it just consumes itself and do nothing as a prefix
// operator, and it pushes the result of evaluating its rhs as an infix operator (if there
// is any).
Rule r(
parseSemicolon, []( const Parser& p ) { return precedence::StmtSeparator; },
parseSemicolon );
RegisterRule( e, ";"_sid, move( r ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/operators/tupass.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include "builtins/builtins.h"
#include "precedence.h"
#include "helpers.h"
#include "tuple.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::cir;
using namespace goose::parse;
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 |
#include "builtins/builtins.h"
#include "precedence.h"
#include "helpers.h"
#include "tuple.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::cir;
using namespace goose::parse;
namespace goose::builtins
{
Generator< Value > GenerateRHSValuesFromTupleRef(
const ptr< OverloadSet >& assOp, const Context& c, const Value& tup )
{
auto cfg = GetCFG( c );
if( !cfg )
co_yield Value( PoisonValue() );
auto tupRefType = *FromValue< ReferenceType >( *EIRToValue( tup.type() ) );
// Store the source tuple as a temporary, otherwise we're going to recompute rhs
// for each member assigned (which is bad)
// TODO_SSA rewrite this (since CreateTemporary makes no sense anymore)
co_return;
/* auto index = util::GenerateNewUID();
cfg->currentBB()->append( tup, CreateTemporary( index, false, tup.locationId() ) );
auto srcTup = BuildComputedValue( tupRefType.type(), GetTemporary( tupRefType.type(),
index, tup.locationId() ) );
auto anonVarDecl = ToValue( TNamedDecl( ValueToEIR( ToValue( TVar( "_"_sid ) ) ), ""_sid
) ); co_yield GenerateValuesFromComputedTuple( srcTup );*/
}
// Assignment of a compile time tuple or tuple reference to another compile time tuple,
// directly. Most tuple assignment will go through one of the other overload that takes a mutref
// to a tuple on the lhs, but sometimes we have compilation time tuples of objects that can
// directly be assigned such as decls or refs.
//
// So this overload is for all these cases, which encompass initializing multiple comma
// separated decls from a tuple, as well as variable swapping/shuffling. To make sure that the
// later works properly, we store all the rhs values into temporary variables first, before
// assigning them to the lhs values.
Value NonRefTupleAssignment(
const ptr< OverloadSet >& assOp, Context& c, const Value& lhs, const Value& rhs )
{
auto tupSize = TupleSize( lhs );
// Load the values from the rhs tuple into temporary vars.
vector< Value > tempVars;
tempVars.reserve( tupSize );
Generator< Value > gen;
if( rhs.isConstant() )
gen = GenerateValuesFromConstantTuple( rhs );
else
gen = GenerateRHSValuesFromTupleRef( assOp, c, rhs );
for( auto&& srcVal : gen )
{
// We go at very high level to construct the temporary variables by resolving an
// invocation of the assignment of the rhs value to a TNamedDecl with an empty name. The
// reason we need to work at such high level is so that the initializer value gets
// resolved exactly like if it was a regular assignment. In particular, if the value in
// question is a locvar, we want it to go through the type checking process to be
// replaced with its content. This is the simplest and most robust way to achieve this,
// which should honor every relevant extension point.
auto tmpVar = DeclareLocalVarWithTypeInference(
c, ValueToEIR( ToValue( TVar( "_"_sid ) ) ), ""_sid, srcVal, srcVal.locationId() );
if( tmpVar.isPoison() )
return PoisonValue();
tmpVar.setLocationId( srcVal.locationId() );
tempVars.emplace_back( move( tmpVar ) );
}
if( tupSize != tempVars.size() )
{
DiagnosticsManager::GetInstance().emitErrorMessage( 0, "incompatible tuple sizes." );
return PoisonValue();
}
auto result = EmptyOpenTuple();
size_t i = 0;
bool success = true;
ForEachInTuple( lhs,
[&]( auto&& destVal )
{
auto r = InvokeOverloadSet( c, assOp, MakeClosedTuple( destVal, tempVars[i++] ) );
if( r.isPoison() )
{
success = false;
return false;
}
result = AppendToTuple( result, move( r ) );
return true;
} );
if( !success )
return PoisonValue();
return result;
}
Value MutRefTupleAssignment(
const ptr< OverloadSet >& assOp, Context& c, const Value& lhs, const Value& rhs )
{
auto lhsRefType = *FromValue< ReferenceType >( *EIRToValue( lhs.type() ) );
auto lhsTupType = *EIRToValue( lhsRefType.type() );
auto rhsTupTypeEIR = rhs.type();
if( !rhs.isConstant() )
{
auto rhsRefType = *FromValue< ReferenceType >( *EIRToValue( lhs.type() ) );
rhsTupTypeEIR = rhsRefType.type();
}
auto rhsTupType = *EIRToValue( rhsTupTypeEIR );
if( IsTrivialTupleAssignment( c, lhsTupType, rhsTupType ) )
{
auto cfg = GetCFG( c );
if( !cfg )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
0, "assignments are not allowed here." );
return PoisonValue();
}
else if( auto bb = cfg->currentBB() )
{
bb->append( rhs );
if( !rhs.isConstant() )
bb->append( Load( rhsTupTypeEIR, rhs.locationId() ) );
|
| ︙ | ︙ | |||
139 140 141 142 143 144 145 |
if( rhs.isConstant() )
gen = GenerateValuesFromConstantTuple( rhs );
else
gen = GenerateRHSValuesFromTupleRef( assOp, c, rhs );
uint32_t index = 0;
bool success = true;
| | > | | | | > | | | | | > | | < | | | < | > | 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 |
if( rhs.isConstant() )
gen = GenerateValuesFromConstantTuple( rhs );
else
gen = GenerateRHSValuesFromTupleRef( assOp, c, rhs );
uint32_t index = 0;
bool success = true;
ForEachInTupleType( lhsTupType,
[&]( auto&& t )
{
if( gen.finished() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
0, "incompatible tuple sizes." );
success = false;
return false;
}
auto srcVal = gen.consume();
auto rt = ValueToEIR( ToValue( ReferenceType( t, MutAccessSpecifier() ) ) );
auto destVal =
BuildComputedValue( rt, lhs, cir::Select( index++, lhs.locationId() ) );
auto r = InvokeOverloadSet( c, assOp, MakeClosedTuple( destVal, srcVal ) );
success = !r.isPoison();
return success;
} );
if( !success )
return PoisonValue();
if( !gen.finished() )
{
DiagnosticsManager::GetInstance().emitErrorMessage( 0, "incompatible tuple sizes." );
return PoisonValue();
}
return lhs;
}
} // namespace goose::builtins
|
Changes to bs/builtins/operators/tuple.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#ifndef GOOSE_BUILTINS_OPERATORS_TUPLE_H
#define GOOSE_BUILTINS_OPERATORS_TUPLE_H
namespace goose::builtins
{
using namespace goose::parse;
static inline auto BuildGenericTupleOperator()
{
return []< typename tag >( auto&& e, auto&& pOvlSet, tag t )
{
if constexpr( is_same_v< tag, UnaryOpTag > )
{
| | | > | | < | | | | > > | | > | > | > | | < | | | | | | | | > | | 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_BUILTINS_OPERATORS_TUPLE_H
#define GOOSE_BUILTINS_OPERATORS_TUPLE_H
namespace goose::builtins
{
using namespace goose::parse;
static inline auto BuildGenericTupleOperator()
{
return []< typename tag >( auto&& e, auto&& pOvlSet, tag t )
{
if constexpr( is_same_v< tag, UnaryOpTag > )
{
using intrinsicType = Intrinsic< Value( CustomPattern< Value, TuplePattern > ) >;
auto intrinsicFunc = [pOvlSet]( const Context& c, const Value& operand )
{
auto result = EmptyOpenTuple();
ForEachInTuple( operand,
[&]( auto&& operand )
{
auto r = InvokeOverloadSet( c, pOvlSet, MakeClosedTuple( operand ) );
result = AppendToTuple( result, r );
return true;
} );
return result;
};
pOvlSet->add( e, ToValue< intrinsicType >( move( intrinsicFunc ) ),
GetBuiltinIntrinsicFuncInvocationRule() );
}
else
{
using intrinsicType = Intrinsic< Value(
CustomPattern< Value, TuplePattern >, CustomPattern< Value, TuplePattern > ) >;
auto intrinsicFunc = [pOvlSet](
const Context& c, const Value& lhs, const Value& rhs )
{
if( TupleSize( lhs ) != TupleSize( rhs ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
0, "Incompatible tuple sizes." );
return PoisonValue();
}
auto result = EmptyOpenTuple();
ForEachInTuples( lhs, rhs,
[&]( auto&& lhs, auto&& rhs )
{
auto r = InvokeOverloadSet( c, pOvlSet, MakeClosedTuple( lhs, rhs ) );
// Super inefficient, but that's far from the biggest such problem.
// This is just the bootstrap compiler anyway. Should hopefully be able
// to easily make more optimized stuff when rewriting the self hosted
// compiler, some day far away in the future.
result = AppendToTuple( result, r );
return true;
} );
return result;
};
pOvlSet->add( e, ToValue< intrinsicType >( move( intrinsicFunc ) ),
GetBuiltinIntrinsicFuncInvocationRule() );
}
};
}
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/statements/break.cpp.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 |
{
auto& dm = DiagnosticsManager::GetInstance();
auto level = GetBreakableScopeLevels( p.context() );
if( p.isInParenExpr() || !level )
{
| > | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
{
auto& dm = DiagnosticsManager::GetInstance();
auto level = GetBreakableScopeLevels( p.context() );
if( p.isInParenExpr() || !level )
{
dm.emitSyntaxErrorMessage(
locationId, "the break statement is not allowed here.", 0 );
return false;
}
auto cfg = GetCFG( p.context() );
if( !cfg->currentBB() || cfg->currentBB()->terminator() )
{
|
| ︙ | ︙ | |||
37 38 39 40 41 42 43 |
cfg->currentBB()->setTerminator( cir::Break( level ) );
return true;
};
RegisterRule( e, "break"_sid, Rule( handleBreak ) );
}
| | | 38 39 40 41 42 43 44 45 |
cfg->currentBB()->setTerminator( cir::Break( level ) );
return true;
};
RegisterRule( e, "break"_sid, Rule( handleBreak ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/statements/continue.cpp.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 |
{
auto& dm = DiagnosticsManager::GetInstance();
auto level = GetContinuableScopeLevels( p.context() );
if( p.isInParenExpr() || !level )
{
| > | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
{
auto& dm = DiagnosticsManager::GetInstance();
auto level = GetContinuableScopeLevels( p.context() );
if( p.isInParenExpr() || !level )
{
dm.emitSyntaxErrorMessage(
locationId, "the continue statement is not allowed here.", 0 );
return false;
}
auto cfg = GetCFG( p.context() );
if( !cfg->currentBB() || cfg->currentBB()->terminator() )
{
|
| ︙ | ︙ | |||
37 38 39 40 41 42 43 |
cfg->currentBB()->setTerminator( cir::Continue( level ) );
return true;
};
RegisterRule( e, "continue"_sid, Rule( handleContinue ) );
}
| | | 38 39 40 41 42 43 44 45 |
cfg->currentBB()->setTerminator( cir::Continue( level ) );
return true;
};
RegisterRule( e, "continue"_sid, Rule( handleContinue ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/statements/ct-for.cpp.
| ︙ | ︙ | |||
13 14 15 16 17 18 19 |
auto handleCTFor = []( Parser& p, LocationId locationId, uint32_t prec )
{
auto& dm = DiagnosticsManager::GetInstance();
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence::ForStmt ) )
{
| > | > | > | > | > | > | | > | | > | | | | | < < > | 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 |
auto handleCTFor = []( Parser& p, LocationId locationId, uint32_t prec )
{
auto& dm = DiagnosticsManager::GetInstance();
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence::ForStmt ) )
{
dm.emitSyntaxErrorMessage(
locationId, "expected a declaration following the #for statement.", 0 );
return false;
}
auto decl = np.popValue();
if( !decl )
{
dm.emitSyntaxErrorMessage(
locationId, "expected a declaration following the #for statement.", 0 );
return false;
}
bool isDecl = IsDecl( *decl );
bool isTNamedDecl = IsTNamedDecl( *decl );
bool isTDecl = IsTDecl( *decl );
// TODO also allow a TVar or TTVar, turn them into a TDecl, like for tfunc params?
if( !isDecl && !isTNamedDecl && !isTDecl )
{
dm.emitSyntaxErrorMessage( decl->locationId(), "expected a declaration.", 0 );
return false;
}
auto next = p.resolver()->consumeUnresolved();
if( !next )
{
dm.emitSyntaxErrorMessage(
decl->locationId(), "expected 'in' after declaration.", 0 );
return false;
}
const auto* nextSid = get_if< StringId >( &next->first );
if( !nextSid )
{
dm.emitSyntaxErrorMessage(
decl->locationId(), "expected 'in' after declaration.", 0 );
return false;
}
if( *nextSid != "in"_sid )
{
dm.emitSyntaxErrorMessage( next->second, "expected 'in'.", 0 );
return false;
}
np = p.makeNestedParser();
if( !np.parseExpression( precedence::ForStmt ) )
{
dm.emitSyntaxErrorMessage(
next->second, "expected an expression following 'in'.", 0 );
return false;
}
auto container = np.popValue();
if( !container )
{
dm.emitSyntaxErrorMessage(
next->second, "expected an expression following 'in'.", 0 );
return false;
}
auto body = make_shared< vector< TermLoc > >();
auto g = p.resolver()->consumeUnit();
move( g.begin(), g.end(), back_inserter( *body ) );
Value wrappedDecl;
if( isDecl )
{
wrappedDecl = ToValue( Wrap( make_shared< Decl >( *FromValue< Decl >( *decl ) ) ) )
.setLocationId( decl->locationId() );
}
else if( isTNamedDecl )
{
wrappedDecl = ToValue(
Wrap( make_shared< TNamedDecl >( *FromValue< TNamedDecl >( *decl ) ) ) )
.setLocationId( decl->locationId() );
}
else if( isTDecl )
{
wrappedDecl =
ToValue( Wrap( make_shared< TDecl >( *FromValue< TDecl >( *decl ) ) ) )
.setLocationId( decl->locationId() );
}
CTForEach( p.context(), wrappedDecl, *container, body );
return true;
};
RegisterRule( e, "#for"_sid, Rule( handleCTFor ) );
RegisterRule( e, "in"_sid,
Rule( []( auto&& ) -> optional< uint32_t > { return nullopt; },
[]( auto&&, auto&&, auto&& ) { return false; } ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/statements/ct-if.cpp.
| ︙ | ︙ | |||
13 14 15 16 17 18 19 |
auto handleCTIf = []( Parser& p, LocationId locationId, uint32_t prec )
{
auto& dm = DiagnosticsManager::GetInstance();
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence::IfStmt ) )
{
| > | > | > | > | | | | | | < > | > | > | | > | | | | 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 |
auto handleCTIf = []( Parser& p, LocationId locationId, uint32_t prec )
{
auto& dm = DiagnosticsManager::GetInstance();
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence::IfStmt ) )
{
dm.emitSyntaxErrorMessage(
locationId, "expected an expression following the #if statement.", 0 );
return false;
}
auto condVal = np.popValue();
if( !condVal )
{
dm.emitSyntaxErrorMessage(
locationId, "expected an expression following the #if statement.", 0 );
return false;
}
const auto& context = p.context();
auto converted = ConvertValueToType( context, *condVal, GetValueType< bool >() );
if( holds_alternative< ValUnifyError >( converted ) )
{
switch( get< ValUnifyError >( converted ) )
{
case ValUnifyError::NoSolution:
dm.emitErrorMessage(
condVal->locationId(), "this doesn't evaluate to a bool constant." );
break;
case ValUnifyError::Ambiguous:
dm.emitErrorMessage( condVal->locationId(), "ambiguous bool conversion." );
break;
}
return false;
}
auto condBool = get< Value >( converted );
if( !condBool.isConstant() )
{
dm.emitErrorMessage(
condVal->locationId(), "this doesn't evaluate to a bool constant." );
return false;
}
auto cond = FromValue< bool >( condBool );
// If the cond is invalid, both blocks will be excluded, but parsing will continue, to
// give the best chance of a graceful recovery of the parsing and hopefully be able to
// detect more legit errors before we bail out. We do mark the current diagnostics
// context as busted, however, since what we're going to miss in the #if block that
// should have been parsed is likey to cause cascading errors in the current scope.
if( !cond )
dm.emitSyntaxErrorMessage(
condVal->locationId(), "this doesn't evaluate to a bool constant." );
auto next = p.resolver()->lookAheadUnresolved();
if( !next )
{
dm.emitSyntaxErrorMessage(
p.resolver()->currentLocation(), "brace block expected.", 0 );
return false;
}
auto decomp = Decompose( next->first, Val< Delimiter >() );
if( !decomp || *decomp != Delimiter::OpenBrace )
{
dm.emitSyntaxErrorMessage(
p.resolver()->currentLocation(), "brace block expected.", 0 );
return false;
}
// Deal with the then block.
if( cond && *cond )
{
// Condition is true: parse the 'then' block.
if( !p.parseBraceBlock() )
return false;
}
else
{
// Condition is false: skip the 'then' block.
auto gen = p.resolver()->consumeUnit();
for_each( gen.begin(), gen.end(), []( auto&& ) {} );
}
next = p.resolver()->lookAheadUnresolved();
if( next )
{
const auto* nextSid = get_if< StringId >( &next->first );
if( nextSid && *nextSid == "#else"_sid )
{
p.resolver()->consumeUnresolved();
auto next = p.resolver()->lookAheadUnresolved();
auto decomp = Decompose( next->first, Val< Delimiter >() );
if( !decomp || *decomp != Delimiter::OpenBrace )
{
dm.emitSyntaxErrorMessage(
p.resolver()->currentLocation(), "brace block expected.", 0 );
return false;
}
// Deal with the else block.
if( cond && !*cond )
{
// Condition is false: parse the 'else' block.
if( !p.parseBraceBlock() )
return false;
}
else
{
// Condition is true: skip the 'else' block.
auto gen = p.resolver()->consumeUnit();
for_each( gen.begin(), gen.end(), []( auto&& ) {} );
}
}
}
return true;
};
RegisterRule( e, "#if"_sid, Rule( handleCTIf ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/statements/forall.cpp.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 |
auto handleForAll = []( Parser& p, LocationId locationId, uint32_t prec )
{
auto& dm = DiagnosticsManager::GetInstance();
auto& c = p.context();
if( !InVerificationCode( c ) )
{
| > | > | > | > | | > | | | | | | | | > | | | < | | | | | > | > | | | | | 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 |
auto handleForAll = []( Parser& p, LocationId locationId, uint32_t prec )
{
auto& dm = DiagnosticsManager::GetInstance();
auto& c = p.context();
if( !InVerificationCode( c ) )
{
dm.emitSyntaxErrorMessage(
locationId, "the forall statement is not allowed here.", 0 );
return false;
}
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence::IfStmt ) )
{
dm.emitSyntaxErrorMessage( locationId,
"expected a decl or a tuple of decls following the forall statement.", 0 );
return false;
}
auto decls = np.popValue();
if( !decls )
{
dm.emitSyntaxErrorMessage( locationId,
"expected a decl or a tuple of decls following the forall statement.", 0 );
return false;
}
if( !IsTuple( *decls ) )
decls = MakeOpenTuple( move( *decls ) );
auto is = make_shared< InstrSeq >();
auto identity =
AppendToVectorTerm( c.identity(), TERM( StringId( Env::NewUniqueId() ) ) );
c.env()->addVisibilityRule( c.identity(), identity );
bool failed = false;
uint32_t numArgs = 0;
ForEachInTuple( *decls,
[&]( auto&& d )
{
auto decl = FromValue< Decl >( d );
if( !decl )
{
dm.emitErrorMessage( d.locationId(), "expected a decl." );
failed = true;
return false;
}
auto ph = BuildComputedValue(
decl->type(), Placeholder( decl->type(), decl->name(), d.locationId() ) );
c.env()->storeValue( AppendToVectorTerm( identity, decl->name() ), ANYTERM( _ ),
ValueToEIR( ph ) );
AppendToInstrSeq( *is, PushType( decl->type(), d.locationId() ),
PushStringId( decl->name(), d.locationId() ) );
++numArgs;
return true;
} );
if( failed )
return false;
AppendToInstrSeq( *is, ForAllSetup( numArgs, locationId ) );
auto delim = np.resolver()->lookAheadUnresolved();
if( !delim )
{
dm.emitSyntaxErrorMessage(
np.resolver()->currentLocation(), format( "'[' expected." ), 0 );
return false;
}
auto decomp = Decompose( delim->first, Val< Delimiter >() );
if( !decomp || *decomp != Delimiter::OpenBracket )
{
dm.emitSyntaxErrorMessage(
np.resolver()->currentLocation(), format( "'[' expected." ), 0 );
return false;
}
// Parse the propositions block.
auto [props, ploc] = p.tokenizeBracketBlock();
props->setIdentity( identity );
if( !props->parse( c ) )
return true;
// Merge the propositions into a conjunction
bool first = true;
for( auto&& p : props->props() )
{
AppendToInstrSeq( *is, move( p ) );
if( !first )
AppendToInstrSeq( *is, And( locationId ) );
first = false;
}
AppendToInstrSeq( *is, ForAll( locationId ) );
p.pushValue( BuildComputedValue( GetValueType< bool >(), move( is ) )
.setLocationId( locationId ) );
return true;
};
RegisterRule( e, "forall"_sid, Rule( handleForAll ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/statements/if.cpp.
| ︙ | ︙ | |||
33 34 35 36 37 38 39 |
// Create a scope for the entire if, so that any var declared
// inside of the condition is alive and visible throughout the if.
Scope s( p.context() );
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence::IfStmt ) )
{
| > | > | | > > | | > | > | > | < < | | 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 |
// Create a scope for the entire if, so that any var declared
// inside of the condition is alive and visible throughout the if.
Scope s( p.context() );
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence::IfStmt ) )
{
dm.emitSyntaxErrorMessage(
locationId, "expected an expression following the if statement.", 0 );
return false;
}
auto condVal = np.popValue();
if( !condVal )
{
dm.emitSyntaxErrorMessage(
locationId, "expected an expression following the if statement.", 0 );
return false;
}
const auto& context = p.context();
auto converted = ConvertValueToType( context, *condVal, GetValueType< bool >() );
if( holds_alternative< ValUnifyError >( converted ) )
{
switch( get< ValUnifyError >( converted ) )
{
// If the condition is invalid, bail out and mark the current
// diagnostics context as bust to avoid spamming cascading errors.
case ValUnifyError::NoSolution:
dm.emitSyntaxErrorMessage( condVal->locationId(),
"the if condition can't be converted to a bool." );
break;
case ValUnifyError::Ambiguous:
dm.emitSyntaxErrorMessage(
condVal->locationId(), "ambiguous if condition bool conversion." );
break;
}
return false;
}
if( get< Value >( converted ).isPoison() )
PoisonBuilder( p.context() );
// The condition may have emitted additional basic blocks, so get the current block
// again.
pPrecBB = cfg->currentBB();
auto pThenBB = ParseSubStatement( p, precedence::IfStmt );
if( !pThenBB )
{
dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(),
"expected a statement after the if condition.", 0 );
return false;
}
// Retrieve the successor block of the then branch, if any
auto pThenSuccBB = cfg->currentBB();
ptr< cir::BasicBlock > pElseBB, pElseSuccBB;
auto next = p.resolver()->lookAheadUnresolved();
if( next )
{
const auto* nextSid = get_if< StringId >( &next->first );
if( nextSid && *nextSid == "else"_sid )
{
p.resolver()->consumeUnresolved();
pElseBB = ParseSubStatement( p, precedence::IfStmt );
if( !pElseBB )
{
dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(),
"expected a statement after 'else'.", 0 );
return false;
}
// Retrieve the successor block of the else branch, if any
pElseSuccBB = cfg->currentBB();
}
}
if( !pPrecBB )
return true;
ptr< cir::BasicBlock > pSuccBB;
// If both the then and the else blocks successors exist and are terminated,
// we don't need a successor block.
if( !pElseBB || ( pThenSuccBB && !pThenSuccBB->terminator() )
|| ( pElseSuccBB && !pElseSuccBB->terminator() ) )
pSuccBB = cfg->createBB();
pPrecBB->append( get< Value >( converted ) );
pPrecBB->setTerminator( cir::CondBranch( pThenBB, pElseBB ? pElseBB : pSuccBB ) );
if( pThenSuccBB && !pThenSuccBB->terminator() )
pThenSuccBB->setTerminator( cir::Branch( pSuccBB ) );
if( pElseSuccBB && !pElseSuccBB->terminator() )
pElseSuccBB->setTerminator( cir::Branch( pSuccBB ) );
// Intentionally set the current BB to null if no
// successor block was created: it means all our code paths
// are already terminated and there is no point in emitting any more
// instructions.
cfg->setCurrentBB( pSuccBB );
return true;
};
RegisterRule( e, "if"_sid, Rule( handleIf ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/statements/return.cpp.
| ︙ | ︙ | |||
13 14 15 16 17 18 19 |
auto handleReturn = []( Parser& p, LocationId locationId, uint32_t prec )
{
auto& dm = DiagnosticsManager::GetInstance();
auto cfg = GetCFG( p.context() );
if( p.isInParenExpr() || !cfg || !IsBranchingAllowed( p.context() ) )
{
| > | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
auto handleReturn = []( Parser& p, LocationId locationId, uint32_t prec )
{
auto& dm = DiagnosticsManager::GetInstance();
auto cfg = GetCFG( p.context() );
if( p.isInParenExpr() || !cfg || !IsBranchingAllowed( p.context() ) )
{
dm.emitSyntaxErrorMessage(
locationId, "the return statement is not allowed here.", 0 );
return false;
}
if( !cfg->currentBB() || cfg->currentBB()->terminator() )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "unreachable code.", 0 );
|
| ︙ | ︙ | |||
40 41 42 43 44 45 46 |
cfg->emitTerminator( locationId, cir::RetVoid( locationId ) );
return true;
}
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence::ReturnStmt + 1 ) )
{
| > | > | > | | | 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 |
cfg->emitTerminator( locationId, cir::RetVoid( locationId ) );
return true;
}
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence::ReturnStmt + 1 ) )
{
dm.emitSyntaxErrorMessage(
locationId, "expected an expression following the return statement.", 0 );
cfg->currentBB()->append( PoisonValue() );
cfg->emitTerminator( locationId, cir::Ret( locationId ) );
return false;
}
auto retVal = np.popValue();
if( !retVal )
{
dm.emitSyntaxErrorMessage(
locationId, "expected an expression following the return statement.", 0 );
cfg->currentBB()->append( PoisonValue() );
cfg->emitTerminator( locationId, cir::Ret( locationId ) );
return false;
}
auto converted = ConvertValueToType( context, *retVal, *context.returnType() );
if( holds_alternative< ValUnifyError >( converted ) )
{
switch( get< ValUnifyError >( converted ) )
{
case ValUnifyError::NoSolution:
dm.emitErrorMessage( retVal->locationId(), "return value type mismatch." );
break;
case ValUnifyError::Ambiguous:
dm.emitErrorMessage(
retVal->locationId(), "ambiguous return value conversion." );
break;
}
// Emit a terminator with a poison value to avoid the function compilation
// code to complain about a missing return.
cfg->currentBB()->append( PoisonValue() );
cfg->emitTerminator( locationId, cir::Ret( locationId ) );
PoisonBuilder( p.context() );
return true;
}
cfg->currentBB()->append( get< Value >( converted ) );
cfg->emitTerminator( locationId, cir::Ret( locationId ) );
return true;
};
RegisterRule( e, "return"_sid, Rule( handleReturn ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/statements/statements.h.
| ︙ | ︙ | |||
21 22 23 24 25 26 27 |
SetupCTIfStmt( e );
SetupWhileStmt( e );
SetupCTForStmt( e );
SetupBreakStmt( e );
SetupContinueStmt( e );
SetupForAllStmt( e );
}
| | | 21 22 23 24 25 26 27 28 29 30 |
SetupCTIfStmt( e );
SetupWhileStmt( e );
SetupCTForStmt( e );
SetupBreakStmt( e );
SetupContinueStmt( e );
SetupForAllStmt( e );
}
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/statements/using.cpp.
| ︙ | ︙ | |||
13 14 15 16 17 18 19 |
auto handleUsing = []( Parser& p, LocationId locationId, uint32_t prec )
{
auto& dm = DiagnosticsManager::GetInstance();
auto nameTerm = p.resolver()->consume();
if( !nameTerm )
{
| > | | | | > | | | | > | | | > | > | | | > | | | > | | | > | | | 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 |
auto handleUsing = []( Parser& p, LocationId locationId, uint32_t prec )
{
auto& dm = DiagnosticsManager::GetInstance();
auto nameTerm = p.resolver()->consume();
if( !nameTerm )
{
dm.emitSyntaxErrorMessage(
p.resolver()->currentLocation(), "expected an identifier.", 0 );
return false;
}
const auto* name = get_if< StringId >( &nameTerm->first );
if( !name )
{
dm.emitSyntaxErrorMessage( nameTerm->second, "expected an identifier.", 0 );
return false;
}
sema::Context::CurrentContextGuard ccg( p.context() );
auto& context = p.context();
// we don't have a builder to parse using expresions. It disables
// a number of things, in particular, the ability to overload functions.
// This avoids unfortunate parsing ambiguiousness in some cases
// (cf test g0/statements/e-using-2.g0)
context.setBuilder( ToValue< void >() );
// Create an identity to contain the visibility of any symbol defined inside of the
// using statement. Since the current identity is that of the local variables of the
// current function and those shouldn't be visible inside of the using statement, we
// simply replace the last element of the identity by the name of the using definition,
// so that its parent becomes the current function's identity.
//
// TODO: we'll probably need a more sophisticated scheme to be able to deal with lambda
// captures where localvars from the parent(s) functions should be visible, but still
// not visible inside of using statements. But one mess of a feature at a time.
auto localIdentity =
TakeVectorTerm( context.identity(), VecSize( context.identity() ) - 1 );
get< pvec >( localIdentity )->terms().back() = nameTerm->first;
// TODO this probably makes locvars visible from inside the using expression, which is
// wrong see struct/parse.cpp for a better approach
context.env()->addVisibilityRule( context.identity(), localIdentity );
context.setIdentity( localIdentity );
auto eqTerm = p.resolver()->consumeUnresolved();
if( !eqTerm )
{
dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(), "expected '='.", 0 );
context.env()->storeValue(
localIdentity, ANYTERM( _ ), ValueToEIR( PoisonValue() ) );
return true;
}
const auto* eq = get_if< StringId >( &eqTerm->first );
if( !eq || *eq != "="_sid )
{
dm.emitSyntaxErrorMessage( eqTerm->second, "expected '='.", 0 );
context.env()->storeValue(
localIdentity, ANYTERM( _ ), ValueToEIR( PoisonValue() ) );
return true;
}
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence::UsingStmt ) )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "expected an expression.", 0 );
context.env()->storeValue(
localIdentity, ANYTERM( _ ), ValueToEIR( PoisonValue() ) );
return true;
}
if( !np.peekLastValue() )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "expected an expression.", 0 );
context.env()->storeValue(
localIdentity, ANYTERM( _ ), ValueToEIR( PoisonValue() ) );
return true;
}
auto val = np.popValue();
if( !val->isConstant() )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
val->locationId(), "this expression is not a comptime constant.", 0 );
context.env()->storeValue(
localIdentity, ANYTERM( _ ), ValueToEIR( PoisonValue() ) );
return true;
}
context.env()->storeValue( localIdentity, ANYTERM( _ ), ValueToEIR( *val ) );
return true;
};
RegisterRule( e, "using"_sid, Rule( handleUsing ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/statements/while.cpp.
| ︙ | ︙ | |||
13 14 15 16 17 18 19 |
auto handleWhile = []( Parser& p, LocationId locationId, uint32_t prec )
{
auto& dm = DiagnosticsManager::GetInstance();
auto cfg = GetCFG( p.context() );
if( p.isInParenExpr() || !cfg || !IsBranchingAllowed( p.context() ) )
{
| > | > | > | | > > | | > | > > | | > | 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 |
auto handleWhile = []( Parser& p, LocationId locationId, uint32_t prec )
{
auto& dm = DiagnosticsManager::GetInstance();
auto cfg = GetCFG( p.context() );
if( p.isInParenExpr() || !cfg || !IsBranchingAllowed( p.context() ) )
{
dm.emitSyntaxErrorMessage(
locationId, "the while statement is not allowed here.", 0 );
return false;
}
auto pPrecBB = cfg->currentBB();
if( !pPrecBB || pPrecBB->terminator() )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "unreachable code.", 0 );
cfg->poison();
}
// Create a scope for the entire while, so that any var declared
// inside of the condition is alive and visible throughout the while.
Scope s( p.context() );
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence::IfStmt ) )
{
dm.emitSyntaxErrorMessage(
locationId, "expected an expression following the while statement.", 0 );
return false;
}
auto condVal = np.popValue();
if( !condVal )
{
dm.emitSyntaxErrorMessage(
locationId, "expected an expression following the while statement.", 0 );
return false;
}
const auto& context = p.context();
auto converted = ConvertValueToType( context, *condVal, GetValueType< bool >() );
if( holds_alternative< ValUnifyError >( converted ) )
{
switch( get< ValUnifyError >( converted ) )
{
// If the condition is invalid, bail out and mark the current
// diagnostics context as bust to avoid spamming cascading errors.
case ValUnifyError::NoSolution:
dm.emitSyntaxErrorMessage( condVal->locationId(),
"the while condition can't be converted to a bool." );
break;
case ValUnifyError::Ambiguous:
dm.emitSyntaxErrorMessage(
condVal->locationId(), "ambiguous while condition bool conversion." );
break;
}
return false;
}
if( get< Value >( converted ).isPoison() )
PoisonBuilder( p.context() );
// The condition may have emitted additional basic blocks, so get the current block
// again.
pPrecBB = cfg->currentBB();
auto pHeaderBB = cfg->createBB();
// Set the loop header's location to a span including the while keyword and the
// condition
pHeaderBB->setLocationId(
Location::CreateSpanningLocation( locationId, condVal->locationId() ) );
BreakableScopeGuard bsg( p.context() );
ContinuableScopeGuard csg( p.context() );
auto pBodyBB = ParseSubStatement( p, precedence::IfStmt );
if( !pBodyBB )
{
dm.emitSyntaxErrorMessage( p.resolver()->currentLocation(),
"expected a statement after the while condition.", 0 );
return false;
}
if( !pPrecBB )
return true;
// Retrieve the final block of the loop body, if any
|
| ︙ | ︙ | |||
106 107 108 109 110 111 112 |
auto breakLevel = GetBreakableScopeLevels( p.context() );
auto continueLevel = GetContinuableScopeLevels( p.context() );
// Go through all basic blocks, find all break and continue terminators
// for our scope level and replace them with branches respectively to
// the successor BB or to the loop header BB.
| | > | | | | | | | | | | | | | | | | | | > | < | | 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 breakLevel = GetBreakableScopeLevels( p.context() );
auto continueLevel = GetContinuableScopeLevels( p.context() );
// Go through all basic blocks, find all break and continue terminators
// for our scope level and replace them with branches respectively to
// the successor BB or to the loop header BB.
cfg->forEachBB(
[&]( auto&& bb )
{
const auto& t = bb->terminator();
if( !t )
return;
if( const auto* pBreak = get_if< cir::Break >( &t->content() ) )
{
if( pBreak->level() == breakLevel )
bb->setTerminator( cir::Branch( pSuccBB ) );
return;
}
if( const auto* pCont = get_if< cir::Continue >( &t->content() ) )
{
if( pCont->level() == continueLevel )
bb->setTerminator( cir::Branch( pHeaderBB ) );
return;
}
} );
// Emit the conditional branch that will either run an iteration of the loop or exit to
// the succ bb.
pHeaderBB->append( get< Value >( converted ) );
pHeaderBB->setTerminator( cir::CondBranch( pBodyBB, pSuccBB ) );
cfg->setCurrentBB( pSuccBB );
return true;
};
RegisterRule( e, "while"_sid, Rule( handleWhile ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/basic.cpp.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 |
DefineConstant( e, "type"_sid, TypeType() );
DefineConstant( e, "void"_sid, GetValueType< void >() );
DefineConstant( e, "bool"_sid, GetValueType< bool >() );
DefineConstant( e, "ct_int"_sid, GetValueType< BigInt >() );
DefineConstant( e, "ct_char"_sid, GetValueType< char32_t >() );
DefineConstant( e, "ct_string"_sid, GetValueType< string >() );
| | < | < | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
DefineConstant( e, "type"_sid, TypeType() );
DefineConstant( e, "void"_sid, GetValueType< void >() );
DefineConstant( e, "bool"_sid, GetValueType< bool >() );
DefineConstant( e, "ct_int"_sid, GetValueType< BigInt >() );
DefineConstant( e, "ct_char"_sid, GetValueType< char32_t >() );
DefineConstant( e, "ct_string"_sid, GetValueType< string >() );
RegisterBuiltinFunc< Eager< Value >( Value ) >( e, "ct_sequence"_sid,
[]( const Value& containedType )
{ return ToValue( SequenceType( ValueToEIR( containedType ) ) ); } );
// bool literals
DefineConstant( e, "true"_sid, ValueToEIR( ToValue( true ) ) );
DefineConstant( e, "false"_sid, ValueToEIR( ToValue( false ) ) );
// _ and var are both aliases for an anonymous tvar ($_)
DefineConstant( e, "_"_sid, ValueToEIR( ToValue( TVar( "_"_sid ) ) ) );
|
| ︙ | ︙ | |||
47 48 49 50 51 52 53 |
bool IsSequenceType( const Value& v )
{
if( !v.isConstant() )
return false;
auto result = Decompose( v.val(),
| < | | < < < < < < | | < < < | < < | < > > | | 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 |
bool IsSequenceType( const Value& v )
{
if( !v.isConstant() )
return false;
auto result = Decompose( v.val(),
Vec( Lit( "ct_type"_sid ), SubTerm(), Val< void* >(), Lit( "sequence"_sid ),
SubTerm() ) );
return !!result;
}
Term GetSequenceSubType( const Value& v )
{
auto result = Decompose( v.val(),
Vec( Lit( "ct_type"_sid ), SubTerm(), Val< void* >(), Lit( "sequence"_sid ),
SubTerm() ) );
auto&& [_, __, type] = *result;
return type;
}
} // namespace goose::builtins
namespace goose::eir
{
// void
const Term& Bridge< void >::Type()
{
static auto type = ValueToEIR( Value( TypeType(), TSID( void ) ) );
return type;
}
const Value& Bridge< void >::ToValue()
{
static auto val = Value( Type(), TSID( void ) );
return val;
}
// identifier (StringId)
const Term& Bridge< StringId >::Type()
{
static auto type =
ValueToEIR( Value( TypeType(), MkStdType( TSID( ct_type ), TSID( identifier ) ) ) );
return type;
}
Value Bridge< StringId >::ToValue( StringId x )
{
return Value( Type(), TERM( x ) );
}
|
| ︙ | ︙ | |||
118 119 120 121 122 123 124 |
return *pint;
}
// bool
const Term& Bridge< bool >::Type()
{
| > | | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
return *pint;
}
// bool
const Term& Bridge< bool >::Type()
{
static auto type =
ValueToEIR( Value( TypeType(), MkStdType( TSID( rt_type ), TSID( bool ) ) ) );
return type;
}
Value Bridge< bool >::ToValue( bool x )
{
return Value( Type(), TERM( x ? 1U : 0U ) );
}
|
| ︙ | ︙ | |||
142 143 144 145 146 147 148 |
return *pint;
}
// Integers
const Term& Bridge< BigInt >::Type()
{
| > | > | > | | < < | | < < < < < | | > | | 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 |
return *pint;
}
// Integers
const Term& Bridge< BigInt >::Type()
{
static auto type =
ValueToEIR( Value( TypeType(), MkStdType( TSID( ct_type ), TSID( ct_int ) ) ) );
return type;
}
const BigInt* Bridge< BigInt >::FromValue( const Value& v )
{
if( v.type() != Type() )
return nullptr;
return get_if< BigInt >( &v.val() );
}
// char
const Term& Bridge< char32_t >::Type()
{
static auto type =
ValueToEIR( Value( TypeType(), MkStdType( TSID( ct_type ), TSID( ct_char ) ) ) );
return type;
}
Value Bridge< char32_t >::ToValue( char32_t x )
{
return Value( Type(), TERM( x ) );
}
optional< char32_t > Bridge< char32_t >::FromValue( const Value& v )
{
if( v.type() != Type() )
return nullopt;
return get< uint64_t >( v.val() );
}
// strings
const Term& Bridge< string >::Type()
{
static auto type =
ValueToEIR( Value( TypeType(), MkStdType( TSID( ct_type ), TSID( ct_string ) ) ) );
return type;
}
Value Bridge< string >::ToValue( const string& x )
{
return Value( Type(), TERM( x ) );
}
const string* Bridge< string >::FromValue( const Value& v )
{
if( !v.isConstant() || v.type() != Type() )
return nullptr;
return get_if< string >( &v.val() );
}
// sequences
Value Bridge< SequenceType >::ToValue( const SequenceType& st )
{
return Value( Type(), MkStdType( TSID( ct_type ), TSID( sequence ), st.m_containedType ) );
}
optional< SequenceType > Bridge< SequenceType >::FromValue( const Value& v )
{
auto result = Decompose( v.val(),
Vec( Lit( "ct_type"_sid ), SubTerm(), Val< void* >(), Lit( "sequence"_sid ),
SubTerm() ) );
if( !result )
return nullopt;
auto&& [predicates, cgType, containedType] = *result;
return SequenceType( containedType );
}
// Generic values
const Term& Bridge< Value >::Type()
{
// This is a trick to get a generic signature for this type when creating a builtin
// function. This way we can define a builtin function that take any value of any type as
// is, without conversion.
static auto type = HOLE( "_"_sid, "tvar"_sid );
return type;
}
} // namespace goose::eir
|
Changes to bs/builtins/types/basic.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#ifndef GOOSE_BUILTINS_TYPES_BASIC_H
#define GOOSE_BUILTINS_TYPES_BASIC_H
namespace goose::builtins
{
extern void SetupBasicTypes( Env& e );
extern void SetupBasicTypesPrettyPrinting();
struct SequenceType
{
template< typename T >
SequenceType( T&& containedType ) :
m_containedType( forward< T >( containedType ) )
| < > > | < < | | < | < | < | < | | < < | < | < | > | < | | | < | < < < | > > > | < | 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 |
#ifndef GOOSE_BUILTINS_TYPES_BASIC_H
#define GOOSE_BUILTINS_TYPES_BASIC_H
namespace goose::builtins
{
extern void SetupBasicTypes( Env& e );
extern void SetupBasicTypesPrettyPrinting();
struct SequenceType
{
template< typename T >
SequenceType( T&& containedType ) :
m_containedType( forward< T >( containedType ) )
{
}
Term m_containedType;
};
struct ValuePatternAny
{
static const Term& GetPattern();
};
struct ValuePatternT
{
static const Term& GetPattern();
};
template< typename T > struct PatternValueTypeOf
{
static const Term& GetPattern()
{
static auto pat = GetValueType< T >();
return pat;
}
};
template< typename T > using ValueTypeOf = CustomPattern< Value, PatternValueTypeOf< T > >;
extern bool IsSequenceType( const Value& v );
extern Term GetSequenceSubType( const Value& v );
} // namespace goose::builtins
namespace goose::eir
{
template<> struct Bridge< void >
{
static const Term& Type();
static const Value& ToValue();
};
template<> struct Bridge< StringId >
{
static const Term& Type();
static Value ToValue( StringId x );
static optional< StringId > FromValue( const Value& v );
};
template<> struct Bridge< bool >
{
static const Term& Type();
static Value ToValue( bool x );
static optional< bool > FromValue( const Value& v );
};
template<> struct Bridge< BigInt >
{
static const Term& Type();
template< typename T > static Value ToValue( T&& x )
{
return Value( Type(), TERM( BigInt( forward< T >( x ) ) ) );
}
static const BigInt* FromValue( const Value& v );
};
template<> struct Bridge< char32_t >
{
static const Term& Type();
static Value ToValue( char32_t x );
static optional< char32_t > FromValue( const Value& v );
};
template<> struct Bridge< string >
{
static const Term& Type();
static Value ToValue( const string& x );
static const string* FromValue( const Value& v );
};
template<> struct Bridge< builtins::SequenceType >
{
static const Term& Type() { return TypeType(); }
static Value ToValue( const builtins::SequenceType& st );
static optional< builtins::SequenceType > FromValue( const Value& v );
};
template< typename T > struct Bridge< Sequence< T > >
{
static const Term& Type()
{
static auto type =
ValueToEIR( goose::eir::ToValue( builtins::SequenceType{ GetValueType< T >() } ) );
return type;
}
template< typename S > static Value ToValue( S&& s )
{
return Value( Type(), make_shared< Sequence< T > >( forward< S >( s ) ) );
}
static optional< Sequence< T > > FromValue( const Value& v )
{
auto result = Decompose( v.val(), Val< ptr< void > >() );
if( !result )
return nullopt;
return *static_pointer_cast< Sequence< T > >( result->get() );
}
};
template<> struct Bridge< Value >
{
static const Term& Type();
static const Value& ToValue( const Value& v ) { return v; }
static const Value* FromValue( const Value& v ) { return &v; }
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/constrainedfunc/constrainedfunc.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include "builtins/builtins.h"
using namespace goose::builtins;
bool goose::builtins::IsConstrainedFunc( const Value& tcc )
{
return tcc.type() == GetValueType< ConstrainedFunc >();
}
namespace goose::eir
{
const Term& Bridge< builtins::ConstrainedFunc >::Type()
{
| > | | < | | | > < | | | | < | < | < < > | 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 "builtins/builtins.h"
using namespace goose::builtins;
bool goose::builtins::IsConstrainedFunc( const Value& tcc )
{
return tcc.type() == GetValueType< ConstrainedFunc >();
}
namespace goose::eir
{
const Term& Bridge< builtins::ConstrainedFunc >::Type()
{
static auto type =
ValueToEIR( Value( TypeType(), VEC( TSID( ct_type ), TSID( constrainedfunc ) ) ) );
return type;
}
Value Bridge< builtins::ConstrainedFunc >::ToValue( const builtins::ConstrainedFunc& cf )
{
return Value( Type(),
VEC( Quote( cf.constraintPat() ), TERM( static_pointer_cast< void >( cf.invRule() ) ),
ValueToEIR( cf.func() ) ) );
}
optional< builtins::ConstrainedFunc > Bridge< builtins::ConstrainedFunc >::FromValue(
const Value& v )
{
if( !IsConstrainedFunc( v ) )
return nullopt;
auto result = Decompose( v.val(),
Vec( SubTerm(), // constraint pattern
Val< ptr< void > >(), // pInvRule
SubTerm() // func
) );
if( !result )
return nullopt;
auto&& [constraintPat, pInvRule, func] = *result;
return builtins::ConstrainedFunc( *Unquote( constraintPat ),
static_pointer_cast< InvocationRule >( pInvRule ), *EIRToValue( func ) );
}
} // namespace goose::eir
|
Changes to bs/builtins/types/constrainedfunc/constrainedfunc.h.
1 2 3 4 5 6 7 8 9 10 |
#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
{
| | | | | | | < | > > | > | > | | | | | < > < | | | 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 |
#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 ) ),
m_pInvRule( forward< IR >( pInvRule ) ),
m_func( forward< F >( func ) )
{
}
const auto& constraintPat() const { return m_constraintPat; }
const auto& invRule() const { return m_pInvRule; }
const auto& func() const { return m_func; }
private:
Term m_constraintPat;
ptr< InvocationRule > m_pInvRule;
Value m_func;
};
extern bool IsConstrainedFunc( const Value& tcc );
} // namespace goose::builtins
namespace goose::eir
{
template<> struct Bridge< builtins::ConstrainedFunc >
{
static const Term& Type();
static Value ToValue( const builtins::ConstrainedFunc& cf );
static optional< builtins::ConstrainedFunc > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/constrainedfunc/invoke.cpp.
1 2 3 4 5 6 7 8 |
#include "builtins/builtins.h"
using namespace goose::sema;
namespace goose::builtins
{
class ConstrainedFuncInvocationRule : public InvocationRule
{
| | > | | | | | | | | | | < | | | < | | | | | < | < < > | 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 |
#include "builtins/builtins.h"
using namespace goose::sema;
namespace goose::builtins
{
class ConstrainedFuncInvocationRule : public InvocationRule
{
public:
Value resolveInvocation(
Context& c, LocationId loc, const Value& callee, const Term& args ) const final
{
auto callPat = PrependToVectorTerm( args, HOLE( "_"_sid, "fwd"_sid ) );
// 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 );
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();
}
};
void SetupConstrainedFuncInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule(
ValueToEIR( Value( GetValueType< ConstrainedFunc >(), ANYTERM( _ ) ) ),
make_shared< ConstrainedFuncInvocationRule >() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/constrainedfunc/typecheck.cpp.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
{
// func type param / constrainedfunc arg
e.typeCheckingRuleSet()->addTypeCheckingRule(
ParamPat( FuncTypePattern() ),
ValueToEIR( ValuePattern(
| < | < | | | < | < < < | < < | | | | | | | | | | | | | | | | | < | < < | < | | | < | < < < | < < | | | | | | | | | | | | | | | | | | | | < | < | | | < | < | < < < | < | < < > | 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 |
{
// func type param / constrainedfunc arg
e.typeCheckingRuleSet()->addTypeCheckingRule(
ParamPat( FuncTypePattern() ),
ValueToEIR( 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( *EIRToValue( type ) );
auto rhsVal = *EIRToValue( 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 = ValueToEIR( cfunc->func() );
co_yield TypeCheck( lhs, func, tcc );
co_return;
}
}
} );
// tfunc type param / constrainedfunc arg
e.typeCheckingRuleSet()->addTypeCheckingRule(
ValueToEIR( ValuePattern(
ANYTERM( _ ), TFuncTypeSigPattern( ANYTERM( _ ), ANYTERM( _ ) ), ANYTERM( _ ) ) ),
ValueToEIR( 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 [tfType, _] = DecomposeTFTSig( type );
auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *EIRToValue( tfType ) );
assert( callPat );
auto rhsVal = *EIRToValue( 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 = ValueToEIR( 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(
ValueToEIR( ValuePattern(
TSID( constant ), GetValueType< builtins::ConstrainedFunc >(), ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern( ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{ co_yield { lhs, tcc }; } );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/convert.cpp.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
{
void SetupConvert( Env& e )
{
using FwdValue = CustomPattern< Value, ForwarderPattern >;
// Default implementation of ConvertFuncParam():
// return the param as is.
| | | < < < | | | | < < < < > | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
{
void SetupConvert( Env& e )
{
using FwdValue = CustomPattern< Value, ForwarderPattern >;
// Default implementation of ConvertFuncParam():
// return the param as is.
RegisterBuiltinFunc< Intrinsic< Value( FwdValue ) > >(
e, e.extConvertFuncParam(), []( const Context& c, const Value& p ) { return p; } );
// Default implementation of ConvertFuncArgs():
// return the args as is.
// Use generic arguments so that the prelude can override it with a more specific
// version that explicitely takes Vecs, while still letting us have this default
// implementation for pre-prelude stuff to be able to work.
RegisterBuiltinFunc< Intrinsic< Value( Value, Value ) > >( e, e.extConvertFuncArgs(),
[]( const Context& c, const Value& a, const Value& p ) { return a; } );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/decl.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include "builtins/builtins.h"
using namespace goose::builtins;
namespace goose::builtins
{
bool IsDecl( const Value& d )
{
auto typeVal = EIRToValue( d.type() );
if( !typeVal )
return false;
| | < < < < < | | | | | | < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
#include "builtins/builtins.h"
using namespace goose::builtins;
namespace goose::builtins
{
bool IsDecl( const Value& d )
{
auto typeVal = EIRToValue( d.type() );
if( !typeVal )
return false;
auto result = Decompose( typeVal->val(), Vec( Lit( "decl"_sid ), SubTerm() ) );
return !!result;
}
const Term& Decl::Pattern::GetPattern()
{
static auto pattern =
ValueToEIR( Value( TypeType(), VEC( TSID( decl ), HOLE( "_"_sid ) ) ) );
return pattern;
}
const Term& DeclWithInit::Pattern::GetPattern()
{
static auto pattern =
ValueToEIR( Value( TypeType(), VEC( TSID( decl_with_init ), HOLE( "_"_sid ) ) ) );
return pattern;
}
} // namespace goose::builtins
namespace goose::eir
{
Term Bridge< Decl >::Type( const Term& declType )
{
return ValueToEIR( Value( TypeType(), VEC( TSID( decl ), declType ) ) );
}
Value Bridge< Decl >::ToValue( const Decl& d )
{
return Value( Type( d.type() ), TERM( d.name() ) );
}
optional< Decl > Bridge< Decl >::FromValue( const Value& v )
{
auto typeVal = EIRToValue( v.type() );
auto result = Decompose( typeVal->val(), Vec( Lit( "decl"_sid ), SubTerm() ) );
if( !result )
return nullopt;
auto&& [type] = *result;
const auto& name = get< StringId >( v.val() );
|
| ︙ | ︙ | |||
77 78 79 80 81 82 83 |
{
return Value( Type( d.type() ), VEC( d.name(), ValueToEIR( d.init() ), d.declLoc() ) );
}
optional< DeclWithInit > Bridge< DeclWithInit >::FromValue( const Value& v )
{
auto typeVal = EIRToValue( v.type() );
| | < < < < < | < < < | < < | | | 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
{
return Value( Type( d.type() ), VEC( d.name(), ValueToEIR( d.init() ), d.declLoc() ) );
}
optional< DeclWithInit > Bridge< DeclWithInit >::FromValue( const Value& v )
{
auto typeVal = EIRToValue( v.type() );
auto result = Decompose( typeVal->val(), Vec( Lit( "decl_with_init"_sid ), SubTerm() ) );
if( !result )
return nullopt;
auto result2 =
Decompose( v.val(), Vec( Val< StringId >(), SubTerm(), Val< LocationId >() ) );
if( !result2 )
return nullopt;
auto&& [type] = *result;
auto&& [name, init, declLoc] = *result2;
return DeclWithInit( move( type ), name, *EIRToValue( init ), declLoc );
}
} // namespace goose::eir
|
Changes to bs/builtins/types/decl.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_BUILTINS_TYPES_DECL_H
#define GOOSE_BUILTINS_TYPES_DECL_H
namespace goose::builtins
{
class Decl
{
| | | | | | < | > > | > | | | | | | | | | | | | | | | < | > > | > | > | > | | | | | | | | | | < > < | < | | | 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 |
#ifndef GOOSE_BUILTINS_TYPES_DECL_H
#define GOOSE_BUILTINS_TYPES_DECL_H
namespace goose::builtins
{
class Decl
{
public:
template< typename T >
Decl( T&& type, StringId name ) :
m_type( forward< T >( type ) ),
m_name( name )
{
}
const auto& type() const { return m_type; }
const auto& name() const { return m_name; }
struct Pattern
{
static const Term& GetPattern();
};
private:
Term m_type;
StringId m_name;
};
class DeclWithInit
{
public:
template< typename T, typename V >
DeclWithInit( T&& type, StringId name, V&& init, LocationId declLoc ) :
m_init( forward< V >( init ) ),
m_type( forward< T >( type ) ),
m_name( name ),
m_declLoc( declLoc )
{
}
const auto& type() const { return m_type; }
const auto& name() const { return m_name; }
const auto& init() const { return m_init; }
auto declLoc() const { return m_declLoc; }
struct Pattern
{
static const Term& GetPattern();
};
private:
Value m_init;
Term m_type;
StringId m_name;
LocationId m_declLoc;
};
extern bool IsDecl( const Value& d );
} // namespace goose::builtins
namespace goose::eir
{
template<> struct Bridge< builtins::Decl >
{
static Term Type( const Term& declType );
static Value ToValue( const builtins::Decl& d );
static optional< builtins::Decl > FromValue( const Value& v );
};
template<> struct Bridge< builtins::DeclWithInit >
{
static Term Type( const Term& declType );
static Value ToValue( const builtins::DeclWithInit& d );
static optional< builtins::DeclWithInit > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/destroy.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupDestroyValue( Env& e )
{
// Default implementation of DestroyValue().
// Does nothing.
| | | | | 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::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupDestroyValue( Env& e )
{
// Default implementation of DestroyValue().
// Does nothing.
RegisterBuiltinFunc< Intrinsic< void( Value ) > >(
e, e.extDestroyValue(), []( auto&& c, const Value& v ) {} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/drop.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupDropValue( Env& e )
{
// Default implementation of DropValue().
| | | | | | > | | > | | > > | | > | > | > < > | 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 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupDropValue( Env& e )
{
// Default implementation of DropValue().
// The cir of computed values with side effects is appended to the current BB of the current
// parser, all other values are discarded and destroyed.
RegisterBuiltinFunc< Intrinsic< void( Value, Value ) > >( e, e.extDropValue(),
[]( const Context& c, const Value& b, const Value& v )
{
if( v.isConstant() || !DoesInstrSeqHaveSideEffects( *v.cir() ) )
{
DestroyLiveValue( c, v );
return;
}
auto cfg = GetCFG( c );
if( !cfg )
return;
auto bb = cfg->currentBB();
bb->append( move( *v.cir() ) );
} );
// DropValue for identifier: emit an "undefined identifier" error.
RegisterBuiltinFunc< Intrinsic< void( Value, StringId ) > >( e, e.extDropValue(),
[]( const Context& c, const Value& b, const Value& v )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
v.locationId(), "undefined identifier.", 0 );
return PoisonValue();
} );
using AnyDeclType = CustomPattern< Decl, Decl::Pattern >;
// DropValue for Decls: declare a local variable with default initialization.
// TODO: if the invocation to InitializeValue fails, we should have a way to
// replace the generic "function arguments mismatch" error message with something
// more specific such as "can't default-initialize a variable of type XXX"
RegisterBuiltinFunc< Intrinsic< void( Value, AnyDeclType ) > >( e, e.extDropValue(),
[]( const Context& c, const Value& b, const Value& v )
{
if( !GetCFG( c ) )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
0, "variable declarations are not allowed here." );
return PoisonValue();
}
auto decl = *FromValue< Decl >( v );
return DeclareLocalVar( c, decl.type(), decl.name(), nullopt, v.locationId() );
} );
using AnyDeclWithInitType = CustomPattern< DeclWithInit, DeclWithInit::Pattern >;
// DropValue for DeclWithInit: declare a local variable.
RegisterBuiltinFunc< Intrinsic< void( Value, AnyDeclWithInitType ) > >( e, e.extDropValue(),
[]( const Context& c, const Value& b, const Value& v )
{
if( !GetCFG( c ) )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
0, "variable declarations are not allowed here." );
return PoisonValue();
}
auto decl = *FromValue< DeclWithInit >( v );
return DeclareLocalVar( c, decl.type(), decl.name(), decl.init(), decl.declLoc() );
} );
using AnyTNamedDeclWithInitType =
CustomPattern< TNamedDeclWithInit, TNamedDeclWithInit::Pattern >;
// DropValue for TNamedDeclWithInit: declare a local variable with local type inference.
RegisterBuiltinFunc< Intrinsic< void( Value, AnyTNamedDeclWithInitType ) > >( e,
e.extDropValue(),
[]( const Context& c, const Value& b, const Value& v )
{
if( !GetCFG( c ) )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
0, "variable declarations are not allowed here." );
return PoisonValue();
}
auto decl = *FromValue< TNamedDeclWithInit >( v );
return DeclareLocalVarWithTypeInference(
c, decl.type(), decl.name(), decl.init(), decl.declLoc() );
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/bfunc.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "builtins/builtins.h"
namespace goose::builtins
{
bool IsBuiltinFunc( const Value& func )
{
auto funcType = EIRToValue( func.type() );
assert( funcType );
auto decomp = Decompose( funcType->val(),
| < < | | | | | | < < < | | | | | | < < < | | | | | | < < < | | | | | | < | | | > | | | 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 |
#include "builtins/builtins.h"
namespace goose::builtins
{
bool IsBuiltinFunc( const Value& func )
{
auto funcType = EIRToValue( func.type() );
assert( funcType );
auto decomp = Decompose( funcType->val(),
Vec( Lit( "func"_sid ), Lit( "builtin"_sid ),
SubTerm(), // return type
SubTerm(), // param types
SubTerm(), // verif infos
SubTerm() // Lowering infos
) );
if( decomp )
return true;
decomp = Decompose( funcType->val(),
Vec( Lit( "func"_sid ), Lit( "builtin_eager"_sid ),
SubTerm(), // return type
SubTerm(), // param types
SubTerm(), // verif infos
SubTerm() // Lowering infos
) );
return !!decomp;
}
bool IsNonEagerBuiltinFunc( const Value& func )
{
auto funcType = EIRToValue( func.type() );
assert( funcType );
auto decomp = Decompose( funcType->val(),
Vec( Lit( "func"_sid ), Lit( "builtin"_sid ),
SubTerm(), // return type
SubTerm(), // param types
SubTerm(), // verif infos
SubTerm() // Lowering infos
) );
return !!decomp;
}
bool IsEagerBuiltinFunc( const Value& func )
{
auto funcType = EIRToValue( func.type() );
assert( funcType );
auto decomp = Decompose( funcType->val(),
Vec( Lit( "func"_sid ), Lit( "builtin_eager"_sid ),
SubTerm(), // return type
SubTerm(), // param types
SubTerm(), // verif infos
SubTerm() // Lowering infos
) );
return !!decomp;
}
const Term& BFuncTypePattern()
{
static auto BfuncTypePat = ValueToEIR( Value( TypeType(),
VEC( TSID( func ), TSID( builtin ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );
return BfuncTypePat;
}
const Term& EagerBFuncTypePattern()
{
static auto EagerBfuncTypePat = ValueToEIR( Value( TypeType(),
VEC(
TSID( func ), TSID( builtin_eager ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) );
return EagerBfuncTypePat;
}
const BuiltinFuncWrapper& GetBuiltinFuncWrapper( const Value& func )
{
assert( IsBuiltinFunc( func ) );
const auto& p = get< ptr< void > >( func.val() );
return *static_pointer_cast< BuiltinFuncWrapper >( p );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/bfunc.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_BUILTINS_TYPES_BFUNC_H
#define GOOSE_BUILTINS_TYPES_BFUNC_H
namespace goose::builtins
{
class FuncVerificationInfos;
| | | < > | | < > | < | > | < | | < | < < | | < > | | < > | | < < | > > | < > | | < > | > | < | < | < < < | > | | < | | < < < | < | < < < < > | 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 |
#ifndef GOOSE_BUILTINS_TYPES_BFUNC_H
#define GOOSE_BUILTINS_TYPES_BFUNC_H
namespace goose::builtins
{
class FuncVerificationInfos;
using BuiltinFuncWrapper = function< Value( const Term& argVec ) >;
template< typename FT, typename F >
void RegisterBuiltinFunc( Env& env, StringId name, F&& func, CURRENT_LOC );
template< typename FT, typename F >
void RegisterBuiltinFunc( Env& env, ptr< OverloadSet > pOvlSet, F&& func, CURRENT_LOC );
extern bool IsBuiltinFunc( const Value& func );
extern bool IsEagerBuiltinFunc( const Value& func );
extern bool IsNonEagerBuiltinFunc( const Value& func );
extern const Term& BFuncTypePattern();
extern const Term& EagerBFuncTypePattern();
extern const BuiltinFuncWrapper& GetBuiltinFuncWrapper( const Value& func );
// If the return type of a builtin function is wrapped with this,
// the function will be eargerly executed (if possible) if it is invoked
// by the compiled code.
template< typename T > struct Eager
{
};
template< typename T > struct is_eager : public false_type
{
};
template< typename T > struct is_eager< Eager< T > > : public true_type
{
};
template< typename T > constexpr bool is_eager_v = is_eager< T >::value;
template< typename T > struct remove_eager
{
using type = T;
};
template< typename T > struct remove_eager< Eager< T > >
{
using type = T;
};
template< typename T > using remove_eager_t = typename remove_eager< T >::type;
// This wrapper provides a way to use a custom pattern
// to match the type.
template< typename T, typename PP > struct CustomPattern
{
};
// This wrapper provides a way to use a custom pattern
// to match the type, but only for a constant value.
template< typename T, typename PP > struct CustomConstantPattern
{
};
// This wrapper defines a builtin function param that takes
// a type constant representing the given type.
template< typename T > struct TypeParam
{
};
// Same as above, but takes a type pattern provider.
template< typename T, typename PP > struct TypePatternParam
{
};
// Wrapper to define a constant valued parameter
template< typename T, T val > struct ConstantParam
{
};
} // namespace goose::builtins
namespace goose::eir
{
template< typename R, typename... T > struct Bridge< R( T... ) >
{
using functype = function< R( T... ) >;
static const Term& Type();
template< typename F > static Value ToValue( F&& func );
template< typename F > static auto WrapFunc( F&& func );
};
template< typename T, typename PP > struct Bridge< builtins::CustomPattern< T, PP > >
{
static auto Type() { return GetValueType< T >(); }
template< typename TT > static auto ToValue( TT&& val )
{
return eir::ToValue( forward< TT >( val ) );
}
static auto FromValue( const Value& v ) { return eir::FromValue< T >( v ); }
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/func/bfunc.inl.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 |
return RegisterBuiltinFunc< FT >( env, pOvlSet, forward< F >( func ), sloc );
}
template< typename FT, typename F >
void RegisterBuiltinFunc( Env& env, ptr< OverloadSet > pOvlSet, F&& func, source_location sloc )
{
auto loc = Location::Create( sloc );
| | > < > | < | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
return RegisterBuiltinFunc< FT >( env, pOvlSet, forward< F >( func ), sloc );
}
template< typename FT, typename F >
void RegisterBuiltinFunc( Env& env, ptr< OverloadSet > pOvlSet, F&& func, source_location sloc )
{
auto loc = Location::Create( sloc );
if( !pOvlSet->add( env, ToValue< FT >( forward< F >( func ) ).setLocationId( loc ),
GetInvocationRule< FT >() ) )
G_ERROR( "duplicate overload registered for builtin func." );
}
} // namespace goose::builtins
namespace goose::eir
{
template< typename T > struct BuildBuiltinFuncParamPat
{
static const auto& GetParamPattern()
{
static auto pat = builtins::ParamPat( GetValueType< T >() );
return pat;
}
};
|
| ︙ | ︙ | |||
47 48 49 50 51 52 53 |
static const auto& GetParamPattern()
{
static auto pat = ValueToEIR( Value( PP::GetPattern(), HOLE( "_"_sid ) ) );
return pat;
}
};
| < | | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
static const auto& GetParamPattern()
{
static auto pat = ValueToEIR( Value( PP::GetPattern(), HOLE( "_"_sid ) ) );
return pat;
}
};
template< typename T > struct BuildBuiltinFuncParamPat< builtins::TypeParam< T > >
{
static const auto& GetParamPattern()
{
static auto pat = GetValueType< T >();
return pat;
}
};
|
| ︙ | ︙ | |||
77 78 79 80 81 82 83 |
static const auto& GetParamPattern()
{
static auto pat = ValueToEIR( ToValue( val ) );
return pat;
}
};
| | < | < | | | | | < | > | | < < | < | | > | > > | > | < | 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 |
static const auto& GetParamPattern()
{
static auto pat = ValueToEIR( ToValue( val ) );
return pat;
}
};
template< typename... T > Term BuildBuiltinFuncParamTypeList()
{
return VEC( BuildBuiltinFuncParamPat< T >::GetParamPattern()... );
}
template< typename R, typename... T > const Term& Bridge< R( T... ) >::Type()
{
static auto type = ValueToEIR( Value( TypeType(),
VEC( TSID( func ), builtins::is_eager_v< R > ? TSID( builtin_eager ) : TSID( builtin ),
GetValueType< builtins::remove_eager_t< R > >(),
sema::Quote( BuildBuiltinFuncParamTypeList< T... >() ), ptr< void >(),
ptr< void >() ) ) );
return type;
}
template< typename R, typename... T >
template< typename F >
Value Bridge< R( T... ) >::ToValue( F&& func )
{
ptr< void > pWrapper =
make_shared< builtins::BuiltinFuncWrapper >( WrapFunc( forward< F >( func ) ) );
return Value( Type(), TERM( move( pWrapper ) ) );
}
template< typename T > struct StripCustomPattern
{
using type = T;
};
template< typename T, typename PP >
struct StripCustomPattern< builtins::CustomPattern< T, PP > >
{
using type = T;
};
template< typename T, typename PP >
struct StripCustomPattern< builtins::CustomConstantPattern< T, PP > >
{
using type = T;
};
template< typename T > struct StripCustomPattern< builtins::TypeParam< T > >
{
using type = Value;
};
template< typename T, typename PP >
struct StripCustomPattern< builtins::TypePatternParam< T, PP > >
{
using type = T;
};
template< typename T, T val > struct StripCustomPattern< builtins::ConstantParam< T, val > >
{
using type = T;
};
template< typename R, typename... T >
template< typename F >
auto Bridge< R( T... ) >::WrapFunc( F&& func )
{
return [func]( const Term& argVec ) -> Value
{
auto params =
Bridge< tuple< typename StripCustomPattern< T >::type... > >::FromVectorTerm(
argVec );
if( !params )
return PoisonValue();
if constexpr( is_void_v< builtins::remove_eager_t< R > > )
{
apply( func, *params );
return Value( GetValueType< void >(), 0U );
}
else
return eir::ToValue(
static_cast< builtins::remove_eager_t< R > >( apply( func, *params ) ) );
};
}
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/func/bintrinsic.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "builtins/builtins.h"
namespace goose::builtins
{
bool IsBuiltinIntrinsicFunc( const Value& func )
{
auto funcType = EIRToValue( func.type() );
assert( funcType );
auto decomp = Decompose( funcType->val(),
| < < | | | | | | < | | 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 |
#include "builtins/builtins.h"
namespace goose::builtins
{
bool IsBuiltinIntrinsicFunc( const Value& func )
{
auto funcType = EIRToValue( func.type() );
assert( funcType );
auto decomp = Decompose( funcType->val(),
Vec( Lit( "func"_sid ), Lit( "intrinsic_builtin"_sid ),
SubTerm(), // return type
SubTerm(), // param types
SubTerm(), // verif infos
SubTerm() // Lowering infos
) );
return !!decomp;
}
const BuiltinIntrinsicFuncWrapper& GetBuiltinIntrinsicFuncWrapper( const Value& func )
{
assert( IsBuiltinIntrinsicFunc( func ) );
const auto& p = get< ptr< void > >( func.val() );
return *static_pointer_cast< BuiltinIntrinsicFuncWrapper >( p );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/bintrinsic.h.
1 2 3 4 5 6 7 8 9 10 |
#ifndef GOOSE_BUILTINS_TYPES_FUNC_BINTRINSIC_H
#define GOOSE_BUILTINS_TYPES_FUNC_BINTRINSIC_H
namespace goose::builtins
{
extern bool IsBuiltinIntrinsicFunc( const Value& func );
// A builtin intrinsic is defined on the C++ side by creating a builtin func
// whose type is wrapped in the Intrinsic type.
// The provided function should take and return only Values.
| | < > | | | | < | > | | < | | 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 |
#ifndef GOOSE_BUILTINS_TYPES_FUNC_BINTRINSIC_H
#define GOOSE_BUILTINS_TYPES_FUNC_BINTRINSIC_H
namespace goose::builtins
{
extern bool IsBuiltinIntrinsicFunc( const Value& func );
// A builtin intrinsic is defined on the C++ side by creating a builtin func
// whose type is wrapped in the Intrinsic type.
// The provided function should take and return only Values.
template< typename F > struct Intrinsic
{
};
using BuiltinIntrinsicFuncWrapper = function< Value( Context& c, const Term& argVec ) >;
extern const BuiltinIntrinsicFuncWrapper& GetBuiltinIntrinsicFuncWrapper( const Value& func );
} // namespace goose::builtins
namespace goose::eir
{
template< typename R, typename... T > struct Bridge< builtins::Intrinsic< R( T... ) > >
{
using functype = function< R( T... ) >;
static const Term& Type( const ptr< builtins::FuncVerificationInfos >& fvi = nullptr );
template< typename F >
static Value ToValue(
F&& func, const ptr< builtins::FuncVerificationInfos >& fvi = nullptr );
template< typename F > static auto WrapFunc( F&& func );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/func/bintrinsic.inl.
1 2 3 4 5 6 |
#ifndef GOOSE_BUILTINS_TYPES_FUNC_BINTRINSIC_INL
#define GOOSE_BUILTINS_TYPES_FUNC_BINTRINSIC_INL
namespace goose::eir
{
template< typename R, typename... T >
| | > | | < | | < > | | > | | | 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 |
#ifndef GOOSE_BUILTINS_TYPES_FUNC_BINTRINSIC_INL
#define GOOSE_BUILTINS_TYPES_FUNC_BINTRINSIC_INL
namespace goose::eir
{
template< typename R, typename... T >
const Term& Bridge< builtins::Intrinsic< R( T... ) > >::Type(
const ptr< builtins::FuncVerificationInfos >& fvi )
{
static auto type = ValueToEIR( Value( TypeType(),
VEC( TSID( func ), TSID( intrinsic_builtin ), GetValueType< R >(),
sema::Quote( BuildBuiltinFuncParamTypeList< T... >() ),
static_pointer_cast< void >( fvi ), ptr< void >() ) ) );
return type;
}
template< typename R, typename... T >
template< typename F >
Value Bridge< builtins::Intrinsic< R( T... ) > >::ToValue(
F&& func, const ptr< builtins::FuncVerificationInfos >& fvi )
{
ptr< void > pWrapper = make_shared< builtins::BuiltinIntrinsicFuncWrapper >(
WrapFunc( forward< F >( func ) ) );
return Value( Type( fvi ), TERM( move( pWrapper ) ) );
}
template< typename T > using AsValue = Value;
template< typename R, typename... T >
template< typename F >
auto Bridge< builtins::Intrinsic< R( T... ) > >::WrapFunc( F&& func )
{
return [func]( sema::Context& c, const Term& argVec ) -> Value
{
auto params = Bridge< tuple< AsValue< T >... > >::FromVectorTerm( argVec );
assert( params );
if constexpr( is_void_v< R > )
{
apply( func, tuple_cat( tuple< sema::Context& >( c ), *params ) );
return Value( GetValueType< void >(), 0U );
}
else
return apply( func, tuple_cat( make_tuple( c ), *params ) );
};
}
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/func/build.cpp.
1 2 3 4 5 6 7 8 9 |
#include "builtins/builtins.h"
namespace goose::builtins
{
Term BuildParamPat( const Context& c, Value type, LocationId loc )
{
if( !type.isType() )
type = ToType( c, type );
| | > > | | > | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | > | | | | | | | | | | | | | > | | > > | > | | | | > | | | | | 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 |
#include "builtins/builtins.h"
namespace goose::builtins
{
Term BuildParamPat( const Context& c, Value type, LocationId loc )
{
if( !type.isType() )
type = ToType( c, type );
return ValueToEIR( ValuePattern( TSID( param ), ValueToEIR( type ), HOLE( "_"_sid ) )
.setLocationId( loc ) );
}
optional< FuncType > BuildFuncType(
const Context& c, const Value& returnType, const Value& params )
{
auto verificationIdentity = VEC( Env::NewUniqueId() );
c.env()->addVisibilityRule( c.identity(), verificationIdentity );
auto paramCount = TupleSize( params );
auto tv = make_shared< Vector >();
tv->reserve( paramCount );
FuncLoweringInfos fli;
fli.loweredParamTypes.reserve( paramCount );
bool failed = false;
uint32_t varId = 0;
ForEachInTuple( params,
[&]( auto&& param )
{
if( IsDecl( param ) )
{
auto decl = *FromValue< Decl >( param );
auto declType = *EIRToValue( decl.type() );
auto loweredDeclType = LowerType( c, declType );
if( !loweredDeclType )
{
failed = true;
return false;
}
// Handle parameter packs (open tuples)
if( IsOpenTuple( declType ) )
{
ForEachInTuples( declType, *loweredDeclType,
[&]( auto&& type, auto&& loweredType )
{
fli.loweredParamTypes.emplace_back( loweredType );
tv->append( BuildParamPat( c, type, param.locationId() ) );
return true;
} );
if( failed )
return false;
}
else
{
fli.loweredParamTypes.emplace_back( *loweredDeclType );
tv->append( BuildParamPat( c, declType, param.locationId() ) );
}
// Bind a stand-in value with the parameters name to be used inside of
// verification expressions.
auto paramVerificationIdentity =
AppendToVectorTerm( verificationIdentity, TERM( decl.name() ) );
c.env()->storeValue( paramVerificationIdentity, ANYTERM( _ ),
ValueToEIR( BuildComputedValue( decl.type(),
cir::DataPathOf( varId++, param.locationId() ),
cir::Load( ValueToEIR( *loweredDeclType ), param.locationId() ) ) ) );
}
else if( param.isConstant() )
tv->append( ValueToEIR( param ) );
return true;
} );
if( failed )
return nullopt;
auto rtType = ToType( c, returnType );
auto loweredRTType = LowerType( c, rtType );
if( !loweredRTType )
return nullopt;
fli.loweredReturnType = move( *loweredRTType );
// If the return type is non-void, expose @result under the verification identity as a
// computed value whose type is the function's return type, and the value is a placeholder
// cir instruction. This will allow verification expressions to refer to the current
// function's return value as a value of the correct type.
auto rtTerm = ValueToEIR( rtType );
if( rtTerm != GetValueType< void >() )
{
auto name = "@result"_sid;
auto retValVerificationIdentity =
AppendToVectorTerm( verificationIdentity, TERM( name ) );
c.env()->storeValue( retValVerificationIdentity, ANYTERM( _ ),
ValueToEIR( BuildComputedValue(
rtTerm, cir::Placeholder( rtTerm, name, returnType.locationId() ) ) ) );
}
auto pVerifInfos = make_shared< FuncVerificationInfos >( move( verificationIdentity ) );
return FuncType(
rtTerm, tv, move( pVerifInfos ), make_shared< FuncLoweringInfos >( move( fli ) ) );
}
Func BuildExternalFunc( FuncType funcType, const string& symbol, bool varArg )
{
if( varArg )
funcType.setKind( FuncType::Kind::VarArg );
return Func( funcType, symbol );
}
optional< Func > BuildFunc( const Context& c, const Term& parentIdentity,
const Term& funcIdentity, const Value& returnType, const Value& paramsDecl,
const ptr< void >& unparsedBody, Context& out_bodyContext )
{
auto funcType = BuildFuncType( c, returnType, paramsDecl );
if( !funcType )
return nullopt;
return BuildFunc(
c, *funcType, parentIdentity, funcIdentity, paramsDecl, unparsedBody, out_bodyContext );
}
Func BuildFunc( const Context& c, const FuncType& funcType, const Term& parentIdentity,
const Term& funcIdentity, const Value& paramsDecl, const ptr< void >& unparsedBody,
Context& out_bodyContext )
{
// TODO: instead of a normal import rule from the parent scope, we should use
// a custom visibility rule that deals with variables from the parent context
// in a special way:
// If the function body tries to access variables from the parent function,
// we should invoke an overridable global function that can transform both the
// variable object and the function type. This way, we can implement/customize
|
| ︙ | ︙ | |||
138 139 140 141 142 143 144 |
out_bodyContext = Context( c.env(), identity, funcType.returnType() );
auto pFuncCIR = Env::CreateCIRFunc( identity );
return Func( funcType, unparsedBody, get< pvec >( paramsDecl.val() ), pFuncCIR.get() );
}
| | < | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
out_bodyContext = Context( c.env(), identity, funcType.returnType() );
auto pFuncCIR = Env::CreateCIRFunc( identity );
return Func( funcType, unparsedBody, get< pvec >( paramsDecl.val() ), pFuncCIR.get() );
}
optional< Func > BuildFunc( const Context& c, const Term& funcIdentity, const Value& returnType,
const Value& paramsDecl, const ptr< cir::CFG >& cfg )
{
auto funcType = BuildFuncType( c, returnType, paramsDecl );
if( !funcType )
return nullopt;
auto funcTypeTerm = ValueToEIR( ToValue( *funcType ) );
|
| ︙ | ︙ | |||
165 166 167 168 169 170 171 |
if( !ftype )
return ANYTERM( _ );
auto apv = make_shared< Vector >();
apv->reserve( VecSize( ftype->params() ) + 1 );
apv->append( ftype->returnType() );
| | > | | < | < < < | < < | | | | | > | | | < > | 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 |
if( !ftype )
return ANYTERM( _ );
auto apv = make_shared< Vector >();
apv->reserve( VecSize( ftype->params() ) + 1 );
apv->append( ftype->returnType() );
ForEachInVectorTerm( ftype->params(),
[&]( auto&& param )
{
auto result = Decompose( param,
Vec( Lit( "value"_sid ), SubTerm(), SubTerm(), SubTerm(),
Val< LocationId >() ) );
assert( result );
auto&& [sort, type, val, locId] = *result;
if( sort == TSID( constant ) )
apv->append( param );
else
apv->append( ValueToEIR(
ValuePattern( TSID( computed ), type, TERM( ptr< void >() ) ) ) );
return true;
} );
return TERM( apv );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/build.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_BUILTINS_FUNC_BUILD_H
#define GOOSE_BUILTINS_FUNC_BUILD_H
namespace goose::builtins
{
extern Term BuildParamPat( const Context& c, Value type, LocationId loc );
| | > | | | | | | | < | | | 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_BUILTINS_FUNC_BUILD_H
#define GOOSE_BUILTINS_FUNC_BUILD_H
namespace goose::builtins
{
extern Term BuildParamPat( const Context& c, Value type, LocationId loc );
extern optional< FuncType > BuildFuncType(
const Context& c, const Value& returnType, const Value& params );
extern Func BuildExternalFunc( FuncType funcType, const string& symbol, bool varArg = false );
extern optional< Func > BuildFunc( const Context& c, const Term& parentIdentity,
const Term& funcIdentity, const Value& returnType, const Value& paramsDecl,
const ptr< void >& unparsedBody, Context& out_bodyContext );
extern Func BuildFunc( const Context& c, const FuncType& funcType, const Term& parentIdentity,
const Term& funcIdentity, const Value& paramsDecl, const ptr< void >& unparsedBody,
Context& out_bodyContext );
extern optional< Func > BuildFunc( const Context& c, const Term& funcIdentity,
const Value& returnType, const Value& paramsDecl, const ptr< cir::CFG >& cfg );
extern Term BuildCallPatternFromFuncType( const Value& funcType );
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/func/compilation/common.cpp.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 |
if( !fvi->parse( c ) )
return false;
// Perform lazy parsing on param types' predicates
bool predsOk = true;
| | > | | < | < < < | < < | | | | | | > | | 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 |
if( !fvi->parse( c ) )
return false;
// Perform lazy parsing on param types' predicates
bool predsOk = true;
ForEachInVectorTerm( f.type().params(),
[&]( auto&& param )
{
auto result = Decompose( param,
Vec( Lit( "value"_sid ), SubTerm(), SubTerm(), SubTerm(),
Val< LocationId >() ) );
assert( result );
auto&& [sort, type, val, locId] = *result;
if( !ParseTypePredicates( c, *EIRToValue( type ) ) )
predsOk = false;
return true;
} );
if( !predsOk )
return false;
// Perform lazy parsing on return type predicates
return ParseTypePredicates( c, *EIRToValue( f.type().returnType() ) );
}
Term CreateFuncBodyIdentity( const Context& c, const Term& funcIdentity )
{
auto bodyIdentity = AppendToVectorTerm( funcIdentity, TSID( 0_locvars ) );
c.env()->addVisibilityRule( funcIdentity, bodyIdentity );
return bodyIdentity;
}
bool ParseFunctionBody( const Context& localContext, CodeBuilder& cb, const Func& f )
{
const auto& pFuncCIR = f.cir();
const auto& cfg = cb.cfg();
auto tokProvider =
lex::MakeVectorAdapter( *static_pointer_cast< vector< TermLoc > >( f.tokens() ) );
auto r = make_shared< parse::Resolver >( tokProvider, localContext );
Parser p( r );
bool success = false;
{
LifetimeScopeGuard lsg( localContext );
success = p.parseBraceBlock();
|
| ︙ | ︙ | |||
91 92 93 94 95 96 97 |
"missing return statement in a function with non-void return type." );
return false;
}
// Implicit return at the end of a void function:
// Emit the cleanups, and the terminator.
// TODO: at some point we'll want to check for reachability in the static verifier,
| | | | | | | 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
"missing return statement in a function with non-void return type." );
return false;
}
// Implicit return at the end of a void function:
// Emit the cleanups, and the terminator.
// TODO: at some point we'll want to check for reachability in the static verifier,
// and either emit the implicit return or declare the code unreachable depending on the
// result. The reachability analysis will have to be done before contract validation, as
// the calls to DestroyValue() may also have requirements to enforce, so we'll need to
// emit the eventual implicit return first.
p.flushValue();
cb.destroyAllLiveValues( localContext );
cfg->emitTerminator( r->currentLocation(), cir::RetVoid( r->currentLocation() ) );
}
return true;
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/compilation/common.h.
1 2 3 4 5 6 7 8 9 10 |
#ifndef GOOSE_BUILTINS_TYPES_FUNC_COMPILATION_COMMON_H
#define GOOSE_BUILTINS_TYPES_FUNC_COMPILATION_COMMON_H
namespace goose::builtins
{
extern bool ParseAllFuncPredicates( const Context& c, const Func& f );
extern Term CreateFuncBodyIdentity( const Context& c, const Term& funcIdentity );
extern bool ParseFunctionBody( const Context& localContext, CodeBuilder& cb, const Func& f );
template< typename MPARAMF, typename MPACKPARAMF >
| | > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#ifndef GOOSE_BUILTINS_TYPES_FUNC_COMPILATION_COMMON_H
#define GOOSE_BUILTINS_TYPES_FUNC_COMPILATION_COMMON_H
namespace goose::builtins
{
extern bool ParseAllFuncPredicates( const Context& c, const Func& f );
extern Term CreateFuncBodyIdentity( const Context& c, const Term& funcIdentity );
extern bool ParseFunctionBody( const Context& localContext, CodeBuilder& cb, const Func& f );
template< typename MPARAMF, typename MPACKPARAMF >
void MaterializeFuncParams( const Context& c, CodeBuilder& cb, const Func& f,
const Term& bodyIdentity, uint32_t startVarId, MPARAMF&& matParamFunc,
MPACKPARAMF&& matPackParamFunc )
{
uint32_t actualParamIndex = 0;
uint32_t varId = startVarId;
for( auto&& t : f.paramsDecl()->terms() )
{
auto param = *EIRToValue( t );
|
| ︙ | ︙ | |||
29 30 31 32 33 34 35 |
// Handle parameter packs (open tuples)
auto declType = *EIRToValue( decl.type() );
if( IsOpenTuple( declType ) )
{
auto packId = varId;
auto pack = EmptyClosedTuple();
| | > | | | | | | < > | > | | 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 |
// Handle parameter packs (open tuples)
auto declType = *EIRToValue( decl.type() );
if( IsOpenTuple( declType ) )
{
auto packId = varId;
auto pack = EmptyClosedTuple();
ForEachInTuple( declType,
[&]( auto&& type )
{
auto val = matPackParamFunc( type, varId++ );
pack = AppendToTuple( pack, move( val ) );
return true;
} );
auto paramIdentity = AppendToVectorTerm( bodyIdentity, TERM( decl.name() ) );
c.env()->storeValue( paramIdentity, ANYTERM( _ ), ValueToEIR( pack ) );
cb.declareValue( c, pack, packId );
}
else
{
auto type = EIRToValuePattern(
get< pvec >( f.type().params() )->terms()[actualParamIndex++] )
->type();
if( IsWrappedType( type ) )
type = GetValueType< TypeWrapper< Value > >();
matParamFunc( move( type ), decl.name(), varId++, param.locationId() );
}
}
}
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/func/compilation/compilation.h.
1 2 3 4 5 6 7 8 |
#ifndef GOOSE_BUILTINS_TYPES_FUNC_COMPILATION_H
#define GOOSE_BUILTINS_TYPES_FUNC_COMPILATION_H
namespace goose::builtins
{
extern bool CompileRegularFunc( const Context& c, const Func& f );
extern bool CompileIntrinsicFunc( const Context& c, const Func& f );
extern bool CompileInlineFunc( const Context& c, const Func& f );
| | | 1 2 3 4 5 6 7 8 9 10 11 |
#ifndef GOOSE_BUILTINS_TYPES_FUNC_COMPILATION_H
#define GOOSE_BUILTINS_TYPES_FUNC_COMPILATION_H
namespace goose::builtins
{
extern bool CompileRegularFunc( const Context& c, const Func& f );
extern bool CompileIntrinsicFunc( const Context& c, const Func& f );
extern bool CompileInlineFunc( const Context& c, const Func& f );
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/func/compilation/inline.cpp.
| ︙ | ︙ | |||
30 31 32 33 34 35 36 |
cfg->createBB();
cfg->setCurrentBB( cfg->entryBB() );
auto cb = make_shared< CodeBuilder >( cfg );
localContext.setBuilder( ToValue( TypeWrapper< ptr< CodeBuilder > >( cb ) ) );
// TODO_SSA rewrite this
| | | | | | | | | | | | | | | | | | | | | | | > | | | | | | 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 |
cfg->createBB();
cfg->setCurrentBB( cfg->entryBB() );
auto cb = make_shared< CodeBuilder >( cfg );
localContext.setBuilder( ToValue( TypeWrapper< ptr< CodeBuilder > >( cb ) ) );
// TODO_SSA rewrite this
/* uint32_t argId = 0;
auto matParamFunc = [&]( Term&& type, StringId name, uint32_t index, LocationId loc )
{
// Materialize the params as temporaries with incremental indices.
// The caller will have to emit the creation of those temporaries to pass the params,
// allocaitng them new UIDs, and then replacing those indices by the correpsonding UIDs
//when inlining the function.
auto paramVal = BuildComputedValue( type, cir::GetTemporary( type, argId++, loc ) )
.setLocationId( loc );
auto paramIdentity = AppendToVectorTerm( bodyIdentity, TERM( name ) );
c.env()->storeValue( paramIdentity, ANYTERM( _ ),
ValueToEIR( paramVal ) );
};*/
// TODO_SSA rewrite
/* auto matPackParamFunc = [&]( const Value& type, uint32_t index )
{
auto t = ValueToEIR( ToType( c, type ) );
// Materialize varargs as temporaries with incremental indices, like regular args.
return BuildComputedValue( t, cir::GetTemporary( move( t ), argId++, type.locationId()
) ) .setLocationId( type.locationId() );
};
MaterializeFuncParams( c, *cb, f, bodyIdentity, 0, matParamFunc, matPackParamFunc );*/
if( !ParseFunctionBody( localContext, *cb, f ) )
return false;
pFuncCIR->body() = cfg;
if( f.haveUnmetDependencies() )
{
Env::DepsGraph().setCallBack( pFuncCIR,
[localContext, pFuncCIR, f]()
{
ExpandInlineCalls( *pFuncCIR->body() );
pFuncCIR->setState( cir::Func::State::Complete );
} );
return true;
}
pFuncCIR->setState( cir::Func::State::Complete );
Env::DepsGraph().satisfy( pFuncCIR );
return true;
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/compilation/intrinsic.cpp.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 |
if( !f.tokens() )
return false;
auto bodyIdentity = CreateFuncBodyIdentity( c, pFuncCIR->identity() );
Context localContext( c.env(), bodyIdentity,
| | < | | | | > | < | | < | | | | 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 |
if( !f.tokens() )
return false;
auto bodyIdentity = CreateFuncBodyIdentity( c, pFuncCIR->identity() );
Context localContext( c.env(), bodyIdentity,
IsWrappedType( f.type().returnType() ) ? GetValueType< TypeWrapper< Value > >() :
f.type().returnType() );
auto paramCount = VecSize( f.type().params() ) + 1;
auto cfg = make_shared< cir::CFG >( paramCount );
cfg->createBB();
cfg->setCurrentBB( cfg->entryBB() );
auto cb = make_shared< CodeBuilder >( cfg );
localContext.setBuilder( ToValue( TypeWrapper< ptr< CodeBuilder > >( cb ) ) );
// Intrinsic have an implicit extra context parameter, materialize it explicitely since it
// doesn't appear in the params list
LocalVar lv( "context"_sid, GetValueType< TypeWrapper< ptr< Context > > >(), 0 );
auto locVar = ToValue( lv );
c.env()->storeValue( AppendToVectorTerm( bodyIdentity, TSID( context ) ), ANYTERM( _ ),
ValueToEIR( locVar ) );
auto matParamFunc = [&]( Term&& type, StringId name, uint32_t index, LocationId loc )
{
// Create a locvar to hold the param.
LocalVar lv( name, move( type ), index );
auto locVar = ToValue( lv ).setLocationId( loc );
auto paramIdentity = AppendToVectorTerm( bodyIdentity, TERM( name ) );
c.env()->storeValue( paramIdentity, ANYTERM( _ ), ValueToEIR( locVar ) );
cb->declareValue( c, locVar, lv.index() );
};
auto matPackParamFunc = [&]( const Value& type, uint32_t index )
{
auto t = ValueToEIR( ToType( c, type ) );
return BuildComputedValue(
t, cir::DataPathOf( index, type.locationId() ), cir::Load( t, type.locationId() ) );
};
MaterializeFuncParams( c, *cb, f, bodyIdentity, 1, matParamFunc, matPackParamFunc );
if( !ParseFunctionBody( localContext, *cb, f ) )
return false;
ReindexVars( cfg );
pFuncCIR->body() = cfg;
pFuncCIR->setState( cir::Func::State::Complete );
// TODO_SSA reenable
// verify::Func fv( localContext, f );
// return fv.verify();
return true;
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/compilation/regular.cpp.
| ︙ | ︙ | |||
37 38 39 40 41 42 43 |
auto matParamFunc = [&]( Term&& type, StringId name, uint32_t index, LocationId loc )
{
// Create a locvar to hold the param.
LocalVar lv( name, move( type ), index );
auto locVar = ToValue( lv ).setLocationId( loc );
auto paramIdentity = AppendToVectorTerm( bodyIdentity, TERM( name ) );
| | < | | < | > | | | | | | | | | | | | | | | | | < | | | | | 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 |
auto matParamFunc = [&]( Term&& type, StringId name, uint32_t index, LocationId loc )
{
// Create a locvar to hold the param.
LocalVar lv( name, move( type ), index );
auto locVar = ToValue( lv ).setLocationId( loc );
auto paramIdentity = AppendToVectorTerm( bodyIdentity, TERM( name ) );
c.env()->storeValue( paramIdentity, ANYTERM( _ ), ValueToEIR( locVar ) );
cb->declareValue( c, locVar, lv.index() );
};
auto matPackParamFunc = [&]( const Value& type, uint32_t index )
{
auto t = ValueToEIR( ToType( c, type ) );
return BuildComputedValue(
t, cir::DataPathOf( index, type.locationId() ), cir::Load( t, type.locationId() ) )
.setLocationId( type.locationId() );
};
MaterializeFuncParams( c, *cb, f, bodyIdentity, 0, matParamFunc, matPackParamFunc );
pFuncCIR->body() = cfg;
if( !ParseFunctionBody( localContext, *cb, f ) )
return false;
if( f.haveUnmetDependencies() )
{
Env::DepsGraph().setCallBack( pFuncCIR,
[localContext, pFuncCIR, f]()
{
ExpandInlineCalls( *pFuncCIR->body() );
ReindexVars( pFuncCIR->body() );
pFuncCIR->setState( cir::Func::State::Complete );
// TODO_SSA reenable
// verify::Func fv( localContext, f );
// fv.verify();
// TODO: perhaps store a "verified" bool to be able to check that a function was
// actually verified before trying to execute it or codegen it? Or perhaps
// unneeded since we won't try to eager eval if unmet deps? But maybe we do want
// to know if verif failed, to skip eager exec that will explode anyway? (at
// that point we're in dead man walking mode trying to report additional
// meaningful errors before giving up anyway - so any call that attemp to eager
// eval a func that failed verif would let us give up on compiling the current
// func)
} );
return true;
}
// If all dependencies are already met, finish up right now. Finishing this up as soon as
// possible is necessary because we need to be able to greredily attempt to eagerly
// evaluate function calls, as any function call might be intended to be done at compilation
// time to construct something (for instance a type).
ReindexVars( cfg );
pFuncCIR->setState( cir::Func::State::Complete );
// TODO_SSA reenable
/*verify::Func fv( localContext, f );
fv.verify();*/
return true;
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/compile.cpp.
1 2 3 4 5 6 7 | #include "builtins/builtins.h" #include "compilation/compilation.h" #include "compilation/common.h" #include "lex/lex.h" #include "parse/parse.h" // TODO_SSA reenable | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include "builtins/builtins.h"
#include "compilation/compilation.h"
#include "compilation/common.h"
#include "lex/lex.h"
#include "parse/parse.h"
// TODO_SSA reenable
// #include "verify/verify.h"
using namespace goose::builtins;
using namespace goose::parse;
namespace goose::builtins
{
Value CompileFunc( const Context& c, const Value& f )
|
| ︙ | ︙ | |||
52 53 54 55 56 57 58 |
// All the other cases for now are functions without bodies,
// nothing more to do.
break;
}
return f;
}
| | | 52 53 54 55 56 57 58 59 |
// All the other cases for now are functions without bodies,
// nothing more to do.
break;
}
return f;
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/func.cpp.
1 2 3 4 | #include "builtins/builtins.h" #include "lex/lex.h" #include "parse/parse.h" // TODO_SSA reenable | | | | < | < | | < < | 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 |
#include "builtins/builtins.h"
#include "lex/lex.h"
#include "parse/parse.h"
// TODO_SSA reenable
// #include "verify/verify.h"
using namespace goose::builtins;
using namespace goose::parse;
namespace goose::builtins
{
llvm::SmallVector< Func::Summary, 16 > Func::ms_funcStack;
cir::InstrSeq BuildArgsInstrSeq( const Term& args )
{
return BuildArgsInstrSeq( args, []( auto&&, auto&& ) {} );
}
const Term& FuncPattern::GetPattern()
{
static auto pattern = ValueToEIR( Value( TypeType(),
VEC( TSID( func ), HOLE( "_"_sid ), HOLE( "_"_sid ), HOLE( "_"_sid ), HOLE( "_"_sid ),
HOLE( "_"_sid ) ) ) );
return pattern;
}
bool IsExternalFunc( const Value& f )
{
if( !f.isConstant() )
return false;
// Try to decode it as an external func
auto result = Decompose( f.val(), Val< string >() );
return !!result;
}
bool IsGhostFunc( const Value& f )
{
if( !f.isConstant() )
|
| ︙ | ︙ | |||
89 90 91 92 93 94 95 |
optional< Term > GetFuncRType( const Value& func )
{
auto funcType = EIRToValue( func.type() );
if( !funcType )
return nullopt;
auto typeDecomp = Decompose( funcType->val(),
| < | | | | | | | < | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
optional< Term > GetFuncRType( const Value& func )
{
auto funcType = EIRToValue( func.type() );
if( !funcType )
return nullopt;
auto typeDecomp = Decompose( funcType->val(),
Vec( Lit( "func"_sid ),
SubTerm(), // kind
SubTerm(), // return type
SubTerm(), // param types
SubTerm(), // verif infos
SubTerm() // Lowering infos
) );
if( !typeDecomp )
return nullopt;
auto&& [kind, rtype, ptypes, vinf, li] = *typeDecomp;
return rtype;
}
|
| ︙ | ︙ | |||
119 120 121 122 123 124 125 |
assert( func );
return func->cir();
}
optional< FuncVerifData > ExtractFuncVerifData( const Value& funcType )
{
auto typeDecomp = Decompose( funcType.val(),
| < | | | | | | | < | | | > | | < | | | | < | | < < | | 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 |
assert( func );
return func->cir();
}
optional< FuncVerifData > ExtractFuncVerifData( const Value& funcType )
{
auto typeDecomp = Decompose( funcType.val(),
Vec( Lit( "func"_sid ),
SubTerm(), // kind
SubTerm(), // return type
SubTerm(), // param types
Val< ptr< void > >(), // verif infos
SubTerm() // Lowering infos
) );
if( !typeDecomp )
return nullopt;
auto&& [kind, rtype, params, vinf, li] = *typeDecomp;
return FuncVerifData{ rtype, *Unquote( params ),
static_pointer_cast< FuncVerificationInfos >( vinf ) };
}
} // namespace goose::builtins
namespace goose::eir
{
Term Bridge< Func >::Type( const builtins::Func& func )
{
return ValueToEIR( ::ToValue( func.type() ) );
}
Value Bridge< Func >::ToValue( const builtins::Func& func )
{
if( func.isExternal() )
return Value( Type( func ), TERM( *func.symbol() ) );
return Value( Type( func ),
VEC( TERM( func.tokens() ), TERM( func.paramsDecl() ), TERM( func.cir() ) ) );
}
optional< Func > Bridge< Func >::FromValue( const Value& v )
{
if( !v.isConstant() || v.isPoison() )
return nullopt;
auto funcTypeVal = EIRToValue( v.type() );
if( !funcTypeVal )
return nullopt;
auto funcType = ::FromValue< FuncType >( *funcTypeVal );
if( !funcType )
return nullopt;
auto result = Decompose( v.val(),
Vec( Val< ptr< void > >(), // body toks
Val< pvec >(), // param decls
Val< void* >() // cir
) );
if( result )
{
auto&& [toks, paramDecls, cir] = *result;
return Func( move( *funcType ), toks, paramDecls, static_cast< cir::Func* >( cir ) );
}
// Try to decode it as an external func
auto result2 = Decompose( v.val(), Val< string >() );
if( !result2 )
return nullopt;
return Func( move( *funcType ), *result2 );
}
} // namespace goose::eir
|
Changes to bs/builtins/types/func/func.h.
| ︙ | ︙ | |||
22 23 24 25 26 27 28 |
ptr< FuncVerificationInfos > verifInfos;
};
// Extracts the return type, param types and verfif infos of any function,
// either builtin or not. (these parts are encoded in a common location for all function types)
extern optional< FuncVerifData > ExtractFuncVerifData( const Value& func );
| < | | | | | | | | < | > > | | | | | < > > | | | | | < | > > | | | | < | > > | | > | > | > | > | > | | | < < < | < < < | | | | | | | | | | | | | | | | | | < < < | | > | | | > | | | | | | | | | | < | < > < | | | 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 |
ptr< FuncVerificationInfos > verifInfos;
};
// Extracts the return type, param types and verfif infos of any function,
// either builtin or not. (these parts are encoded in a common location for all function types)
extern optional< FuncVerifData > ExtractFuncVerifData( const Value& func );
template< typename F > void ForEachDeclInTuple( const Value& tup, F&& func );
extern bool IsExternalFunc( const Value& f );
extern bool IsGhostFunc( const Value& f );
extern bool IsG0Func( const Value& f );
extern bool HaveUnmetDependencies( const Value& f );
class Func
{
public:
template< typename T, typename B, typename P >
Func( T&& funcType, B&& toks, P&& paramsDecls, cir::Func* cir ) :
m_type( forward< T >( funcType ) ),
m_tokens( forward< B >( toks ) ),
m_paramsDecl( forward< P >( paramsDecls ) ),
m_cir( cir )
{
}
template< typename T, typename P >
Func( T&& funcType, P&& paramsDecls, cir::Func* cir ) :
m_type( forward< T >( funcType ) ),
m_paramsDecl( forward< P >( paramsDecls ) ),
m_cir( cir )
{
}
template< typename T, typename B, typename P >
Func( T&& funcType, B&& toks, P&& paramsDecls ) :
m_type( forward< T >( funcType ) ),
m_tokens( forward< B >( toks ) ),
m_paramsDecl( forward< P >( paramsDecls ) )
{
}
template< typename T >
Func( T&& funcType, const string& symbol ) :
m_type( forward< T >( funcType ) ),
m_symbol( symbol )
{
}
bool isExternal() const { return m_symbol.has_value(); }
const auto& type() const { return m_type; }
auto& type() { return m_type; }
const auto& tokens() const { return m_tokens; }
const auto& paramsDecl() const { return m_paramsDecl; }
const auto& cir() const { return m_cir; }
const auto& symbol() const { return m_symbol; }
void setCIR( cir::Func* cir ) { m_cir = cir; }
void setTokens( const ptr< void >& pToks ) { m_tokens = pToks; }
bool haveUnmetDependencies() const
{
if( !m_cir )
return false;
return Env::DepsGraph().haveUnmetDependencies( m_cir );
}
struct Summary
{
cir::Func* cir = nullptr;
FuncType::Kind kind = FuncType::Kind::Regular;
};
struct StackHelper
{
StackHelper( const Func& func )
{
Func::PushFunc( Summary( func.m_cir, func.type().kind() ) );
}
~StackHelper() { Func::PopFunc(); }
};
static inline const Summary& CurrentCaller() { return ms_funcStack.back(); }
static inline const auto& FuncStack() { return ms_funcStack; }
private:
static inline void PushFunc( Summary fs ) { ms_funcStack.emplace_back( fs ); }
static inline void PopFunc() { ms_funcStack.resize( ms_funcStack.size() - 1 ); }
FuncType m_type;
ptr< void > m_tokens; // the unparsed body
pvec m_paramsDecl;
cir::Func* m_cir = nullptr;
// The function's symbol, if this is an external function.
optional< string > m_symbol;
// The current callstack that we are compiling, used to detect
// invalid recursive inline calls
static llvm::SmallVector< Summary, 16 > ms_funcStack;
};
extern Value CompileFunc( const Context& c, const Value& f );
template< typename F > cir::InstrSeq BuildArgsInstrSeq( const Term& args, F&& argPostProc );
extern cir::InstrSeq BuildArgsInstrSeq( const Term& args );
} // namespace goose::builtins
namespace goose::eir
{
template<> struct Bridge< builtins::Func >
{
static Term Type( const builtins::Func& func );
static Value ToValue( const builtins::Func& func );
static optional< builtins::Func > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/func/func.inl.
1 2 3 4 5 |
#ifndef GOOSE_BUILTINS_TYPES_FUNC_INL
#define GOOSE_BUILTINS_TYPES_FUNC_INL
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 |
#ifndef GOOSE_BUILTINS_TYPES_FUNC_INL
#define GOOSE_BUILTINS_TYPES_FUNC_INL
namespace goose::builtins
{
template< typename F > void ForEachDeclInTuple( const Value& tup, F&& func )
{
ForEachInTuple( tup, [&]( auto&& tupVal ) { func( *FromValue< Decl >( tupVal ) ); } );
}
template< typename F > cir::InstrSeq BuildArgsInstrSeq( const Term& args, F&& argPostProc )
{
cir::InstrSeq result;
ForEachInVectorTerm( args,
[&]( auto&& arg )
{
auto argVal = *EIRToValue( arg );
cir::AppendToInstrSeq( result, argVal );
argPostProc( argVal, result );
return true;
} );
return result;
}
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/func/functype.cpp.
1 2 3 4 | #include "builtins/builtins.h" #include "lex/lex.h" #include "parse/parse.h" // TODO_SSA reenable | | | > | | | | | | | | | | | > < | | | | | | | < | < | | | | | | | < | 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 |
#include "builtins/builtins.h"
#include "lex/lex.h"
#include "parse/parse.h"
// TODO_SSA reenable
// #include "verify/verify.h"
using namespace goose::builtins;
using namespace goose::parse;
namespace goose::builtins
{
void FuncType::setKind( Kind k )
{
m_kind = k;
// When converting a function to an intrinsic function,
// we need to convert the param signatures to constants,
// unless they are wrapped.
if( m_kind == FuncType::Kind::Intrinsic )
{
ForEachInVectorTerm( m_params,
[]( auto& t )
{
auto p = *EIRToValuePattern( t );
if( !IsWrappedType( p.type() ) )
{
p.sort() = TSID( constant );
t = ValueToEIR( p );
}
return true;
} );
}
}
const Term& FuncTypePattern()
{
static auto funcTypePat = ValueToEIR( Value( TypeType(),
VEC( TSID( func ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ),
ANYTERM( _ ) ) ) );
return funcTypePat;
}
bool IsFuncType( const Value& t )
{
auto result = Decompose( t.val(),
Vec( Lit( "func"_sid ),
SubTerm(), // kind
SubTerm(), // return type
SubTerm(), // param types
SubTerm(), // verif infos
SubTerm() // Lowering infos
) );
return !!result;
}
Term GetFuncSigFromType( const Value& funcType )
{
// G_TRACE_VAL( funcType );
auto typeDecomp = Decompose( funcType.val(),
Vec( Lit( "func"_sid ),
SubTerm(), // kind
SubTerm(), // return type
SubTerm(), // param types
SubTerm(), // verif infos
SubTerm() // Lowering infos
) );
assert( typeDecomp );
auto&& [kind, rtype, ptypes, vinf, li] = *typeDecomp;
return PrependToVectorTerm( *Unquote( ptypes ), rtype );
}
ParamListKind CheckParamListKind( const Value& tup )
|
| ︙ | ︙ | |||
93 94 95 96 97 98 99 |
else if( !IsDecl( *v ) && !v->isConstant() )
return ParamListKind::Invalid;
}
return result;
}
| | > | > | | | | | | | | | | | | | | > | | | | | | | | > | | | | | | | | | | | > | > | | | | | | | | | | | | | | | | | | | | | | < | | | | | | | < | < < > | 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 |
else if( !IsDecl( *v ) && !v->isConstant() )
return ParamListKind::Invalid;
}
return result;
}
optional< Term > BuildArgListForCall(
const Context& c, const Value& func, const Term& typeCheckedArgs )
{
pvec argVec = make_shared< Vector >();
pvec paramVec = make_shared< Vector >();
auto count = VecSize( typeCheckedArgs );
argVec->reserve( count );
paramVec->reserve( count );
auto ft = *FromValue< FuncType >( *EIRToValue( func.type() ) );
// Filter out constant params
ForEachInVectorTerms( ft.params(), typeCheckedArgs,
[&]( auto&& p, auto&& a )
{
auto vp = EIRToValuePattern( p );
if( vp->val() == HOLE( "_"_sid ) )
{
argVec->append( a );
paramVec->append( EIRToValuePattern( p )->type() );
}
return true;
} );
// Do not perform arg conversion on g0 funcs themselves
// because we don't want to infinitely recurse into the
// extension points implementations
if( IsG0Func( func ) || IsGhostFunc( func ) )
return TERM( argVec );
auto result = InvokeOverloadSet(
c, c.env()->extConvertFuncArgs(), MakeClosedTuple( Wrap( argVec ), Wrap( paramVec ) ) );
auto resultVec = FromValue< TypeWrapper< pvec > >( result );
if( !resultVec )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
func.locationId(), "_ConvertArgs returned an invalid value." );
return nullopt;
}
ForEachInVectorTerms( ft.params(), TERM( *resultVec ),
[&]( auto&& p, Term& a )
{
auto vp = EIRToValuePattern( p );
if( vp->val() == HOLE( "_"_sid ) )
{
auto arg = *EIRToValue( a );
if( IsWrappedType( arg.type() ) )
{
auto argVal = *EIRToValue( a );
Value unwrappedArg(
GetUnwrappedType( *EIRToValue( argVal.type() ) ), argVal.payload() );
unwrappedArg.setLocationId( argVal.locationId() );
auto wrappedArg = ToValue( TypeWrapper< Value >( unwrappedArg ) );
a = ValueToEIR( wrappedArg );
}
}
return true;
} );
return TERM( *resultVec );
}
// Identical to BuildArgListForCall except for the insertion of the implicit context parameter
// and not calling the parg conversion extension point.
// A bit redundant but most of that stuff is going ot move to the prelude anyway
// (single ext point to convert all the args versus doing each arg individually) so there will
// be an opportunity to factorize this later
optional< Term > BuildArgListForIntrinsicCall(
const Context& c, const FuncType& ft, const Term& typeCheckedArgs )
{
auto av = make_shared< Vector >();
av->reserve( VecSize( ft.params() ) + 1 );
auto pCBWrapper = ToValue( TypeWrapper< ptr< Context > >( make_shared< Context >( c ) ) );
av->append( ValueToEIR( pCBWrapper ) );
bool failed = false;
ForEachInVectorTerms( ft.params(), typeCheckedArgs,
[&]( auto&& p, auto&& a )
{
auto vp = EIRToValuePattern( p );
if( vp->val() == HOLE( "_"_sid ) )
{
if( IsWrappedType( vp->type() ) )
{
auto argVal = *EIRToValue( a );
auto unwrappedArg = GetUnwrapped( argVal );
unwrappedArg.setLocationId( argVal.locationId() );
auto wrappedArg = ToValue( TypeWrapper< Value >( unwrappedArg ) );
av->append( ValueToEIR( wrappedArg ) );
}
else
av->append( a );
}
return true;
} );
if( failed )
return nullopt;
return av;
}
} // namespace goose::builtins
namespace goose::eir
{
const Term& Bridge< FuncType >::Type()
{
return TypeType();
}
Value Bridge< FuncType >::ToValue( const FuncType& ft )
{
return Value( Type(),
VEC( TSID( func ), TERM( static_cast< uint64_t >( ft.kind() ) ), ft.returnType(),
Quote( ft.params() ), static_pointer_cast< void >( ft.verifInfos() ),
ft.loweringInfos() ) );
}
optional< FuncType > Bridge< FuncType >::FromValue( const Value& v )
{
auto result = Decompose( v.val(),
Vec( Lit( "func"_sid ),
Val< uint64_t >(), // kind
SubTerm(), // return type
SubTerm(), // param types
Val< ptr< void > >(), // verif infos
Val< ptr< void > >() // Lowering infos
) );
if( !result )
return nullopt;
auto&& [kind, rtype, params, vinf, li] = *result;
return FuncType( rtype, *Unquote( params ),
static_pointer_cast< FuncVerificationInfos >( vinf ),
static_pointer_cast< FuncLoweringInfos >( li ), static_cast< FuncType::Kind >( kind ) );
}
} // namespace goose::eir
|
Changes to bs/builtins/types/func/functype.h.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 |
extern Term GetFuncSigFromType( const Value& funcType );
// Given a tuple, determines whether it is a valid param list, and of which kind.
extern ParamListKind CheckParamListKind( const Value& tup );
class FuncVerificationInfos
{
| | | < | | | | | | | | | | | < | | | | < | | | | > | | > | | | | | | | | | | | | | | | | < | | | | < | > > | | | | | | | | < | > > | | | | < | > > | > | > | | | > | | | | | | | | > | | > | < > > < | | | 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 |
extern Term GetFuncSigFromType( const Value& funcType );
// Given a tuple, determines whether it is a valid param list, and of which kind.
extern ParamListKind CheckParamListKind( const Value& tup );
class FuncVerificationInfos
{
public:
template< typename I > FuncVerificationInfos( I&& identity )
{
m_preConds->setIdentity( identity );
m_postConds->setIdentity( forward< I >( identity ) );
}
bool parse( const Context& c )
{
bool b1 = m_preConds->parse( c );
bool b2 = m_postConds->parse( c );
return b1 && b2;
}
template< typename T > void setPreCondToks( T&& preCondToks )
{
m_preConds->setUnparsedProps( forward< T >( preCondToks ) );
}
template< typename T > void setPostCondToks( T&& postCondToks )
{
m_postConds->setUnparsedProps( forward< T >( postCondToks ) );
}
const auto& preConds() const { return m_preConds; }
const auto& postConds() const { return m_postConds; }
auto& preConds() { return m_preConds; }
auto& postConds() { return m_postConds; }
private:
ptr< Propositions > m_preConds = make_shared< Propositions >();
ptr< Propositions > m_postConds = make_shared< Propositions >();
};
struct FuncLoweringInfos
{
vector< Value > loweredParamTypes;
Value loweredReturnType;
};
class FuncType
{
public:
enum class Kind
{
Regular,
Comptime,
VarArg,
Intrinsic,
Inline,
Ghost
};
template< typename R, typename P, typename VI >
FuncType( R&& returnType, P&& params, VI&& verifInfos, Kind kind = Kind::Regular ) :
m_returnType( forward< R >( returnType ) ),
m_params( forward< P >( params ) ),
m_verifInfos( forward< VI >( verifInfos ) ),
m_kind( kind )
{
}
template< typename R, typename P, typename VI, typename LI >
FuncType( R&& returnType, P&& params, VI&& verifInfos, LI&& loweringInfos,
Kind kind = Kind::Regular ) :
m_returnType( forward< R >( returnType ) ),
m_params( forward< P >( params ) ),
m_verifInfos( forward< VI >( verifInfos ) ),
m_loweringInfos( forward< LI >( loweringInfos ) ),
m_kind( kind )
{
}
template< typename R, typename P >
FuncType( R&& returnType, P&& params ) :
m_returnType( forward< R >( returnType ) ),
m_params( forward< P >( params ) )
{
}
const auto& returnType() const { return m_returnType; }
const auto& params() const { return m_params; }
const auto& verifInfos() const { return m_verifInfos; }
const auto& loweringInfos() const { return m_loweringInfos; }
auto kind() const { return m_kind; }
void setKind( Kind k );
private:
Term m_returnType;
Term m_params;
ptr< FuncVerificationInfos > m_verifInfos;
ptr< FuncLoweringInfos > m_loweringInfos;
Kind m_kind = Kind::Regular;
};
extern bool IsFuncType( const Value& t );
// The args for a function may include constants that are specialization args.
// When building a call instruction, we need to strip those out and keep only
// the args that actually exist at runtime / execution time.
// We also need to run the args through the ConvertFuncArg() extension point.
extern optional< Term > BuildArgListForCall(
const Context& c, const Value& func, const Term& typeCheckedArgs );
// Takes argument intended to call an intrinsic function and wraps them using TypeWrapper< Value
// >, so that the intrinsic function code can manipulate the value representations rather than
// the actual values.
extern optional< Term > BuildArgListForIntrinsicCall(
const Context& c, const FuncType& ft, const Term& typeCheckedArgs );
} // namespace goose::builtins
namespace goose::eir
{
template<> struct Bridge< builtins::FuncType >
{
static const Term& Type();
static Value ToValue( const builtins::FuncType& ft );
static optional< builtins::FuncType > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/func/invocation/beagerfunc.cpp.
1 2 3 4 5 6 7 8 9 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class BuiltinEagerFuncInvocationRule : public BaseFuncInvocationRule
{
| | | > | | < < | | | | | | | | | | | | | > | | | | < | | | | 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 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class BuiltinEagerFuncInvocationRule : public BaseFuncInvocationRule
{
public:
Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args,
const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
auto callDecomp = Decompose( typeCheckedCallPat, Val< pvec >() );
const auto& typeCheckedRType = callDecomp->get()->terms().front();
auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
auto argCount = VecSize( typeCheckedArgs );
auto argsInstrSeq = BuildArgsInstrSeq( typeCheckedArgs );
auto call = BuildComputedValue(
typeCheckedRType, argsInstrSeq, callee, cir::Call( argCount, loc ) );
if( !cir::CanValueBeEagerlyEvaluated( call ) )
return call;
Context::DefaultContextGuard dfg( c );
execute::VM vm;
return execute::Evaluate( call, vm );
}
Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee,
const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
return callee;
}
};
const ptr< InvocationRule >& GetBuiltinEagerFuncInvocationRule()
{
static ptr< InvocationRule > pRule = make_shared< BuiltinEagerFuncInvocationRule >();
return pRule;
}
void SetupBuiltinEagerFuncInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule(
ValueToEIR( ValuePattern( ANYTERM( _ ),
ValueToEIR( Value( TypeType(),
VEC( TSID( func ), TSID( builtin_eager ), ANYTERM( _ ), ANYTERM( _ ),
ANYTERM( _ ), ANYTERM( _ ) ) ) ),
ANYTERM( _ ) ) ),
GetBuiltinEagerFuncInvocationRule() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/invocation/bfunc.cpp.
1 2 3 4 5 6 7 8 9 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class BuiltinFuncInvocationRule : public BaseFuncInvocationRule
{
| | | > | | < < | | | | > | | | > | | | | < | | < | | < > | 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 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class BuiltinFuncInvocationRule : public BaseFuncInvocationRule
{
public:
Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args,
const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
auto callDecomp = Decompose( typeCheckedCallPat, Val< pvec >() );
const auto& typeCheckedRType = callDecomp->get()->terms().front();
auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
auto argCount = VecSize( typeCheckedArgs );
auto argsInstrSeq = BuildArgsInstrSeq( typeCheckedArgs );
return BuildComputedValue(
typeCheckedRType, move( argsInstrSeq ), callee, cir::Call( argCount, loc ) );
}
Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee,
const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
return callee;
}
};
const ptr< InvocationRule >& GetBuiltinFuncInvocationRule()
{
static ptr< InvocationRule > pRule = make_shared< BuiltinFuncInvocationRule >();
return pRule;
}
void SetupBuiltinFuncInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule( ValueToEIR( ValuePattern( ANYTERM( _ ),
ValueToEIR( Value( TypeType(),
VEC( TSID( func ), TSID( builtin ), ANYTERM( _ ),
ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) ),
ANYTERM( _ ) ) ),
GetBuiltinFuncInvocationRule() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/invocation/bintrinsic.cpp.
1 2 3 4 5 6 7 8 9 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class BuiltinIntrinsicFuncInvocationRule : public BaseFuncInvocationRule
{
| | | > | | | | | > | | | | < | | | | 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"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class BuiltinIntrinsicFuncInvocationRule : public BaseFuncInvocationRule
{
public:
Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args,
const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
return GetBuiltinIntrinsicFuncWrapper( callee )( c, move( typeCheckedArgs ) );
}
Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee,
const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
return callee;
}
};
const ptr< InvocationRule >& GetBuiltinIntrinsicFuncInvocationRule()
{
static ptr< InvocationRule > pRule = make_shared< BuiltinIntrinsicFuncInvocationRule >();
return pRule;
}
void SetupBuiltinIntrinsicFuncInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule(
ValueToEIR( ValuePattern( ANYTERM( _ ),
ValueToEIR( Value( TypeType(),
VEC( TSID( func ), TSID( intrinsic_builtin ), ANYTERM( _ ), ANYTERM( _ ),
ANYTERM( _ ), ANYTERM( _ ) ) ) ),
ANYTERM( _ ) ) ),
GetBuiltinIntrinsicFuncInvocationRule() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/invocation/common.cpp.
1 2 3 4 5 6 7 8 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
using namespace goose::cir;
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 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
using namespace goose::cir;
namespace goose::builtins
{
Value BaseFuncInvocationRule::resolveInvocation(
Context& c, LocationId loc, const Value& callee, const Term& args ) const
{
optional< TypeCheckingContext > bestTCC;
optional< Term > bestSol;
auto sig = GetFuncSig( callee );
auto callPat = PrependToVectorTerm( args, HOLE( "_"_sid, "fwd"_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 );
}
optional< Term > BaseFuncInvocationRule::getSignature( const Value& callee ) const
{
return GetFuncSig( callee );
}
Value BaseFuncInvocationRule::EmitTypePredicatesCheck(
CFG& cfg, const Value& val, const ptr< Propositions >& props )
{
// TODO_SSA reenable
/*if( props->empty() )
return val;
auto uid = util::GenerateNewUID();
auto bb = cfg.currentBB();
|
| ︙ | ︙ | |||
87 88 89 90 91 92 93 |
return true;
}*/
execute::VM vm;
call = execute::Evaluate( call, vm );
return call.isConstant();
}
| | | 88 89 90 91 92 93 94 95 |
return true;
}*/
execute::VM vm;
call = execute::Evaluate( call, vm );
return call.isConstant();
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/invocation/common.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_BUILTINS_TYPES_FUNC_INVOCATION_COMMON_H
#define GOOSE_BUILTINS_TYPES_FUNC_INVOCATION_COMMON_H
namespace goose::builtins
{
class BaseFuncInvocationRule : public InvocationRule
{
| | > | | > | | < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#ifndef GOOSE_BUILTINS_TYPES_FUNC_INVOCATION_COMMON_H
#define GOOSE_BUILTINS_TYPES_FUNC_INVOCATION_COMMON_H
namespace goose::builtins
{
class BaseFuncInvocationRule : public InvocationRule
{
public:
Value resolveInvocation(
Context& c, LocationId loc, const Value& callee, const Term& args ) const final;
optional< Term > getSignature( const Value& callee ) const final;
static Value EmitTypePredicatesCheck(
cir::CFG& cfg, const Value& val, const ptr< Propositions >& props );
static bool EvaluateComptimeCall( const Context& c, Value& call );
};
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/func/invocation/comptime.cpp.
1 2 3 4 5 6 7 8 9 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class ComptimeFuncInvocationRule : public BaseFuncInvocationRule
{
| | | > | | | | | < < | | | | | | | | | > | | | | | | | | | | > | | > | | | | | | | < | | 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 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class ComptimeFuncInvocationRule : public BaseFuncInvocationRule
{
public:
Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args,
const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
auto preparedCallee = prepareFunc( c, 0, callee, typeCheckedCallPat, tcc );
if( preparedCallee.isPoison() )
return PoisonValue();
auto callDecomp = Decompose( typeCheckedCallPat, Val< pvec >() );
const auto& typeCheckedRType = callDecomp->get()->terms().front();
auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
preparedCallee.setLocationId( loc );
auto argList = BuildArgListForCall( c, preparedCallee, typeCheckedArgs );
if( !argList )
return PoisonValue();
auto argsInstrSeq = BuildArgsInstrSeq( *argList );
auto argCount = VecSize( *argList );
auto result = BuildComputedValue(
typeCheckedRType, argsInstrSeq, preparedCallee, cir::Call( argCount, loc ) )
.setLocationId( loc );
if( HaveUnmetDependencies( preparedCallee ) || !EvaluateComptimeCall( c, result ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
loc, "this call couldn't be evaluated at compilation time." );
return PoisonValue();
}
return result;
}
Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee,
const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
// 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 );
return CompileFunc( c, callee );
}
};
void SetupComptimeFuncInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule(
ValueToEIR( ValuePattern( ANYTERM( _ ),
ValueToEIR( Value( TypeType(),
VEC( TSID( func ), TERM( static_cast< uint64_t >( FuncType::Kind::Comptime ) ),
ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) ),
ANYTERM( _ ) ) ),
make_shared< ComptimeFuncInvocationRule >() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/invocation/ghostfunc.cpp.
1 2 3 4 5 6 7 8 9 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class GhostFuncInvocationRule : public BaseFuncInvocationRule
{
| | | > | | | | | | | | < < | | | | | | | | | > | | < | | | > | | | | | | < | | 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 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class GhostFuncInvocationRule : public BaseFuncInvocationRule
{
public:
Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args,
const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
if( !InVerificationCode( c ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
loc, "ghost functions can't be called in this context." );
return PoisonValue();
}
auto callDecomp = Decompose( typeCheckedCallPat, Val< pvec >() );
const auto& typeCheckedRType = callDecomp->get()->terms().front();
auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
auto argCount = VecSize( typeCheckedArgs );
auto argList = BuildArgListForCall( c, callee, typeCheckedArgs );
if( !argList )
return PoisonValue();
auto argsInstrSeq = BuildArgsInstrSeq( *argList );
// A ghost call is a mutable reference to a ghost closure, using the special "GhostCall"
// as the instruction to compute its "address"
auto rt =
ValueToEIR( ToValue( ReferenceType{ typeCheckedRType, MutAccessSpecifier() } ) );
return BuildComputedValue( rt, argsInstrSeq, callee, cir::CallCheck( argCount, loc ),
move( argsInstrSeq ), callee, cir::GhostCall( argCount, loc ) );
}
Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee,
const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
return callee;
}
};
void SetupGhostFuncInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule(
ValueToEIR( ValuePattern( ANYTERM( _ ),
ValueToEIR( Value( TypeType(),
VEC( TSID( func ), TERM( static_cast< uint64_t >( FuncType::Kind::Ghost ) ),
ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) ),
ANYTERM( _ ) ) ),
make_shared< GhostFuncInvocationRule >() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/invocation/inline.cpp.
1 2 3 4 5 6 7 8 9 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class InlineFuncInvocationRule : public BaseFuncInvocationRule
{
| | | > | | | | > | | | > | > | | | | | | | | | > | | | | | | | | | > | > | | | < < | | | | | | | | | > | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | < | | | | | > | | > | | > | | | | | | | < | | 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 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class InlineFuncInvocationRule : public BaseFuncInvocationRule
{
public:
Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args,
const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
if( !callee.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
loc, "indirect inline function call." );
return PoisonValue().setLocationId( loc );
}
auto funcCIR = GetFuncCIR( callee );
if( !Func::FuncStack()
.empty() ) // TODO: can happen today when called from toplevel. Remove after
// factoring toplevel parsing into a function wrapper.
{
// Check for invalid recursive inline calls.
for( auto&& fi : Func::FuncStack() | views::reverse )
{
if( fi.kind == FuncType::Kind::Regular )
break;
if( fi.cir == funcCIR )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
loc, "recursive inline call." );
return PoisonValue().setLocationId( loc );
}
}
}
auto cfg = GetCFG( c );
if( !cfg )
{
// TODO: currently this mean among other things "not from proposition lists aka type
// predicates", which sucks. Need to once again revisit the idea of getting rid of
// proposition lists and replace them with just CFGs.
DiagnosticsManager::GetInstance().emitErrorMessage(
loc, "inline functions can't be called here." );
return PoisonValue().setLocationId( loc );
}
auto callDecomp = Decompose( typeCheckedCallPat, Val< pvec >() );
const auto& typeCheckedRType = callDecomp->get()->terms().front();
auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
auto argList = BuildArgListForCall( c, callee, typeCheckedArgs );
if( !argList )
return PoisonValue();
auto argCount = VecSize( *argList );
cir::InlineCall::ArgUidArray argUids( argCount );
uint32_t index = 0;
auto argsInstrSeq = BuildArgsInstrSeq( *argList,
[&]( auto&& argVal, auto&& is )
{
auto uid = util::GenerateNewUID();
argUids[index++] = uid;
/* AppendToInstrSeq( is, cir::CreateTemporary( uid, false,
argVal.locationId() ) );*/
} );
// If the function we want to call have unmet dependencies, or if the function we are
// calling is currently being compiled (it it's present in the func stack), then we
// can't perform the inlining eagerly. So we emit an InlineCall instr that will be
// expanded once the dependencies are met, and we register that dependendy in the
// DepsGraph so it can make it happen.
bool shouldDeferInlining = HaveUnmetDependencies( callee );
if( !shouldDeferInlining )
{
for( auto&& fi : Func::FuncStack() | views::reverse )
{
if( fi.cir == funcCIR )
{
shouldDeferInlining = true;
break;
}
}
}
if( shouldDeferInlining )
{
const auto& caller = Func::CurrentCaller();
auto result = BuildComputedValue( typeCheckedRType, argsInstrSeq, callee,
cir::InlineCall( move( argUids ), funcCIR, typeCheckedRType, loc ) );
Env::DepsGraph().add( caller.cir, funcCIR );
return result;
}
auto preparedCallee = prepareFunc( c, 0, callee, typeCheckedCallPat, tcc );
if( preparedCallee.isPoison() )
return PoisonValue();
preparedCallee.setLocationId( loc );
// no unmet dependencies: eagerly inline the function, so that the function have a
// chance to be eagerly evaluated later
cfg->currentBB()->append( move( argsInstrSeq ) );
auto retIs = AppendCFG( *cfg, *funcCIR->body(), typeCheckedRType, argUids );
// After AppendCFG, the resulting code could push the return value in a way
// or another, or give us an instruction sequence to load it from some var slot.
// Since we need to package it into a computed value that may not necessarily
// be appended right away, if we are receiving an empty instr sequence,
// we need to store it into a temporary.
if( retIs->empty() )
{
/* auto retValUid = util::GenerateNewUID();
cfg->currentBB()->append(
cir::CreateTemporary( retValUid, false, loc ) );
AppendToInstrSeq( *retIs, cir::GetTemporary{ typeCheckedRType, retValUid, loc }
);*/
}
return BuildComputedValue( typeCheckedRType, move( retIs ) );
// TODO: we need to support eager evaluation of inline function calls. (Otherwise
// whenever we make generic inline wrapper functions they will not really work for types
// and it will be annoying) If all dependencies are met, and if a regular call to it
// could be eagerly evaluated, Then attempt to perform an eager evaluation like regular
// function does (move that code from regular func invoke to a helper func in common)
// The current inline func tests may need to be rewritten to prevent eager evaluation.
// (or perhaps add a directive for test purpose only to disable eager evaluation of
// inline funcs?)
}
Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee,
const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
// 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 );
return CompileFunc( c, callee );
}
};
void SetupInlineFuncInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule(
ValueToEIR( ValuePattern( ANYTERM( _ ),
ValueToEIR( Value( TypeType(),
VEC( TSID( func ), TERM( static_cast< uint64_t >( FuncType::Kind::Inline ) ),
ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) ),
ANYTERM( _ ) ) ),
make_shared< InlineFuncInvocationRule >() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/invocation/intrinsic.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "builtins/builtins.h"
#include "builtins/helpers.h"
#include "common.h"
using namespace goose::sema;
using namespace goose::cir;
namespace goose::builtins
{
class IntrinsicFuncInvocationRule : public BaseFuncInvocationRule
{
| | | > | | | | > | | | > | > | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | > | | | | | | | < | | 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 |
#include "builtins/builtins.h"
#include "builtins/helpers.h"
#include "common.h"
using namespace goose::sema;
using namespace goose::cir;
namespace goose::builtins
{
class IntrinsicFuncInvocationRule : public BaseFuncInvocationRule
{
public:
Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args,
const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
if( !callee.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
loc, "indirect intrinsic function call." );
return PoisonValue().setLocationId( loc );
}
auto funcCIR = GetFuncCIR( callee );
if( !Func::FuncStack()
.empty() ) // TODO: can happen today when called from toplevel. Remove after
// factoring toplevel parsing into a function wrapper.
{
// Check for invalid recursive intrinsic calls.
for( auto&& fi : Func::FuncStack() | views::reverse )
{
if( fi.kind == FuncType::Kind::Regular )
break;
if( fi.cir == funcCIR )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
loc, "recursive intrinsic call." );
return PoisonValue().setLocationId( loc );
}
}
}
// TODO: if the callee have unmet dependencies, error out. intrinsic functions
// can only be eagerly evaluated.
auto preparedCallee = prepareFunc( c, 0, callee, typeCheckedCallPat, tcc );
if( preparedCallee.isPoison() )
return PoisonValue();
auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
preparedCallee.setLocationId( loc );
auto ft = *FromValue< FuncType >( *EIRToValue( preparedCallee.type() ) );
// Intrinsic call: we insert the context wrapper as first param,
// wrap all args with TypeWrapper< Value >, and execute the function directly.
auto argList = BuildArgListForIntrinsicCall( c, ft, typeCheckedArgs );
if( !argList )
return PoisonValue();
auto argsInstrSeq = BuildArgsInstrSeq( *argList );
auto argCount = VecSize( *argList );
execute::VM vm;
cir::InstrSeq is;
AppendToInstrSeq(
is, move( argsInstrSeq ), preparedCallee, cir::Call( argCount, loc ) );
if( !vm.execute( is ) )
return PoisonValue();
if( ft.returnType() == GetValueType< void >() )
return Value( GetValueType< void >(), 0U );
auto result = vm.pop();
if( !result )
return PoisonValue();
if( result->type() != GetValueType< TypeWrapper< Value > >() )
return *result;
auto& dm = DiagnosticsManager::GetInstance();
auto unwrapped = *FromValue< TypeWrapper< Value > >( *result );
auto converted = ConvertValueToType(
c, unwrapped, GetUnwrappedType( *EIRToValue( ft.returnType() ) ) );
if( holds_alternative< ValUnifyError >( converted ) )
{
switch( get< ValUnifyError >( converted ) )
{
case ValUnifyError::NoSolution:
dm.emitErrorMessage( loc, "return value type mismatch." );
break;
case ValUnifyError::Ambiguous:
dm.emitErrorMessage( loc, "ambiguous return value conversion." );
break;
}
}
const auto& convVal = get< Value >( converted );
// TODO_SSA reenable
// If the type have predicates, emit assertion checks.
/*auto preds = GetTypePredicates( *EIRToValue( convVal.type() ) );
if( !preds || !*preds )
return convVal;
if( ( *preds )->props().empty() )
return convVal;
auto cfg = GetCFG( c );
if( !cfg )
return convVal;
return EmitTypePredicatesCheck( *cfg, convVal, *preds );*/
return convVal;
}
Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee,
const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
// 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 );
return CompileFunc( c, callee );
}
};
void SetupIntrinsicFuncInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule(
ValueToEIR( ValuePattern( ANYTERM( _ ),
ValueToEIR( Value( TypeType(),
VEC( TSID( func ), TERM( static_cast< uint64_t >( FuncType::Kind::Intrinsic ) ),
ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) ),
ANYTERM( _ ) ) ),
make_shared< IntrinsicFuncInvocationRule >() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/invocation/invocation.h.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
extern void SetupIntrinsicFuncInvocationRule( Env& e );
extern void SetupInlineFuncInvocationRule( Env& e );
extern const ptr< InvocationRule >& GetBuiltinFuncInvocationRule();
extern const ptr< InvocationRule >& GetBuiltinEagerFuncInvocationRule();
extern const ptr< InvocationRule >& GetBuiltinIntrinsicFuncInvocationRule();
| | < | < < | < | | | 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 |
extern void SetupIntrinsicFuncInvocationRule( Env& e );
extern void SetupInlineFuncInvocationRule( Env& e );
extern const ptr< InvocationRule >& GetBuiltinFuncInvocationRule();
extern const ptr< InvocationRule >& GetBuiltinEagerFuncInvocationRule();
extern const ptr< InvocationRule >& GetBuiltinIntrinsicFuncInvocationRule();
template< typename F > struct InvocationRuleSelector
{
static const auto& Get() { return GetBuiltinFuncInvocationRule(); }
};
template< typename R, typename... T > struct InvocationRuleSelector< Eager< R >( T... ) >
{
static const auto& Get() { return GetBuiltinEagerFuncInvocationRule(); }
};
template< typename F > struct InvocationRuleSelector< Intrinsic< F > >
{
static const auto& Get() { return GetBuiltinIntrinsicFuncInvocationRule(); }
};
template< typename F > const ptr< InvocationRule >& GetInvocationRule()
{
return InvocationRuleSelector< F >::Get();
}
static inline void SetupFuncInvocationRules( Env& e )
{
SetupBuiltinEagerFuncInvocationRule( e );
SetupBuiltinFuncInvocationRule( e );
SetupBuiltinIntrinsicFuncInvocationRule( e );
SetupRegularFuncInvocationRule( e );
SetupComptimeFuncInvocationRule( e );
SetupGhostFuncInvocationRule( e );
SetupIntrinsicFuncInvocationRule( e );
SetupInlineFuncInvocationRule( e );
}
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/func/invocation/regular.cpp.
1 2 3 4 5 6 7 8 9 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class RegularFuncInvocationRule : public BaseFuncInvocationRule
{
| | | > | | | | | < < | | | | | | | | | > | | | | | | | | | > | | | | | | | | | | | | | | | | | | | > | | > | | | | | < | | | < | < > | 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 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class RegularFuncInvocationRule : public BaseFuncInvocationRule
{
public:
Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args,
const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
auto preparedCallee = prepareFunc( c, 0, callee, typeCheckedCallPat, tcc );
if( preparedCallee.isPoison() )
return PoisonValue();
auto callDecomp = Decompose( typeCheckedCallPat, Val< pvec >() );
const auto& typeCheckedRType = callDecomp->get()->terms().front();
auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
preparedCallee.setLocationId( loc );
auto argList = BuildArgListForCall( c, preparedCallee, typeCheckedArgs );
if( !argList )
return PoisonValue();
auto argsInstrSeq = BuildArgsInstrSeq( *argList );
auto argCount = VecSize( *argList );
auto result = BuildComputedValue(
typeCheckedRType, argsInstrSeq, preparedCallee, cir::Call( argCount, loc ) )
.setLocationId( loc );
if( IsExternalFunc( callee ) )
{
// No dependency handling or eager evaluation on external funcs,
// so we're done.
return result;
}
bool comptime = false;
// If we are part of call chain involving a comptime function, we need the call to be
// comptime as well.
for( auto&& fi : Func::FuncStack() | views::reverse )
{
if( fi.kind == FuncType::Kind::Comptime )
{
comptime = true;
break;
}
}
if( !comptime )
return result;
if( HaveUnmetDependencies( preparedCallee ) || !EvaluateComptimeCall( c, result ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
loc, "this call couldn't be evaluated at compilation time." );
return PoisonValue();
}
return result;
}
Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee,
const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
// 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 );
return CompileFunc( c, callee );
}
};
void SetupRegularFuncInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule( ValueToEIR( ValuePattern( ANYTERM( _ ),
ValueToEIR( Value( TypeType(),
VEC( TSID( func ), ANYTERM( _ ), ANYTERM( _ ),
ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ) ),
ANYTERM( _ ) ) ),
make_shared< RegularFuncInvocationRule >() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/lower.cpp.
| ︙ | ︙ | |||
32 33 34 35 36 37 38 |
paramTypes.emplace_back( pCGType );
}
return codegen::FunctionType::Get( pRTType, paramTypes,
ft.kind() == FuncType::Kind::VarArg );*/
return nullptr;
}
| | | 32 33 34 35 36 37 38 39 |
paramTypes.emplace_back( pCGType );
}
return codegen::FunctionType::Get( pRTType, paramTypes,
ft.kind() == FuncType::Kind::VarArg );*/
return nullptr;
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/typecheck.cpp.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
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(
| | < < < | < < < | | | | | | | < < < | < < < | | | | | | | | | | > | | | | | | < < < | | | | | | | | | | | | | | | | | | | | | < < < | | | < | < < < | < < | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | 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 |
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(
ValueToEIR( ValuePattern( TSID( param ), ANYTERM( _ ), ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern( ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto lhsVal = *EIRToValuePattern( lhs );
lhsVal.sort() = HOLE( "_"_sid );
co_yield TypeCheck( ValueToEIR( lhsVal ), rhs, tcc );
} );
// Constant param rule
e.typeCheckingRuleSet()->addTypeCheckingRule(
ValueToEIR( ValuePattern( TSID( constant ), ANYTERM( _ ), ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern( TSID( constant ), ANYTERM( _ ), ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto lhsVal = *EIRToValuePattern( lhs );
auto rhsVal = *EIRToValuePattern( rhs );
// Check the types
for( auto&& [ut, tcc] : TypeCheck( lhsVal.type(), rhsVal.type(), tcc ) )
{
// Check the contents
for( auto&& [uv, tcc] : TypeCheck( lhsVal.val(), rhsVal.val(), tcc ) )
{
ValuePattern result(
TSID( constant ), move( ut ), move( uv ), rhsVal.locationId() );
co_yield { ValueToEIR( move( result ) ), tcc };
}
}
} );
// func type param / func arg
e.typeCheckingRuleSet()->addTypeCheckingRule(
ParamPat( FuncTypePattern() ),
ValueToEIR( ValuePattern( TSID( constant ), FuncTypePattern(), ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto rhsVal = *EIRToValue( 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 ValueToEIR( func );
} );
co_yield { move( wrapped ), tcc };
} );
// tfunc type param / func arg
e.typeCheckingRuleSet()->addTypeCheckingRule(
ParamPat( TFuncTypeSigPattern( ANYTERM( _ ), ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern( TSID( constant ), FuncTypePattern(), 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 [tfType, _] = DecomposeTFTSig( type );
auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *EIRToValue( tfType ) );
assert( callPat );
auto rhsVal = *EIRToValue( rhs );
auto sig = GetFuncSig( rhsVal );
for( auto&& [s, tcc] : TypeCheck( sig, *callPat, tcc ) )
{
if( IsBuiltinFunc( rhsVal ) )
{
co_yield { ValueToEIR( 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 ValueToEIR( func );
} );
co_yield { move( wrapped ), tcc };
}
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/func/wrapped.h.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
extern Term GetUnwrappedType( const Value& wrapppedArgType );
extern Value GetWrapped( const Value& arg );
extern Value GetUnwrapped( const Value& wrappedArg );
extern bool IsWrappedType( const Value& type );
extern bool IsWrappedType( const Term& type );
| | | 8 9 10 11 12 13 14 15 16 17 |
extern Term GetUnwrappedType( const Value& wrapppedArgType );
extern Value GetWrapped( const Value& arg );
extern Value GetUnwrapped( const Value& wrappedArg );
extern bool IsWrappedType( const Value& type );
extern bool IsWrappedType( const Term& type );
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/ghostcode/drop.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupGhostCodeDropValue( Env& e )
{
// When a boolean is dropped into a ghost code block, it is appended as an assertion check.
| | > | < < | > > | > | | | 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"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupGhostCodeDropValue( Env& e )
{
// When a boolean is dropped into a ghost code block, it is appended as an assertion check.
RegisterBuiltinFunc< Intrinsic< void( TypeWrapper< ptr< GhostCode > >, bool ) > >( e,
e.extDropValue(),
[]( const Context& c, const Value& gcv, const Value& b )
{
auto gc = *FromValue< TypeWrapper< ptr< GhostCode > > >( gcv );
gc->cfg()->currentBB()->append( b, cir::Assert( b.locationId() ) );
} );
// When a ghost code block is dropped into a code builder, we append it using a GhostBranch
// terminator.
RegisterBuiltinFunc< Intrinsic< void( TypeWrapper< ptr< CodeBuilder > >,
TypeWrapper< ptr< GhostCode > > ) > >( e, e.extDropValue(),
[]( const Context& c, const Value& cbv, const Value& gcv )
{
auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( cbv );
auto gc = *FromValue< TypeWrapper< ptr< GhostCode > > >( gcv );
assert( cb->cfg() == gc->cfg() );
auto contBB = cb->cfg()->createBB();
gc->fixUpExitBranches( contBB );
cb->cfg()->emitTerminator(
cbv.locationId(), cir::GhostBranch( gc->entryBB(), contBB ) );
cb->cfg()->setCurrentBB( contBB );
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/ghostcode/ghostcode.h.
1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_BUILTINS_TYPE_GHOSTCODE_H
#define GOOSE_BUILTINS_TYPE_GHOSTCODE_H
namespace goose::builtins
{
void SetupGhostCodeDropValue( Env& e );
class GhostCode : public LifeCycleManager< GhostCode >
{
| | | | | | | | > | | > | | < | | | | | | | < < < | | | > | | | | | | | | 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 |
#ifndef GOOSE_BUILTINS_TYPE_GHOSTCODE_H
#define GOOSE_BUILTINS_TYPE_GHOSTCODE_H
namespace goose::builtins
{
void SetupGhostCodeDropValue( Env& e );
class GhostCode : public LifeCycleManager< GhostCode >
{
public:
GhostCode( const ptr< cir::CFG >& cfg ) :
m_cfg( cfg )
{
m_entryBB = m_cfg->createBB();
}
const auto& cfg() const { return m_cfg; }
const auto& entryBB() const { return m_entryBB; }
bool branchingAllowed() const { return m_branchingAllowed; }
void setBranchingAllowed( bool b ) { m_branchingAllowed = b; }
template< typename V > void append( V&& v )
{
cfg()->currentBB()->append( forward< V >( v ) );
}
// Used to mark the ghost code as poisoned if something goes wrong during
// its parsing.
void poison() { m_poison = true; }
void fixUpExitBranches( const ptr< cir::BasicBlock >& contBB )
{
m_cfg->forEachReachableBlock( m_entryBB,
[&]( auto&& bb )
{
if( !bb->terminator() )
bb->setTerminator( cir::Branch( contBB ) );
} );
}
private:
ptr< cir::CFG > m_cfg;
ptr< cir::BasicBlock > m_entryBB;
bool m_branchingAllowed = true;
bool m_poison = false;
};
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/infer.cpp.
1 2 3 4 5 6 7 8 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::sema;
using namespace goose::builtins;
namespace goose::builtins
{
| | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::sema;
using namespace goose::builtins;
namespace goose::builtins
{
Value InferTypeFromTExprAndInitializer(
const Context& c, const Term& typeTExpr, const Value& initVal )
{
// To infer the type and obtain a suitable initialization function, we typecheck
// ( $$T, _ ( MutRef[$$T], initValType ) initFunc )
// against ( pat, Initialize )
//
// Where:
// pat is the type pattern provided for the declaration,
|
| ︙ | ︙ | |||
25 26 27 28 29 30 31 |
// Create the _ texpr.
static auto TVarAny = ValueToEIR( ToValue( TVar( "_"_sid ) ) );
// The $$T texpr.
static auto TTVarT = ValueToEIR( ToValue( TTVar( "T"_sid ) ) );
// Create the MutRef[$$T] param.
| | > > | > > | > | | < | | < < | | < | | | < | < | | | < | | | | | 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 |
// Create the _ texpr.
static auto TVarAny = ValueToEIR( ToValue( TVar( "_"_sid ) ) );
// The $$T texpr.
static auto TTVarT = ValueToEIR( ToValue( TTVar( "T"_sid ) ) );
// Create the MutRef[$$T] param.
static auto mutRefParam =
ValueToEIR( ToValue( TNamedDecl( ValueToEIR( ToDeclValue( ReferenceType{
TTVarT,
MutAccessSpecifier(),
} ) ),
"ref"_sid ) ) );
auto initValParam = ValueToEIR( ToValue( Decl( initVal.type(), "initVal"_sid ) ) );
// Create the _ ( MutRef[$$T], initType ) initFunc param.
auto initFuncTFTParam = ValueToEIR( ToValue( TNamedDecl(
ValueToEIR(
ToValue( TFuncType( TVarAny, VEC( move( mutRefParam ), move( initValParam ) ) ) ) ),
"initFunc"_sid ) ) );
TemplateContext tc( c );
// Create our parameter list.
auto paramPat = VEC( *BuildTemplateSignature( tc, TTVarT ),
*BuildTemplateSignature( tc, initFuncTFTParam ) );
// Create our arg list.
auto args = VEC( *BuildTemplateArgPattern( c, typeTExpr ),
ValueToEIR( ToValue( c.env()->extInitialize() ) ) );
auto us = FindBestTyping( paramPat, args, c );
if( holds_alternative< NoUnification >( us ) )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage(
0, "initialization type mismatch." );
return PoisonValue();
}
if( holds_alternative< AmbiguousTypeCheck >( us ) )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( 0, "ambiguous 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 = TakeVectorTerm( c.identity(), VecSize( c.identity() ) - 2 );
Context ctxt( c.env(), parentIdentity );
// The TVar setup expects to find its name on the left hand side of the type checking,
// But in our case we have put it on the rhs, so we need to flip the context around
// before setup.
TemplateSetup( ctxt, tcc.flip(), typeTExpr );
auto callDecomp = Decompose( s,
Vec( SubTerm(), // Infered type
SubTerm() // Infered initialize overload (can't use it anyway
) );
auto&& [type, _] = *callDecomp;
auto typeVal = *EIRToValue( type );
// Retrieve the texpr's location and set it on the inferred type. This way if an
// error occurs later with it, for instance when calling LowerTypeForRuntime on it during
// codegen, it will have a meaningful location for the error message to attach itself on.
auto typeLoc = EIRToValue( typeTExpr )->locationId();
typeVal.setLocationId( typeLoc );
// TODO_SSA reenable
// if( !ParseTypePredicates( c, typeVal ) )
// return PoisonValue();
return typeVal;
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/init.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupInitialize( Env& e )
{
using ValueOfTypeT = CustomPattern< Value, ValuePatternT >;
| < | | > | | | | | | | | < > | 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 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupInitialize( Env& e )
{
using ValueOfTypeT = CustomPattern< Value, ValuePatternT >;
using MutRefOfTypeT = CustomPattern< Value, ReferenceType::PatternAnyMutableOfTypeT >;
using CTIntMutRefType =
CustomPattern< Value, ReferenceType::PatternMutableOfType< BigInt > >;
using CTStringMutRefType =
CustomPattern< Value, ReferenceType::PatternMutableOfType< string > >;
// Initialization for any builtin type. Only allowed at compilation time,
// all types at runtime will have to go through their own specialization
// or partial specialization of this, since it will have to emit specialized
// binary runtime code.
RegisterBuiltinFunc< Intrinsic< Value( MutRefOfTypeT, ValueOfTypeT ) > >( e,
e.extInitialize(),
[]( auto&& c, const Value& r, const Value& initVal )
{
G_VAL_ASSERT( r, !r.isConstant() );
auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
return BuildComputedValue( GetValueType< void >(), initVal, r,
Store( refType.type(), initVal.locationId(), r.locationId() ) );
} );
// Default initialization for ct_int vars
RegisterBuiltinFunc< Intrinsic< Value( CTIntMutRefType ) > >( e, e.extInitialize(),
[]( auto&& c, const Value& r )
{
G_VAL_ASSERT( r, !r.isConstant() );
auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
return BuildComputedValue( GetValueType< void >(), ToValue( BigInt() ), r,
Store( refType.type(), {}, r.locationId() ) );
} );
// Default initialization for ct_string vars
RegisterBuiltinFunc< Intrinsic< Value( CTStringMutRefType ) > >( e, e.extInitialize(),
[]( auto&& c, const Value& r )
{
G_VAL_ASSERT( r, !r.isConstant() );
auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
return BuildComputedValue( GetValueType< void >(), ToValue( ""s ), r,
Store( refType.type(), {}, r.locationId() ) );
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/localvar/drop.cpp.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
{
void SetupLocalVarDropValue( Env& e )
{
using AnyLocVarType = CustomPattern< LocalVar, LocalVar::PatternAny >;
// When a localvar is dropped, it means it has escaped from the scope of its statement:
// Make it visible from outside its statement, and extends its lifetime to its parent scope.
| > | > | > | < | | | | | | > | | 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 |
{
void SetupLocalVarDropValue( Env& e )
{
using AnyLocVarType = CustomPattern< LocalVar, LocalVar::PatternAny >;
// When a localvar is dropped, it means it has escaped from the scope of its statement:
// Make it visible from outside its statement, and extends its lifetime to its parent scope.
RegisterBuiltinFunc<
Intrinsic< void( TypeWrapper< ptr< CodeBuilder > >, AnyLocVarType ) > >( e,
e.extDropValue(),
[]( const Context& c, const Value& b, const Value& v )
{
auto lv = *FromValue< LocalVar >( v );
auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( b );
if( lv.name() == ""_sid )
return;
// Make the variable visible from the current scope (the parent scope of the
// statement)
auto identity = AppendToVectorTerm( c.identity(), lv.name() );
c.env()->storeValue( identity, ANYTERM( _ ), ValueToEIR( v ) );
// Extend the variable's lifetime, so that it won't be destroyed as the statement's
// lifetime scope ends, but instead when its parent lifetime scope ends.
cb->extendValueLifetime( lv.index() );
} );
// When a localvar is dropped in any context that doesn't explicitely allow for variable
// declarations, emit an error.
RegisterBuiltinFunc< Intrinsic< void( Value, AnyLocVarType ) > >( e, e.extDropValue(),
[]( const Context& c, const Value& b, const Value& v )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
v.locationId(), "variable declarations are not allowed here." );
PoisonBuilder( c );
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/localvar/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 LocVarInvocationRule : public InvocationRule
{
public:
bool canBeInvoked( const Context& c, const Value& callee ) const final
{
auto lv = *FromValue< LocalVar >( 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 |
#include "builtins/builtins.h"
using namespace goose::sema;
namespace goose::builtins
{
class LocVarInvocationRule : public InvocationRule
{
public:
bool canBeInvoked( const Context& c, const Value& callee ) const final
{
auto lv = *FromValue< LocalVar >( callee );
auto val = BuildComputedValue(
lv.type(), GetAddrFromLocalVar( lv ), cir::Load( lv.type(), {} ) );
return sema::CanBeInvoked( c, val );
}
Value resolveInvocation( Context& c, LocationId locationId, const Value& callee,
const Term& args ) const final
{
auto lv = *FromValue< LocalVar >( callee );
auto val = BuildComputedValue(
lv.type(), GetAddrFromLocalVar( lv ), cir::Load( lv.type(), locationId ) );
return sema::GetInvocationRule( *c.env(), val )
->resolveInvocation( c, locationId, val, args );
}
};
void SetupLocalVarInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule(
ValueToEIR( Value( GetValueType< LocalVar >( ANYTERM( _ ) ), ANYTERM( _ ) ) ),
make_shared< LocVarInvocationRule >() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/localvar/localvar.cpp.
| ︙ | ︙ | |||
15 16 17 18 19 20 21 |
const Term& LocalVar::PatternAnyOfTypeT::GetPattern()
{
static auto pattern = GetValueType< LocalVar >( HOLE( "T"_sid ) );
return pattern;
}
| | > | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
const Term& LocalVar::PatternAnyOfTypeT::GetPattern()
{
static auto pattern = GetValueType< LocalVar >( HOLE( "T"_sid ) );
return pattern;
}
Value DeclareLocalVar( const Context& c, const Term& type, StringId name,
const optional< Value >& initializer, LocationId locId )
{
auto cfg = GetCFG( c );
if( !cfg )
return PoisonValue();
auto bb = cfg->currentBB();
if( !bb )
|
| ︙ | ︙ | |||
43 44 45 46 47 48 49 |
Value initResult;
{
DiagnosticsContext dc( 0, "When invoking _Initialize." );
if( initializer )
{
| | < | < | < | > | | | < | | | < | | < < | < < < | 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 |
Value initResult;
{
DiagnosticsContext dc( 0, "When invoking _Initialize." );
if( initializer )
{
initResult = InvokeOverloadSet( c, c.env()->extInitialize(),
MakeClosedTuple( ToValue( lv ).setLocationId( locId ), *initializer ), locId );
}
else
{
initResult = InvokeOverloadSet( c, c.env()->extInitialize(),
MakeClosedTuple( ToValue( lv ).setLocationId( locId ) ), locId );
}
}
if( !initResult.isPoison() )
{
DiagnosticsContext dc2( initResult.locationId(), "When invoking _DropValue." );
InvokeOverloadSet( c, c.env()->extDropValue(),
MakeClosedTuple( c.builder(), move( initResult ) ), initResult.locationId() );
}
auto locVar = ToValue( lv );
auto identity = AppendToVectorTerm( c.identity(), name );
c.env()->storeValue( identity, ANYTERM( _ ), ValueToEIR( locVar ) );
DeclareValue( c, locVar, lv.index() );
return locVar;
}
Value DeclareLocalVarWithTypeInference( const Context& c, const Term& typeTExpr, StringId name,
const Value& initVal, LocationId locId )
{
auto cfg = GetCFG( c );
if( !cfg )
return PoisonValue();
auto bb = cfg->currentBB();
if( !bb )
return PoisonValue();
auto type = InferTypeFromTExprAndInitializer( c, typeTExpr, initVal );
if( type.isPoison() )
return PoisonValue();
auto index = util::GenerateNewUID();
// Retrieve the texpr's location and set it on the inferred type. This way if an
// error occurs later with it, for instance when calling LowerTypeForRuntime on it during
// codegen, it will have a meaningful location for the error message to attach itself on.
LocalVar lv( name, ValueToEIR( type ), index );
bb->append( AllocVar( lv.type(), index, locId ) );
DiagnosticsContext dc( 0, "When invoking _Initialize." );
auto initResult = InvokeOverloadSet( c, c.env()->extInitialize(),
MakeClosedTuple( ToValue( lv ).setLocationId( locId ), initVal ) );
if( !initResult.isPoison() )
{
DiagnosticsContext dc2( initResult.locationId(), "When invoking _DropValue." );
InvokeOverloadSet(
c, c.env()->extDropValue(), MakeClosedTuple( c.builder(), move( initResult ) ) );
}
auto locVar = ToValue( lv );
if( name != ""_sid )
{
auto identity = AppendToVectorTerm( c.identity(), name );
c.env()->storeValue( identity, ANYTERM( _ ), ValueToEIR( locVar ) );
}
DeclareValue( c, locVar, lv.index() );
return locVar;
}
} // namespace goose::builtins
namespace goose::eir
{
const Term& Bridge< LocalVarType >::Type()
{
return TypeType();
}
Value Bridge< LocalVarType >::ToValue( const LocalVarType& t )
{
return Value( Type(), TVEC( TSID( ct_type ), TSID( local_var ), t.type() ) );
}
Value Bridge< LocalVarType >::ToValue( const Term& type )
{
return Value( Type(), TVEC( TSID( ct_type ), TSID( local_var ), type ) );
}
optional< LocalVarType > Bridge< LocalVarType >::FromValue( const Value& v )
{
auto result =
Decompose( v.val(), Vec( Lit( "ct_type"_sid ), Lit( "local_var"_sid ), SubTerm() ) );
if( !result )
return nullopt;
auto&& [type] = *result;
return LocalVarType( move( type ) );
}
|
| ︙ | ︙ | |||
178 179 180 181 182 183 184 |
if( !v.isConstant() )
{
// This is an abstract local variable value, this may happen when
// typechecking abstract arguments while resolving function typed parameters
return LocalVar( move( t->type() ) );
}
| | < < < < < | | | 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
if( !v.isConstant() )
{
// This is an abstract local variable value, this may happen when
// typechecking abstract arguments while resolving function typed parameters
return LocalVar( move( t->type() ) );
}
auto result = Decompose( v.val(), Vec( Val< StringId >(), Val< uint64_t >() ) );
if( !result )
return nullopt;
auto&& [name, index] = *result;
return LocalVar( name, move( t->type() ), index );
}
} // namespace goose::eir
|
Changes to bs/builtins/types/localvar/localvar.h.
1 2 3 4 5 6 7 8 9 10 11 12 |
#ifndef GOOSE_BUILTINS_TYPES_LOCALVAR_H
#define GOOSE_BUILTINS_TYPES_LOCALVAR_H
namespace goose::builtins
{
class LocalVar;
extern void SetupLocalVarInvocationRule( Env& e );
extern void SetupLocalVarTypeChecking( Env& e );
extern void SetupLocalVarInitialize( Env& e );
extern void SetupLocalVarDropValue( Env& 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 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 |
#ifndef GOOSE_BUILTINS_TYPES_LOCALVAR_H
#define GOOSE_BUILTINS_TYPES_LOCALVAR_H
namespace goose::builtins
{
class LocalVar;
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, LocationId locId );
Value DeclareLocalVarWithTypeInference( const Context& c, const Term& typeTExpr, StringId name,
const Value& initVal, LocationId locId );
class LocalVarType
{
public:
template< typename T >
LocalVarType( T&& type ) :
m_type( forward< T >( type ) )
{
}
const auto& type() const { return m_type; }
private:
Term m_type;
};
class LocalVar
{
public:
template< typename T >
LocalVar( StringId name, T&& type, uint32_t index ) :
m_name( name ),
m_type( forward< T >( type ) ),
m_index( index )
{
}
template< typename T >
LocalVar( T&& type ) :
LocalVar( "<unknown>"_sid, forward< T >( type ), ~0U )
{
}
auto name() const { return m_name; }
const auto& type() const { return m_type; }
auto& type() { return m_type; }
const auto& index() const { return m_index; }
struct PatternAny
{
static const Term& GetPattern();
};
struct PatternAnyOfTypeT
{
static const Term& GetPattern();
};
template< typename T > struct PatternOfType
{
static const Term& GetPattern()
{
static auto pattern = GetValueType< LocalVar >( GetValueType< T >() );
return pattern;
}
};
template< typename PP > struct Pattern
{
static auto GetPattern()
{
static auto pattern = GetValueType< LocalVar >( PP::GetPattern() );
return pattern;
}
};
private:
StringId m_name;
Term m_type;
uint32_t m_index = 0;
};
} // namespace goose::builtins
namespace goose::eir
{
template<> struct Bridge< builtins::LocalVarType >
{
static const Term& Type();
static Value ToValue( const builtins::LocalVarType& t );
static Value ToValue( const Term& type );
static optional< builtins::LocalVarType > FromValue( const Value& v );
};
template<> struct Bridge< builtins::LocalVar >
{
static Term Type( const Term& type );
static Value ToValue( const builtins::LocalVar& lv );
static optional< builtins::LocalVar > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/localvar/typecheck.cpp.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 |
auto localVarPattern = GetValueType< LocalVar >( ANYTERM( _ ) );
// LocalVar type checking against another LocalVar: unify their types.
e.typeCheckingRuleSet()->addTypeCheckingRule(
ParamPat( localVarPattern ),
| | < < < > | > | | > | | 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 |
auto localVarPattern = GetValueType< LocalVar >( ANYTERM( _ ) );
// LocalVar type checking against another LocalVar: unify their types.
e.typeCheckingRuleSet()->addTypeCheckingRule(
ParamPat( localVarPattern ),
ValueToEIR( ValuePattern( ANYTERM( _ ), localVarPattern, ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto lvarType =
*FromValue< LocalVarType >( *EIRToValue( EIRToValuePattern( lhs )->type() ) );
auto rhsVal = *EIRToValuePattern( rhs );
auto rvarType = *FromValue< LocalVarType >( *EIRToValue( rhsVal.type() ) );
for( auto&& [s, tcc] : Unify( lvarType.type(), rvarType.type(), tcc ) )
{
co_yield { ValueToEIR(
Value( ValueToEIR( ToValue( LocalVarType( s ) ) ), rhsVal.val() )
.setLocationId( rhsVal.locationId() ) ),
tcc };
}
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/lower.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupLower( Env& e )
{
// Default implementation of GetUnderlyingType():
// Do nothing.
| | | < < < | | < < < | | < < < < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupLower( Env& e )
{
// Default implementation of GetUnderlyingType():
// Do nothing.
RegisterBuiltinFunc< Intrinsic< Value( Value ) > >(
e, e.extGetUnderlyingType(), []( const Context& c, const Value& v ) { return v; } );
// Default implementation of LowerType():
// Do nothing.
RegisterBuiltinFunc< Intrinsic< Value( Value ) > >(
e, e.extLowerType(), []( const Context& c, const Value& v ) { return v; } );
// Default implementation of LowerValue():
// Do nothing.
RegisterBuiltinFunc< Intrinsic< Value( Value ) > >(
e, e.extLowerValue(), []( const Context& c, const Value& v ) { return v; } );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/overloadset/helpers.cpp.
| ︙ | ︙ | |||
53 54 55 56 57 58 59 |
case sema::Env::Status::AmbiguousMatch:
G_ERROR( format( "fatal: ambiguous match for overload set {}", name.str() ) );
}
return nullptr;
}
| > | > | > | > | | | 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 |
case sema::Env::Status::AmbiguousMatch:
G_ERROR( format( "fatal: ambiguous match for overload set {}", name.str() ) );
}
return nullptr;
}
Value InvokeOverloadSet(
const Context& c, const ptr< OverloadSet >& pOvlSet, Value args, LocationId loc )
{
assert( pOvlSet );
Context localC( c.env(), c.identity(), GetValueType< uint32_t >() );
localC.setBuilder( c.builder() );
execute::VM vm;
if( !args.isConstant() && cir::CanValueBeEagerlyEvaluated( args ) )
args = execute::Evaluate( args, vm );
if( args.isPoison() )
return PoisonValue();
auto val = ResolveInvocation(
localC, GetOverloadSetInvocationRule(), ToValue( pOvlSet ).setLocationId( loc ), args );
if( val.isConstant() || !cir::CanValueBeEagerlyEvaluated( val ) )
return val;
return execute::Evaluate( val, vm );
}
Value InvokeOverloadSet(
Context& c, const ptr< OverloadSet >& pOvlSet, Value args, LocationId loc )
{
assert( pOvlSet );
execute::VM vm;
if( !args.isConstant() && cir::CanValueBeEagerlyEvaluated( args ) )
args = execute::Evaluate( args, vm );
if( args.isPoison() )
return PoisonValue();
auto val = ResolveInvocation(
c, GetOverloadSetInvocationRule(), ToValue( pOvlSet ).setLocationId( loc ), args );
if( val.isConstant() || !cir::CanValueBeEagerlyEvaluated( val ) )
return val;
return execute::Evaluate( val, vm );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/overloadset/helpers.h.
1 2 3 4 5 6 7 8 9 10 11 12 |
#ifndef GOOSE_BUILTINS_TYPES_OVERLOADSET_HELPERS_H
#define GOOSE_BUILTINS_TYPES_OVERLOADSET_HELPERS_H
namespace goose::builtins
{
extern ptr< OverloadSet > CreateOverloadSet( Env& env, StringId name );
extern ptr< OverloadSet > GetOverloadSet( Env& env, StringId name );
extern ptr< OverloadSet > GetOrCreateOverloadSet( Env& env, StringId name );
extern Value InvokeOverloadSet( const Context& c, const ptr< OverloadSet >& pOvlSet, Value args,
LocationId loc = Location::Create( source_location::current() ) );
extern Value InvokeOverloadSet( Context& c, const ptr< OverloadSet >& pOvlSet, Value args,
LocationId loc = Location::Create( source_location::current() ) );
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#ifndef GOOSE_BUILTINS_TYPES_OVERLOADSET_HELPERS_H
#define GOOSE_BUILTINS_TYPES_OVERLOADSET_HELPERS_H
namespace goose::builtins
{
extern ptr< OverloadSet > CreateOverloadSet( Env& env, StringId name );
extern ptr< OverloadSet > GetOverloadSet( Env& env, StringId name );
extern ptr< OverloadSet > GetOrCreateOverloadSet( Env& env, StringId name );
extern Value InvokeOverloadSet( const Context& c, const ptr< OverloadSet >& pOvlSet, Value args,
LocationId loc = Location::Create( source_location::current() ) );
extern Value InvokeOverloadSet( Context& c, const ptr< OverloadSet >& pOvlSet, Value args,
LocationId loc = Location::Create( source_location::current() ) );
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/overloadset/invoke.cpp.
1 2 | #include "builtins/builtins.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 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 |
#include "builtins/builtins.h"
// #define OVL_TC_DEBUG
using namespace goose::sema;
namespace goose::builtins
{
class OverloadSetInvocationRule : public InvocationRule
{
public:
Value resolveInvocation(
Context& c, LocationId loc, const Value& callee, const Term& args ) const final
{
ProfileZoneScoped;
auto pOvlSet = *FromValue< ptr< OverloadSet > >( callee );
#if TRACY_ENABLE
stringstream sstr;
sstr << pOvlSet->identity();
ProfileZoneName( sstr.str().c_str(), sstr.str().size() );
#endif
if( auto ovl = pOvlSet->getResolutionFromCache( args ) )
return ovl->pInvRule->resolveInvocation( c, loc, *ovl->callee, args );
else
return resolve( c, loc, pOvlSet, args );
}
private:
Value resolve(
Context& c, LocationId loc, const ptr< OverloadSet >& pOvlSet, const Term& args ) const
{
const OverloadSet::Overload* bestOvl = nullptr;
optional< TypeCheckingContext > bestTCC;
optional< Term > bestSol;
{
ProfileZoneScopedN( "Overload resolution" );
bool ambiguous = false;
if( pOvlSet->verboseResolution() )
{
DiagnosticsManager::GetInstance().emitTraceMessage(
loc, std::format( "invoking overload set: {}", pOvlSet->identity() ) );
}
auto callPat = PrependToVectorTerm( args, HOLE( "_"_sid, "fwd"_sid ) );
TypeCheckingContext tcc( c );
for( auto&& [s, ovl, tcc] : pOvlSet->typeCheck( callPat, tcc ) )
{
if( tcc.numUnknownValues() )
{
if( pOvlSet->verboseResolution() )
{
DiagnosticsManager::GetInstance().emitTraceMessage(
ovl.callee ? ovl.callee->locationId() : 0,
format( "rejected candidate", tcc.score() ) );
}
continue;
}
auto subs = Substitute( s, tcc );
// Typechecking rules often end up stripping part of the original type,
// and we want to invoke the overload where these removals are minimized.
//
// Obvious example: if there is an overload that accepts a reference
// and one that accepts a value of the same type and we started with a
// reference, then we want to call the overload where the typechecking
// solution didn't strip the reference.
//
// So we add the weight of the original arguments to the cost,
// and remove the cost of the typechecking solution to account for that.
int32_t cost = tcc.cost();
cost += GetWeight( callPat );
cost -= GetWeight( subs );
tcc.setCost( cost );
if( pOvlSet->verboseResolution() )
{
DiagnosticsManager::GetInstance().emitTraceMessage(
ovl.callee ? ovl.callee->locationId() : 0,
format( "candidate score: {}", tcc.score() ) );
}
auto score = tcc.score();
if( bestTCC && score < bestTCC->score() )
continue;
auto pps = Postprocess( subs, tcc );
if( !pps )
continue;
if( bestTCC && score == bestTCC->score() )
{
ambiguous = true;
continue;
}
bestTCC = tcc;
bestSol = move( *pps );
bestOvl = &ovl;
ambiguous = false;
}
if( ambiguous )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage(
loc, "ambiguous function call." );
return PoisonValue();
}
if( !bestSol )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage(
loc, "function arguments mismatch." );
return PoisonValue();
}
#if defined( OVL_TC_DEBUG ) && !defined( NDEBUG )
bestTCC->DumpParamsTraces( cout );
cout << endl;
#endif
}
pOvlSet->addResolutionToCache( args, *bestOvl );
return bestOvl->pInvRule->invoke( c, loc, *bestOvl->callee, args, *bestSol, *bestTCC );
}
};
ptr< InvocationRule >& GetOverloadSetInvocationRule()
{
static ptr< InvocationRule > pRule = make_shared< OverloadSetInvocationRule >();
return pRule;
}
void SetupOverloadSetInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule(
ValueToEIR( Value( GetValueType< ptr< OverloadSet > >(), ANYTERM( _ ) ) ),
GetOverloadSetInvocationRule() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/overloadset/overloadset.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include "builtins/builtins.h"
using namespace goose::builtins;
bool goose::builtins::IsOverloadSet( const Value& os )
{
return os.type() == GetValueType< ptr< OverloadSet > >();
}
namespace goose::eir
{
const Term& Bridge< ptr< sema::OverloadSet > >::Type()
{
| > | | > | < < | | 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"
using namespace goose::builtins;
bool goose::builtins::IsOverloadSet( const Value& os )
{
return os.type() == GetValueType< ptr< OverloadSet > >();
}
namespace goose::eir
{
const Term& Bridge< ptr< sema::OverloadSet > >::Type()
{
static auto type =
ValueToEIR( Value( TypeType(), VEC( TSID( ct_type ), TSID( overloadset ) ) ) );
return type;
}
Value Bridge< ptr< sema::OverloadSet > >::ToValue( const ptr< sema::OverloadSet >& os )
{
return Value( Type(), TERM( static_pointer_cast< void >( os ) ) );
}
optional< ptr< sema::OverloadSet > > Bridge< ptr< sema::OverloadSet > >::FromValue(
const Value& v )
{
if( !IsOverloadSet( v ) )
return nullopt;
auto result = Decompose( v.val(), Val< ptr< void > >() );
if( !result )
return nullopt;
return static_pointer_cast< sema::OverloadSet >( result->get() );
}
} // namespace goose::eir
|
Changes to bs/builtins/types/overloadset/overloadset.h.
1 2 3 4 5 6 7 8 9 10 11 12 |
#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 );
| | < | | | 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 |
#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::builtins
namespace goose::eir
{
template<> struct Bridge< ptr< sema::OverloadSet > >
{
static const Term& Type();
static Value ToValue( const ptr< sema::OverloadSet >& os );
static optional< ptr< sema::OverloadSet > > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/overloadset/typecheck.cpp.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
{
// func type param / overloadset arg
e.typeCheckingRuleSet()->addTypeCheckingRule(
ParamPat( FuncTypePattern() ),
ValueToEIR( ValuePattern(
| < | < | | | < | < < < | < < | | | | | | | | | | | | | | | | | | | | > | | | | > | | | | | | | < | < < | < | | | < | < < < | < < | | | | | | | | | | | | | | | | | | | | | > | | | | > | | | | | | | | | 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 |
{
// func type param / overloadset arg
e.typeCheckingRuleSet()->addTypeCheckingRule(
ParamPat( FuncTypePattern() ),
ValueToEIR( ValuePattern(
TSID( constant ), GetValueType< ptr< sema::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 = BuildCallPatternFromFuncType( *EIRToValue( type ) );
auto rhsVal = *EIRToValue( 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 savedRHSSubContext = tcc.RHSSubContext();
auto localNSId = tcc.newNamespaceIndex();
tcc.RHSSubContext().namespaceIndex = localNSId;
for( auto&& [s, ovl, tcc] : os->typeCheck( callPat, tcc.flip() ) )
{
if( !ovl.callee )
continue;
// Restore the namespace
tcc.flip();
tcc.RHSSubContext() = savedRHSSubContext;
auto overload = ovl;
auto wrapped = WrapWithPostprocFunc( s,
[overload, localNSId, rhsLocId](
const Term& t, TypeCheckingContext tcc ) -> optional< Term >
{
tcc.RHSSubContext().namespaceIndex = localNSId;
auto func = overload.pInvRule->prepareFunc(
tcc.context(), rhsLocId, *overload.callee, t, tcc.flip() );
if( func.isPoison() )
return nullopt;
return ValueToEIR( move( func ) );
} );
co_yield { move( wrapped ), tcc };
}
} );
// tfunc type param / overloadset arg
e.typeCheckingRuleSet()->addTypeCheckingRule(
ValueToEIR( ValuePattern(
ANYTERM( _ ), TFuncTypeSigPattern( ANYTERM( _ ), ANYTERM( _ ) ), ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern(
TSID( constant ), GetValueType< ptr< sema::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 [tfType, _] = DecomposeTFTSig( type );
auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *EIRToValue( tfType ) );
assert( callPat );
auto rhsVal = *EIRToValue( 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 savedRHSSubContext = tcc.RHSSubContext();
auto localNSId = tcc.newNamespaceIndex();
tcc.RHSSubContext().namespaceIndex = localNSId;
for( auto&& [s, ovl, tcc] : os->typeCheck( *callPat, tcc.flip() ) )
{
if( !ovl.callee )
continue;
tcc.flip();
tcc.RHSSubContext() = savedRHSSubContext;
auto overload = ovl;
auto wrapped = WrapWithPostprocFunc( s,
[overload, localNSId, rhsLocId](
const Term& t, TypeCheckingContext tcc ) -> optional< Term >
{
tcc.RHSSubContext().namespaceIndex = localNSId;
auto func = overload.pInvRule->prepareFunc(
tcc.context(), rhsLocId, *overload.callee, t, tcc.flip() );
if( func.isPoison() )
return nullopt;
return ValueToEIR( move( func ) );
} );
co_yield { move( wrapped ), tcc };
}
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/param.h.
1 2 3 4 5 |
#ifndef GOOSE_BUILTINS_TYPES_PARAM_H
#define GOOSE_BUILTINS_TYPES_PARAM_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 |
#ifndef GOOSE_BUILTINS_TYPES_PARAM_H
#define GOOSE_BUILTINS_TYPES_PARAM_H
namespace goose::builtins
{
template< typename T, typename V > auto ParamPat( T&& type, V&& val )
{
// If the type is a tuple of type, convert it to the type of a tuple.
// (the type of a tuple is just a list of values, so just extract the value part of the
// tuple)
if( auto typeVal = EIRToValue( type ); typeVal && IsTuple( *typeVal ) )
return ValueToEIR(
Value( ValueToEIR( TupleOfTypesToTupleType( *typeVal ) ), HOLE( "_"_sid ) ) );
return ValueToEIR( Value( forward< T >( type ), forward< V >( val ) ) );
}
template< typename T > auto ParamPat( T&& type )
{
// If the type is a tuple of type, convert it to the type of a tuple.
// (the type of a tuple is just a list of values, so just extract the value part of the
// tuple)
if( auto typeVal = EIRToValue( type ); typeVal && IsTuple( *typeVal ) )
return ValueToEIR( ValuePattern( TSID( param ),
ValueToEIR( TupleOfTypesToTupleType( *typeVal ) ), HOLE( "_"_sid ) ) );
return ValueToEIR( ValuePattern( TSID( param ), forward< T >( type ), HOLE( "_"_sid ) ) );
}
// This special param pattern forces the argument to be passed as is without any typechecking.
struct ForwarderPattern
{
static auto GetPattern() { return HOLE( "_"_sid, "fwd"_sid ); }
};
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/predicates/predicates.cpp.
| ︙ | ︙ | |||
13 14 15 16 17 18 19 |
}
optional< ptr< Propositions > > GetTypePredicates( const Value& t )
{
if( !t.isType() )
return nullopt;
| | < < | < | < < < | < < | 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 |
}
optional< ptr< Propositions > > GetTypePredicates( const Value& t )
{
if( !t.isType() )
return nullopt;
auto vec = Decompose( t.val(), Val< pvec >() );
if( !vec )
return nullopt;
if( vec->get()->terms().size() < 3 )
return nullopt;
auto result = Decompose(
vec->get()->terms()[1], Vec( Lit( "predicates"_sid ), Val< ptr< void > >() ) );
if( !result )
return nullopt;
auto&& [pt] = *result;
return static_pointer_cast< Propositions >( pt );
}
Term InjectPredicatesIntoStdType( const Term& t, const ptr< Propositions >& predicates )
{
Term result( t );
auto vec = Decompose( t, Val< pvec >() );
assert( vec );
assert( vec->get()->terms().size() >= 3 );
// We need to copy vec, otherwise we're actually going to change the original type
auto newVec = make_shared< Vector >( *vec->get() );
|
| ︙ | ︙ | |||
81 82 83 84 85 86 87 |
const auto& vec = *get< pvec >( identity );
auto result = make_shared< Vector >();
result->reserve( vec.terms().size() );
for( auto&& t : vec.terms() )
{
| | < < < < < | | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
const auto& vec = *get< pvec >( identity );
auto result = make_shared< Vector >();
result->reserve( vec.terms().size() );
for( auto&& t : vec.terms() )
{
auto decomp = Decompose( t, Vec( Lit( "pred_hash"_sid ), Val< uint64_t >() ) );
if( decomp )
continue;
const auto* pNestedVec = get_if< pvec >( &t );
if( pNestedVec )
result->append( StripPredicateHashesFromIdentity( t ) );
else
result->append( t );
}
return result;
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/predicates/predicates.h.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 |
extern optional< ptr< Propositions > > GetTypePredicates( const Value& t );
extern Term InjectPredicatesIntoStdType( const Term& t, const ptr< Propositions >& predicates );
extern bool ParseTypePredicates( const Context& c, const Value& type );
extern Term AppendPredicatesHashToIdentity( const Term& identity, uint64_t hash );
extern Term StripPredicateHashesFromIdentity( const Term& identity );
| | | 10 11 12 13 14 15 16 17 18 19 |
extern optional< ptr< Propositions > > GetTypePredicates( const Value& t );
extern Term InjectPredicatesIntoStdType( const Term& t, const ptr< Propositions >& predicates );
extern bool ParseTypePredicates( const Context& c, const Value& type );
extern Term AppendPredicatesHashToIdentity( const Term& identity, uint64_t hash );
extern Term StripPredicateHashesFromIdentity( const Term& identity );
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/predicates/typecheck.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::cir;
namespace goose::builtins
{
void SetupPredicatesTypeChecking( Env& e )
{
| | < < | < < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::cir;
namespace goose::builtins
{
void SetupPredicatesTypeChecking( Env& e )
{
e.typeCheckingRuleSet()->addTypeCheckingRule( VEC( TSID( predicates ), ANYTERM( _ ) ),
VEC( TSID( predicates ), ANYTERM( _ ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{ co_yield { rhs, tcc }; } );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/pretty.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::sema;
using namespace goose::builtins;
namespace goose::builtins
{
void SetupBasicTypesPrettyPrinting()
{
auto& pp = PrettyPrinter::GetInstance();
| | > | > > > > | | | | | | > | > | | > > > > > | > | > > > > | > > > > > | > | > > > > | > > > > > | > > > > | | > | 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 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::sema;
using namespace goose::builtins;
namespace goose::builtins
{
void SetupBasicTypesPrettyPrinting()
{
auto& pp = PrettyPrinter::GetInstance();
pp.addRule( TypeType(),
[&]( auto&& out, auto&& t )
{
out << "TypeType";
return true;
} );
pp.addRule( ValueToEIR( ValuePattern( TSID( constant ), ANYTERM( _ ), ANYTERM( _ ) ) ),
[&]( auto&& out, auto&& t )
{
auto v = *EIRToValue( t );
auto ty = EIRToValue( v.type() );
out << "constant[ ";
pp.print( out, ty ? ty->val() : v.type() );
out << " ]{ ";
pp.print( out, v.val() );
out << " }";
return true;
} );
pp.addRule( ValueToEIR( ValuePattern( TSID( constant ), TypeType(), ANYTERM( _ ) ) ),
[&]( auto&& out, auto&& t )
{
auto v = *EIRToValue( t );
out << "type[ ";
pp.print( out, v.val() );
out << " ]";
return true;
} );
pp.addRule( ValueToEIR( ValuePattern( TSID( computed ), ANYTERM( _ ), ANYTERM( _ ) ) ),
[&]( auto&& out, auto&& t )
{
auto v = *EIRToValue( t );
auto ty = EIRToValue( v.type() );
out << "value[ ";
pp.print( out, ty ? ty->val() : v.type() );
out << " ]";
if( v.cir() )
{
out << "{ ";
for( const auto& instr : *v.cir() )
out << instr << ' ';
out << "}";
}
return true;
} );
pp.addRule( ValueToEIR( ValuePattern( TSID( constant ),
ValueToEIR( Value( TypeType(), VEC( TSID( decl ), ANYTERM( _ ) ) ) ),
ANYTERM( _ ) ) ),
[&]( auto&& out, auto&& t )
{
auto decl = *FromValue< Decl >( *EIRToValue( t ) );
out << "decl(" << decl.type() << ", " << decl.name() << ")";
return true;
} );
pp.addRule( ValueToEIR( ValuePattern( TSID( param ), ANYTERM( _ ), ANYTERM( _ ) ) ),
[&]( auto&& out, auto&& t )
{
auto v = *EIRToValuePattern( t );
auto ty = EIRToValue( v.type() );
out << "param[ ";
pp.print( out, ty ? ty->val() : v.type() );
out << " ]{ ";
pp.print( out, v.val() );
out << " }";
return true;
} );
pp.addRule(
ValueToEIR( ValuePattern( TSID( constant ), BFuncTypePattern(), ANYTERM( _ ) ) ),
[&]( auto&& out, auto&& t )
{
out << "builtin_func";
auto v = *EIRToValue( t );
auto loc = Location::Get( v.locationId() );
if( !loc )
return true;
out << '@' << loc->filename() << ':' << loc->line();
return true;
} );
pp.addRule(
ValueToEIR( ValuePattern( TSID( constant ), EagerBFuncTypePattern(), ANYTERM( _ ) ) ),
[&]( auto&& out, auto&& t )
{
out << "eager_builtin_func";
auto v = *EIRToValue( t );
auto loc = Location::Get( v.locationId() );
if( !loc )
return true;
out << '@' << loc->filename() << ':' << loc->line();
return true;
} );
pp.addRule( GetValueType< bool >(),
[&]( auto&& out, auto&& t )
{
out << "bool";
return true;
} );
pp.addRule( MkStdType( TSID( rt_type ), TSID( bool ) ),
[&]( auto&& out, auto&& t )
{
out << "bool";
return true;
} );
pp.addRule( GetValueType< string >(),
[&]( auto&& out, auto&& t )
{
out << "ct_string";
return true;
} );
pp.addRule( MkStdType( TSID( ct_type ), TSID( ct_string ) ),
[&]( auto&& out, auto&& t )
{
out << "ct_string";
return true;
} );
pp.addRule( GetValueType< BigInt >(),
[&]( auto&& out, auto&& t )
{
out << "ct_int";
return true;
} );
pp.addRule( MkStdType( TSID( ct_type ), TSID( ct_int ) ),
[&]( auto&& out, auto&& t )
{
out << "ct_int";
return true;
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/propositions/drop.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupPropositionsDropValue( Env& e )
{
// When a boolean is dropped into Propositions, it is appended as a new proposition.
| | > | > | | 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"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupPropositionsDropValue( Env& e )
{
// When a boolean is dropped into Propositions, it is appended as a new proposition.
RegisterBuiltinFunc< Intrinsic< void( TypeWrapper< ptr< Propositions > >, bool ) > >( e,
e.extDropValue(),
[]( const Context& c, const Value& p, const Value& v )
{
auto props = *FromValue< TypeWrapper< ptr< Propositions > > >( p );
props->append( v );
} );
// When anything else is dropped into Propositions, complain if we're only supposed
// to have bools.
RegisterBuiltinFunc< Intrinsic< void( TypeWrapper< ptr< Propositions > >, Value ) > >( e,
e.extDropValue(),
[]( const Context& c, const Value& p, const Value& v )
{
auto props = *FromValue< TypeWrapper< ptr< Propositions > > >( p );
props->poison();
DiagnosticsManager::GetInstance().emitErrorMessage( v.locationId(),
"only boolean expressions are allowed in proposition lists.", 0 );
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/propositions/propositions.cpp.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 |
if( m_unparsedPropositions.empty() )
return true;
Context localContext( c.env(), identity() );
// We don't have a propositions builder class, because we don't need any specific logic atm.
// We just want to be able to override DropValue() in the context of parsing propositions
| | | > | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
if( m_unparsedPropositions.empty() )
return true;
Context localContext( c.env(), identity() );
// We don't have a propositions builder class, because we don't need any specific logic atm.
// We just want to be able to override DropValue() in the context of parsing propositions
// to append any dropped boolean expression as a proposition, and politely reject anything
// else as contextually invalid.
localContext.setBuilder(
ToValue( TypeWrapper< ptr< Propositions > >( shared_from_this() ) ) );
for( auto&& toks : m_unparsedPropositions )
{
auto tokProvider = lex::MakeVectorAdapter( toks );
auto r = make_shared< parse::Resolver >( tokProvider, localContext );
parse::Parser p( r );
p.parseSequence();
|
| ︙ | ︙ | |||
49 50 51 52 53 54 55 |
/*verify::Propositions pv( c );
if( !pv.addPropositions( m_propositions ) )
return false;
return pv.verify();*/
return true;
}
| | | 50 51 52 53 54 55 56 57 |
/*verify::Propositions pv( c );
if( !pv.addPropositions( m_propositions ) )
return false;
return pv.verify();*/
return true;
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/propositions/propositions.h.
1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_BUILTINS_TYPE_PROPOSITIONS_H
#define GOOSE_BUILTINS_TYPE_PROPOSITIONS_H
namespace goose::builtins
{
void SetupPropositionsDropValue( Env& e );
using UnparsedPropositions = llvm::SmallVector< vector< TermLoc >, 8 >;
| | > | | | | | < | > > | | < | > > | | | < | | < | | > | | | | | | | | < < < | | < | | | < | | | | | | | | 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 |
#ifndef GOOSE_BUILTINS_TYPE_PROPOSITIONS_H
#define GOOSE_BUILTINS_TYPE_PROPOSITIONS_H
namespace goose::builtins
{
void SetupPropositionsDropValue( Env& e );
using UnparsedPropositions = llvm::SmallVector< vector< TermLoc >, 8 >;
class Propositions : public LifeCycleManager< Propositions >,
public enable_shared_from_this< Propositions >
{
public:
Propositions() = default;
Propositions( const Propositions& ) = default;
Propositions( ValueVec&& props ) :
m_propositions( move( props ) )
{
}
Propositions( const ValueVec& props ) :
m_propositions( props )
{
}
size_t hash() const;
bool parse( const Context& c );
template< typename T > void setIdentity( T&& identity );
const auto& identity() const { return m_identity; }
template< typename T > void append( T&& val );
void append( Propositions&& props )
{
m_unparsedPropositions.reserve(
m_unparsedPropositions.size() + props.m_unparsedPropositions.size() );
for( auto&& x : props.m_unparsedPropositions )
m_unparsedPropositions.emplace_back( move( x ) );
}
// Used to mark the proposition list as poisoned if something goes wrong during
// its parsing.
void poison() { m_poison = true; }
auto& unparsed() { return m_unparsedPropositions; }
template< typename T > void setUnparsedProps( T&& toks );
const auto& props() const { return m_propositions; }
bool empty() const { return m_propositions.empty(); }
template< typename F > void forEach( F&& func );
private:
ValueVec m_propositions;
UnparsedPropositions m_unparsedPropositions;
Term m_identity;
mutable optional< size_t > m_hash;
bool m_poison = false;
};
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/propositions/propositions.inl.
1 2 3 4 5 |
#ifndef GOOSE_BUILTINS_TYPE_PROPOSITIONS_INL
#define GOOSE_BUILTINS_TYPE_PROPOSITIONS_INL
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 |
#ifndef GOOSE_BUILTINS_TYPE_PROPOSITIONS_INL
#define GOOSE_BUILTINS_TYPE_PROPOSITIONS_INL
namespace goose::builtins
{
template< typename T > void Propositions::setIdentity( T&& identity )
{
m_identity = forward< T >( identity );
}
template< typename T > void Propositions::append( T&& val )
{
m_propositions.emplace_back( forward< T >( val ) );
}
template< typename T > void Propositions::setUnparsedProps( T&& toks )
{
m_unparsedPropositions = forward< T >( toks );
}
template< typename F > void Propositions::forEach( F&& func )
{
for( auto&& v : m_propositions )
func( v );
}
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/reference/init.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupReferenceInitialize( Env& e )
{
| | > | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupReferenceInitialize( Env& e )
{
RegisterBuiltinFunc< Intrinsic< Value(
CustomPattern< Value,
ReferenceType::PatternMutableOf< ReferenceType::PatternXOfTypeT > >,
CustomPattern< Value, ReferenceType::PatternXOfTypeT > ) > >( e, e.extInitialize(),
[]( auto&& c, const Value& r, const Value& initVal )
{
G_VAL_ASSERT( r, !r.isConstant() );
auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
return BuildComputedValue( GetValueType< void >(), initVal, r,
Store( refType.type(), initVal.locationId(), r.locationId() ) );
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/reference/lower.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::builtins;
namespace goose::builtins
{
void SetupReferenceLowering( Env& 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 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::builtins;
namespace goose::builtins
{
void SetupReferenceLowering( Env& e )
{
RegisterBuiltinFunc<
Intrinsic< Value( TypePatternParam< Value, ReferenceType::PatternAny > ) > >( e,
e.extLowerType(),
[]( const Context& c, const Value& refType )
{
auto rt = *FromValue< ReferenceType >( refType );
auto loweredType = LowerType( c, *EIRToValue( rt.type() ) );
if( !loweredType || loweredType->isPoison() )
return PoisonValue();
return ToValue( PointerType( ValueToEIR( *loweredType ) ) );
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/reference/parse.cpp.
| ︙ | ︙ | |||
27 28 29 30 31 32 33 |
dm.emitSyntaxErrorMessage( lhs.locationId(),
"expected an access specifier or a template variable.", 0 );
return PoisonValue();
}
if( !IsTExpr( rhs ) && !IsType( p.context(), rhs ) )
{
| | < | | | 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 |
dm.emitSyntaxErrorMessage( lhs.locationId(),
"expected an access specifier or a template variable.", 0 );
return PoisonValue();
}
if( !IsTExpr( rhs ) && !IsType( p.context(), rhs ) )
{
dm.emitSyntaxErrorMessage( rhs.locationId(), "expected a type.", 0 );
return PoisonValue();
}
// If the accessSpecifier is a TVar, turn it into a TDecl, so we can constrain it to
// the AccessSpecifier type. Otherwise, it woud be possible to do all kind of
// funky stuff to create refs with pretty much any old value of any type as
// the access level.
if( IsTVar( lhs ) )
{
auto tv = *FromValue< TVar >( lhs );
lhs = ToValue( TDecl( GetValueType< AccessSpecifier >(), tv.name() ) )
.setLocationId( lhs.locationId() );
}
ReferenceType rt( ValueToEIR( rhs ), ValueToEIR( lhs ) );
return ToDeclValue( rt );
} );
RegisterRule( e, "ref"_sid, move( r ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/reference/reference.cpp.
| ︙ | ︙ | |||
22 23 24 25 26 27 28 |
return nullopt;
return FromValue< Lifetime >( *ltVal );
}
bool IsReferenceType( const Term& t )
{
| | < | < < < < > | > | > | > | | 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 |
return nullopt;
return FromValue< Lifetime >( *ltVal );
}
bool IsReferenceType( const Term& t )
{
auto result = Decompose(
EIRToValue( t )->val(), Vec( Lit( "reference"_sid ), SubTerm(), SubTerm() ) );
return !!result;
}
bool IsMutReferenceType( const ReferenceType& rt )
{
auto asVal = EIRToValue( rt.accessSpec() );
if( !asVal )
return false;
auto as = FromValue< AccessSpecifier >( *asVal );
if( !as )
return false;
return as->mode() == TSID( mut );
}
const Term& ReferenceType::PatternAny::GetPattern()
{
static auto pattern =
ValueToEIR( ToValue( ReferenceType( HOLE( "_"_sid ), HOLE( "_"_sid ) ) ) );
return pattern;
}
const Term& ReferenceType::PatternAnyTRef::GetPattern()
{
static auto pattern = ValueToEIR( Value( GetValueType< ReferenceType >(),
TVecToEIR( Vector::Make( TSID( reference ), HOLE( "_"_sid ), HOLE( "_"_sid ) ) ) ) );
return pattern;
}
const Term& ReferenceType::PatternAnyMutable::GetPattern()
{
static auto pattern =
ValueToEIR( ToValue( ReferenceType( HOLE( "_"_sid ), MutAccessSpecifier() ) ) );
return pattern;
}
const Term& ReferenceType::PatternAnyMutableOfTypeT::GetPattern()
{
static auto pattern = ValueToEIR(
ToValue( ReferenceType( HOLE( "T"_sid, "tvar"_sid ), MutAccessSpecifier() ) ) );
return pattern;
}
const Term& ReferenceType::PatternXOfTypeT::GetPattern()
{
static auto pattern =
ValueToEIR( ToValue( ReferenceType( HOLE( "T"_sid, "fwd"_sid ), HOLE( "X"_sid ) ) ) );
return pattern;
}
// Returns an instruction that computes the address of whatever's contained in the locvar.
cir::Instruction GetAddrFromLocalVar( const LocalVar& lv )
{
// TODO LOC: may need loc in LocalVar
|
| ︙ | ︙ | |||
108 109 110 111 112 113 114 |
}
const Term& TempAccessSpecifier()
{
static auto al = ValueToEIR( ToValue( AccessSpecifier( "temp"_sid ) ) );
return al;
}
| | | < | < < < < | < < < < < < > | | < < < < < | | 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 |
}
const Term& TempAccessSpecifier()
{
static auto al = ValueToEIR( ToValue( AccessSpecifier( "temp"_sid ) ) );
return al;
}
} // namespace goose::builtins
namespace goose::eir
{
const Term& Bridge< ReferenceType >::Type()
{
return TypeType();
}
Value Bridge< ReferenceType >::ToDeclValue( const ReferenceType& t )
{
return Value( Type(), TVEC( TSID( reference ), t.accessSpec(), t.type() ) );
}
optional< ReferenceType > Bridge< ReferenceType >::FromDeclValue( const Value& v )
{
auto tv = TVecFromEIR( v.val() );
if( !tv )
return FromValue( v );
auto result =
Decompose( tv->content(), Vec( Lit( "reference"_sid ), SubTerm(), SubTerm() ) );
if( !result )
return nullopt;
auto&& [bhv, type] = *result;
return ReferenceType( move( type ), move( bhv ) );
}
Value Bridge< ReferenceType >::ToValue( const ReferenceType& t )
{
return Value( Type(), VEC( TSID( reference ), t.accessSpec(), t.type() ) );
}
optional< ReferenceType > Bridge< ReferenceType >::FromValue( const Value& v )
{
auto result = Decompose( v.val(), Vec( Lit( "reference"_sid ), SubTerm(), SubTerm() ) );
if( !result )
return nullopt;
auto&& [bhv, type] = *result;
return ReferenceType( move( type ), move( bhv ) );
}
const Term& Bridge< AccessSpecifier >::Type()
{
static auto type =
ValueToEIR( Value( TypeType(), TVEC( TSID( ct_type ), TSID( access_specifier ) ) ) );
return type;
}
Value Bridge< AccessSpecifier >::ToValue( const AccessSpecifier& a )
{
return Value( Type(), TVEC( a.mode(), a.lifetime() ) );
}
optional< AccessSpecifier > Bridge< AccessSpecifier >::FromValue( const Value& v )
{
auto result = Decompose( v.val(), Vec( SubTerm(), SubTerm() ) );
if( !result )
return nullopt;
auto&& [mode, lifetime] = *result;
return AccessSpecifier( mode, lifetime );
}
} // namespace goose::eir
|
Changes to bs/builtins/types/reference/reference.h.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
extern const Term& MutAccessSpecifier();
extern const Term& ConstAccessSpecifier();
extern const Term& TempAccessSpecifier();
class AccessSpecifier
{
| | | | | < | > > | > | > | | | | | | | | | | < | > > | > | | | | | | | | | | | | | | | | | | | | | | | | < | | | > | | | | | < | | | > | | | | | > | | < | | | | | < | | | > | | | | | < | | | > | | | | | | | | | | | < | < | | | 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 |
extern const Term& MutAccessSpecifier();
extern const Term& ConstAccessSpecifier();
extern const Term& TempAccessSpecifier();
class AccessSpecifier
{
public:
AccessSpecifier( const Term& mode, const Term& lifetime = cir::Lifetime::Unspecified() ) :
m_mode( mode ),
m_lifetime( lifetime )
{
}
const auto& mode() const { return m_mode; }
const auto& lifetime() const { return m_lifetime; }
auto& lifetime() { return m_lifetime; }
private:
// Either of these can be TVars, so we store them as Terms.
Term m_mode;
Term m_lifetime;
};
class ReferenceType
{
public:
template< typename T, typename B >
ReferenceType( T&& type, B&& bhv ) :
m_type( forward< T >( type ) ),
m_accessSpec( bhv )
{
}
const auto& type() const { return m_type; }
const auto& accessSpec() const { return m_accessSpec; }
auto& type() { return m_type; }
optional< cir::Lifetime > lifetime() const;
struct PatternAny
{
static const Term& GetPattern();
};
struct PatternAnyTRef
{
static const Term& GetPattern();
};
struct PatternAnyMutable
{
static const Term& GetPattern();
};
struct PatternAnyMutableOfTypeT
{
static const Term& GetPattern();
};
struct PatternXOfTypeT
{
static const Term& GetPattern();
};
template< typename T > struct PatternMutableOfType
{
static const Term& GetPattern()
{
static auto pattern = ValueToEIR(
ToValue( ReferenceType{ GetValueType< T >(), MutAccessSpecifier() } ) );
return pattern;
}
};
template< typename T > struct PatternConstOfType
{
static const Term& GetPattern()
{
static auto pattern = ValueToEIR(
ToValue( ReferenceType{ GetValueType< T >(), ConstAccessSpecifier() } ) );
return pattern;
}
};
template< typename PP > struct PatternAnyOf
{
static const Term& GetPattern()
{
static auto pattern =
ValueToEIR( ToValue( ReferenceType{ PP::GetPattern(), HOLE( "_"_sid ) } ) );
return pattern;
}
};
template< typename PP > struct PatternMutableOf
{
static const Term& GetPattern()
{
static auto pattern = ValueToEIR(
ToValue( ReferenceType{ PP::GetPattern(), MutAccessSpecifier() } ) );
return pattern;
}
};
template< typename PP > struct PatternConst
{
static auto GetPattern()
{
static auto pattern = ValueToEIR(
ToValue( ReferenceType{ PP::GetPattern(), ConstAccessSpecifier() } ) );
return pattern;
}
};
private:
Term m_type;
// Not an enum because we want to be able
// to have holes there and it's a pain in
// the ass
Term m_accessSpec;
};
extern bool IsMutReferenceType( const ReferenceType& rt );
extern cir::Instruction GetAddrFromLocalVar( const LocalVar& lv );
extern Value BuildLocalVarMutRef( const LocalVar& lv );
} // namespace goose::builtins
namespace goose::eir
{
template<> struct Bridge< builtins::ReferenceType >
{
static const Term& Type();
static Value ToDeclValue( const builtins::ReferenceType& t );
static optional< builtins::ReferenceType > FromDeclValue( const Value& v );
static Value ToValue( const builtins::ReferenceType& t );
static optional< builtins::ReferenceType > FromValue( const Value& v );
};
template<> struct Bridge< builtins::AccessSpecifier >
{
static const Term& Type();
static Value ToValue( const builtins::AccessSpecifier& t );
static optional< builtins::AccessSpecifier > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/reference/typecheck.cpp.
| ︙ | ︙ | |||
13 14 15 16 17 18 19 |
auto ltype = EIRToValuePattern( lhs )->type();
auto lvval = *EIRToValue( rhs );
auto locvar = FromValue< LocalVar >( lvval );
if( !locvar )
co_return;
| | | | | | | > | > | | | | | < < < | < < | | | | | | | | | | | | > | | | < | | | | | | | | | | | | | < < < > | > | | > > | | | | | | < < < < < < < < < < < < < < | | > > | > > | < < < | | < < | < < < | < < | < < < | < | < < < | < < | < < | < < < | < < < | < | > < | < | < < < | < | | | < < < | | | | | | | | | | > | | | < < < | < < < | < < > | 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 |
auto ltype = EIRToValuePattern( lhs )->type();
auto lvval = *EIRToValue( rhs );
auto locvar = FromValue< LocalVar >( lvval );
if( !locvar )
co_return;
auto ref = ValueToEIR(
ToValue( BuildLocalVarMutRef( *locvar ) ).setLocationId( lvval.locationId() ) );
co_yield TypeCheck( lhs, ref, tcc );
}
TCGen TypeCheckingDereference( const Term& lhs, const Term& rhs, TypeCheckingContext tcc )
{
auto refVal = EIRToValue( rhs );
if( !refVal )
co_return;
auto refType = FromValue< ReferenceType >( *EIRToValue( refVal->type() ) );
if( !refType )
co_return;
auto loc = refVal->locationId();
auto content = ValueToEIR(
BuildComputedValue( refType->type(), *refVal, cir::Load( refType->type(), loc ) )
.setLocationId( loc ) );
// TypeCheck the param with the ref's content
co_yield TypeCheck( lhs, move( content ), tcc );
}
TCGen TypeCheckingBuildTempRef(
const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc )
{
auto lRefType =
*FromValue< ReferenceType >( *EIRToValue( EIRToValuePattern( lhs )->type() ) );
if( IsMutReferenceType( lRefType ) )
co_return;
auto lhsPat = ValueToEIR( ValuePattern( TSID( param ), lRefType.type(), HOLE( "_"_sid ) ) );
for( auto&& [s, tcc] : TypeCheck( lhsPat, rhs, tcc ) )
{
auto wrapped = WrapWithPostprocFunc( VEC( s, rhs ),
[lhs]( const Term& t, TypeCheckingContext tcc ) -> optional< Term >
{
auto result = Decompose( t, Vec( SubTerm(), SubTerm() ) );
assert( result );
auto&& [arg, rhs] = *result;
auto val = *EIRToValue( arg );
ReferenceType rt( val.type(), TempAccessSpecifier() );
auto rhsVal = *EIRToValue( rhs );
auto cfg = GetCFG( tcc.context() );
if( !cfg )
return nullopt;
// TODO create an ext point for this
auto loc = rhsVal.locationId();
auto tempIndex = util::GenerateNewUID();
return ValueToEIR( BuildComputedValue(
ValueToEIR( ToValue( rt ) ), rhsVal, DataPathOf( tempIndex, loc ) )
.setLocationId( loc ) );
} );
// Override the weight because we don't want this solution to count more than directly
// using the value without wrapping it into a tempref
SetWeight( wrapped, GetWeight( rhs ) - 1 );
co_yield { move( wrapped ), tcc };
}
}
} // namespace
namespace goose::builtins
{
void SetupReferenceTypeChecking( Env& e )
{
auto refTypePattern = ValueToEIR( Value( GetValueType< ReferenceType >(),
TVEC( TSID( reference ), ANYTERM( _ ), ANYTERM( _ ) ) ) );
auto refRefTypePattern = ValueToEIR( Value( GetValueType< ReferenceType >(),
TVEC( TSID( reference ), ANYTERM( _ ), refTypePattern ) ) );
auto refTypePatternConstant = ValueToEIR( Value( GetValueType< ReferenceType >(),
TVEC( TSID( reference ), ConstAccessSpecifier(), ANYTERM( _ ) ) ) );
auto refTypePatternMutable = ValueToEIR( Value( GetValueType< ReferenceType >(),
TVEC( TSID( reference ), MutAccessSpecifier(), ANYTERM( _ ) ) ) );
auto refTypePatternTemporary = ValueToEIR( Value( GetValueType< ReferenceType >(),
TVEC( TSID( reference ), TempAccessSpecifier(), ANYTERM( _ ) ) ) );
auto localVarPattern = GetValueType< LocalVar >( ANYTERM( _ ) );
// Reference type checking rule.
e.typeCheckingRuleSet()->addTypeCheckingRule(
ParamPat( refTypePattern ),
ValueToEIR( ValuePattern( ANYTERM( _ ), refTypePattern, ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto lRefType =
*FromValue< ReferenceType >( *EIRToValue( EIRToValuePattern( lhs )->type() ) );
auto rhsVal = *EIRToValuePattern( rhs );
auto rRefType = *FromValue< ReferenceType >( *EIRToValue( rhsVal.type() ) );
// Type check the accessSpecs
for( auto&& [b, tcc] :
TypeCheck( lRefType.accessSpec(), rRefType.accessSpec(), tcc ) )
{
// Check the types
for( auto&& [t, tcc] : TypeCheck( lRefType.type(), rRefType.type(), tcc ) )
{
co_yield { ValueToEIR( ValuePattern( rhsVal.sort(),
ValueToEIR( ToValue( ReferenceType( t, b ) ) ),
rhsVal.val() )
.setLocationId( rhsVal.locationId() ) ),
tcc };
}
}
} );
// mut -> const reference type checking rule.
e.typeCheckingRuleSet()->addTypeCheckingRule( ConstAccessSpecifier(), MutAccessSpecifier(),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{ co_yield { lhs, tcc }; } );
// mut -> const reference unification rule.
e.typeCheckingRuleSet()->addUnificationRule( ConstAccessSpecifier(), MutAccessSpecifier(),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{ co_yield { lhs, tcc }; } );
// temp -> const reference type checking rule.
e.typeCheckingRuleSet()->addTypeCheckingRule( ConstAccessSpecifier(), TempAccessSpecifier(),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{ co_yield { lhs, tcc }; } );
// temp -> const reference unification rule.
e.typeCheckingRuleSet()->addUnificationRule( ConstAccessSpecifier(), TempAccessSpecifier(),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{ co_yield { lhs, tcc }; } );
// Reference type checking against a param (implicit dereferencing):
// Unify the referenced value with the param.
e.typeCheckingRuleSet()->addTypeCheckingRule(
ParamPat( ANYTERM( _ ) ),
ValueToEIR( ValuePattern( ANYTERM( _ ), refTypePattern, ANYTERM( _ ) ) ),
TypeCheckingDereference );
// References versus TVars: a tvar should never directly bind to a reference.
// Therefore, when unifying a TVar against a reference, we strip the reference.
// And when typechecking a TVar typed parameter against a reference, we dereference.
e.typeCheckingRuleSet()->addTypeCheckingRule(
ParamPat( HOLE( ""_sid, "tvar"_sid ) ),
ValueToEIR( ValuePattern( ANYTERM( _ ), refTypePattern, ANYTERM( _ ) ) ),
TypeCheckingDereference );
// Unifying a TVar with a reference type isn't allowed.
// A reference whose type is a TVar, on the other hand,
// can be unified with another reference type.
e.typeCheckingRuleSet()->addUnificationRule(
HOLE( ""_sid, "tvar"_sid ),
refTypePattern,
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{ co_return; } );
// LocalVar type checking against a param (implicit referencing)
e.typeCheckingRuleSet()->addTypeCheckingRule(
ValueToEIR( ValuePattern( ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern( ANYTERM( _ ), localVarPattern, ANYTERM( _ ) ) ),
LocVarToRef );
// The rule above is ambiguous with the WrappedArg rule when passing a locvar
// to a wrappedarg param. So we add a disambiguation rule that prioritizes
// turning the locvar into a ref in that case (since typically we don't want to manipulate
// 0_locvars directly, so anything that wants a wrapped value probably wants whatever's in
// the var)
e.typeCheckingRuleSet()->addTypeCheckingRule(
ValueToEIR( ValuePattern(
TSID( param ), ValueToEIR( BuildWrappedType( ANYTERM( _ ) ) ), ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern( ANYTERM( _ ), localVarPattern, ANYTERM( _ ) ) ),
LocVarToRef );
// Typechecking ref of ref against a ref: dereference the first ref and attempt to typecheck
// it against the other ref
e.typeCheckingRuleSet()->addTypeCheckingRule(
ParamPat( refTypePattern ),
ValueToEIR( ValuePattern( ANYTERM( _ ), refRefTypePattern, ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto ltype = EIRToValuePattern( lhs )->type();
auto rrefVal = *EIRToValue( rhs );
auto rrType = FromValue< ReferenceType >( *EIRToValue( rrefVal.type() ) );
if( !rrType )
co_return;
auto loc = rrefVal.locationId();
auto content = ValueToEIR(
BuildComputedValue( rrType->type(), rrefVal, cir::Load( rrType->type(), loc ) )
.setLocationId( loc ) );
co_yield TypeCheck( lhs, content, tcc );
} );
// Implicit referencing of non-variables against a non mutable ref: build a tempref
e.typeCheckingRuleSet()->addTypeCheckingRule(
ValueToEIR( ValuePattern( ANYTERM( _ ), refTypePattern, ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern( ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ),
TypeCheckingBuildTempRef );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/runtime/array.cpp.
1 2 3 4 5 6 7 8 9 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::builtins;
namespace goose::builtins
{
void SetupRuntimeArrayType( Env& 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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::builtins;
namespace goose::builtins
{
void SetupRuntimeArrayType( Env& e )
{
RegisterBuiltinFunc< Eager< Value >( Value, uint32_t ) >( e, "array"_sid,
[]( const Value& containedType, uint32_t count )
{
if( !GetCodegenType( containedType ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage( containedType.locationId(),
"runtime arrays can only contain runtime types." );
return PoisonValue();
}
return ToValue( ArrayType( ValueToEIR( containedType ), count ) );
} );
}
const codegen::Type* GetCodegenType( const ArrayType& a )
{
// TODO_SSA reenable
/*return codegen::Type::Get( llvm::ArrayType::get(
*GetCodegenType( *EIRToValue( a.m_containedType ) ), a.m_count ) );*/
return nullptr;
}
} // namespace goose::builtins
namespace goose::eir
{
Value Bridge< ArrayType >::ToValue( const ArrayType& a )
{
return Value( Type(),
MkStdRTType( TSID( rt_type ), GetCodegenType( a ), TSID( array ), TERM( a.m_count ),
a.m_containedType ) );
}
optional< ArrayType > Bridge< ArrayType >::FromValue( const Value& v )
{
auto result = Decompose( v.val(),
Vec( Lit( "rt_type"_sid ), SubTerm(), Val< void* >(), Lit( "array"_sid ),
Val< uint64_t >(), SubTerm() ) );
if( !result )
return nullopt;
auto&& [predicates, cgType, count, containedType] = *result;
return ArrayType( containedType, count );
}
} // namespace goose::eir
|
Changes to bs/builtins/types/runtime/array.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#ifndef GOOSE_BUILTINS_TYPES_RUNTIME_ARRAY_H
#define GOOSE_BUILTINS_TYPES_RUNTIME_ARRAY_H
namespace goose::builtins
{
extern void SetupRuntimeArrayType( Env& e );
struct ArrayType
{
template< typename T >
ArrayType( T&& containedType, uint64_t count ) :
m_containedType( forward< T >( containedType ) ),
m_count( count )
| < > > | < | > | | 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 |
#ifndef GOOSE_BUILTINS_TYPES_RUNTIME_ARRAY_H
#define GOOSE_BUILTINS_TYPES_RUNTIME_ARRAY_H
namespace goose::builtins
{
extern void SetupRuntimeArrayType( Env& e );
struct ArrayType
{
template< typename T >
ArrayType( T&& containedType, uint64_t count ) :
m_containedType( forward< T >( containedType ) ),
m_count( count )
{
}
Term m_containedType;
uint64_t m_count = 1;
};
extern const codegen::Type* GetCodegenType( const ArrayType& a );
} // namespace goose::builtins
namespace goose::eir
{
template<> struct Bridge< builtins::ArrayType >
{
static const Term& Type() { return TypeType(); }
static Value ToValue( const builtins::ArrayType& a );
static optional< builtins::ArrayType > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/runtime/basic.cpp.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
{
void SetupRuntimeBasicTypes( Env& e )
{
DefineConstant( e, "half"_sid, ValueToEIR( ToValue( HalfFloatType() ) ) );
DefineConstant( e, "float"_sid, ValueToEIR( ToValue( FloatType() ) ) );
DefineConstant( e, "double"_sid, ValueToEIR( ToValue( DoubleFloatType() ) ) );
| | < < | < | < < | < | | | | | | | | | | | | | | | | > < > | 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 |
{
void SetupRuntimeBasicTypes( Env& e )
{
DefineConstant( e, "half"_sid, ValueToEIR( ToValue( HalfFloatType() ) ) );
DefineConstant( e, "float"_sid, ValueToEIR( ToValue( FloatType() ) ) );
DefineConstant( e, "double"_sid, ValueToEIR( ToValue( DoubleFloatType() ) ) );
RegisterBuiltinFunc< Eager< Value >( uint32_t ) >(
e, "uint"_sid, []( uint32_t numBits ) { return ToValue( IntegerType( numBits ) ); } );
RegisterBuiltinFunc< Eager< Value >( uint32_t ) >( e, "sint"_sid,
[]( uint32_t numBits ) { return ToValue( IntegerType( numBits, true ) ); } );
}
const Term& IntegerType::Pattern::GetPattern()
{
static auto pattern = ValueToEIR( Value( TypeType(),
MkStdRTType( TSID( rt_type ), nullptr, TSID( integer ),
VEC( HOLE( "size"_sid ), HOLE( "signedness"_sid ) ) ) ) );
return pattern;
}
const Term& IntegerType::PatternSigned::GetPattern()
{
static auto pattern = ValueToEIR( Value( TypeType(),
MkStdRTType( TSID( rt_type ), nullptr, TSID( integer ),
VEC( HOLE( "size"_sid ), TERM( 1U ) ) ) ) );
return pattern;
}
const Term& IntegerType::PatternUnsigned::GetPattern()
{
static auto pattern = ValueToEIR( Value( TypeType(),
MkStdRTType( TSID( rt_type ), nullptr, TSID( integer ),
VEC( HOLE( "size"_sid ), TERM( 0U ) ) ) ) );
return pattern;
}
const Term& IntegerType::PatternUnsigned32::GetPattern()
{
static auto pattern = ValueToEIR( Value( TypeType(),
MkStdRTType(
TSID( rt_type ), nullptr, TSID( integer ), VEC( TERM( 32U ), TERM( 0U ) ) ) ) );
return pattern;
}
const codegen::Type* GetCodegenType( const HalfFloatType& t )
{
// TODO_SSA reenable
return nullptr; // codegen::Type::Get( llvm::Type::getHalfTy( GetLLVMContext() ) );
}
const codegen::Type* GetCodegenType( const FloatType& t )
{
// TODO_SSA reenable
return nullptr; // codegen::Type::Get( llvm::Type::getFloatTy( GetLLVMContext() ) );
}
const codegen::Type* GetCodegenType( const DoubleFloatType& t )
{
// TODO_SSA reenable
return nullptr; // codegen::Type::Get( llvm::Type::getDoubleTy( GetLLVMContext() ) );
}
const codegen::Type* GetCodegenType( const IntegerType& t )
{
// TODO_SSA reenable
return nullptr; // codegen::Type::Get( llvm::IntegerType::get( GetLLVMContext(), t.m_numBits
// ) );
}
} // namespace goose::builtins
namespace goose::eir
{
//// Half
Value Bridge< HalfFloatType >::ToValue( const HalfFloatType& t )
{
return Value( Type(), MkStdRTType( TSID( rt_type ), GetCodegenType( t ), TSID( half ) ) );
|
| ︙ | ︙ | |||
130 131 132 133 134 135 136 |
return {};
}
//// Integer
Value Bridge< IntegerType >::ToValue( const IntegerType& t )
{
| > | | < | < < < < | < < < < | < | < < < | < < | < < | < < | < | | 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 |
return {};
}
//// Integer
Value Bridge< IntegerType >::ToValue( const IntegerType& t )
{
return Value( Type(),
MkStdRTType( TSID( rt_type ), GetCodegenType( t ), TSID( integer ),
VEC( TERM( t.m_numBits ), t.m_signed ? TERM( 1U ) : TERM( 0U ) ) ) );
}
optional< IntegerType > Bridge< IntegerType >::FromValue( const Value& v )
{
auto result = Decompose( v.val(),
Vec( Lit( "rt_type"_sid ), SubTerm(), Val< void* >(), Lit( "integer"_sid ),
Vec( Val< uint64_t >(), Val< uint64_t >() ) ) );
if( !result )
return nullopt;
auto&& [predicates, cgType, params] = *result;
auto&& [numBits, signd] = params;
return IntegerType( numBits, !!signd );
}
Term Bridge< APSInt >::Type( const APSInt& i )
{
return ValueToEIR( eir::ToValue( IntegerType( i.getBitWidth(), i.isSigned() ) ) );
}
Value Bridge< APSInt >::ToValue( const APSInt& i )
{
return Value( Type( i ), TERM( i ) );
}
optional< APSInt > Bridge< APSInt >::FromValue( const Value& v )
{
auto result = Decompose( v.val(), Val< APSInt >() );
if( !result )
return nullopt;
return *result;
}
const Term& Bridge< uint8_t >::Type()
{
static auto type = ValueToEIR( eir::ToValue( IntegerType( 8, false ) ) );
return type;
}
Value Bridge< uint8_t >::ToValue( uint8_t x )
{
return eir::ToValue( APSInt::getUnsigned( x ).trunc( 8 ) );
}
optional< uint8_t > Bridge< uint8_t >::FromValue( const Value& v )
{
return FromValue< APSInt >( v )->getLimitedValue();
}
const Term& Bridge< uint32_t >::Type()
{
static auto type = ValueToEIR( eir::ToValue( IntegerType( 32, false ) ) );
return type;
}
Value Bridge< uint32_t >::ToValue( uint32_t x )
{
return eir::ToValue( APSInt::getUnsigned( x ).trunc( 32 ) );
}
optional< uint32_t > Bridge< uint32_t >::FromValue( const Value& v )
{
return FromValue< APSInt >( v )->getLimitedValue();
}
const Term& Bridge< int32_t >::Type()
{
static auto type = ValueToEIR( eir::ToValue( IntegerType( 32, true ) ) );
return type;
}
Value Bridge< int32_t >::ToValue( int32_t x )
{
return eir::ToValue( APSInt::get( x ).trunc( 32 ) );
}
optional< int32_t > Bridge< int32_t >::FromValue( const Value& v )
{
return FromValue< APSInt >( v )->getExtValue();
}
const Term& Bridge< uint64_t >::Type()
{
static auto type = ValueToEIR( eir::ToValue( IntegerType( 64, false ) ) );
return type;
}
Value Bridge< uint64_t >::ToValue( uint64_t x )
{
return eir::ToValue( APSInt::getUnsigned( x ) );
}
optional< uint64_t > Bridge< uint64_t >::FromValue( const Value& v )
{
if( !v.isConstant() )
return nullopt;
return FromValue< APSInt >( v )->getLimitedValue();
}
} // namespace goose::eir
|
Changes to bs/builtins/types/runtime/basic.h.
1 2 3 4 5 6 7 8 |
#ifndef GOOSE_BUILTINS_TYPES_RUNTIME_BASIC_H
#define GOOSE_BUILTINS_TYPES_RUNTIME_BASIC_H
namespace goose::builtins
{
extern void SetupRuntimeBasicTypes( Env& e );
extern void SetupRuntimeBasicTypesInitialize( Env& 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 |
#ifndef GOOSE_BUILTINS_TYPES_RUNTIME_BASIC_H
#define GOOSE_BUILTINS_TYPES_RUNTIME_BASIC_H
namespace goose::builtins
{
extern void SetupRuntimeBasicTypes( Env& e );
extern void SetupRuntimeBasicTypesInitialize( Env& e );
struct HalfFloatType
{
};
struct FloatType
{
};
struct DoubleFloatType
{
};
struct IntegerType
{
IntegerType( uint32_t numBits, bool signd = false ) :
m_numBits( numBits ),
m_signed( signd )
{
}
uint32_t m_numBits = 1;
bool m_signed = false;
// Helpers to provide generic param patterns for builtin funcs
struct Pattern
{
|
| ︙ | ︙ | |||
42 43 44 45 46 47 48 |
};
};
extern const codegen::Type* GetCodegenType( const HalfFloatType& t );
extern const codegen::Type* GetCodegenType( const FloatType& t );
extern const codegen::Type* GetCodegenType( const DoubleFloatType& t );
extern const codegen::Type* GetCodegenType( const IntegerType& t );
| | < | > < | > < | > < | > < | < | | < | | < | | < | | | | 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 |
};
};
extern const codegen::Type* GetCodegenType( const HalfFloatType& t );
extern const codegen::Type* GetCodegenType( const FloatType& t );
extern const codegen::Type* GetCodegenType( const DoubleFloatType& t );
extern const codegen::Type* GetCodegenType( const IntegerType& t );
} // namespace goose::builtins
namespace goose::eir
{
template<> struct Bridge< builtins::HalfFloatType >
{
static const Term& Type() { return TypeType(); }
static Value ToValue( const builtins::HalfFloatType& i );
static optional< builtins::HalfFloatType > FromValue( const Value& v );
};
template<> struct Bridge< builtins::FloatType >
{
static const Term& Type() { return TypeType(); }
static Value ToValue( const builtins::FloatType& i );
static optional< builtins::FloatType > FromValue( const Value& v );
};
template<> struct Bridge< builtins::DoubleFloatType >
{
static const Term& Type() { return TypeType(); }
static Value ToValue( const builtins::DoubleFloatType& i );
static optional< builtins::DoubleFloatType > FromValue( const Value& v );
};
template<> struct Bridge< builtins::IntegerType >
{
static const Term& Type() { return TypeType(); }
static Value ToValue( const builtins::IntegerType& i );
static optional< builtins::IntegerType > FromValue( const Value& v );
};
template<> struct Bridge< APSInt >
{
static Term Type( const APSInt& i );
static Value ToValue( const APSInt& i );
static optional< APSInt > FromValue( const Value& v );
};
template<> struct Bridge< uint8_t >
{
static const Term& Type();
static Value ToValue( uint8_t x );
static optional< uint8_t > FromValue( const Value& v );
};
template<> struct Bridge< uint32_t >
{
static const Term& Type();
static Value ToValue( uint32_t x );
static optional< uint32_t > FromValue( const Value& v );
};
template<> struct Bridge< int32_t >
{
static const Term& Type();
static Value ToValue( int32_t x );
static optional< int32_t > FromValue( const Value& v );
};
template<> struct Bridge< uint64_t >
{
static const Term& Type();
static Value ToValue( uint64_t x );
static optional< uint64_t > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/runtime/init.cpp.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 |
{
using IntegerMutRefType =
CustomPattern< Value, ReferenceType::PatternMutableOf< IntegerType::Pattern > >;
using IntegerType = CustomPattern< IntegerType, IntegerType::Pattern >;
// Initialization for integer vars
| | > | | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
{
using IntegerMutRefType =
CustomPattern< Value, ReferenceType::PatternMutableOf< IntegerType::Pattern > >;
using IntegerType = CustomPattern< IntegerType, IntegerType::Pattern >;
// Initialization for integer vars
RegisterBuiltinFunc< Intrinsic< Value( IntegerMutRefType, IntegerType ) > >( e,
e.extInitialize(),
[]( auto&& c, const Value& r, const Value& initVal )
{
G_VAL_ASSERT( r, !r.isConstant() );
auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
return BuildComputedValue( GetValueType< void >(), initVal, r,
Store( refType.type(), initVal.locationId(), r.locationId() ) );
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/runtime/pointer.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::builtins;
namespace goose::builtins
{
void SetupRuntimePointerType( Env& e )
{
// null pointer literal
| | > | | | | | < < | | < < < < < | < < | 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 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::builtins;
namespace goose::builtins
{
void SetupRuntimePointerType( Env& e )
{
// null pointer literal
e.storeValue( AppendToVectorTerm( RootG0Identity(), TSID( nullptr ) ), ANYTERM( _ ),
ValueToEIR( ToValue( NullPointer() ) ) );
RegisterBuiltinFunc< Eager< Value >( Value ) >( e, "pointer"_sid,
[]( const Value& pointedType )
{
if( !GetCodegenType( pointedType ) )
{
// TODO come up with some lightweight builtin option type
// for the builtin apis, because this is a very bullshit
// way to handle errors
return ToValue( "error"s );
}
return ToValue( PointerType( ValueToEIR( pointedType ) ) );
} );
}
const codegen::Type* GetCodegenType( const PointerType& p )
{
// TODO_SSA reenable
// return codegen::PointerType::Get( GetCodegenType( *EIRToValue( p.m_pointedType ) ) );
return nullptr;
}
} // namespace goose::builtins
namespace goose::eir
{
Value Bridge< PointerType >::ToValue( const PointerType& p )
{
return Value( Type(),
MkStdRTType( TSID( rt_type ), GetCodegenType( p ), TSID( pointer ), p.m_pointedType ) );
}
optional< PointerType > Bridge< PointerType >::FromValue( const Value& v )
{
auto result = Decompose( v.val(),
Vec( Lit( "rt_type"_sid ), SubTerm(), Val< void* >(), Lit( "pointer"_sid ),
SubTerm() ) );
if( !result )
return nullopt;
auto&& [predicates, cgType, pointedType] = *result;
return PointerType( pointedType );
}
const Value& Bridge< NullPointerType >::ToValue( const NullPointerType& np )
{
static auto val = Value( Type(), TSID( nullptr ) );
return val;
}
optional< NullPointerType > Bridge< NullPointerType >::FromValue( const Value& v )
{
auto result = Decompose( v.val(), Lit( "nullptr"_sid ) );
if( !result )
return nullopt;
return NullPointerType();
}
|
| ︙ | ︙ | |||
92 93 94 95 96 97 98 |
}
optional< NullPointer > Bridge< NullPointer >::FromValue( const Value& v )
{
if( !FromValue< NullPointerType >( *EIRToValue( v.type() ) ) )
return nullopt;
| | < < | | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
}
optional< NullPointer > Bridge< NullPointer >::FromValue( const Value& v )
{
if( !FromValue< NullPointerType >( *EIRToValue( v.type() ) ) )
return nullopt;
auto result = Decompose( v.val(), Lit( "nullptr"_sid ) );
if( !result )
return nullopt;
return NullPointer();
}
} // namespace goose::eir
|
Changes to bs/builtins/types/runtime/pointer.h.
1 2 3 4 5 6 7 8 9 10 11 12 |
#ifndef GOOSE_BUILTINS_TYPES_RUNTIME_POINTER_H
#define GOOSE_BUILTINS_TYPES_RUNTIME_POINTER_H
namespace goose::builtins
{
extern void SetupRuntimePointerType( Env& e );
struct PointerType
{
template< typename T >
PointerType( T&& pointedType ) :
m_pointedType( forward< T >( pointedType ) )
| < > > > | > | < > < | > < | > < | | | 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 |
#ifndef GOOSE_BUILTINS_TYPES_RUNTIME_POINTER_H
#define GOOSE_BUILTINS_TYPES_RUNTIME_POINTER_H
namespace goose::builtins
{
extern void SetupRuntimePointerType( Env& e );
struct PointerType
{
template< typename T >
PointerType( T&& pointedType ) :
m_pointedType( forward< T >( pointedType ) )
{
}
Term m_pointedType;
};
struct NullPointerType
{
};
struct NullPointer
{
};
extern const codegen::Type* GetCodegenType( const PointerType& p );
} // namespace goose::builtins
namespace goose::eir
{
template<> struct Bridge< builtins::PointerType >
{
static const Term& Type() { return TypeType(); }
static Value ToValue( const builtins::PointerType& p );
static optional< builtins::PointerType > FromValue( const Value& v );
};
template<> struct Bridge< builtins::NullPointerType >
{
static const Term& Type() { return TypeType(); }
static const Value& ToValue( const builtins::NullPointerType& np );
static optional< builtins::NullPointerType > FromValue( const Value& v );
};
template<> struct Bridge< builtins::NullPointer >
{
static const Term& Type();
static const Value& ToValue( const builtins::NullPointer& np );
static optional< builtins::NullPointer > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/runtime/runtime.cpp.
| ︙ | ︙ | |||
15 16 17 18 19 20 21 |
const codegen::Type* GetCodegenType( const Value& t )
{
if( !t.isType() )
return nullptr;
if( t.val() == TSID( void ) )
// TODO_SSA reenable
| | | < < | < < | | 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 |
const codegen::Type* GetCodegenType( const Value& t )
{
if( !t.isType() )
return nullptr;
if( t.val() == TSID( void ) )
// TODO_SSA reenable
// return codegen::Type::Get( llvm::Type::getVoidTy( GetLLVMContext() ) );
return nullptr;
if( IsTupleType( t ) )
return GetTupleCodegenType( t );
if( IsFuncType( t ) )
return GetFuncCodegenType( *FromValue< FuncType >( t ) );
auto vec = Decompose( t.val(), Val< pvec >() );
if( vec )
{
if( vec->get()->terms().size() < 3 )
return nullptr;
auto lt = Decompose( vec->get()->terms()[2], Val< void* >() );
if( !lt )
return nullptr;
if( lt->get() )
return static_cast< codegen::Type* >( lt->get() );
const auto& id = get< StringId >( vec->get()->terms()[3] );
if( id == "bool"_sid )
// TODO_SSA reenable
// return codegen::Type::Get( llvm::Type::getInt1Ty( GetLLVMContext() ) );
return nullptr;
if( id == "integer"_sid )
{
if( auto it = FromValue< IntegerType >( t ) )
return GetCodegenType( *it );
}
|
| ︙ | ︙ | |||
78 79 80 81 82 83 84 |
if( auto at = FromValue< ArrayType >( t ) )
return GetCodegenType( *at );
}
}
// If it is not a run time data type, it may still be a runtime function type.
auto result = Decompose( t.val(),
| < | < | | | < | | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
if( auto at = FromValue< ArrayType >( t ) )
return GetCodegenType( *at );
}
}
// If it is not a run time data type, it may still be a runtime function type.
auto result = Decompose( t.val(),
Vec( Lit( "func"_sid ), Val< void* >(),
SubTerm(), // return type
SubTerm() // param types
) );
if( !result )
return nullptr;
auto&& [cgType, rtype, paramTypes] = *result;
return static_cast< codegen::Type* >( cgType );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/runtime/runtime.h.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 |
SetupRuntimePointerType( e );
SetupRuntimeArrayType( e );
SetupRuntimeTypesChecking( e );
}
extern bool IsRuntimeType( const Value& t );
extern const codegen::Type* GetCodegenType( const Value& t );
| | | 16 17 18 19 20 21 22 23 24 25 |
SetupRuntimePointerType( e );
SetupRuntimeArrayType( e );
SetupRuntimeTypesChecking( e );
}
extern bool IsRuntimeType( const Value& t );
extern const codegen::Type* GetCodegenType( const Value& t );
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/runtime/typecheck.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::eir;
namespace goose::builtins
{
// TODO: converting a computed ct_int to a rt int is legit in compile time code.
// Consider this:
// var someTuple = (123,456)
// void someCompileTimeFunc( ( uint(32), uint(32) tup )
| | > | > | | | | | < | < | < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | < | | | | | | | | < < < | | | | | < > | 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 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::eir;
namespace goose::builtins
{
// TODO: converting a computed ct_int to a rt int is legit in compile time code.
// Consider this:
// var someTuple = (123,456)
// void someCompileTimeFunc( ( uint(32), uint(32) tup )
// Can't perform a compile time to someCompileTimeFunc with someTuple as the later contains
// computed ct_ints
// So we need to allow this conversion, but only at compile time. Perhaps emit the conversion as
// a call to a compile time conversion func, which therefore would fail when compiling because
// the ct_int wouldn't be able to be lowered to a runtime type? (we'd also need to emit asserton
// checks for the ct_int bounds)
// Probably something to do in the prelude.
void SetupRuntimeTypesChecking( Env& e )
{
auto rtIntTypePattern = Value( TypeType(),
MkStdType( TSID( rt_type ), TSID( integer ), VEC( ANYTERM( _ ), ANYTERM( _ ) ) ) );
// Conversion rule for ct_int to runtime int:
// we verify that the ct_int fits in the bitsize/signage
// of the runtime int
e.typeCheckingRuleSet()->addTypeCheckingRule(
ValueToEIR(
ValuePattern( ANYTERM( _ ), ValueToEIR( rtIntTypePattern ), ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern( ANYTERM( _ ), GetValueType< BigInt >(), ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
// The rhs might not be constant. It can happen even with ct_int if the rhs is a
// fake argument pattern generated to unify a higher-order function param.
// It means that we are unifying a function parameter against any ct_int value,
// which we can't (because we have to know the value to safely convert it),
// so we reject it.
auto rhsVal = *EIRToValue( rhs );
if( !rhsVal.isConstant() )
co_return;
auto lhsVal = EIRToValuePattern( lhs );
if( !lhsVal )
co_return;
auto s = HalfUnify( lhsVal->type(), tcc );
if( !s )
co_return;
auto wrapped = WrapWithPostprocFunc( *s,
[rhs]( const Term& t, TypeCheckingContext tcc ) -> optional< Term >
{
auto argVal = EIRToValue( rhs );
assert( argVal );
auto ct = *FromValue< BigInt >( *argVal );
auto rttypeVal = EIRToValue( t );
assert( rttypeVal );
auto rttype = FromValue< IntegerType >( *rttypeVal );
assert( rttype );
APSInt valToLoad;
if( rttype->m_signed )
{
if( ct.getSignificantBits() > rttype->m_numBits )
return nullopt;
valToLoad = ct.sext( rttype->m_numBits );
valToLoad.setIsSigned( true );
}
else
{
if( ct.isNegative() )
return nullopt;
if( ct.getActiveBits() > rttype->m_numBits )
return nullopt;
valToLoad = ct.zext( rttype->m_numBits );
valToLoad.setIsSigned( false );
}
return TERM( valToLoad );
} );
co_yield { ValueToEIR(
Value( *s, move( wrapped ) ).setLocationId( rhsVal.locationId() ) ),
tcc };
} );
auto rtInt8TypePattern = Value( TypeType(),
MkStdType( TSID( rt_type ), TSID( integer ), VEC( TERM( 8U ), ANYTERM( _ ) ) ) );
auto rtInt8PtrTypePattern = Value( TypeType(),
MkStdType( TSID( rt_type ), TSID( pointer ), ValueToEIR( rtInt8TypePattern ) ) );
// ct_string type against a char*:
// return the char* type.
e.typeCheckingRuleSet()->addTypeCheckingRule( ValueToEIR( rtInt8PtrTypePattern ),
GetValueType< string >(),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext c ) -> TCGen
{
if( auto s = HalfUnify( lhs, c ) )
co_yield { *s, c };
} );
auto ptrTypePattern =
Value( TypeType(), MkStdType( TSID( rt_type ), TSID( pointer ), ANYTERM( _ ) ) );
// nullptr constant type checking 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(
ParamPat( ValueToEIR( ptrTypePattern ) ),
ValueToEIR( ValuePattern( ANYTERM( _ ), GetValueType< NullPointer >(), ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& c ) -> TCGen
{
auto lVal = *EIRToValuePattern( lhs );
co_yield { ValueToEIR( Value( lVal.type(), 0U ) ), c };
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/struct/builder.cpp.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 |
underlyingType = AppendToTupleType( underlyingType, mv.type );
auto tVal = *EIRToValue( mv.type );
auto loweredMemberType = LowerType( c, tVal );
if( !loweredMemberType )
{
| | > | | | | | > > | > | | 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 |
underlyingType = AppendToTupleType( underlyingType, mv.type );
auto tVal = *EIRToValue( mv.type );
auto loweredMemberType = LowerType( c, tVal );
if( !loweredMemberType )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
tVal.locationId(), "can't lower this type." );
return false;
}
loweredType = AppendToTupleType( loweredType, *loweredMemberType );
}
c.env()->storeValue( AppendToVectorTerm( identity(), TERM( 0U ), TSID( underlying_type ) ),
ANYTERM( _ ), ValueToEIR( underlyingType ) );
c.env()->storeValue( AppendToVectorTerm( identity(), TERM( 0U ), TSID( lowered_type ) ),
ANYTERM( _ ), ValueToEIR( loweredType ) );
unordered_map< StringId, LocationId > memberNames;
// Store the index matching each struct member, to be able to remap member names
// to tuple indices in operator_dot
uint32_t index = 0;
for( const auto& mv : m_memberVars )
{
if( auto it = memberNames.find( mv.name ); it != memberNames.end() )
{
DiagnosticsContext dc( it->second, "Previous declaration." );
DiagnosticsManager::GetInstance().emitErrorMessage(
mv.loc, "a member with this name was already defined for this struct." );
return false;
}
memberNames.emplace( mv.name, mv.loc );
c.env()->storeValue( AppendToVectorTerm( identity(), TERM( mv.name ) ), ANYTERM( _ ),
ValueToEIR( ToValue( index++ ) ) );
}
auto pCtorEntryBB = m_defaultCtorCFG->entryBB();
// Complete the ctor's CFG with a RetVoid
m_defaultCtorCFG->currentBB()->setTerminator( RetVoid( {} ) );
ReindexVars( m_defaultCtorCFG );
// Create the default ctor function
ReferenceType refType( ValueToEIR( st ), MutAccessSpecifier() );
auto params =
MakeClosedTuple( ToValue( Decl( ValueToEIR( ToValue( refType ) ), "instance"_sid ) ) );
auto ctorFunc = BuildFunc( c, AppendToVectorTerm( identity(), TERM( 0U ), TSID( ctor ) ),
*EIRToValue( GetValueType< void >() ), ToValue( params ), m_defaultCtorCFG );
// BuildFunc can fail if a type can't be lowered.
if( !ctorFunc )
return false;
// Add it as an overload of _Initialize
c.env()->extInitialize()->add(
*c.env(), ToValue( *ctorFunc ).setLocationId( st.locationId() ) );
return true;
}
|
Changes to bs/builtins/types/struct/builder.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_BUILTINS_TYPES_STRUCT_BUILDER_H
#define GOOSE_BUILTINS_TYPES_STRUCT_BUILDER_H
namespace goose::builtins
{
class StructBuilder
{
| | | | | | | | | | > | | | | > | | | | | | | | | | | | | | | 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_BUILTINS_TYPES_STRUCT_BUILDER_H
#define GOOSE_BUILTINS_TYPES_STRUCT_BUILDER_H
namespace goose::builtins
{
class StructBuilder
{
public:
template< typename I >
StructBuilder( I&& identity ) :
m_identity( forward< I >( identity ) )
{
m_defaultCtorCFG->createBB();
m_defaultCtorCFG->setCurrentBB( m_defaultCtorCFG->entryBB() );
}
const auto& identity() const { return m_identity; }
const auto& defaultCtorCFG() const { return m_defaultCtorCFG; }
template< typename T >
void declareMemberVar( const Context& c, StringId name, T&& type, LocationId loc );
template< typename T, typename V >
void declareMemberVar(
const Context& c, StringId name, T&& type, LocationId loc, V&& init );
bool finalize( const Context& c, const Value& st ) const;
private:
Term m_identity;
ptr< cir::CFG > m_defaultCtorCFG = make_shared< cir::CFG >( 0 );
struct MemberVar
{
optional< Value > init;
Term type;
StringId name;
LocationId loc;
};
llvm::SmallVector< MemberVar, 8 > m_memberVars;
};
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/struct/builder.inl.
1 2 3 4 5 6 |
#ifndef GOOSE_BUILTINS_TYPES_STRUCT_BUILDER_INL
#define GOOSE_BUILTINS_TYPES_STRUCT_BUILDER_INL
namespace goose::builtins
{
template< typename T >
| | > | < | | > | | < | > | | < | | > | | < | > | < > | 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 |
#ifndef GOOSE_BUILTINS_TYPES_STRUCT_BUILDER_INL
#define GOOSE_BUILTINS_TYPES_STRUCT_BUILDER_INL
namespace goose::builtins
{
template< typename T >
void StructBuilder::declareMemberVar(
const Context& c, StringId name, T&& type, LocationId loc )
{
auto rt = ValueToEIR( ToValue( ReferenceType{ type, MutAccessSpecifier() } ) );
auto ref = BuildComputedValue(
move( rt ), cir::DataPathOf( 0, {} ), cir::Select( m_memberVars.size(), {} ) )
.setLocationId( loc );
c.env()->storeValue(
AppendToVectorTerm( identity(), TERM( name ) ), identity(), ValueToEIR( ref ) );
auto initResult =
InvokeOverloadSet( c, c.env()->extInitialize(), MakeClosedTuple( ref ), loc );
if( !initResult.isPoison() )
{
DiagnosticsContext dc2( initResult.locationId(), "When invoking _DropValue." );
InvokeOverloadSet( c, c.env()->extDropValue(),
MakeClosedTuple( c.builder(), move( initResult ) ), initResult.locationId() );
}
m_memberVars.emplace_back( MemberVar{ nullopt, forward< T >( type ), name, loc } );
}
template< typename T, typename V >
void StructBuilder::declareMemberVar(
const Context& c, StringId name, T&& type, LocationId loc, V&& init )
{
auto rt = ValueToEIR( ToValue( ReferenceType{ type, MutAccessSpecifier() } ) );
auto ref = BuildComputedValue(
move( rt ), cir::DataPathOf( 0, {} ), cir::Select( m_memberVars.size(), {} ) )
.setLocationId( loc );
c.env()->storeValue(
AppendToVectorTerm( identity(), TERM( name ) ), identity(), ValueToEIR( ref ) );
auto initResult =
InvokeOverloadSet( c, c.env()->extInitialize(), MakeClosedTuple( ref, init ), loc );
if( !initResult.isPoison() )
{
DiagnosticsContext dc2( initResult.locationId(), "When invoking _DropValue." );
InvokeOverloadSet( c, c.env()->extDropValue(),
MakeClosedTuple( c.builder(), move( initResult ) ), initResult.locationId() );
}
m_memberVars.emplace_back(
MemberVar{ forward< V >( init ), forward< T >( type ), name, loc } );
}
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/struct/init.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupStructInitialize( Env& e )
{
using StructMutRefType =
CustomPattern< Value, ReferenceType::PatternMutableOf< StructType::Pattern > >;
| | | < < > | 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 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupStructInitialize( Env& e )
{
using StructMutRefType =
CustomPattern< Value, ReferenceType::PatternMutableOf< StructType::Pattern > >;
RegisterBuiltinFunc< Intrinsic< Value( StructMutRefType ) > >( e, e.extInitialize(),
[]( auto&& c, const Value& stRef )
{
auto refType = *FromValue< ReferenceType >( *EIRToValue( stRef.type() ) );
auto st = *EIRToValue( refType.type() );
auto structType = *FromValue< StructType >( st );
if( structType.wasParsed() )
G_VAL_ERROR( stRef, "can't resolve _Initialize() for this struct" );
if( !structType.parse( c.env() ) )
return PoisonValue();
return InvokeOverloadSet( c, c.env()->extInitialize(), MakeClosedTuple( stRef ) );
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/struct/lower.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::builtins;
namespace goose::builtins
{
void SetupStructLowering( Env& 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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::builtins;
namespace goose::builtins
{
void SetupStructLowering( Env& e )
{
RegisterBuiltinFunc<
Intrinsic< Value( TypePatternParam< StructType, StructType::Pattern > ) > >( e,
e.extGetUnderlyingType(),
[]( const Context& c, const Value& structType )
{
auto st = *FromValue< StructType >( structType );
if( !st.parse( c.env() ) )
return PoisonValue();
auto tupType = c.env()->retrieveValue(
TSID( underlying_type ), AppendToVectorTerm( st.identity(), TERM( 0U ) ) );
G_VAL_ASSERT( structType, tupType );
return *EIRToValue( *tupType );
} );
RegisterBuiltinFunc<
Intrinsic< Value( TypePatternParam< StructType, StructType::Pattern > ) > >( e,
e.extLowerType(),
[]( const Context& c, const Value& structType )
{
auto st = *FromValue< StructType >( structType );
if( !st.parse( c.env() ) )
return PoisonValue();
auto tupType = c.env()->retrieveValue(
TSID( lowered_type ), AppendToVectorTerm( st.identity(), TERM( 0U ) ) );
G_VAL_ASSERT( structType, tupType );
return *EIRToValue( *tupType );
} );
RegisterBuiltinFunc<
Intrinsic< Value( CustomPattern< StructType, StructType::Pattern > ) > >( e,
e.extLowerValue(),
[]( const Context& c, const Value& structVal )
{
G_VAL_ERROR( structVal, "LowerValue for structs NYI" );
return PoisonValue();
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/struct/parse.cpp.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 |
auto handleStruct = []( Parser& p, LocationId locationId, uint32_t prec )
{
auto& dm = DiagnosticsManager::GetInstance();
auto nameTok = p.resolver()->consume();
if( !nameTok )
{
| | | | | < | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
auto handleStruct = []( Parser& p, LocationId locationId, uint32_t prec )
{
auto& dm = DiagnosticsManager::GetInstance();
auto nameTok = p.resolver()->consume();
if( !nameTok )
{
dm.emitSyntaxErrorMessage(
p.resolver()->currentLocation(), "expected an identifier.", 0 );
return false;
}
const auto& [name, nameLoc] = *nameTok;
if( !holds_alternative< StringId >( name ) )
{
dm.emitSyntaxErrorMessage( nameLoc, "expected an identifier.", 0 );
return false;
}
auto next = p.resolver()->lookAheadUnresolved();
if( !next )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
|
| ︙ | ︙ | |||
48 49 50 51 52 53 54 |
}
StructType::Unparsed::value_type toks;
auto g = p.resolver()->consumeUnit();
move( g.begin(), g.end(), back_inserter( toks ) );
| | | > | | | | | | | | | 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 |
}
StructType::Unparsed::value_type toks;
auto g = p.resolver()->consumeUnit();
move( g.begin(), g.end(), back_inserter( toks ) );
// Take the current identity, but remove "0_locvars" from the end: the struct def should
// live in the "constant compile time" visibility hierarchy, and also only see that.
auto parentIdentity =
TakeVectorTerm( p.context().identity(), VecSize( p.context().identity() ) - 1 );
auto structIdentity = TakeVectorTerm( parentIdentity, VecSize( parentIdentity ) - 1 );
structIdentity = AppendToVectorTerm( structIdentity, name );
p.context().env()->addVisibilityRule( parentIdentity, structIdentity );
auto structLoc = Location::CreateSpanningLocation( locationId, nameLoc );
auto st = ValueToEIR( ToValue( StructType( structIdentity, structLoc,
make_shared< StructType::Unparsed >( move( toks ) ) ) ) );
// Like functions, structs are parsed lazily to make out of order and circular
// references possible. However, unlike functions which can only be consumed in two ways
// (calling and passing as value), structs can be consumed in a number of ways (passing
// instances, referencing instances, passing struct types, referencing struct type...)
// and all of them need to perform lazy parsing. Instead of handling it on a per case
// basis, we setup a custom value provider which will trigger the struct's lazy parsing
// whenever its name is invoked.
p.context().env()->storeValue( structIdentity, ANYTERM( _ ), st );
return true;
};
RegisterRule( e, "struct"_sid, Rule( handleStruct ) );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/struct/struct.h.
1 2 3 4 5 6 7 8 9 10 11 |
#ifndef GOOSE_BUILTINS_TYPES_STRUCT_H
#define GOOSE_BUILTINS_TYPES_STRUCT_H
namespace goose::builtins
{
extern void SetupStructParsingRule( Env& e );
extern void SetupStructInitialize( Env& e );
extern void SetupStructLowering( Env& e );
class Struct
{
| | | | | < | | | 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 |
#ifndef GOOSE_BUILTINS_TYPES_STRUCT_H
#define GOOSE_BUILTINS_TYPES_STRUCT_H
namespace goose::builtins
{
extern void SetupStructParsingRule( Env& e );
extern void SetupStructInitialize( Env& e );
extern void SetupStructLowering( Env& e );
class Struct
{
public:
private:
ptr< StructType > m_type;
};
} // namespace goose::builtins
namespace goose::eir
{
template<> struct Bridge< builtins::Struct >
{
static Term Type( const Term& type );
static Value ToValue( const builtins::Struct& lv );
static optional< builtins::Struct > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/struct/structtype.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::builtins;
namespace goose::builtins
{
bool StructType::parse( const ptr< Env > e )
{
if( wasParsed() )
return true;
Context ctxt( e, identity() );
| | | | | | > > < > > > | > < | < < < < < | < < < | | > | < > | 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 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::builtins;
namespace goose::builtins
{
bool StructType::parse( const ptr< Env > e )
{
if( wasParsed() )
return true;
Context ctxt( e, identity() );
ctxt.setBuilder( ToValue(
TypeWrapper< ptr< StructBuilder > >( make_shared< StructBuilder >( m_identity ) ) ) );
auto tokProvider = lex::MakeVectorAdapter( **m_unparsed );
auto r = make_shared< parse::Resolver >( tokProvider, ctxt );
parse::Parser p( r );
bool success = p.parseBraceBlock();
p.flushValue();
m_unparsed->reset();
if( !success )
return false;
DiagnosticsContext dc2( m_loc, "When invoking _Finalize." );
auto result = InvokeOverloadSet(
ctxt, e->extFinalize(), MakeClosedTuple( ctxt.builder(), ToValue( *this ) ), m_loc );
if( result.isPoison() )
return false;
auto res = FromValue< bool >( result );
return res && *res;
}
const Term& StructType::Pattern::GetPattern()
{
static auto pattern = ValueToEIR( Value( TypeType(),
MkStdType(
TSID( rt_type ), TSID( struct ), VEC( HOLE( "_"_sid ), HOLE( "_"_sid ) ) ) ) );
return pattern;
}
} // namespace goose::builtins
namespace goose::eir
{
const Term& Bridge< StructType >::Type()
{
return TypeType();
}
Value Bridge< StructType >::ToValue( const StructType& t )
{
return Value( Type(),
MkStdType( TSID( rt_type ), TSID( struct ),
VEC( t.identity(), static_pointer_cast< void >( t.unparsed() ) ) ) )
.setLocationId( t.location() );
}
optional< StructType > Bridge< StructType >::FromValue( const Value& v )
{
auto result = Decompose( v.val(),
Vec( Lit( "rt_type"_sid ), SubTerm(), SubTerm(), Lit( "struct"_sid ),
Vec( SubTerm(), Val< ptr< void > >() ) ) );
if( !result )
return nullopt;
auto&& [_, __, infos] = *result;
auto&& [identity, unparsed] = infos;
return StructType(
identity, v.locationId(), static_pointer_cast< StructType::Unparsed >( unparsed ) );
}
} // namespace goose::eir
|
Changes to bs/builtins/types/struct/structtype.h.
1 2 3 4 5 6 |
#ifndef GOOSE_BUILTINS_TYPES_STRUCT_TYPE_H
#define GOOSE_BUILTINS_TYPES_STRUCT_TYPE_H
namespace goose::builtins
{
// struct type encoding:
| | > | | > | > | | | | | | < | > > | | > | > | | > | | | | | | | | | > | < < | | | 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 |
#ifndef GOOSE_BUILTINS_TYPES_STRUCT_TYPE_H
#define GOOSE_BUILTINS_TYPES_STRUCT_TYPE_H
namespace goose::builtins
{
// struct type encoding:
// We use MkStdType (because predicates + llvm type are both useful to have), and append a ptr<
// void > to unparsed token as extra (since MkStdType allows to provide extra data) The funcs
// identity is the context of the struct's decl + its name (struct are nominative, unlike
// tuples). The struct's identity will serve as root to store all the details of the struct type
// (members etc.)
// Should contain the struct's identity + pointer to unparsed tokens (or null if already
// parsed)
class StructType
{
public:
template< typename T, typename UP >
StructType( T&& identity, LocationId loc, UP&& unparsed ) :
m_identity( identity ),
m_unparsed( unparsed ),
m_loc( loc )
{
}
using Unparsed = optional< llvm::SmallVector< TermLoc, 8 > >;
const auto& identity() const { return m_identity; }
auto location() const { return m_loc; }
const auto& unparsed() const { return m_unparsed; }
bool wasParsed() const { return !( *m_unparsed ); }
bool parse( const ptr< Env > e );
struct Pattern
{
static const Term& GetPattern();
};
private:
Term m_identity;
ptr< Unparsed > m_unparsed;
LocationId m_loc;
};
} // namespace goose::builtins
namespace goose::eir
{
template<> struct Bridge< builtins::StructType >
{
static const Term& Type();
static Value ToValue( const builtins::StructType& t );
static optional< builtins::StructType > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/template/build.cpp.
1 2 3 4 5 6 7 8 9 |
#include "builtins/builtins.h"
namespace goose::builtins
{
TFuncType BuildTFuncType( const Value& returnType, const Value& params )
{
auto v = make_shared< Vector >();
v->reserve( TupleSize( params ) );
| | > | | | | | > | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 |
#include "builtins/builtins.h"
namespace goose::builtins
{
TFuncType BuildTFuncType( const Value& returnType, const Value& params )
{
auto v = make_shared< Vector >();
v->reserve( TupleSize( params ) );
ForEachInTuple( params,
[&]( auto&& param )
{
v->append( ValueToEIR( param ) );
return true;
} );
return TFuncType( ValueToEIR( returnType ), v );
}
optional< Term > BuildTFuncSignature( const Context& c, const TFuncType& tft )
{
TemplateContext tc( c );
auto v = make_shared< Vector >();
v->reserve( VecSize( tft.params() ) + 1 );
if( !PrepareTemplate( tc, tft.returnType() ) )
return nullopt;
auto rtSig = BuildTemplateSignature( tc, tft.returnType() );
if( !rtSig )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
EIRToValue( tft.returnType() )->locationId(),
"Invalid template return type or texpr." );
return nullopt;
}
v->append( move( *rtSig ) );
bool success = true;
LocationId packLoc;
ForEachInVectorTerm( tft.params(),
[&]( auto&& param )
{
if( !packLoc.invalid() )
{
DiagnosticsManager::GetInstance().emitErrorMessage( packLoc,
"pack expressions can only appear at the end of parameter lists." );
success = false;
return false;
}
if( !PrepareTemplate( tc, param ) )
{
success = false;
return false;
}
auto teSig = BuildTemplateSignature( tc, param );
if( !teSig )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
EIRToValue( param )->locationId(), "invalid template parameter." );
success = false;
return false;
}
if( IsTemplatePackExpr( c, param ) )
{
packLoc = EIRToValue( param )->locationId();
v->setRepetitionTerm( move( *teSig ) );
}
else
v->append( move( *teSig ) );
return true;
} );
if( !success )
return nullopt;
return TERM( v );
}
|
| ︙ | ︙ | |||
97 98 99 100 101 102 103 |
auto apv = make_shared< Vector >();
apv->reserve( VecSize( ftype->params() ) + 1 );
auto rtArgPat = BuildTemplateArgPattern( c, ftype->returnType() );
if( !rtArgPat )
{
| | > | > | | | | | | | | | | | | | | 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 |
auto apv = make_shared< Vector >();
apv->reserve( VecSize( ftype->params() ) + 1 );
auto rtArgPat = BuildTemplateArgPattern( c, ftype->returnType() );
if( !rtArgPat )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
EIRToValue( ftype->returnType() )->locationId(),
"Invalid template return type or texpr." );
return nullopt;
}
apv->append( move( *rtArgPat ) );
bool success = true;
ForEachInVectorTerm( ftype->params(),
[&]( auto&& param )
{
auto teArgPat = BuildTemplateArgPattern( c, param );
if( !teArgPat )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
EIRToValue( param )->locationId(), "invalid template parameter." );
success = false;
return false;
}
apv->append( move( *teArgPat ) );
return true;
} );
if( !success )
return nullopt;
return TERM( apv );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/template/build.h.
1 2 3 4 5 6 7 8 9 10 11 12 |
#ifndef GOOSE_BUILTINS_TEMPLATE_BUILD_H
#define GOOSE_BUILTINS_TEMPLATE_BUILD_H
namespace goose::builtins
{
extern TFuncType BuildTFuncType( const Value& returnType, const Value& params );
extern optional< Term > BuildTFuncSignature( const Context& c, const TFuncType& tft );
extern optional< TFunc > BuildTFunc( const Context& c, const TFuncType& tft,
const Term& parentIdentity, const Term& identity, const Value& params, ptr< void > body );
| | < > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#ifndef GOOSE_BUILTINS_TEMPLATE_BUILD_H
#define GOOSE_BUILTINS_TEMPLATE_BUILD_H
namespace goose::builtins
{
extern TFuncType BuildTFuncType( const Value& returnType, const Value& params );
extern optional< Term > BuildTFuncSignature( const Context& c, const TFuncType& tft );
extern optional< TFunc > BuildTFunc( const Context& c, const TFuncType& tft,
const Term& parentIdentity, const Term& identity, const Value& params, ptr< void > body );
extern optional< Term > BuildArgPatternFromTFuncType(
const Context& c, const Value& tfuncType );
} // namespace goose::builtins
#endif
|
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 17 18 19 20 21 22 |
#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& checkedCallPat,
const TypeCheckingContext& tcContext )
{
auto tf = FromValue< TFunc >( callee );
assert( tf );
auto callDecomp = Decompose( checkedCallPat, Val< pvec >() );
const auto& typecheckedRType = callDecomp->get()->terms().front();
auto typecheckedArgs = DropVectorTerm( checkedCallPat, 1 );
// Build the instance function param types
auto instanceParams = EmptyClosedTuple();
auto argGen = ForEachInVectorTerm( typecheckedArgs );
|
| ︙ | ︙ | |||
41 42 43 44 45 46 47 |
auto instanceSig = GetFuncSigFromType( ToValue( *instanceType ) );
const auto& vec = get< pvec >( tf->type().params() );
auto g = ContainerTypePredicatesHashGenerator( c, tcContext, vec->terms() );
uint64_t predHash = llvm::hash_combine_range( g.begin(), g.end() );
Term instanceIdentity = AppendPredicatesHashToIdentity(
| | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
auto instanceSig = GetFuncSigFromType( ToValue( *instanceType ) );
const auto& vec = get< pvec >( tf->type().params() );
auto g = ContainerTypePredicatesHashGenerator( c, tcContext, vec->terms() );
uint64_t predHash = llvm::hash_combine_range( g.begin(), g.end() );
Term instanceIdentity = AppendPredicatesHashToIdentity(
AppendToVectorTerm( tf->identity(), instanceSig ), predHash );
// Look for an already existing instanced function
Value instanceFunc = PoisonValue();
Term result;
switch( c.env()->retrieveValue( instanceIdentity, c.identity(), result ) )
{
|
| ︙ | ︙ | |||
64 65 66 67 68 69 70 |
instanceType->verifInfos()->setPostCondToks( tf->type().postConds()->unparsed() );
auto verifContext = c;
verifContext.setIdentity( instanceType->verifInfos()->preConds()->identity() );
// Setup the template TVars binding in the verification context.
auto tcc = tcContext;
| | > | | | | | > | > | | | | > | | | 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 |
instanceType->verifInfos()->setPostCondToks( tf->type().postConds()->unparsed() );
auto verifContext = c;
verifContext.setIdentity( instanceType->verifInfos()->preConds()->identity() );
// Setup the template TVars binding in the verification context.
auto tcc = tcContext;
ForEachInVectorTerm( tf->type().params(),
[&]( auto&& param )
{
TemplateSetup( verifContext, tcc, param );
return true;
} );
auto bodyContext = c;
auto func = BuildFunc( c, *instanceType, tf->parentIdentity(), instanceIdentity,
instanceParams, tf->tokens(), bodyContext );
tcc = tcContext;
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 );
instanceFunc = CompileFunc( c, instanceFunc );
c.env()->storeValue( instanceIdentity, ANYTERM( _ ), ValueToEIR( instanceFunc ) );
break;
}
case Env::Status::AmbiguousMatch:
DiagnosticsManager::GetInstance().emitErrorMessage( callee.locationId(),
"unexpected ambiguous match when looking up a template function instance." );
return PoisonValue();
}
return instanceFunc;
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/template/invoke.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::sema;
using namespace goose::parse;
namespace goose::builtins
{
class TemplateFunctionInvocationRule : public InvocationRule
{
| | > | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | > | > | | | | < < < > | 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 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::sema;
using namespace goose::parse;
namespace goose::builtins
{
class TemplateFunctionInvocationRule : public InvocationRule
{
public:
Value resolveInvocation(
Context& c, LocationId loc, const Value& callee, const Term& args ) const final
{
auto tf = FromValue< TFunc >( callee );
assert( tf );
optional< TypeCheckingContext > bestTCC;
optional< Term > bestSol;
auto callPat = PrependToVectorTerm( args, HOLE( "_"_sid, "fwd"_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( Context& c, LocationId loc, const Value& callee, const Term& args,
const Term& checkedCallPat, TypeCheckingContext& tcc ) const final
{
DiagnosticsContext dc( loc, loc.invalid() ? "" : "Called here.", false );
auto instanceFunc = InstantiateTFunc( c, callee, checkedCallPat, tcc );
if( instanceFunc.isPoison() )
return PoisonValue();
auto pIR = GetInvocationRule( *c.env(), instanceFunc );
G_VAL_ASSERT( callee, pIR );
G_VAL_ASSERT( callee, pIR->canBeInvoked( c, instanceFunc ) );
return pIR->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, LocationId funcValLocation, const Value& callee,
const Term& checkedCallPat, TypeCheckingContext& tcc ) const final
{
DiagnosticsContext dc(
funcValLocation, funcValLocation.invalid() ? "" : "Instantiated here.", false );
return InstantiateTFunc( c, callee, checkedCallPat, tcc );
}
};
ptr< InvocationRule >& GetTFuncInvocationRule()
{
static ptr< InvocationRule > pRule = make_shared< TemplateFunctionInvocationRule >();
return pRule;
}
void SetupTemplateFunctionInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule(
ValueToEIR( ValuePattern( TSID( constant ), TFuncTypePattern(), ANYTERM( _ ) ) ),
GetTFuncInvocationRule() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/template/pretty.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::sema;
using namespace goose::builtins;
namespace goose::builtins
{
void SetupTemplatePrettyPrinting()
{
auto& pp = PrettyPrinter::GetInstance();
pp.addRule( ValueToEIR( ValuePattern( TSID( constant ),
| | | | | | | | | 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"
using namespace goose;
using namespace goose::sema;
using namespace goose::builtins;
namespace goose::builtins
{
void SetupTemplatePrettyPrinting()
{
auto& pp = PrettyPrinter::GetInstance();
pp.addRule( ValueToEIR( ValuePattern( TSID( constant ),
ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( tvar ) ) ) ),
ANYTERM( _ ) ) ),
[&]( auto&& out, auto&& t )
{
auto tvar = *FromValue< TVar >( *EIRToValue( t ) );
out << "TVar(" << tvar.name() << ')';
return true;
} );
pp.addRule( ValueToEIR( ValuePattern( TSID( constant ),
ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( ttvar ) ) ) ),
ANYTERM( _ ) ) ),
[&]( auto&& out, auto&& t )
{
auto tvar = *FromValue< TTVar >( *EIRToValue( t ) );
out << "TTVar(" << tvar.name() << ')';
return true;
} );
pp.addRule( ValueToEIR( ValuePattern( TSID( constant ),
ValueToEIR( Value( TypeType(), TSID( tnameddecl ) ) ),
VEC( ANYTERM( _ ), ANYTERM( _ ) ) ) ),
[&]( auto&& out, auto&& t )
{
auto decl = *FromValue< TNamedDecl >( *EIRToValue( t ) );
out << "tnameddecl(" << decl.type() << ", " << decl.name() << ")";
return true;
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/template/rules/decl.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include "builtins/builtins.h"
namespace goose::builtins
{
class DeclTemplateRule : public TemplateRule
{
optional< Term > buildSignature( const TemplateContext& c, const Term& val ) const final
{
auto decl = FromValue< Decl >( *EIRToValue( val ) );
assert( decl );
return ParamPat( decl->type() );
}
| > | > | | < < < | | 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 |
#include "builtins/builtins.h"
namespace goose::builtins
{
class DeclTemplateRule : public TemplateRule
{
optional< Term > buildSignature( const TemplateContext& c, const Term& val ) const final
{
auto decl = FromValue< Decl >( *EIRToValue( val ) );
assert( decl );
return ParamPat( decl->type() );
}
Generator< Value > buildParamDecls(
const Context& c, const Term& param, TermGen& args ) const final
{
args.consume();
co_yield *EIRToValue( param );
}
optional< Term > buildArgPattern( const Context& c, const Term& val ) const final
{
auto decl = FromValue< Decl >( *EIRToValue( val ) );
assert( decl );
return ValueToEIR(
ValuePattern( TSID( computed ), decl->type(), TERM( ptr< void >() ) ) );
}
bool isPackExpr( const Context& c, const Term& val ) const final { return false; }
};
void SetupDeclTemplateRule( Env& e )
{
auto declTypePat = ValueToEIR( Value( TypeType(), VEC( TSID( decl ), ANYTERM( _ ) ) ) );
e.templateRuleSet()->addRule(
ValueToEIR( ValuePattern( TSID( constant ), declTypePat, ANYTERM( _ ) ) ),
make_shared< DeclTemplateRule >() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/template/rules/rules.h.
1 2 3 4 5 |
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_RULES_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_RULES_H
namespace goose::builtins
{
| > | > | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_RULES_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_RULES_H
namespace goose::builtins
{
extern void SetupTVar(
const Context& c, TypeCheckingContext& tcc, StringId name, bool forwarding );
extern uint64_t HashTVarTypePredicates(
const Context& c, const TypeCheckingContext& tcc, StringId name, bool forwarding );
template< typename C >
Generator< uint64_t > ContainerTypePredicatesHashGenerator(
const Context& context, const TypeCheckingContext& tcc, const C& c )
{
for( const auto& x : c )
co_yield TemplateHashTypePredicates( context, tcc, x );
}
extern void SetupTVarTemplateRule( Env& e );
extern void SetupDeclTemplateRule( Env& e );
|
| ︙ | ︙ | |||
31 32 33 34 35 36 37 |
SetupTDeclTemplateRule( e );
SetupTVecTypeTemplateRule( e );
SetupValueTemplateRule( e );
SetupTFuncTypeTemplateRule( e );
SetupTPackTemplateRule( e );
SetupTupleTemplateRule( e );
}
| | | 34 35 36 37 38 39 40 41 42 43 |
SetupTDeclTemplateRule( e );
SetupTVecTypeTemplateRule( e );
SetupValueTemplateRule( e );
SetupTFuncTypeTemplateRule( e );
SetupTPackTemplateRule( e );
SetupTupleTemplateRule( e );
}
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/template/rules/tdecl.cpp.
| ︙ | ︙ | |||
15 16 17 18 19 20 21 |
{
auto td = *FromValue< TDecl >( *EIRToValue( val ) );
auto sig = BuildTemplateSignature( c, td.type() );
if( !sig )
return nullopt;
| > | > | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
{
auto td = *FromValue< TDecl >( *EIRToValue( val ) );
auto sig = BuildTemplateSignature( c, td.type() );
if( !sig )
return nullopt;
return TDeclSigPattern( TERM( td.name() ),
c.getTTVarPackIndex( td.name() ) ? Hole::Behavior::Pack : Hole::Behavior::Standard,
move( *sig ) );
}
Generator< Value > buildParamDecls(
const Context& c, const Term& param, TermGen& args ) const final
{
auto tdecl = FromValue< TDecl >( *EIRToValue( param ) );
assert( tdecl );
if( IsTemplatePackExpr( c, tdecl->type() ) )
{
co_yield buildParamDecls( c, tdecl->type(), args );
|
| ︙ | ︙ | |||
42 43 44 45 46 47 48 |
auto tdecl = FromValue< TDecl >( *EIRToValue( val ) );
assert( tdecl );
TemplateSetup( c, tcc, tdecl->type() );
SetupTVar( c, tcc, tdecl->name(), false );
}
| > | < | | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
auto tdecl = FromValue< TDecl >( *EIRToValue( val ) );
assert( tdecl );
TemplateSetup( c, tcc, tdecl->type() );
SetupTVar( c, tcc, tdecl->name(), false );
}
uint64_t hashTypePredicates(
const Context& c, const TypeCheckingContext& tcc, const Term& val ) const final
{
auto tdecl = FromValue< TDecl >( *EIRToValue( val ) );
assert( tdecl );
return llvm::hash_combine( TemplateHashTypePredicates( c, tcc, tdecl->type() ),
HashTVarTypePredicates( c, tcc, tdecl->name(), false ) );
}
optional< Term > buildArgPattern( const Context& c, const Term& val ) const final
{
return buildSignature( c, val );
}
|
| ︙ | ︙ | |||
71 72 73 74 75 76 77 |
void SetupTDeclTemplateRule( Env& e )
{
e.templateRuleSet()->addRule(
ValueToEIR( ValuePattern( TSID( constant ), GetValueType< TDecl >(), ANYTERM( _ ) ) ),
make_shared< TDeclTemplateRule >() );
}
| | | 73 74 75 76 77 78 79 80 |
void SetupTDeclTemplateRule( Env& e )
{
e.templateRuleSet()->addRule(
ValueToEIR( ValuePattern( TSID( constant ), GetValueType< TDecl >(), ANYTERM( _ ) ) ),
make_shared< TDeclTemplateRule >() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/template/rules/tfunctype.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include "builtins/builtins.h"
namespace goose::builtins
{
class TFuncTypeTemplateRule : public TemplateRule
{
bool prepare( TemplateContext& c, const Term& val ) const final
{
auto tft = FromValue< TFuncType >( *EIRToValue( val ) );
assert( tft );
bool success = true;
| | > | | | | | | | | > | | > | | | | > | | | | < < < | < < > | 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 |
#include "builtins/builtins.h"
namespace goose::builtins
{
class TFuncTypeTemplateRule : public TemplateRule
{
bool prepare( TemplateContext& c, const Term& val ) const final
{
auto tft = FromValue< TFuncType >( *EIRToValue( val ) );
assert( tft );
bool success = true;
ForEachInVectorTerm( tft->params(),
[&]( auto&& param )
{
if( !PrepareTemplate( c, param ) )
{
success = false;
return false;
}
return true;
} );
if( !success )
return false;
return PrepareTemplate( c, tft->returnType() );
}
optional< Term > buildSignature( const TemplateContext& c, const Term& val ) const final
{
auto tfuncType = FromValue< TFuncType >( *EIRToValue( val ) );
auto sig = BuildTFuncSignature( c.semaContext(), *tfuncType );
if( !sig )
return nullopt;
return TFuncTypeSigPattern( val, move( *sig ) );
}
Generator< Value > buildParamDecls(
const Context& c, const Term& param, TermGen& args ) const final
{
co_yield *EIRToValue( *args.consume() );
}
void setup( const Context& c, TypeCheckingContext& tcc, const Term& val ) const final
{
auto tft = FromValue< TFuncType >( *EIRToValue( val ) );
assert( tft );
ForEachInVectorTerm( tft->params(),
[&]( auto&& param )
{
TemplateSetup( c, tcc, param );
return true;
} );
TemplateSetup( c, tcc, tft->returnType() );
}
uint64_t hashTypePredicates(
const Context& c, const TypeCheckingContext& tcc, const Term& val ) const final
{
auto tft = FromValue< TFuncType >( *EIRToValue( val ) );
assert( tft );
const auto& vec = *get< pvec >( tft->params() );
auto g = ContainerTypePredicatesHashGenerator( c, tcc, vec.terms() );
auto paramsHash = llvm::hash_combine_range( g.begin(), g.end() );
return llvm::hash_combine(
paramsHash, TemplateHashTypePredicates( c, tcc, tft->returnType() ) );
}
optional< Term > buildArgPattern( const Context& c, const Term& val ) const final
{
return buildSignature( c, val );
}
bool isPackExpr( const Context& c, const Term& val ) const final { return false; }
};
void SetupTFuncTypeTemplateRule( Env& e )
{
e.templateRuleSet()->addRule( TFuncTypePattern(), make_shared< TFuncTypeTemplateRule >() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/template/rules/tnameddecl.cpp.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 |
auto typeSig = BuildTemplateSignature( c, tnd->type() );
if( !typeSig )
return nullopt;
return ParamPat( *typeSig );
}
| > | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
auto typeSig = BuildTemplateSignature( c, tnd->type() );
if( !typeSig )
return nullopt;
return ParamPat( *typeSig );
}
Generator< Value > buildParamDecls(
const Context& c, const Term& param, TermGen& args ) const final
{
auto tnd = FromValue< TNamedDecl >( *EIRToValue( param ) );
assert( tnd );
if( IsTemplatePackExpr( c, tnd->type() ) )
{
auto tup = EmptyOpenTuple();
|
| ︙ | ︙ | |||
45 46 47 48 49 50 51 |
void setup( const Context& c, TypeCheckingContext& tcc, const Term& val ) const final
{
auto tnd = FromValue< TNamedDecl >( *EIRToValue( val ) );
assert( tnd );
TemplateSetup( c, tcc, tnd->type() );
}
| > | | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
void setup( const Context& c, TypeCheckingContext& tcc, const Term& val ) const final
{
auto tnd = FromValue< TNamedDecl >( *EIRToValue( val ) );
assert( tnd );
TemplateSetup( c, tcc, tnd->type() );
}
uint64_t hashTypePredicates(
const Context& c, const TypeCheckingContext& tcc, const Term& val ) const final
{
auto tnd = FromValue< TNamedDecl >( *EIRToValue( val ) );
assert( tnd );
return TemplateHashTypePredicates( c, tcc, tnd->type() );
}
optional< Term > buildArgPattern( const Context& c, const Term& val ) const final
|
| ︙ | ︙ | |||
74 75 76 77 78 79 80 |
assert( tnd );
return IsTemplatePackExpr( c, tnd->type() );
}
};
void SetupTNamedDeclTemplateRule( Env& e )
{
| | | | | 76 77 78 79 80 81 82 83 84 85 86 87 |
assert( tnd );
return IsTemplatePackExpr( c, tnd->type() );
}
};
void SetupTNamedDeclTemplateRule( Env& e )
{
e.templateRuleSet()->addRule( ValueToEIR( ValuePattern( TSID( constant ),
GetValueType< TNamedDecl >(), ANYTERM( _ ) ) ),
make_shared< TNamedDeclTemplateRule >() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/template/rules/tpack.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include "builtins/builtins.h"
namespace goose::builtins
{
class TPackTemplateRule : public TemplateRule
{
bool prepare( TemplateContext& c, const Term& val ) const final
{
auto v = *EIRToValue( val );
if( c.currentPackIndex() != 0 )
{
| | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include "builtins/builtins.h"
namespace goose::builtins
{
class TPackTemplateRule : public TemplateRule
{
bool prepare( TemplateContext& c, const Term& val ) const final
{
auto v = *EIRToValue( val );
if( c.currentPackIndex() != 0 )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
v.locationId(), "pack expressions can't be nested." );
return false;
}
DiagnosticsContext dc( v.locationId(), "Inside this pack expression." );
uint32_t packIndex = c.newPackIndex();
c.setCurrentPackIndex( packIndex );
|
| ︙ | ︙ | |||
32 33 34 35 36 37 38 |
{
auto tp = FromValue< TPack >( *EIRToValue( val ) );
assert( tp );
return BuildTemplateSignature( c, tp->pattern() );
}
| > | > | | < < < | | 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 |
{
auto tp = FromValue< TPack >( *EIRToValue( val ) );
assert( tp );
return BuildTemplateSignature( c, tp->pattern() );
}
Generator< Value > buildParamDecls(
const Context& c, const Term& param, TermGen& args ) const final
{
auto tp = FromValue< TPack >( *EIRToValue( param ) );
assert( tp );
while( !args.finished() )
co_yield BuildTemplateParamDecls( c, tp->pattern(), args );
}
void setup( const Context& c, TypeCheckingContext& tcc, const Term& val ) const final
{
auto tp = FromValue< TPack >( *EIRToValue( val ) );
assert( tp );
TemplateSetup( c, tcc, tp->pattern() );
}
uint64_t hashTypePredicates(
const Context& c, const TypeCheckingContext& tcc, const Term& val ) const final
{
auto tp = FromValue< TPack >( *EIRToValue( val ) );
assert( tp );
return TemplateHashTypePredicates( c, tcc, tp->pattern() );
}
optional< Term > buildArgPattern( const Context& c, const Term& val ) const final
{
auto tp = FromValue< TPack >( *EIRToValue( val ) );
assert( tp );
return BuildTemplateArgPattern( c, tp->pattern() );
}
bool isPackExpr( const Context& c, const Term& val ) const final { return true; }
};
void SetupTPackTemplateRule( Env& e )
{
e.templateRuleSet()->addRule(
ValueToEIR( ValuePattern( TSID( constant ), GetValueType< TPack >(), ANYTERM( _ ) ) ),
make_shared< TPackTemplateRule >() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/template/rules/tuple.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "builtins/builtins.h"
namespace goose::builtins
{
class TupleTemplateRule : public TemplateRule
{
bool prepare( TemplateContext& c, const Term& t ) const final
{
auto tup = *EIRToValue( t );
LocationId packLoc;
| | > | | | | | | | | | | | | | | > | | < | | | | < | | | | | > > | | > | | | | > | | > | | < | | | | < | | | | | > | < < < | > < > | 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 |
#include "builtins/builtins.h"
namespace goose::builtins
{
class TupleTemplateRule : public TemplateRule
{
bool prepare( TemplateContext& c, const Term& t ) const final
{
auto tup = *EIRToValue( t );
LocationId packLoc;
ForEachTermInTuple( tup,
[&]( auto&& t )
{
if( !packLoc.invalid() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
packLoc, "pack expressions can only appear at the end of tuples." );
return false;
}
if( !PrepareTemplate( c, t ) )
return false;
if( IsTemplatePackExpr( c.semaContext(), t ) )
packLoc = EIRToValue( t )->locationId();
return true;
} );
return true;
}
optional< Term > buildSignature( const TemplateContext& c, const Term& t ) const final
{
auto tup = *EIRToValue( t );
auto v = make_shared< Vector >();
v->reserve( TupleSize( tup ) );
ForEachTermInTuple( tup,
[&]( auto&& t )
{
if( auto s = BuildTemplateSignature( c, t ) )
if( IsTemplatePackExpr( c.semaContext(), t ) )
v->setRepetitionTerm( move( *s ) );
else
v->append( move( *s ) );
else
v->append( t );
return true;
} );
return ValueToEIR( ValuePattern( TSID( constant ),
ValueToEIR( MkTupleType( TSID( closed ), HOLE( "_"_sid ) ) ), v ) );
}
Generator< Value > buildParamDecls(
const Context& c, const Term& param, TermGen& args ) const final
{
co_yield *EIRToValue( *args.consume() );
}
void setup( const Context& c, TypeCheckingContext& tcc, const Term& t ) const final
{
auto tup = *EIRToValue( t );
ForEachTermInTuple( tup,
[&]( auto&& t )
{
TemplateSetup( c, tcc, t );
return true;
} );
}
uint64_t hashTypePredicates(
const Context& c, const TypeCheckingContext& tcc, const Term& t ) const final
{
auto tup = EIRToValuePattern( t );
auto vec = get< pvec >( tup->val() );
assert( vec );
auto g = ContainerTypePredicatesHashGenerator( c, tcc, vec->terms() );
return llvm::hash_combine_range( g.begin(), g.end() );
}
optional< Term > buildArgPattern( const Context& c, const Term& t ) const final
{
auto tup = *EIRToValue( t );
auto v = make_shared< Vector >();
v->reserve( TupleSize( tup ) );
ForEachTermInTuple( tup,
[&]( auto&& t )
{
if( auto s = BuildTemplateArgPattern( c, t ) )
if( IsTemplatePackExpr( c, t ) )
v->setRepetitionTerm( move( *s ) );
else
v->append( move( *s ) );
else
v->append( t );
return true;
} );
return ValueToEIR( ValuePattern( TSID( constant ),
ValueToEIR( MkTupleType( TSID( closed ), HOLE( "_"_sid ) ) ), v ) );
}
bool isPackExpr( const Context& c, const Term& val ) const final { return false; }
};
void SetupTupleTemplateRule( Env& e )
{
e.templateRuleSet()->addRule(
ValueToEIR( ValuePattern( TSID( constant ),
ValueToEIR( MkTupleType( TSID( closed ), ANYTERM( _ ) ) ), ANYTERM( _ ) ) ),
make_shared< TupleTemplateRule >() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/template/rules/tvar.cpp.
| ︙ | ︙ | |||
32 33 34 35 36 37 38 |
valueToStore = MakeClosedTuple( *valueToStore, *EIRToValue( t ) );
pack = true;
}
else
valueToStore = AppendToTuple( *valueToStore, *EIRToValue( t ) );
}
| | < | | | | | | > > | | < | | | | | | 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 |
valueToStore = MakeClosedTuple( *valueToStore, *EIRToValue( t ) );
pack = true;
}
else
valueToStore = AppendToTuple( *valueToStore, *EIRToValue( t ) );
}
// 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.
if( !valueToStore )
return;
auto captureIdentity = AppendToVectorTerm( c.identity(), TERM( decoratedName ) );
c.env()->storeValue( captureIdentity, ANYTERM( _ ), ValueToEIR( *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 type checking context.
tcc.eraseLHSName( decoratedName );
}
Generator< size_t > PackPredicateHashGenerator(
const Context& c, const TypeCheckingContext& tcc, StringId name )
{
// TODO_SSA: reenable
/*for( auto&& t : CapturedPackTermGen( tcc, name ) )
{
auto val = EIRToValue( t );
auto preds = GetTypePredicates( *val );
if( !preds || !*preds )
continue;
( *preds )->parse( c );
co_yield ( *preds )->hash();
}*/
co_yield 0;
}
uint64_t HashTVarTypePredicates(
const Context& c, const TypeCheckingContext& tcc, StringId name, bool forwarding )
{
// By convention, the callee's pattern will always be on the lhs of the type checking,
// which means that the captures TVars will always be in the "left hand side" namespace
// of the type checking context.
StringId decoratedName = forwarding ? DecorateTTVarName( name ) : DecorateTVarName( name );
// 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 g = PackPredicateHashGenerator( c, tcc, decoratedName );
return llvm::hash_combine_range( g.begin(), g.end() );
}
class TVarTemplateRule : public TemplateRule
{
bool prepare( TemplateContext& c, const Term& val ) const final
|
| ︙ | ︙ | |||
106 107 108 109 110 111 112 |
optional< Term > buildSignature( const TemplateContext& c, const Term& val ) const final
{
auto tvar = FromValue< TVar >( *EIRToValue( val ) );
assert( tvar );
auto decoratedName = DecorateTVarName( tvar->name() );
return HOLE( decoratedName, "tvar"_sid,
| | > > | > | | < < < | > > | > | | < < < | | 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 |
optional< Term > buildSignature( const TemplateContext& c, const Term& val ) const final
{
auto tvar = FromValue< TVar >( *EIRToValue( val ) );
assert( tvar );
auto decoratedName = DecorateTVarName( tvar->name() );
return HOLE( decoratedName, "tvar"_sid,
c.getTVarPackIndex( decoratedName ) ? Hole::Behavior::Pack :
Hole::Behavior::Standard );
}
Generator< Value > buildParamDecls(
const Context& c, const Term& param, TermGen& args ) const final
{
co_yield *EIRToValue( *args.consume() );
}
void setup( const Context& c, TypeCheckingContext& tcc, const Term& val ) const final
{
auto tvar = FromValue< TVar >( *EIRToValue( val ) );
assert( tvar );
SetupTVar( c, tcc, tvar->name(), false );
}
uint64_t hashTypePredicates(
const Context& c, const TypeCheckingContext& tcc, const Term& val ) const final
{
auto tvar = FromValue< TVar >( *EIRToValue( val ) );
assert( tvar );
return HashTVarTypePredicates( c, tcc, tvar->name(), false );
}
optional< Term > buildArgPattern( const Context& c, const Term& val ) const final
{
return buildSignature( c, val );
}
bool isPackExpr( const Context& c, const Term& val ) const final { return false; }
};
class TTVarTemplateRule : public TemplateRule
{
bool prepare( TemplateContext& c, const Term& val ) const final
{
auto ttvar = FromValue< TTVar >( *EIRToValue( val ) );
assert( ttvar );
c.TTVarOccurred( DecorateTTVarName( ttvar->name() ) );
return true;
}
optional< Term > buildSignature( const TemplateContext& c, const Term& val ) const final
{
auto ttvar = FromValue< TTVar >( *EIRToValue( val ) );
assert( ttvar );
auto decoratedName = DecorateTTVarName( ttvar->name() );
return HOLE( decoratedName, "fwd"_sid,
c.getTTVarPackIndex( decoratedName ) ? Hole::Behavior::Pack :
Hole::Behavior::Standard );
}
Generator< Value > buildParamDecls(
const Context& c, const Term& param, TermGen& args ) const final
{
co_yield *EIRToValue( *args.consume() );
}
void setup( const Context& c, TypeCheckingContext& tcc, const Term& val ) const final
{
auto ttvar = FromValue< TTVar >( *EIRToValue( val ) );
assert( ttvar );
SetupTVar( c, tcc, ttvar->name(), true );
}
uint64_t hashTypePredicates(
const Context& c, const TypeCheckingContext& tcc, const Term& val ) const final
{
auto ttvar = FromValue< TTVar >( *EIRToValue( val ) );
assert( ttvar );
return HashTVarTypePredicates( c, tcc, ttvar->name(), true );
}
optional< Term > buildArgPattern( const Context& c, const Term& val ) const final
{
return buildSignature( c, val );
}
bool isPackExpr( const Context& c, const Term& val ) const final { return false; }
};
void SetupTVarTemplateRule( Env& e )
{
// TVar
e.templateRuleSet()->addRule(
ValueToEIR( ValuePattern( TSID( constant ), GetValueType< TVar >(), ANYTERM( _ ) ) ),
make_shared< TVarTemplateRule >() );
// TTVar
e.templateRuleSet()->addRule(
ValueToEIR( ValuePattern( TSID( constant ), GetValueType< TTVar >(), ANYTERM( _ ) ) ),
make_shared< TTVarTemplateRule >() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/template/rules/tvectype.cpp.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
{
auto tvecType = EIRToValuePattern( t );
auto tvec = TVecFromEIR( tvecType->val() );
assert( tvec );
for( auto&& x : tvec->content()->terms() )
{
| | | > | | | < < > | > | | 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 |
{
auto tvecType = EIRToValuePattern( t );
auto tvec = TVecFromEIR( tvecType->val() );
assert( tvec );
for( auto&& x : tvec->content()->terms() )
{
// TVec is a way to substitute a Vec for a TVec when building a type
// representation to easily make TExpr versions of complex types without having
// to handle them specifically. However, it's still a fixed structuring element,
// we don't want to allow packs in there.
if( IsTemplatePackExpr( c.semaContext(), x ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
EIRToValue( x )->locationId(), "pack expressions aren't allowed here." );
return false;
}
if( !PrepareTemplate( c, x ) )
return false;
}
return true;
}
optional< Term > buildSignature( const TemplateContext& c, const Term& t ) const final
{
auto tvecType = EIRToValuePattern( t );
auto tvec = TVecFromEIR( tvecType->val() );
assert( tvec );
auto v = make_shared< Vector >();
v->reserve( tvec->content()->terms().size() );
for( auto&& x : tvec->content()->terms() )
if( auto s = BuildTemplateSignature( c, x ) )
v->append( move( *s ) );
else
v->append( x );
return ValueToEIR( ValuePattern( TSID( constant ), TypeType(), v ) );
}
Generator< Value > buildParamDecls(
const Context& c, const Term& param, TermGen& args ) const final
{
co_yield *EIRToValue( *args.consume() );
}
void setup( const Context& c, TypeCheckingContext& tcc, const Term& t ) const final
{
auto tvecType = EIRToValuePattern( t );
auto tvec = TVecFromEIR( tvecType->val() );
assert( tvec );
for( auto&& x : tvec->content()->terms() )
TemplateSetup( c, tcc, x );
}
uint64_t hashTypePredicates(
const Context& c, const TypeCheckingContext& tcc, const Term& t ) const final
{
auto tvecType = EIRToValuePattern( t );
auto tvec = TVecFromEIR( tvecType->val() );
assert( tvec );
auto g = ContainerTypePredicatesHashGenerator( c, tcc, tvec->content()->terms() );
return llvm::hash_combine_range( g.begin(), g.end() );
|
| ︙ | ︙ | |||
91 92 93 94 95 96 97 |
else
v->append( x );
}
return ValueToEIR( ValuePattern( TSID( constant ), TypeType(), v ) );
}
| | < < < | | | | 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
else
v->append( x );
}
return ValueToEIR( ValuePattern( TSID( constant ), TypeType(), v ) );
}
bool isPackExpr( const Context& c, const Term& val ) const final { return false; }
};
void SetupTVecTypeTemplateRule( Env& e )
{
e.templateRuleSet()->addRule( ValueToEIR( ValuePattern( TSID( constant ), TypeType(),
VEC( TSID( texpr ), TSID( tvec ), ANYTERM( _ ) ) ) ),
make_shared< TVecTypeTemplateRule >() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/template/rules/value.cpp.
1 2 3 4 5 6 7 8 9 |
#include "builtins/builtins.h"
namespace goose::builtins
{
class ValueTemplateRule : public TemplateRule
{
bool prepare( TemplateContext& c, const Term& valEIR ) const final
{
auto val = *EIRToValue( valEIR );
| | < | > > | < | | < < < | | 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 |
#include "builtins/builtins.h"
namespace goose::builtins
{
class ValueTemplateRule : public TemplateRule
{
bool prepare( TemplateContext& c, const Term& valEIR ) const final
{
auto val = *EIRToValue( valEIR );
return PrepareTemplate( c, val.type() ) && PrepareTemplate( c, val.val() );
}
optional< Term > buildSignature( const TemplateContext& c, const Term& valEIR ) const final
{
auto val = *EIRToValue( valEIR );
auto vsig = BuildTemplateSignature( c, val.val() );
if( IsType( c.semaContext(), val ) )
return ParamPat( val.type(), vsig ? move( *vsig ) : val.val() );
auto tsig = BuildTemplateSignature( c, val.type() );
return ParamPat( tsig ? move( *tsig ) : val.type(), vsig ? move( *vsig ) : val.val() );
}
Generator< Value > buildParamDecls(
const Context& c, const Term& val, TermGen& args ) const final
{
args.consume();
co_yield *EIRToValue( val );
}
void setup( const Context& c, TypeCheckingContext& tcc, const Term& valEIR ) const final
{
auto val = *EIRToValue( valEIR );
TemplateSetup( c, tcc, val.type() );
TemplateSetup( c, tcc, val.val() );
}
uint64_t hashTypePredicates(
const Context& c, const TypeCheckingContext& tcc, const Term& valEIR ) const final
{
auto val = *EIRToValue( valEIR );
return llvm::hash_combine( TemplateHashTypePredicates( c, tcc, val.type() ),
TemplateHashTypePredicates( c, tcc, val.val() ) );
}
optional< Term > buildArgPattern( const Context& c, const Term& val ) const final
{
return buildSignature( c, val );
}
bool isPackExpr( const Context& c, const Term& val ) const final { return false; }
};
void SetupValueTemplateRule( Env& e )
{
e.templateRuleSet()->addRule(
ValueToEIR( ValuePattern( TSID( constant ), ANYTERM( _ ), ANYTERM( _ ) ) ),
make_shared< ValueTemplateRule >() );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/template/tc-tdecl.cpp.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 |
void SetupTDeclTypeChecking( Env& e )
{
auto tDeclPat = TDeclSigPattern( ANYTERM( _ ), Hole::Behavior::Any, ANYTERM( _ ) );
e.typeCheckingRuleSet()->addHalfUnificationRule( tDeclPat,
[]( const Term& lhs, TypeCheckingContext& c )
{
| | | | | > | | | | | > | | > | | < < < | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | < | < | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | < > | 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 |
void SetupTDeclTypeChecking( Env& e )
{
auto tDeclPat = TDeclSigPattern( ANYTERM( _ ), Hole::Behavior::Any, ANYTERM( _ ) );
e.typeCheckingRuleSet()->addHalfUnificationRule( tDeclPat,
[]( const Term& lhs, TypeCheckingContext& c )
{
auto [name, bhv, sig] = DecomposeTDSig( lhs );
return HalfUnify( sig, c );
} );
e.typeCheckingRuleSet()->addTypeCheckingRule( tDeclPat, ANYTERM( _ ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
auto [name, bhv, typeSig] = DecomposeTDSig( lhs );
auto tdeclHole = HOLE( DecorateTVarName( name ), ""_sid, bhv );
auto pat = ValueToEIR( Value( move( typeSig ), HOLE( "_"_sid ) ) );
for( auto&& [s, tcc] : TypeCheck( pat, rhs, tcc ) )
{
// We need to typecheck 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 savedRHSSubContext = tcc.RHSSubContext();
tcc.RHSSubContext() = tcc.LHSSubContext();
for( auto&& [s, tcc] : TypeCheck( s, tdeclHole, tcc ) )
{
tcc.RHSSubContext() = savedRHSSubContext;
co_yield { s, tcc };
}
}
} );
e.typeCheckingRuleSet()->addUnificationRule( tDeclPat, ANYTERM( _ ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
auto [name, bhv, typeSig] = DecomposeTDSig( lhs );
auto tdeclHole = HOLE( DecorateTVarName( name ), ""_sid, bhv );
auto pat = ValueToEIR( Value( move( typeSig ), HOLE( "_"_sid ) ) );
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 savedRHSSubContext = tcc.RHSSubContext();
tcc.RHSSubContext() = tcc.LHSSubContext();
for( auto&& [s, tcc] : Unify( s, tdeclHole, tcc ) )
{
tcc.RHSSubContext() = savedRHSSubContext;
co_yield { s, tcc };
}
}
} );
// tfunc tdecl param / tfunc arg
auto tDeclTFuncPat = TDeclSigPattern(
ANYTERM( _ ), Hole::Behavior::Any, TFuncTypeSigPattern( ANYTERM( _ ), ANYTERM( _ ) ) );
e.typeCheckingRuleSet()->addTypeCheckingRule(
tDeclTFuncPat,
ValueToEIR( ValuePattern( TSID( constant ), TFuncTypePattern(), ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
auto [name, bhv, typeSig] = DecomposeTDSig( lhs );
auto [_, tfSig] = DecomposeTFTSig( typeSig );
auto callPat = BuildArgPatternFromTDecl( tcc.context(), move( typeSig ) );
auto tdeclHole = HOLE( DecorateTVarName( name ), ""_sid, bhv );
auto rhsVal = *EIRToValue( rhs );
ConstrainedFunc cfunc( tfSig, GetTFuncInvocationRule(), rhsVal );
auto cFuncTerm = ValueToEIR( ToValue( move( cfunc ) ) );
// Create a new named hole namespace to isolate holes from the passed function from
// those in the called function.
auto savedRHSSubContext = tcc.RHSSubContext();
tcc.RHSSubContext().namespaceIndex = tcc.newNamespaceIndex();
auto oldValueRequired = tcc.isValueResolutionRequired();
tcc.setValueResolutionRequired( false );
for( auto&& [s, tcc] : TypeCheck( callPat, rhs, tcc ) )
{
// Restore the namespace
tcc.RHSSubContext() = savedRHSSubContext;
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.RHSSubContext() = tcc.LHSSubContext();
for( auto&& [s, tcc] : Unify( cFuncTerm, tdeclHole, tcc ) )
{
tcc.RHSSubContext() = savedRHSSubContext;
co_yield { s, tcc };
}
}
} );
// tfunc tdecl param / overloadset arg
e.typeCheckingRuleSet()->addTypeCheckingRule(
move( tDeclTFuncPat ),
ValueToEIR( ValuePattern(
TSID( constant ), GetValueType< ptr< sema::OverloadSet > >(), ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
auto [name, bhv, typeSig] = DecomposeTDSig( lhs );
auto [_, tfSig] = DecomposeTFTSig( typeSig );
auto callPat = BuildArgPatternFromTDecl( tcc.context(), move( typeSig ) );
auto tdeclHole = HOLE( DecorateTVarName( name ), ""_sid, bhv );
auto rhsVal = *EIRToValue( rhs );
ConstrainedFunc cfunc( tfSig, GetOverloadSetInvocationRule(), rhsVal );
auto cFuncTerm = ValueToEIR( ToValue( move( cfunc ) ) );
// Create a new named hole namespace to isolate holes from the passed function from
// those in the called function.
auto savedRHSSubContext = tcc.RHSSubContext();
tcc.RHSSubContext().namespaceIndex = tcc.newNamespaceIndex();
auto oldValueRequired = tcc.isValueResolutionRequired();
tcc.setValueResolutionRequired( false );
for( auto&& [s, tcc] : TypeCheck( callPat, rhs, tcc ) )
{
// Restore the namespace
tcc.RHSSubContext() = savedRHSSubContext;
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.RHSSubContext() = tcc.LHSSubContext();
for( auto&& [s, tcc] : Unify( cFuncTerm, tdeclHole, tcc ) )
{
tcc.RHSSubContext() = savedRHSSubContext;
co_yield { s, tcc };
}
}
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/template/tdecl.cpp.
| ︙ | ︙ | |||
15 16 17 18 19 20 21 |
return VEC( TSID( tdecl_sig ), name, ANYTERM( _ ), move( sig ) );
else
return VEC( TSID( tdecl_sig ), name, TERM( HoleBhvToSid( bhv ) ), move( sig ) );
}
tuple< StringId, Hole::Behavior, Term > DecomposeTDSig( const Term& tdsig )
{
| | < | < < < | < < | | | < < < < < | | | 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 |
return VEC( TSID( tdecl_sig ), name, ANYTERM( _ ), move( sig ) );
else
return VEC( TSID( tdecl_sig ), name, TERM( HoleBhvToSid( bhv ) ), move( sig ) );
}
tuple< StringId, Hole::Behavior, Term > DecomposeTDSig( const Term& tdsig )
{
auto result = Decompose(
tdsig, Vec( Lit( "tdecl_sig"_sid ), Val< StringId >(), SubTerm(), SubTerm() ) );
auto&& [name, bhvTerm, sig] = *result;
if( bhvTerm == ANYTERM( _ ) )
return { name, Hole::Behavior::Any, sig };
return { name, SidToHoleBhv( get< StringId >( bhvTerm ) ), sig };
}
} // namespace goose::builtins
namespace goose::eir
{
const Term& Bridge< TDecl >::Type()
{
static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( tdecl ) ) ) );
return type;
}
Value Bridge< TDecl >::ToValue( const TDecl& td )
{
return Value( Type(), VEC( td.type(), TERM( td.name() ) ) );
}
optional< TDecl > Bridge< TDecl >::FromValue( const Value& v )
{
if( !IsTDecl( v ) )
return nullopt;
auto result = Decompose( v.val(), Vec( SubTerm(), Val< StringId >() ) );
if( !result )
return nullopt;
auto&& [type, name] = *result;
return TDecl( type, name );
}
} // namespace goose::eir
|
Changes to bs/builtins/types/template/tdecl.h.
1 2 3 4 5 6 7 8 9 10 11 |
#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
{
| | | | | | < | > > | > | | | | | < | | | 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 |
#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, StringId name ) :
m_type( forward< T >( type ) ),
m_name( name )
{
}
const auto& type() const { return m_type; }
const auto& name() const { return m_name; }
private:
Term m_type;
StringId m_name;
};
extern bool IsTDecl( const Value& td );
extern Term TDeclSigPattern( const Term& name, Hole::Behavior bhv, Term&& sig );
extern tuple< StringId, Hole::Behavior, Term > DecomposeTDSig( const Term& tdsig );
} // namespace goose::builtins
namespace goose::eir
{
template<> struct Bridge< builtins::TDecl >
{
static const Term& Type();
static Value ToValue( const builtins::TDecl& td );
static optional< builtins::TDecl > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/template/texpr.cpp.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 |
}
bool IsTExpr( const Value& te )
{
if( te.isConstant() )
{
const auto* ppVec = get_if< pvec >( &te.val() );
| | < < < | > | | | | | | | | 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 |
}
bool IsTExpr( const Value& te )
{
if( te.isConstant() )
{
const auto* ppVec = get_if< pvec >( &te.val() );
if( ppVec && !( *ppVec )->empty() && ( **ppVec )[0] == TSID( texpr ) )
return true;
if( IsTuple( te ) )
{
bool isTExpr = false;
ForEachInTuple( te,
[&]( auto&& v )
{
if( !IsTExpr( v ) )
return true;
isTExpr = true;
return false;
} );
return isTExpr;
}
}
auto typeVal = EIRToValue( te.type() );
if( !typeVal )
return false;
const auto* ppVec = get_if< pvec >( &typeVal->val() );
if( !ppVec )
return false;
if( ( *ppVec )->empty() )
return false;
return ( **ppVec )[0] == TSID( texpr );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/template/tfunc.cpp.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 |
Term Bridge< TFunc >::Type( const builtins::TFunc& tf )
{
return ValueToEIR( ::ToValue( tf.type() ) );
}
Value Bridge< TFunc >::ToValue( const TFunc& 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 |
Term Bridge< TFunc >::Type( const builtins::TFunc& tf )
{
return ValueToEIR( ::ToValue( tf.type() ) );
}
Value Bridge< TFunc >::ToValue( const TFunc& tf )
{
return Value( Type( tf ),
VEC( Quote( tf.signature() ), tf.parentIdentity(), tf.identity(),
TERM( tf.tokens() ) ) );
}
optional< TFunc > Bridge< TFunc >::FromValue( const Value& v )
{
auto typeVal = EIRToValue( v.type() );
auto type = ::FromValue< TFuncType >( *typeVal );
if( !type )
return nullopt;
auto result = Decompose( v.val(),
Vec( SubTerm(), // signature
SubTerm(), // parentIdentity
SubTerm(), // identity
Val< ptr< void > >() // tokens
) );
if( !result )
return nullopt;
auto&& [signature, parentIdentity, identity, tokens] = *result;
return TFunc( move( *type ), *Unquote( signature ), parentIdentity, identity, tokens );
}
} // namespace goose::eir
|
Changes to bs/builtins/types/template/tfunc.h.
1 2 3 4 5 6 7 8 9 10 11 |
#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
{
| | | | | | | | | < | > > | > | > | > | > | > | | | < < < | | | | | | | < > > < | | | 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 |
#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 PI, typename I, typename V >
TFunc( T&& type, S&& signature, PI&& parentIdentity, I&& identity, V&& tokens ) :
m_type( forward< T >( type ) ),
m_signature( forward< S >( signature ) ),
m_parentIdentity( forward< PI >( parentIdentity ) ),
m_identity( forward< I >( identity ) ),
m_tokens( forward< V >( tokens ) )
{
}
const auto& type() const { return m_type; }
auto& type() { return m_type; }
const auto& signature() const { return m_signature; }
const auto& parentIdentity() const { return m_parentIdentity; }
const auto& identity() const { return m_identity; }
const auto& tokens() const { return m_tokens; }
void setTokens( const ptr< void >& pToks ) { m_tokens = pToks; }
private:
TFuncType m_type;
Term m_signature;
Term m_parentIdentity;
Term m_identity;
ptr< void > m_tokens;
};
extern Value InstantiateTFunc( const Context& c, const Value& callee,
const Term& checkedCallPat, const TypeCheckingContext& tcc );
} // namespace goose::builtins
namespace goose::eir
{
template<> struct Bridge< builtins::TFunc >
{
static Term Type( const builtins::TFunc& tf );
static Value ToValue( const builtins::TFunc& tf );
static optional< builtins::TFunc > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/template/tfunctype.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include "builtins/builtins.h"
#include "lex/lex.h"
#include "parse/parse.h"
using namespace goose::builtins;
using namespace goose::parse;
namespace goose::builtins
{
bool IsTFuncType( const Value& t )
{
auto result = Decompose( t.val(),
| < | < | | | | | | < | | < | | > | | < < < < | < < | < > | | | | | < | < | | | | | < | < | | 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 |
#include "builtins/builtins.h"
#include "lex/lex.h"
#include "parse/parse.h"
using namespace goose::builtins;
using namespace goose::parse;
namespace goose::builtins
{
bool IsTFuncType( const Value& t )
{
auto result = Decompose( t.val(),
Vec( Lit( "texpr"_sid ), Lit( "tfunc"_sid ),
SubTerm(), // return type
SubTerm(), // param types
SubTerm(), // verif stmts toks
SubTerm(), // verif stmts toks
SubTerm() // intrinsic flag
) );
return !!result;
}
bool IsTFunc( const Value& t )
{
return IsTFuncType( *EIRToValue( t.type() ) );
}
const Term& TFuncPattern::GetPattern()
{
static auto pattern = ValueToEIR( Value( TypeType(),
VEC( TSID( texpr ), TSID( tfunc ), HOLE( "_"_sid ), HOLE( "_"_sid ), HOLE( "_"_sid ),
HOLE( "_"_sid ), HOLE( "_"_sid ) ) ) );
return pattern;
}
const Term& TFuncTypePattern()
{
static auto tFuncTypePat = ValueToEIR( Value( TypeType(),
VEC( TSID( texpr ), TSID( tfunc ), ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ),
ANYTERM( _ ), ANYTERM( _ ) ) ) );
return tFuncTypePat;
}
tuple< Term, Term > DecomposeTFTSig( const Term& tdsig )
{
auto result = Decompose( tdsig, Vec( Lit( "tfunctype_sig"_sid ), SubTerm(), SubTerm() ) );
auto&& [type, sig] = *result;
return { type, sig };
}
} // namespace goose::builtins
namespace goose::eir
{
const Term& Bridge< TFuncType >::Type()
{
return TypeType();
}
Value Bridge< TFuncType >::ToValue( const TFuncType& tft )
{
return Value( Type(),
VEC( TSID( texpr ), TSID( tfunc ), tft.returnType(), tft.params(),
static_pointer_cast< void >( tft.preConds() ),
static_pointer_cast< void >( tft.postConds() ),
TERM( static_cast< uint64_t >( tft.kind() ) ) ) );
}
optional< TFuncType > Bridge< TFuncType >::FromValue( const Value& v )
{
auto result = Decompose( v.val(),
Vec( Lit( "texpr"_sid ), Lit( "tfunc"_sid ),
SubTerm(), // return type
SubTerm(), // param types
Val< ptr< void > >(), // propositions
Val< ptr< void > >(), // propositions
Val< uint64_t >() // kind
) );
if( !result )
return nullopt;
auto&& [rtype, params, preConds, postConds, kind] = *result;
return TFuncType( rtype, params, static_pointer_cast< Propositions >( preConds ),
static_pointer_cast< Propositions >( postConds ),
static_cast< FuncType::Kind >( kind ) );
}
} // namespace goose::eir
|
Changes to bs/builtins/types/template/tfunctype.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TFUNCTYPE_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TFUNCTYPE_H
namespace goose::builtins
{
class TFuncType
{
| | | | > | | | | | < | > > | | | | | < | > > | > | | > | | > | | > | | | | | | | | < | | | 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 |
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TFUNCTYPE_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TFUNCTYPE_H
namespace goose::builtins
{
class TFuncType
{
public:
template< typename R, typename P, typename PRE, typename POST >
TFuncType( R&& returnType, P&& params, PRE&& preConds, POST&& postConds,
FuncType::Kind kind = FuncType::Kind::Regular ) :
m_returnType( forward< R >( returnType ) ),
m_params( forward< P >( params ) ),
m_preConds( forward< PRE >( preConds ) ),
m_postConds( forward< POST >( postConds ) ),
m_kind( kind )
{
}
template< typename R, typename P >
TFuncType( R&& returnType, P&& params, FuncType::Kind kind = FuncType::Kind::Regular ) :
m_returnType( forward< R >( returnType ) ),
m_params( forward< P >( params ) ),
m_kind( kind )
{
}
const auto& returnType() const { return m_returnType; }
const auto& params() const { return m_params; }
const auto& preConds() const { return m_preConds; }
const auto& postConds() const { return m_postConds; }
auto& preConds() { return m_preConds; }
auto& postConds() { return m_postConds; }
auto kind() const { return m_kind; }
void setKind( FuncType::Kind k ) { m_kind = k; }
private:
Term m_returnType;
Term m_params;
ptr< Propositions > m_preConds = make_shared< Propositions >();
ptr< Propositions > m_postConds = make_shared< Propositions >();
FuncType::Kind m_kind = FuncType::Kind::Regular;
};
extern bool IsTFuncType( const Value& t );
extern bool IsTFunc( const Value& t );
// Helper to provide generic param patterns for template functions.
struct TFuncPattern
{
static const Term& GetPattern();
};
extern const Term& TFuncTypePattern();
static inline Term TFuncTypeSigPattern( const Term& type, Term&& sig )
{
return VEC( TSID( tfunctype_sig ), type, move( sig ) );
}
extern tuple< Term, Term > DecomposeTFTSig( const Term& tdsig );
} // namespace goose::builtins
namespace goose::eir
{
template<> struct Bridge< builtins::TFuncType >
{
static const Term& Type();
static Value ToValue( const builtins::TFuncType& tft );
static optional< builtins::TFuncType > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/template/tnameddecl.cpp.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 |
}
const Term& TNamedDeclWithInit::Pattern::GetPattern()
{
static auto pattern = GetValueType< TNamedDeclWithInit >();
return pattern;
}
| | > | | < < < < < | | < < < < | < < | | | 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 |
}
const Term& TNamedDeclWithInit::Pattern::GetPattern()
{
static auto pattern = GetValueType< TNamedDeclWithInit >();
return pattern;
}
} // namespace goose::builtins
namespace goose::eir
{
const Term& Bridge< TNamedDecl >::Type()
{
static auto type =
ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( tnameddecl ) ) ) );
return type;
}
Value Bridge< TNamedDecl >::ToValue( const TNamedDecl& td )
{
return Value( Type(), VEC( td.type(), TERM( td.name() ) ) );
}
optional< TNamedDecl > Bridge< TNamedDecl >::FromValue( const Value& v )
{
if( !IsTNamedDecl( v ) )
return nullopt;
auto result = Decompose( v.val(), Vec( SubTerm(), Val< StringId >() ) );
if( !result )
return nullopt;
auto&& [type, name] = *result;
return TNamedDecl( type, name );
}
const Term& Bridge< TNamedDeclWithInit >::Type()
{
static auto type = ValueToEIR( Value( TypeType(), TSID( tnameddecl_with_init ) ) );
return type;
}
Value Bridge< TNamedDeclWithInit >::ToValue( const TNamedDeclWithInit& td )
{
return Value( Type(), VEC( td.type(), td.name(), ValueToEIR( td.init() ), td.declLoc() ) );
}
optional< TNamedDeclWithInit > Bridge< TNamedDeclWithInit >::FromValue( const Value& v )
{
if( v.type() != GetValueType< TNamedDeclWithInit >() )
return nullopt;
auto result = Decompose(
v.val(), Vec( SubTerm(), Val< StringId >(), SubTerm(), Val< LocationId >() ) );
if( !result )
return nullopt;
auto&& [type, name, init, declLoc] = *result;
return TNamedDeclWithInit( move( type ), name, *EIRToValue( init ), declLoc );
}
} // namespace goose::eir
|
Changes to bs/builtins/types/template/tnameddecl.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TNAMEDDECL_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TNAMEDDECL_H
namespace goose::builtins
{
class TNamedDecl
{
| | | | | | < | > > | > | | | | | | | | | | | | | | | < | > > | > | > | > | | | | | | | | | | < > < | < | | | 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 |
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TNAMEDDECL_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TNAMEDDECL_H
namespace goose::builtins
{
class TNamedDecl
{
public:
template< typename T >
TNamedDecl( T&& type, StringId name ) :
m_type( forward< T >( type ) ),
m_name( name )
{
}
const auto& type() const { return m_type; }
const auto& name() const { return m_name; }
struct Pattern
{
static const Term& GetPattern();
};
private:
Term m_type;
StringId m_name;
};
class TNamedDeclWithInit
{
public:
template< typename T, typename V >
TNamedDeclWithInit( T&& type, StringId name, V&& init, LocationId declLoc ) :
m_init( forward< V >( init ) ),
m_type( forward< T >( type ) ),
m_name( name ),
m_declLoc( declLoc )
{
}
const auto& type() const { return m_type; }
const auto& name() const { return m_name; }
const auto& init() const { return m_init; }
auto declLoc() const { return m_declLoc; }
struct Pattern
{
static const Term& GetPattern();
};
private:
Value m_init;
Term m_type;
StringId m_name;
LocationId m_declLoc;
};
extern bool IsTNamedDecl( const Value& td );
} // namespace goose::builtins
namespace goose::eir
{
template<> struct Bridge< builtins::TNamedDecl >
{
static const Term& Type();
static Value ToValue( const builtins::TNamedDecl& td );
static optional< builtins::TNamedDecl > FromValue( const Value& v );
};
template<> struct Bridge< builtins::TNamedDeclWithInit >
{
static const Term& Type();
static Value ToValue( const builtins::TNamedDeclWithInit& td );
static optional< builtins::TNamedDeclWithInit > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/template/tpack.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "builtins/builtins.h"
using namespace goose::builtins;
namespace goose::builtins
{
bool IsTPack( const Value& tp )
{
return tp.type() == GetValueType< TPack >();
}
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include "builtins/builtins.h"
using namespace goose::builtins;
namespace goose::builtins
{
bool IsTPack( const Value& tp )
{
return tp.type() == GetValueType< TPack >();
}
} // namespace goose::builtins
namespace goose::eir
{
const Term& Bridge< TPack >::Type()
{
static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( tpack ) ) ) );
return type;
|
| ︙ | ︙ | |||
26 27 28 29 30 31 32 |
optional< TPack > Bridge< TPack >::FromValue( const Value& v )
{
if( !IsTPack( v ) )
return nullopt;
return TPack( v.val() );
}
| | | 26 27 28 29 30 31 32 33 |
optional< TPack > Bridge< TPack >::FromValue( const Value& v )
{
if( !IsTPack( v ) )
return nullopt;
return TPack( v.val() );
}
} // namespace goose::eir
|
Changes to bs/builtins/types/template/tpack.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_PACK_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_PACK_H
namespace goose::builtins
{
class TPack
{
| | | | | < | > > | | | | < | | | 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 |
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_PACK_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_PACK_H
namespace goose::builtins
{
class TPack
{
public:
template< typename T >
TPack( T&& pattern ) :
m_pattern( forward< T >( pattern ) )
{
}
const auto& pattern() const { return m_pattern; }
private:
Term m_pattern;
};
extern bool IsTPack( const Value& tp );
} // namespace goose::builtins
namespace goose::eir
{
template<> struct Bridge< builtins::TPack >
{
static const Term& Type();
static Value ToValue( const builtins::TPack& tp );
static optional< builtins::TPack > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/template/ttvar.cpp.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
StringId DecorateTTVarName( StringId name )
{
if( name == "_"_sid )
return name;
return StringId{ "$$" + name.str() };
}
| | | < < | | 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 |
StringId DecorateTTVarName( StringId name )
{
if( name == "_"_sid )
return name;
return StringId{ "$$" + name.str() };
}
} // namespace goose::builtins
namespace goose::eir
{
const Term& Bridge< TTVar >::Type()
{
static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( ttvar ) ) ) );
return type;
}
Value Bridge< TTVar >::ToValue( TTVar&& td )
{
return Value( Type(), TERM( td.name() ) );
}
optional< TTVar > Bridge< TTVar >::FromValue( const Value& v )
{
if( !IsTTVar( v ) )
return nullopt;
auto result = Decompose( v.val(), Val< StringId >() );
if( !result )
return nullopt;
auto&& name = *result;
return TTVar( name );
}
} // namespace goose::eir
|
Changes to bs/builtins/types/template/ttvar.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TTVAR_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TTVAR_H
namespace goose::builtins
{
class TTVar
{
| | | | < | > > | | | | < | | | 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 |
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TTVAR_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TTVAR_H
namespace goose::builtins
{
class TTVar
{
public:
TTVar( StringId name ) :
m_name( name )
{
}
const auto& name() const { return m_name; }
private:
StringId m_name;
};
extern bool IsTTVar( const Value& tv );
extern StringId DecorateTTVarName( StringId name );
} // namespace goose::builtins
namespace goose::eir
{
template<> struct Bridge< builtins::TTVar >
{
static const Term& Type();
static Value ToValue( builtins::TTVar&& tv );
static optional< builtins::TTVar > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/template/tvar.cpp.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
StringId DecorateTVarName( StringId name )
{
if( name == "_"_sid )
return name;
return StringId{ "$" + name.str() };
}
| | | < < | | 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 |
StringId DecorateTVarName( StringId name )
{
if( name == "_"_sid )
return name;
return StringId{ "$" + name.str() };
}
} // namespace goose::builtins
namespace goose::eir
{
const Term& Bridge< TVar >::Type()
{
static auto type = ValueToEIR( Value( TypeType(), VEC( TSID( texpr ), TSID( tvar ) ) ) );
return type;
}
Value Bridge< TVar >::ToValue( TVar&& td )
{
return Value( Type(), TERM( td.name() ) );
}
optional< TVar > Bridge< TVar >::FromValue( const Value& v )
{
if( !IsTVar( v ) )
return nullopt;
auto result = Decompose( v.val(), Val< StringId >() );
if( !result )
return nullopt;
auto&& name = *result;
return TVar( name );
}
} // namespace goose::eir
|
Changes to bs/builtins/types/template/tvar.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TVAR_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TVAR_H
namespace goose::builtins
{
class TVar
{
| | | | < | > > | | | | < | | | 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 |
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TVAR_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TVAR_H
namespace goose::builtins
{
class TVar
{
public:
TVar( StringId name ) :
m_name( name )
{
}
const auto& name() const { return m_name; }
private:
StringId m_name;
};
extern bool IsTVar( const Value& tv );
extern StringId DecorateTVarName( StringId name );
} // namespace goose::builtins
namespace goose::eir
{
template<> struct Bridge< builtins::TVar >
{
static const Term& Type();
static Value ToValue( builtins::TVar&& tv );
static optional< builtins::TVar > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/template/tvec.cpp.
1 2 | #include "builtins/builtins.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 |
#include "builtins/builtins.h"
// Now that TVecs are properly recognized as TExpr, we get an infinite loop during localvar local
// type inference because it contains a MutRef[$T]. This somehow causes something to be wrapped into
// a TVec instead of a Vec at some point because it contained that reference type, still as a TVec
// at that point
//
// should probably try to break in BuildVecOrTVec wherever it is that one of the terms is itself a
// TVec to understand how this happens and why this is an issue
namespace goose::builtins
{
Term TVecToEIR( const TVec& tv )
{
return VEC( TSID( texpr ), TSID( tvec ), tv.content() );
}
optional< TVec > TVecFromEIR( const Term& t )
{
auto result = Decompose( t, Vec( Lit( "texpr"_sid ), Lit( "tvec"_sid ), Val< pvec >() ) );
if( !result )
return nullopt;
auto&& [v] = *result;
return TVec( v );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/template/tvec.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TVEC_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TVEC_H
namespace goose::builtins
{
class TVec
{
| | | | | < | > > | | | | | < | < | | | 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 |
#ifndef GOOSE_BUILTINS_TYPES_TEMPLATE_TVEC_H
#define GOOSE_BUILTINS_TYPES_TEMPLATE_TVEC_H
namespace goose::builtins
{
class TVec
{
public:
template< typename T >
TVec( T&& content ) :
m_content( forward< T >( content ) )
{
}
const auto& content() const { return m_content; }
private:
pvec m_content;
};
extern Term TVecToEIR( const TVec& tv );
extern optional< TVec > TVecFromEIR( const Term& t );
// Construct either a normal Vector term or a TVec.
// If any of the provided term is a TExpr, construct a TVec.
//
// This is used to construct eir expressions for parametric types, so that they are
// automatically turned into a suitable TExpr if TExprs are passed as parameters.
template< typename... T > extern Term BuildVecOrTVec( T&&... terms )
{
auto vec = Vector::Make( terms... );
if( ( IsTExpr( EIRToValue( terms ) ) || ... ) )
return TVecToEIR( TVec( move( vec ) ) );
return vec;
}
#define TVEC( ... ) BuildVecOrTVec( __VA_ARGS__ )
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/template/typecheck.cpp.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
void SetupTemplateFunctionTypeChecking( Env& e )
{
// func type param / tfunc arg
e.typeCheckingRuleSet()->addTypeCheckingRule(
ParamPat( FuncTypePattern() ),
| | < < < | | | < | < < < | < < | | | | | | | | | | | | | | | > | | | | > | | | | | | | | | < < < | | | < | < < < | < < | | | | | | | | | | | | | | | | | > | | | | > | | | | | | | | | | 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 |
void SetupTemplateFunctionTypeChecking( Env& e )
{
// func type param / tfunc arg
e.typeCheckingRuleSet()->addTypeCheckingRule(
ParamPat( FuncTypePattern() ),
ValueToEIR( ValuePattern( TSID( constant ), TFuncTypePattern(), 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( *EIRToValue( type ) );
auto rhsVal = *EIRToValue( 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 savedRHSSubContext = tcc.RHSSubContext();
auto localNSId = tcc.newNamespaceIndex();
tcc.RHSSubContext().namespaceIndex = localNSId;
for( auto&& [s, tcc] : TypeCheck( tf.signature(), callPat, tcc ) )
{
// Restore the namespace
tcc.RHSSubContext() = savedRHSSubContext;
auto wrapped = WrapWithPostprocFunc( s,
[rhsVal, localNSId](
const Term& t, TypeCheckingContext tcc ) -> optional< Term >
{
tcc.RHSSubContext().namespaceIndex = localNSId;
DiagnosticsContext dc( rhsVal.locationId(),
rhsVal.locationId().invalid() ? "" : "Instantiated here.", false );
auto ifunc = InstantiateTFunc( tcc.context(), rhsVal, t, tcc.flip() );
if( ifunc.isPoison() )
return nullopt;
return ValueToEIR( ifunc );
} );
co_yield { move( wrapped ), tcc };
}
} );
// tfunc type param / tfunc arg
e.typeCheckingRuleSet()->addTypeCheckingRule(
ParamPat( TFuncTypeSigPattern( ANYTERM( _ ), ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern( TSID( constant ), TFuncTypePattern(), 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 [tfType, _] = DecomposeTFTSig( type );
auto callPat = BuildArgPatternFromTFuncType( tcc.context(), *EIRToValue( tfType ) );
assert( callPat );
auto rhsVal = *EIRToValue( 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 savedRHSSubContext = tcc.RHSSubContext();
auto localNSId = tcc.newNamespaceIndex();
tcc.RHSSubContext().namespaceIndex = localNSId;
for( auto&& [s, tcc] : TypeCheck( tf.signature(), *callPat, tcc ) )
{
// Restore the namespace
tcc.RHSSubContext() = savedRHSSubContext;
auto wrapped = WrapWithPostprocFunc( s,
[rhsVal, localNSId](
const Term& t, TypeCheckingContext tcc ) -> optional< Term >
{
tcc.RHSSubContext().namespaceIndex = localNSId;
DiagnosticsContext dc( rhsVal.locationId(),
rhsVal.locationId().invalid() ? "" : "Instantiated here.", false );
auto ifunc = InstantiateTFunc( tcc.context(), rhsVal, t, tcc.flip() );
if( ifunc.isPoison() )
return nullopt;
return ValueToEIR( ifunc );
} );
co_yield { move( wrapped ), tcc };
}
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/trivialchecks.cpp.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
{
void SetupTrivialChecks( Env& e )
{
using TypeAny = TypePatternParam< Value, ValuePatternAny >;
using TypeT = TypePatternParam< Value, ValuePatternT >;
// Default implementations for IsTrivialInitialization.
| | > | | < < < | | < < < > | < | < | > | | < < < | | < < < | > < | < < > | 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 |
{
void SetupTrivialChecks( Env& e )
{
using TypeAny = TypePatternParam< Value, ValuePatternAny >;
using TypeT = TypePatternParam< Value, ValuePatternT >;
// Default implementations for IsTrivialInitialization.
// If lhs and rhs are the same type, we consider the assignment trivial by default. If they
// differ, then it can't be trivial.
RegisterBuiltinFunc< Eager< bool >( TypeAny, TypeAny ) >( e, e.extIsTrivialInitialization(),
[]( const Value& lhsType, const Value& rhsType ) { return false; } );
RegisterBuiltinFunc< Eager< bool >( TypeT, TypeT ) >( e, e.extIsTrivialInitialization(),
[]( const Value& lhsType, const Value& rhsType ) { return true; } );
// Overload for tuples
using TupleTypeAny = TypePatternParam< Value, TuplePattern >;
RegisterBuiltinFunc< Intrinsic< bool( TupleTypeAny, TupleTypeAny ) > >( e,
e.extIsTrivialInitialization(),
[]( const Context& c, const Value& lhsTupType, const Value& rhsTupType )
{ return ToValue( IsTrivialTupleInitialization( c, lhsTupType, rhsTupType ) ); } );
// Default implementations for IsTrivialAssignment.
// If lhs and rhs are the same type, we consider the assignment trivial by default. If they
// differ, then it can't be trivial.
RegisterBuiltinFunc< Eager< bool >( TypeAny, TypeAny ) >( e, e.extIsTrivialAssignment(),
[]( const Value& lhsType, const Value& rhsType ) { return false; } );
RegisterBuiltinFunc< Eager< bool >( TypeT, TypeT ) >( e, e.extIsTrivialAssignment(),
[]( const Value& lhsType, const Value& rhsType ) { return true; } );
// Overload for tuples
using TupleTypeAny = TypePatternParam< Value, TuplePattern >;
RegisterBuiltinFunc< Intrinsic< bool( TupleTypeAny, TupleTypeAny ) > >( e,
e.extIsTrivialAssignment(),
[]( const Context& c, const Value& lhsTupType, const Value& rhsTupType )
{ return ToValue( IsTrivialTupleAssignment( c, lhsTupType, rhsTupType ) ); } );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/tuple/destroy.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupTupleDestroyValue( Env& e )
{
// DestroyValue() for tuples: destroy every member value.
| | > | > | | | | | | | | | | | | < > | 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"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupTupleDestroyValue( Env& e )
{
// DestroyValue() for tuples: destroy every member value.
RegisterBuiltinFunc< Intrinsic< void( CustomPattern< Value, TuplePattern > ) > >( e,
e.extDestroyValue(),
[]( const Context& c, const Value& tup )
{
if( !tup.isConstant() )
return;
ForEachInTuple( tup,
[&]( auto&& v )
{
DiagnosticsContext dc( v.locationId(), "When invoking _DestroyValue." );
auto result = InvokeOverloadSet(
c, c.env()->extDestroyValue(), MakeClosedTuple( v ) );
if( !result.isPoison() )
{
DiagnosticsContext dc2( v.locationId(), "When invoking _DropValue." );
InvokeOverloadSet( c, c.env()->extDropValue(),
MakeClosedTuple( c.builder(), move( result ) ) );
}
return true;
} );
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/tuple/drop.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupTupleDropValue( Env& e )
{
// DropValue() for tuples: drop every member value.
| | > | > | | | < | | | | 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 |
#include "builtins/builtins.h"
#include "parse/parse.h"
using namespace goose::parse;
using namespace goose::cir;
namespace goose::builtins
{
void SetupTupleDropValue( Env& e )
{
// DropValue() for tuples: drop every member value.
RegisterBuiltinFunc< Intrinsic< void( Value, CustomPattern< Value, TuplePattern > ) > >( e,
e.extDropValue(),
[]( const Context& c, const Value& b, const Value& tup )
{
if( !tup.isConstant() )
return;
ForEachInTuple( tup,
[&]( auto&& v )
{
DiagnosticsContext dc( v.locationId(), "When invoking _DropValue." );
InvokeOverloadSet( c, c.env()->extDropValue(), MakeClosedTuple( b, v ) );
return true;
} );
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/tuple/init.cpp.
| ︙ | ︙ | |||
23 24 25 26 27 28 29 |
auto rhsTupType = *EIRToValue( initTup.type() );
if( IsTupleConstant( initTup ) && IsTrivialTupleInitialization( c, tupType, rhsTupType ) )
{
auto cfg = GetCFG( c );
if( !cfg )
{
| | > > | | > | | | | | > | | | | | | | | | | | | | | | > | | | | | | > | > | | | > | | | | | | | | | | | | < | | | | < | < < > | 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 |
auto rhsTupType = *EIRToValue( initTup.type() );
if( IsTupleConstant( initTup ) && IsTrivialTupleInitialization( c, tupType, rhsTupType ) )
{
auto cfg = GetCFG( c );
if( !cfg )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
0, "initializing declarations are not allowed here." );
return;
}
if( auto bb = cfg->currentBB() )
bb->append( initTup, tupRef,
Store( lrefType.type(), initTup.locationId(), tupRef.locationId() ) );
return;
}
ForEachInTupleType( tupType,
[&]( auto&& t )
{
auto elemType = *EIRToValue( t );
auto loc = elemType.locationId();
// Create a mutable reference to the element to initialize
ReferenceType rt( t, MutAccessSpecifier() );
auto elemRef =
BuildComputedValue( ValueToEIR( ToValue( rt ) ), tupRef, Select( index, loc ) )
.setLocationId( loc );
auto elemInit = *EIRToValue( GetConstantTupleElement( initTup, index++ ) );
DiagnosticsContext dc( elemType.locationId(), "When invoking _Initialize." );
auto init = InvokeOverloadSet(
c, c.env()->extInitialize(), MakeClosedTuple( elemRef, move( elemInit ) ) );
DiagnosticsContext dc2( elemType.locationId(), "When invoking _DropValue." );
InvokeOverloadSet(
c, c.env()->extDropValue(), MakeClosedTuple( c.builder(), init ) );
return true;
} );
}
void SetupTupleInitialize( Env& e )
{
// Tuples default initialization: attempt to default initialize every element.
RegisterBuiltinFunc< Intrinsic< void(
CustomPattern< Value, ReferenceType::PatternMutableOf< TuplePattern > > ) > >( e,
e.extInitialize(),
[]( const Context& c, const Value& tupRef )
{
G_VAL_ASSERT( tupRef, !tupRef.isConstant() );
auto refType = *FromValue< ReferenceType >( *EIRToValue( tupRef.type() ) );
uint32_t index = 0;
auto tupType = *EIRToValue( refType.type() );
ForEachInTupleType( tupType,
[&]( auto&& t )
{
auto elemType = *EIRToValue( t );
auto loc = elemType.locationId();
// Create a mutable reference to the element to initialize
ReferenceType rt( t, MutAccessSpecifier() );
auto elemRef = BuildComputedValue(
ValueToEIR( ToValue( rt ) ), tupRef, Select( index++, loc ) )
.setLocationId( loc );
DiagnosticsContext dc(
elemType.locationId(), "When invoking _Initialize." );
auto init = InvokeOverloadSet(
c, c.env()->extInitialize(), MakeClosedTuple( elemRef ) );
DiagnosticsContext dc2(
elemType.locationId(), "When invoking _DropValue." );
InvokeOverloadSet(
c, c.env()->extDropValue(), MakeClosedTuple( c.builder(), init ) );
return true;
} );
} );
// Tuples initialization: intialize each element with the corresponding element
// from the initializer tuple.
RegisterBuiltinFunc< Intrinsic< void(
CustomPattern< Value, ReferenceType::PatternMutableOf< TuplePatternOfTypeT > >,
CustomConstantPattern< Value, TuplePatternOfTypeT > ) > >(
e, e.extInitialize(), InitTuple );
RegisterBuiltinFunc< Intrinsic< void(
CustomPattern< Value, ReferenceType::PatternMutableOf< TuplePattern > >,
CustomConstantPattern< Value, TuplePattern > ) > >( e, e.extInitialize(), InitTuple );
RegisterBuiltinFunc< Intrinsic< void(
CustomPattern< Value, ReferenceType::PatternMutableOf< TuplePattern > >,
CustomPattern< Value, TuplePattern > ) > >( e, e.extInitialize(),
[]( const Context& c, const Value& tupRef, const Value& initTup )
{ G_ERROR( "tuple init from a computed tuple is not yet implemented." ); } );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/tuple/lower.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::builtins;
namespace goose::builtins
{
void SetupTupleLowering( Env& 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 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 |
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::builtins;
namespace goose::builtins
{
void SetupTupleLowering( Env& e )
{
RegisterBuiltinFunc< Intrinsic< Value( TypePatternParam< Value, TuplePattern > ) > >( e,
e.extLowerType(),
[]( const Context& c, const Value& tupType )
{
auto resultTupType = EmptyClosedTupleType();
bool success = true;
ForEachInTupleType( tupType,
[&]( auto&& t )
{
auto loweredType = LowerType( c, *EIRToValue( t ) );
if( !loweredType || loweredType->isPoison() )
{
success = false;
return false;
}
resultTupType = AppendToTupleType( resultTupType, *loweredType );
return true;
} );
if( !success )
return PoisonValue();
return resultTupType;
} );
RegisterBuiltinFunc< Intrinsic< Value( CustomPattern< Value, TuplePattern > ) > >( e,
e.extLowerValue(),
[]( const Context& c, const Value& tup )
{
auto resultTup = IsOpenTuple( tup ) ? EmptyOpenTuple() : EmptyClosedTuple();
auto types = make_shared< Vector >();
auto vals = make_shared< Vector >();
bool success = true;
ForEachInTuple( tup,
[&]( auto&& t )
{
auto loweredValue = LowerValue( c, t );
if( !loweredValue || loweredValue->isPoison() )
{
success = false;
return false;
}
resultTup = AppendToTuple( resultTup, *loweredValue );
return true;
} );
if( !success )
return PoisonValue();
return resultTup;
} );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/tuple/tuple.cpp.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 |
}
Value AppendToTuple( const Value& tup, const ValuePattern& valPat )
{
auto tupType = EIRToValue( tup.type() );
assert( tupType );
| < | < | < | < | < | | < < < < < < | < | < < < < | | | 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 |
}
Value AppendToTuple( const Value& tup, const ValuePattern& valPat )
{
auto tupType = EIRToValue( tup.type() );
assert( tupType );
auto result = Value( ValueToEIR( AppendToTupleType( *tupType, valPat.type() ) ),
AppendToVectorTerm( tup.val(), ValueToEIR( valPat ) ) );
if( tup.isPoison() || valPat.isPoison() )
result.setPoison();
return result;
}
Value AppendToTuple( const Value& tup, const Value& val )
{
auto tupType = EIRToValue( tup.type() );
auto valType = EIRToValue( val.type() );
G_VAL_ASSERT( tup, tupType );
G_VAL_ASSERT( val, valType );
auto result = Value( ValueToEIR( AppendToTupleType( *tupType, *valType ) ),
AppendToVectorTerm( tup.val(), ValueToEIR( val ) ) );
if( tup.isPoison() || val.isPoison() )
result.setPoison();
return result;
}
Value PrependToTuple( const Value& val, const Value& tup )
{
auto tupType = EIRToValue( tup.type() );
auto valType = EIRToValue( val.type() );
assert( tupType );
assert( valType );
auto result = Value( ValueToEIR( PrependToTupleType( *valType, *tupType ) ),
PrependToVectorTerm( tup.val(), ValueToEIR( val ) ) );
if( tup.isPoison() || val.isPoison() )
result.setPoison();
return result;
}
Value ConcatenateTuples( const Value& ltup, const Value& rtup )
{
auto ltupType = EIRToValue( ltup.type() );
auto rtupType = EIRToValue( rtup.type() );
assert( ltupType );
assert( rtupType );
auto result = Value( ValueToEIR( ConcatenateTupleTypes( *ltupType, *rtupType ) ),
ConcatenateVectorTerms( ltup.val(), rtup.val() ) );
if( ltup.isPoison() || rtup.isPoison() )
result.setPoison();
return result;
}
Value VectorToTuple( const Term& kind, const pvec& vec )
{
auto types = make_shared< Vector >();
types->reserve( vec->terms().size() );
for( auto&& t : vec->terms() )
{
auto val = EIRToValue( t );
assert( val );
types->append( val->type() );
}
return Value( ValueToEIR( MkTupleType( kind, TERM( move( types ) ) ) ), vec );
}
bool IsTuple( const Value& t )
{
auto typeVal = EIRToValue( t.type() );
if( !typeVal || !typeVal->isConstant() )
return false;
auto result = Decompose( typeVal->val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );
return !!result;
}
bool IsOpenTuple( const Value& t )
{
if( !t.isConstant() )
return false;
auto typeVal = EIRToValue( t.type() );
auto result =
Decompose( typeVal->val(), Vec( Lit( "tuple"_sid ), Lit( "open"_sid ), SubTerm() ) );
return !!result;
}
Value CloseTuple( const Value& tup )
{
auto tupType = EIRToValue( tup.type() );
return Value( ValueToEIR( ToClosedTupleType( *tupType ) ), tup.val() )
.setLocationId( tup.locationId() );
}
size_t TupleSize( const Value& tup )
{
auto tupType = *EIRToValue( tup.type() );
return TupleTypeSize( tupType );
}
|
| ︙ | ︙ | |||
165 166 167 168 169 170 171 |
{
auto& vec = *get< pvec >( tup.val() );
return vec.terms()[index];
}
Value GetComputedTupleElement( const Value& tup, uint32_t index, const Term& accessSpecifier )
{
| > | | > | | | | | | | | | | | | | | 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 |
{
auto& vec = *get< pvec >( tup.val() );
return vec.terms()[index];
}
Value GetComputedTupleElement( const Value& tup, uint32_t index, const Term& accessSpecifier )
{
auto rt = ValueToEIR(
ToValue( ReferenceType( GetTupleElementType( tup, index ), accessSpecifier ) ) );
return BuildComputedValue( rt, tup, cir::Select( index, tup.locationId() ) );
}
bool IsTupleConstant( const Value& tup )
{
bool result = true;
ForEachInTuple( tup,
[&]( auto&& t )
{
if( !t.isConstant() )
{
result = false;
return false;
}
if( IsTuple( t ) && !IsTupleConstant( t ) )
{
result = false;
return false;
}
return true;
} );
return result;
}
Generator< Value > GenerateValuesFromConstantTuple( const Value& tup )
{
const auto& vec = *get< pvec >( tup.val() );
|
| ︙ | ︙ | |||
231 232 233 234 235 236 237 |
}
const Term& TuplePatternOfTypeT::GetPattern()
{
static auto pattern = ValueToEIR( MkTupleType( HOLE( "_"_sid ), HOLE( "T"_sid ) ) );
return pattern;
}
| | | 217 218 219 220 221 222 223 224 |
}
const Term& TuplePatternOfTypeT::GetPattern()
{
static auto pattern = ValueToEIR( MkTupleType( HOLE( "_"_sid ), HOLE( "T"_sid ) ) );
return pattern;
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/tuple/tuple.h.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 |
extern Value AppendToTuple( const Value& tup, const ValuePattern& valPat );
extern Value PrependToTuple( const Value& val, const Value& tup );
extern Value ConcatenateTuples( const Value& ltup, const Value& rtup );
extern Value VectorToTuple( const Term& kind, const pvec& vec );
| | < | < | > < | < | < | | 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 |
extern Value AppendToTuple( const Value& tup, const ValuePattern& valPat );
extern Value PrependToTuple( const Value& val, const Value& tup );
extern Value ConcatenateTuples( const Value& ltup, const Value& rtup );
extern Value VectorToTuple( const Term& kind, const pvec& vec );
template< typename... V > Value MakeClosedTuple( V&&... values );
template< typename... V > Value MakeOpenTuple( V&&... values );
extern bool IsTuple( const Value& t );
extern bool IsOpenTuple( const Value& t );
extern Value CloseTuple( const Value& tup );
extern size_t TupleSize( const Value& tup );
extern const Term& GetTupleElementType( const Value& tup, uint32_t index );
extern const Term& GetConstantTupleElement( const Value& tup, uint32_t index );
extern Term& GetConstantTupleElement( Value& tup, uint32_t index );
extern Value GetComputedTupleElement(
const Value& tup, uint32_t index, const Term& accessSpecifier );
template< typename F > void ForEachTermInTuple( const Value& tup, F&& func );
template< typename F > void ForEachInTuple( const Value& tup, F&& func );
template< typename F > bool ForEachInTuples( const Value& tup1, const Value& tup2, F&& func );
// Returns true if the tuple and all values it contains (including nested tuples)
// are all constant.
extern bool IsTupleConstant( const Value& tup );
extern Generator< Value > GenerateValuesFromConstantTuple( const Value& tup );
extern Generator< Value > GenerateValuesFromComputedTuple( const Value& tup );
|
| ︙ | ︙ | |||
70 71 72 73 74 75 76 |
static const Term& GetPattern();
};
struct TuplePatternOfTypeT
{
static const Term& GetPattern();
};
| | | < | | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
static const Term& GetPattern();
};
struct TuplePatternOfTypeT
{
static const Term& GetPattern();
};
} // namespace goose::builtins
namespace goose::eir
{
template< typename... T > struct Bridge< tuple< T... > >
{
static const Term& Type();
static Value ToValue( const tuple< T... >& x );
static optional< tuple< T... > > FromVectorTerm( const Term& v );
static optional< tuple< T... > > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/tuple/tuple.inl.
1 2 3 4 5 |
#ifndef GOOSE_BUILTINS_TYPES_TUPLE_INL
#define GOOSE_BUILTINS_TYPES_TUPLE_INL
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 |
#ifndef GOOSE_BUILTINS_TYPES_TUPLE_INL
#define GOOSE_BUILTINS_TYPES_TUPLE_INL
namespace goose::builtins
{
template< typename... V > Value MakeClosedTuple( V&&... values )
{
auto loc = Location::CreateSpanningLocation( values.locationId()... );
return Value(
ValueToEIR( MkTupleType( TSID( closed ), TERM( Vector::Make( values.type()... ) ) ) ),
TERM( Vector::Make( ValueToEIR( values )... ) ) )
.setLocationId( loc );
}
template< typename... V > Value MakeOpenTuple( V&&... values )
{
auto loc = Location::CreateSpanningLocation( values.locationId()... );
return Value(
ValueToEIR( MkTupleType( TSID( open ), TERM( Vector::Make( values.type()... ) ) ) ),
TERM( Vector::Make( ValueToEIR( values )... ) ) )
.setLocationId( loc );
}
template< typename F > void ForEachTermInTuple( const Value& tup, F&& func )
{
// Check that the tuple's payload is a vector. It might be a hole.
if( !holds_alternative< pvec >( tup.val() ) )
return;
ForEachInVectorTerm( tup.val(), [&]( auto&& t ) { return func( t ); } );
}
template< typename F > void ForEachInTuple( const Value& tup, F&& func )
{
// Check that the tuple's payload is a vector. It might be a hole.
if( !holds_alternative< pvec >( tup.val() ) )
return;
ForEachInVectorTerm( tup.val(), [&]( auto&& t ) { return func( *EIRToValue( t ) ); } );
}
template< typename F > bool ForEachInTupleType( const Value& tupType, F&& func )
{
auto decomp = Decompose( tupType.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );
if( !decomp )
return false;
auto&& [tupKind, tupTypesVec] = *decomp;
ForEachInVectorTerm( tupTypesVec, func );
return true;
}
template< typename F >
bool ForEachInTupleTypes( const Value& tupType1, const Value& tupType2, F&& func )
{
auto decomp1 = Decompose( tupType1.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );
if( !decomp1 )
return false;
auto&& [tupKind1, tupTypesVec1] = *decomp1;
auto decomp2 = Decompose( tupType2.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );
if( !decomp2 )
return false;
auto&& [tupKind2, tupTypesVec2] = *decomp2;
return ForEachInVectorTerms( tupTypesVec1, tupTypesVec2, func );
}
template< typename F > bool ForEachInTuples( const Value& tup1, const Value& tup2, F&& func )
{
// Check that the tuples' payloads are vector. They might be holes.
if( !holds_alternative< pvec >( tup1.val() ) )
return false;
if( !holds_alternative< pvec >( tup2.val() ) )
return false;
return ForEachInVectorTerms( tup1.val(), tup2.val(),
[&]( auto&& t1, auto&& t2 ) { return func( *EIRToValue( t1 ), *EIRToValue( t2 ) ); } );
}
} // namespace goose::builtins
namespace goose::eir
{
// Type
static inline const Value& BuildTupleType( const Value& typeSoFar )
{
return typeSoFar;
}
template< typename H, typename... T > static Value BuildTupleType( const Value& typeSoFar )
{
if constexpr( sizeof...( T ) > 0 )
{
return BuildTupleType< T... >(
builtins::AppendToTupleType( typeSoFar, *EIRToValue( GetValueType< H >() ) ) );
}
else
return builtins::AppendToTupleType( typeSoFar, *EIRToValue( GetValueType< H >() ) );
}
template< typename... T > const Term& Bridge< tuple< T... > >::Type()
{
if constexpr( sizeof...( T ) == 0 )
{
static auto type = ValueToEIR( builtins::EmptyClosedTupleType() );
return type;
}
else
{
static auto type =
ValueToEIR( BuildTupleType< T... >( builtins::EmptyClosedTupleType() ) );
return type;
}
}
// Val
template< typename T, size_t... I > auto BuildTupleValue( const T& tup, index_sequence< I... > )
{
return VEC( ValueToEIR( ToValue( get< I >( tup ) ) )... );
}
template< typename... T > Value Bridge< tuple< T... > >::ToValue( const tuple< T... >& x )
{
auto val = BuildTupleValue( x, index_sequence_for< T... >() );
return Value( Type(), move( val ) );
}
template< typename TT, size_t... I >
optional< TT > BuildTupleFromValueVec( const Vector& vec, index_sequence< I... > )
{
if constexpr( sizeof...( I ) == 0 )
return TT();
else
{
auto values = make_tuple( EIRToValue( vec[I] )... );
if( ( !get< I >( values ) || ... ) )
return nullopt;
auto items =
make_tuple( FromValue< tuple_element_t< I, TT > >( *get< I >( values ) )... );
if( ( !get< I >( items ) || ... ) )
return nullopt;
return TT( move( *get< I >( items ) )... );
}
}
|
| ︙ | ︙ | |||
195 196 197 198 199 200 201 |
optional< tuple< T... > > Bridge< tuple< T... > >::FromValue( const Value& v )
{
if( v.type() != Type() )
return nullopt;
return FromVectorTerm( v.val() );
}
| | | 163 164 165 166 167 168 169 170 171 172 |
optional< tuple< T... > > Bridge< tuple< T... > >::FromValue( const Value& v )
{
if( v.type() != Type() )
return nullopt;
return FromVectorTerm( v.val() );
}
} // namespace goose::eir
#endif
|
Changes to bs/builtins/types/tuple/tupletype.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include "builtins/builtins.h"
#include "codegen/codegen.h"
using namespace goose;
using namespace goose::builtins;
using namespace goose::eir;
using namespace goose::codegen;
namespace goose::builtins
{
void SetupTupleTypeExtPoints( Env& 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 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 |
#include "builtins/builtins.h"
#include "codegen/codegen.h"
using namespace goose;
using namespace goose::builtins;
using namespace goose::eir;
using namespace goose::codegen;
namespace goose::builtins
{
void SetupTupleTypeExtPoints( Env& e )
{
RegisterBuiltinFunc< Intrinsic< bool( CustomPattern< Value, TuplePattern > ) > >( e,
e.extIsType(),
[]( const Context& c, const Value& v ) { return ToValue( IsTupleOfTypes( c, v ) ); } );
}
Value MkTupleType( const Term& kind, const Term& types )
{
return Value( TypeType(), VEC( TSID( tuple ), kind, types ) );
}
bool IsTupleOfTypes( const Context& c, const Value& v )
{
if( !v.isConstant() )
return false;
bool isType = true;
ForEachInTuple( v,
[&]( auto&& v )
{
if( IsTExpr( v ) || IsType( c, v ) )
return true;
isType = false;
return false;
} );
return isType;
}
extern Value TupleOfTypesToTupleType( const Value& tup )
{
auto typeVal = EIRToValue( tup.type() );
auto result = Decompose( typeVal->val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );
auto&& [kind, typeTypes] = *result;
return MkTupleType( kind, tup.val() );
}
const Value& EmptyOpenTupleType()
{
static auto type = MkTupleType( TSID( open ), VEC() );
return type;
}
const Value& EmptyClosedTupleType()
{
static auto type = MkTupleType( TSID( closed ), VEC() );
return type;
}
Value ToClosedTupleType( const Value& tuptype )
{
auto decomp = Decompose( tuptype.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );
auto&& [tupKind, tupTypesVec] = *decomp;
return MkTupleType( TSID( closed ), tupTypesVec ).setLocationId( tuptype.locationId() );
}
Value AppendToTupleType( const Value& tuptype, const Term& type )
{
auto decomp = Decompose( tuptype.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );
auto&& [tupKind, tupTypesVec] = *decomp;
auto newTypeVec = AppendToVectorTerm( tupTypesVec, type );
auto result = MkTupleType( tupKind, newTypeVec );
if( tuptype.isPoison() )
result.setPoison();
return result;
}
Value AppendToTupleType( const Value& tuptype, const Value& type )
{
auto decomp = Decompose( tuptype.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );
auto&& [tupKind, tupTypesVec] = *decomp;
auto newTypeVec = AppendToVectorTerm( tupTypesVec, ValueToEIR( type ) );
auto result = MkTupleType( tupKind, newTypeVec );
if( tuptype.isPoison() || type.isPoison() )
result.setPoison();
return result;
}
Value PrependToTupleType( const Value& type, const Value& tuptype )
{
auto decomp = Decompose( tuptype.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );
auto&& [tupKind, tupTypesVec] = *decomp;
auto newTypeVec = PrependToVectorTerm( tupTypesVec, ValueToEIR( type ) );
auto result = MkTupleType( tupKind, newTypeVec );
if( tuptype.isPoison() || type.isPoison() )
result.setPoison();
return result;
}
Value ConcatenateTupleTypes( const Value& ltuptype, const Value& rtuptype )
{
auto ldecomp = Decompose( ltuptype.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );
auto&& [ltupKind, ltupTypesVec] = *ldecomp;
auto rdecomp = Decompose( rtuptype.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );
auto&& [rtupKind, rtupTypesVec] = *rdecomp;
auto newTypeVec = ConcatenateVectorTerms( ltupTypesVec, rtupTypesVec );
auto result = MkTupleType( ltupKind, newTypeVec );
if( ltuptype.isPoison() || rtuptype.isPoison() )
result.setPoison();
return result;
}
bool IsTupleType( const Value& typeVal )
{
if( !typeVal.isConstant() )
return false;
auto result = Decompose( typeVal.val(), Vec( Lit( "tuple"_sid ), SubTerm(), SubTerm() ) );
return !!result;
}
size_t TupleTypeSize( const Value& tupType )
{
auto decomp =
Decompose( tupType.val(), Vec( Lit( "tuple"_sid ), SubTerm(), Val< pvec >() ) );
auto&& [tupKind, tupTypesVec] = *decomp;
return tupTypesVec->terms().size();
}
const Term& GetTupleTypeElement( const Value& tupType, uint32_t index )
{
auto decomp =
Decompose( tupType.val(), Vec( Lit( "tuple"_sid ), SubTerm(), Val< pvec >() ) );
auto&& [tupKind, tupTypesVec] = *decomp;
assert( index < tupTypesVec->terms().size() || tupTypesVec->repetitionTerm() );
if( index < tupTypesVec->terms().size() )
return tupTypesVec->terms()[index];
else
return *tupTypesVec->repetitionTerm();
}
bool IsTrivialTupleInitialization(
const Context& c, const Value& lhsTupType, const Value& rhsTupType )
{
bool result = true;
if( !ForEachInTupleTypes( lhsTupType, rhsTupType,
[&]( auto&& lhs, auto&& rhs )
{
if( !IsTrivialInitialization( c, *EIRToValue( lhs ), *EIRToValue( rhs ) ) )
{
result = false;
return false;
}
return true;
} ) )
return false;
return result;
}
bool IsTrivialTupleAssignment(
const Context& c, const Value& lhsTupType, const Value& rhsTupType )
{
bool result = true;
if( !ForEachInTupleTypes( lhsTupType, rhsTupType,
[&]( auto&& lhs, auto&& rhs )
{
if( !IsTrivialAssignment( c, *EIRToValue( lhs ), *EIRToValue( rhs ) ) )
{
result = false;
return false;
}
return true;
} ) )
return false;
return result;
}
const codegen::Type* GetTupleCodegenType( const Value& tupType )
{
|
| ︙ | ︙ | |||
287 288 289 290 291 292 293 |
if( !success )
return nullptr;
return codegen::Type::Get(
llvm::StructType::get( GetLLVMContext(), elements, false ) );*/
return nullptr;
}
| | | 232 233 234 235 236 237 238 239 |
if( !success )
return nullptr;
return codegen::Type::Get(
llvm::StructType::get( GetLLVMContext(), elements, false ) );*/
return nullptr;
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/tuple/tupletype.h.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 |
extern Value ConcatenateTupleTypes( const Value& ltup, const Value& rtup );
extern bool IsTupleType( const Value& typeVal );
extern size_t TupleTypeSize( const Value& tupType );
extern const Term& GetTupleTypeElement( const Value& tupType, uint32_t index );
| < | | | > > < > | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
extern Value ConcatenateTupleTypes( const Value& ltup, const Value& rtup );
extern bool IsTupleType( const Value& typeVal );
extern size_t TupleTypeSize( const Value& tupType );
extern const Term& GetTupleTypeElement( const Value& tupType, uint32_t index );
template< typename F > bool ForEachInTupleType( const Value& tupType, F&& func );
template< typename F >
bool ForEachInTupleTypes( const Value& tupType1, const Value& tupType2, F&& func );
extern bool IsTrivialTupleInitialization(
const Context& c, const Value& lhsTupType, const Value& rhsTupType );
extern bool IsTrivialTupleAssignment(
const Context& c, const Value& lhsTupType, const Value& rhsTupType );
extern const codegen::Type* GetTupleCodegenType( const Value& tupType );
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/tuple/typecheck.cpp.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 |
uint32_t elemIndex = 0;
for( auto&& t : elems->terms() )
{
auto val = EIRToValue( t );
assert( val );
types->append( val->type() );
| | < < | < | | < | > | < > | > | > | | > | > | > | | | | | | | | < | < | < | < | | | | | | | | | | | | < | < | < | < | | | | | | | | | | | > | | | | > | | | < < < | < | < | | | | | | | | | < | < | < < < | | | | | | | | | | | | | | | | | | | < | < | < | < | | | | < | < < > | 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 |
uint32_t elemIndex = 0;
for( auto&& t : elems->terms() )
{
auto val = EIRToValue( t );
assert( val );
types->append( val->type() );
AppendToInstrSeq( is, *val, DataPathOf( tupTempUID, locationId ),
Select( elemIndex++, val->locationId() ),
Store( val->type(), val->locationId(), locationId ) );
}
auto tupType = MkTupleType( closed ? TSID( closed ) : TSID( open ), TERM( move( types ) ) );
auto tupTypeTerm = ValueToEIR( tupType );
auto tupIS = make_shared< InstrSeq >();
AppendToInstrSeq( *tupIS, AllocVar( tupTypeTerm, tupTempUID, locationId ) );
AppendToInstrSeq( *tupIS, move( is ) );
AppendToInstrSeq(
*tupIS, DataPathOf( tupTempUID, locationId ), Load( tupTypeTerm, locationId ) );
return Value( move( tupTypeTerm ), move( tupIS ) );
}
TCGen TypeCheckTuple( const TypeCheckingContext& tcc, const Value& tupType, pvec args,
uint32_t index, bool isComputed, const pvec& out, bool closed, LocationId loc )
{
auto param = ParamPat( GetTupleTypeElement( tupType, index ) );
auto tupSize = args->terms().size();
for( auto&& [s, tcc] : TypeCheck( param, args->terms()[index], tcc ) )
{
if( !EIRToValue( s )->isConstant() )
isComputed = true;
auto newOut = make_shared< Vector >( Vector::MakeAppend( *out, move( s ) ) );
if( index == ( tupSize - 1 ) )
if( isComputed )
co_yield { ValueToEIR( BuildComputedTuple( newOut, closed, loc ) ), tcc };
else
co_yield { ValueToEIR(
VectorToTuple( closed ? TSID( closed ) : TSID( open ), newOut )
.setLocationId( loc ) ),
tcc };
else
co_yield TypeCheckTuple(
tcc, tupType, args, index + 1, isComputed, newOut, closed, loc );
}
}
TCGen TypeCheckConstantTuple( const TypeCheckingContext& tcc, const Value& tupType,
const Value& tupArg, const pvec& out, bool closed )
{
auto argVec = get< pvec >( tupArg.val() );
co_yield TypeCheckTuple( tcc, tupType, argVec, 0, false, out, closed, tupArg.locationId() );
}
TCGen TypeCheckComputedTuple( const TypeCheckingContext& tcc, const Value& tupType,
const Value& tupArg, const Value& tupArgRef, const pvec& out, bool closed )
{
G_VAL_ASSERT( tupArgRef, !tupArgRef.isConstant() );
auto argRefs = make_shared< Vector >();
argRefs->reserve( TupleSize( tupArg ) );
uint32_t index = 0;
ForEachInTupleType( *EIRToValue( tupArg.type() ),
[&]( auto&& type )
{
ReferenceType rt( type, ConstAccessSpecifier() );
auto argRef = ValueToEIR( BuildComputedValue( ValueToEIR( ToValue( rt ) ),
tupArgRef, cir::Select( index++, tupArg.locationId() ) ) );
argRefs->append( move( argRef ) );
return true;
} );
co_yield TypeCheckTuple( tcc, tupType, argRefs, 0, true, out, closed, tupArg.locationId() );
}
void SetupTupleTypeChecking( Env& e )
{
e.typeCheckingRuleSet()->addTypeCheckingRule(
ValueToEIR( ValuePattern( ANYTERM( _ ),
ValueToEIR( MkTupleType( ANYTERM( S ), VECOFLENGTH( L ) ) ), ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern( TSID( constant ),
ValueToEIR( MkTupleType( ANYTERM( S ), VECOFLENGTH( L ) ) ), VECOFLENGTH( L ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto ltup = EIRToValuePattern( lhs );
auto rtup = EIRToValue( rhs );
if( !ltup || !rtup )
co_return;
auto tupType = *EIRToValue( ltup->type() );
auto out = make_shared< Vector >();
out->reserve( TupleSize( *rtup ) );
co_yield TypeCheckConstantTuple( tcc, tupType, *rtup, out, !IsOpenTuple( *rtup ) );
} );
e.typeCheckingRuleSet()->addTypeCheckingRule(
ValueToEIR( ValuePattern( ANYTERM( _ ),
ValueToEIR( MkTupleType( ANYTERM( S ), VECOFLENGTH( L ) ) ), ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern( TSID( computed ),
ValueToEIR( MkTupleType( ANYTERM( S ), VECOFLENGTH( L ) ) ), ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto ltup = EIRToValuePattern( lhs );
auto rtup = EIRToValue( rhs );
if( !ltup || !rtup )
co_return;
auto cfg = GetCFG( tcc.context() );
if( !cfg )
co_return;
auto tupType = *EIRToValue( ltup->type() );
auto tempIndex = util::GenerateNewUID();
auto rtupref = BuildComputedValue(
ValueToEIR( ToValue( ReferenceType{ rtup->type(), ConstAccessSpecifier() } ) ),
*rtup, cir::DataPathOf( tempIndex, rtup->locationId() ) );
auto out = make_shared< Vector >();
out->reserve( TupleSize( *rtup ) );
co_yield TypeCheckComputedTuple(
tcc, tupType, *rtup, rtupref, out, !IsOpenTuple( *rtup ) );
} );
// Single element tuple unwrapping rules: if we encounter such a tuple, attempt to typecheck
// its contained value with whatever's on the other side.
e.typeCheckingRuleSet()->addTypeCheckingRule(
ValueToEIR( ValuePattern( ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern( TSID( constant ),
ValueToEIR( MkTupleType( ANYTERM( _ ), VEC( ANYTERM( _ ) ) ) ), ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto tup = EIRToValue( rhs );
if( !tup )
co_return;
const auto& vec1 = *get< pvec >( tup->val() );
co_yield TypeCheck( lhs, vec1[0], tcc );
} );
e.typeCheckingRuleSet()->addTypeCheckingRule(
ValueToEIR( ValuePattern( ANYTERM( _ ),
ValueToEIR( MkTupleType( ANYTERM( _ ), VEC( ANYTERM( _ ) ) ) ), ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern( TSID( constant ), ANYTERM( _ ), ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto tup = EIRToValue( 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 type checking 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(
ValueToEIR( ValuePattern( ANYTERM( _ ),
ValueToEIR( MkTupleType( ANYTERM( _ ), VEC( ANYTERM( _ ) ) ) ), ANYTERM( _ ) ) ),
ValueToEIR( ValuePattern( ANYTERM( _ ), HolePattern(), ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{ co_yield { lhs, tcc }; } );
// When typechecking a tuple of types against a type,
// convert it to a tuple type.
e.typeCheckingRuleSet()->addTypeCheckingRule(
ParamPat( TypeType() ),
ValueToEIR(
Value( ValueToEIR( MkTupleType( ANYTERM( _ ), VEC( REPEAT( TypeType() ) ) ) ),
ANYTERM( _ ) ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{ co_yield { ValueToEIR( TupleOfTypesToTupleType( *EIRToValue( rhs ) ) ), tcc }; } );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/types.cpp.
1 2 3 4 5 6 7 8 9 |
#include "builtins/builtins.h"
namespace goose::builtins
{
void SetupBuiltinTypes( Env& e )
{
SetupDefaultTypeExtPoints( e );
// TOSO_SSA reenable
| | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include "builtins/builtins.h"
namespace goose::builtins
{
void SetupBuiltinTypes( Env& e )
{
SetupDefaultTypeExtPoints( e );
// TOSO_SSA reenable
// SetupPropositionsDropValue( e );
// SetupGhostCodeDropValue( e );
// SetupPredicatesTypeChecking( e );
SetupBasicTypes( e );
SetupBasicTypesPrettyPrinting();
SetupTupleTypeChecking( e );
SetupFuncInvocationRules( e );
|
| ︙ | ︙ | |||
62 63 64 65 66 67 68 |
SetupTupleLowering( e );
SetupRuntimeBasicTypesInitialize( e );
}
void SetupDefaultTypeExtPoints( Env& e )
{
| | | | < < < | | < < < | < | | | | | 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 |
SetupTupleLowering( e );
SetupRuntimeBasicTypesInitialize( e );
}
void SetupDefaultTypeExtPoints( Env& e )
{
RegisterBuiltinFunc< Intrinsic< bool( Value ) > >(
e, e.extIsType(), []( const Context& c, const Value& v ) { return ToValue( false ); } );
RegisterBuiltinFunc< Intrinsic< bool( CustomPattern< Value, PatternType > ) > >(
e, e.extIsType(), []( const Context& c, const Value& v ) { return ToValue( true ); } );
}
bool IsType( const Context& c, const Value& v, source_location sloc )
{
if( v.isType() )
return true;
auto result = InvokeOverloadSet(
c, c.env()->extIsType(), MakeClosedTuple( v ), Location::Create( sloc ) );
if( result.isPoison() )
{
PoisonBuilder( c );
return false;
}
auto boolRes = FromValue< bool >( result );
if( !boolRes )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
v.locationId(), "the IsType extension point returned a non boolean value." );
PoisonBuilder( c );
return false;
}
return *boolRes;
}
Value ToType( const Context& c, const Value& v )
{
if( v.isType() )
return v;
auto result = ConvertValueToType( c, v, TypeType() );
if( !holds_alternative< Value >( result ) )
return PoisonType();
return get< Value >( result );
}
} // namespace goose::builtins
|
Changes to bs/builtins/types/types.h.
| ︙ | ︙ | |||
85 86 87 88 89 90 91 |
static const Term& GetPattern()
{
static auto pat = TypeType();
return pat;
}
};
| | < > > | 85 86 87 88 89 90 91 92 93 94 95 96 |
static const Term& GetPattern()
{
static auto pat = TypeType();
return pat;
}
};
extern Value InferTypeFromTExprAndInitializer(
const Context& c, const Term& typeTExpr, const Value& initVal );
} // namespace goose::builtins
#endif
|
Changes to bs/builtins/types/wrapper.h.
1 2 3 4 5 6 7 8 9 10 |
#ifndef GOOSE_BUILTINS_TYPES_WRAPPER_H
#define GOOSE_BUILTINS_TYPES_WRAPPER_H
namespace goose::builtins
{
template< typename T, bool convertible_to_term = IsTypeInVariant< T, Term > >
struct TypeWrapperHelper
{
using type = T;
| | < < < < | < < < < < | | < | < < < < < | | < | < < < < < | | < | < < < | < | < < < < < | | | < < < | < | < < < < < | | | < < < | | < < < | < < < < | < | | | < < < | < < < | < | | | | < | < | < | < > | < | | | | < | | < < < | | < < < | | < < < < < | | < | | < < < < < | < | | | < | < > | < | > | | < | > | | < | > | < | < | > | < | < | < | < | < | < | < | < | < | < | < | < | | < | < < < < < | | < < < < | | < < < > | < < | | | | < > | | | 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 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
#ifndef GOOSE_BUILTINS_TYPES_WRAPPER_H
#define GOOSE_BUILTINS_TYPES_WRAPPER_H
namespace goose::builtins
{
template< typename T, bool convertible_to_term = IsTypeInVariant< T, Term > >
struct TypeWrapperHelper
{
using type = T;
template< typename TT > static auto ToTerm( TT&& t ) { return TERM( forward< TT >( t ) ); }
static optional< T > FromTerm( const Term& t )
{
auto result = get_if< T >( &t );
if( !result )
return nullopt;
return *result;
}
template< typename TT > static LocationId Loc( TT& t ) { return {}; }
};
template< typename T > struct TypeWrapperHelper< ptr< T >, false >
{
using type = ptr< T >;
template< typename TT > static auto ToTerm( TT&& t )
{
return TERM( static_pointer_cast< void >( forward< TT >( t ) ) );
}
static optional< ptr< T > > FromTerm( const Term& t )
{
auto pt = get_if< ptr< void > >( &t );
if( !pt )
return nullopt;
return static_pointer_cast< T >( *pt );
}
template< typename TT > static LocationId Loc( TT& t ) { return {}; }
};
template< typename T > struct TypeWrapperHelper< wptr< T >, false >
{
using type = ptr< T >;
template< typename TT > static auto ToTerm( TT&& t )
{
return TERM( static_pointer_cast< void >( forward< TT >( t.lock() ) ) );
}
static optional< ptr< T > > FromTerm( const Term& t )
{
auto pt = get_if< ptr< void > >( &t );
if( !pt )
return nullopt;
return static_pointer_cast< T >( *pt );
}
template< typename TT > static LocationId Loc( TT& t ) { return {}; }
};
template<> struct TypeWrapperHelper< Term, false >
{
using type = Term;
template< typename TT > static auto ToTerm( TT&& t ) { return forward< TT >( t ); }
template< typename TT > static optional< Term > FromTerm( TT&& t )
{
return forward< TT >( t );
}
template< typename TT > static LocationId Loc( TT& t ) { return {}; }
};
template<> struct TypeWrapperHelper< cir::LowerableType, false >
{
using type = Term;
static auto ToTerm( const cir::LowerableType& t ) { return t.get(); }
template< typename TT > static optional< Term > FromTerm( TT&& t )
{
return forward< TT >( t );
}
template< typename TT > static LocationId Loc( TT& t ) { return {}; }
};
template<> struct TypeWrapperHelper< Value, false >
{
using type = Value;
static auto ToTerm( const Value& v ) { return ValueToEIR( v ); }
static optional< Value > FromTerm( const Term& t ) { return EIRToValue( t ); }
static LocationId Loc( const Value& v ) { return v.locationId(); }
};
template<> struct TypeWrapperHelper< cir::LowerableValue, false >
{
using type = Term;
template< typename T > static auto ToTerm( const cir::LowerableValue& t )
{
return ValueToEIR( t.get() );
}
static optional< Value > FromTerm( const Term& t ) { return EIRToValue( t ); }
static LocationId Loc( const cir::LowerableValue& v ) { return v.get().locationId(); }
};
template< typename T > class TypeWrapper
{
public:
template< typename TT >
TypeWrapper( TT&& val ) :
m_val( forward< TT >( val ) )
{
}
using type = T;
const auto& get() const { return m_val; }
auto& get() { return m_val; }
operator T&() { return m_val; }
operator const T &() const { return m_val; }
auto operator->() const { return m_val; }
auto toTerm() const { return TypeWrapperHelper< T >::ToTerm( m_val ); }
static auto FromTerm( const Term& t ) { return TypeWrapperHelper< T >::FromTerm( t ); }
auto locationId() const { return TypeWrapperHelper< T >::Loc( m_val ); }
private:
typename TypeWrapperHelper< T >::type m_val;
};
template< typename T > auto Wrap( T&& val )
{
return ToValue( TypeWrapper< remove_cvref_t< T > >( forward< T >( val ) ) );
}
template< typename T > struct IsTypeWrapper : public false_type
{
};
template< typename T > struct IsTypeWrapper< TypeWrapper< T > > : public true_type
{
};
template< typename T > struct TypeWrapperTraits
{
};
template<> struct TypeWrapperTraits< Term >
{
static auto typeId() { return "Term"_sid; }
};
template<> struct TypeWrapperTraits< cir::LowerableType > : public TypeWrapperTraits< Term >
{
};
template<> struct TypeWrapperTraits< Value >
{
static auto typeId() { return "Value"_sid; }
};
template<> struct TypeWrapperTraits< cir::LowerableValue > : public TypeWrapperTraits< Value >
{
};
template<> struct TypeWrapperTraits< pvec >
{
static auto typeId() { return "Vec"_sid; }
};
template<> struct TypeWrapperTraits< ptr< sema::Context > >
{
static auto typeId() { return "Context"_sid; }
};
template<> struct TypeWrapperTraits< ptr< cir::CFG > >
{
static auto typeId() { return "CFG"_sid; }
};
template<> struct TypeWrapperTraits< ptr< CodeBuilder > >
{
static auto typeId() { return "CodeBuilder"_sid; }
};
template<> struct TypeWrapperTraits< ptr< StructBuilder > >
{
static auto typeId() { return "StructBuilder"_sid; }
};
template<> struct TypeWrapperTraits< ptr< Propositions > >
{
static auto typeId() { return "Propositions"_sid; }
};
template<> struct TypeWrapperTraits< ptr< GhostCode > >
{
static auto typeId() { return "GhostCode"_sid; }
};
template<> struct TypeWrapperTraits< ptr< vector< TermLoc > > >
{
static auto typeId() { return "Tokens"_sid; }
};
template<> struct TypeWrapperTraits< ptr< Decl > >
{
static auto typeId() { return "WrappedDecl"_sid; }
};
template<> struct TypeWrapperTraits< ptr< TDecl > >
{
static auto typeId() { return "WrappedTDecl"_sid; }
};
template<> struct TypeWrapperTraits< ptr< TNamedDecl > >
{
static auto typeId() { return "WrappedTNamedDecl"_sid; }
};
template< typename T > struct SWrapType
{
using type = TypeWrapper< T >;
};
template<> struct SWrapType< bool >
{
using type = bool;
};
template<> struct SWrapType< uint32_t >
{
using type = uint32_t;
};
template< typename T > using WrapType = typename SWrapType< T >::type;
template< typename T > struct WrappedValueAccessor
{
template< typename TT > static auto Get( const TT& src ) { return src.get(); }
};
template<> struct WrappedValueAccessor< bool >
{
static auto Get( bool src ) { return src; }
};
template<> struct WrappedValueAccessor< uint32_t >
{
static auto Get( uint32_t src ) { return src; }
};
} // namespace goose::builtins
namespace goose::eir
{
template< typename T > struct Bridge< builtins::TypeWrapper< T > >
{
static const Term& Type()
{
static auto type = ValueToEIR( Value( TypeType(),
VEC( TSID( ct_type ), TERM( builtins::TypeWrapperTraits< T >::typeId() ) ) ) );
return type;
}
static Value ToValue( const builtins::TypeWrapper< T >& tw )
{
return Value( Type(), sema::Quote( tw.toTerm() ) ).setLocationId( tw.locationId() );
}
static auto FromValue(
const Value& v ) -> decltype( builtins::TypeWrapper< T >::FromTerm( v.val() ) )
{
if( !v.isConstant() || v.type() != Type() )
return {};
return builtins::TypeWrapper< T >::FromTerm( *sema::Unquote( v.val() ) );
}
};
} // namespace goose::eir
#endif
|
Deleted bs/cir/arith.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted bs/cir/ass.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to bs/cir/basicblock.h.
1 2 3 4 5 6 7 8 9 10 11 12 |
#ifndef GOOSE_CIR_BASICBLOCK_H
#define GOOSE_CIR_BASICBLOCK_H
namespace llvm
{
class BasicBlock;
}
namespace goose::cir
{
class BasicBlock
{
| | | | | < | > > | | > | | | | | < | | | | | < | | | < < | | > | > | > | | | > | | > | | | | | | | | | | | | | | | | | < | | | | | < | | | | < | | | | | | | | > | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 |
#ifndef GOOSE_CIR_BASICBLOCK_H
#define GOOSE_CIR_BASICBLOCK_H
namespace llvm
{
class BasicBlock;
}
namespace goose::cir
{
class BasicBlock
{
public:
BasicBlock( const ptr< CFG >& owner, uint32_t index ) :
m_owner( owner ),
m_index( index )
{
}
using const_iterator = vector< Instruction >::const_iterator;
auto locationId() const { return m_locId; }
void setLocationId( LocationId locId ) { m_locId = locId; }
auto index() const { return m_index; }
// /!\ Warning: as of now, back edges exclude continuation edges from GhostBranch
// instructions. It's ok for loop identification because using GhostBranch as a loop
// intruction makes no sense, and other than that back edges are only used by the
// verifier. If they are needed elsewhere, they are basically broken.
void addBackEdge( uint32_t srcBBIndex ) { m_backEdges.emplace_back( srcBBIndex ); }
const auto& backEdges() const { return m_backEdges; }
auto& backEdges() { return m_backEdges; }
void addLoopEdge( uint32_t srcBBIndex ) { m_loopEdges.emplace_back( srcBBIndex ); }
const auto& loopEdges() const { return m_loopEdges; }
uint32_t loopId() const { return m_loopId; }
bool isLoopHeader() const { return m_isLoopHeader; }
void setLoopId( uint32_t id ) { m_loopId = id; }
void setLoopHeader() { m_isLoopHeader = true; }
auto empty() const { return m_instructions.empty(); }
const auto& instructions() const { return m_instructions; }
auto& instructions() { return m_instructions; }
using RunnableInstructions = llvm::SmallVector< Instruction, 16 >;
const RunnableInstructions* runnableInstructions() const
{
if( m_dirty )
{
m_dirty = false;
m_runnableInstructions.clear();
if( !FilterVerificationInstructions( m_instructions, m_runnableInstructions ) )
return nullptr;
}
return &m_runnableInstructions;
}
void clear()
{
m_instructions.clear();
m_dirty = true;
}
template< typename... I > void append( I&&... instrs )
{
AppendToInstrSeq( m_instructions, forward< I >( instrs )... );
m_dirty = true;
}
template< typename T > void setTerminator( T&& terminator );
const auto& terminator() const { return m_terminator; }
void dirty() { m_dirty = true; }
bool canBeExecuted() const
{
return m_canBeExecuted && ( !m_terminator || m_terminator->canBeExecuted() );
}
bool canBeEagerlyEvaluated() const
{
return m_canBeEagerlyEvaluated
&& ( !m_terminator || m_terminator->canBeEagerlyEvaluated() );
}
bool codeGenStarted() const { return m_codeGenStarted; }
void setCodeGenStarted( bool b = true ) { m_codeGenStarted = b; }
template< typename F > void forEachSuccessor( F&& func ) const;
friend ostream& operator<<( ostream& out, const BasicBlock& bb )
{
out << bb.index() << ":\n";
for( auto&& instr : bb.instructions() )
out << " " << instr << endl;
if( bb.terminator() )
out << " " << *bb.terminator() << endl;
return out;
}
private:
InstrSeq m_instructions;
mutable RunnableInstructions m_runnableInstructions;
optional< Terminator > m_terminator;
weak_ptr< CFG > m_owner;
llvm::SmallVector< uint32_t, 8 > m_backEdges;
llvm::SmallVector< uint32_t, 8 > m_loopEdges;
uint32_t m_index = 0;
uint32_t m_loopId = 0; // Id of the header of the loop that this block belongs to, or 0.
LocationId m_locId = 0; // An optional location id. Used by the verifier on loop headers
// to indicate the location of a loop that it failed to verify.
bool m_isLoopHeader = false;
bool m_canBeExecuted = true;
bool m_canBeEagerlyEvaluated = true;
mutable bool m_dirty = false;
mutable bool m_codeGenStarted = false;
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/basicblock.inl.
1 2 3 4 5 |
#ifndef GOOSE_CIR_BASICBLOCK_INL
#define GOOSE_CIR_BASICBLOCK_INL
namespace goose::cir
{
| < | | < < < | < | < | < | < < | < < | | | 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 |
#ifndef GOOSE_CIR_BASICBLOCK_INL
#define GOOSE_CIR_BASICBLOCK_INL
namespace goose::cir
{
template< typename T > void BasicBlock::setTerminator( T&& terminator )
{
m_terminator = forward< T >( terminator );
m_terminator->addCFGEdges( m_owner.lock(), index() );
}
template< typename T, typename F > void ForEachSuccessor( T&& tr, F&& func ) {}
template< typename F > void ForEachSuccessor( const Branch& tr, F&& func )
{
func( tr.dest().lock() );
}
template< typename F > void ForEachSuccessor( const CondBranch& tr, F&& func )
{
func( tr.trueDest().lock() );
func( tr.falseDest().lock() );
}
template< typename F > void ForEachSuccessor( const GhostBranch& tr, F&& func )
{
func( tr.ghostCode().lock() );
func( tr.continuation().lock() );
}
template< typename F > void ForEachSuccessor( const Terminator& tr, F&& func )
{
visit( [&]( auto&& tr ) { ForEachSuccessor( tr, forward< F >( func ) ); }, tr.content() );
}
template< typename F > void BasicBlock::forEachSuccessor( F&& func ) const
{
if( m_terminator )
ForEachSuccessor( *m_terminator, forward< F >( func ) );
}
} // namespace goose::cir
#endif
|
Deleted bs/cir/binaryop.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted bs/cir/bitwise.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to bs/cir/branch.h.
1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_CIR_BRANCH_H
#define GOOSE_CIR_BRANCH_H
namespace goose::cir
{
class BasicBlock;
class Branch
{
| | | | | < | > > | | > | | | | | | | | | | < | > > | > | | > | | | | | | | | | | | < | > > | > | | > | | | | | | < > | 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 |
#ifndef GOOSE_CIR_BRANCH_H
#define GOOSE_CIR_BRANCH_H
namespace goose::cir
{
class BasicBlock;
class Branch
{
public:
template< typename B >
Branch( B&& dest ) :
m_dest( forward< B >( dest ) )
{
}
const auto& dest() const { return m_dest; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex );
friend ostream& operator<<( ostream& out, const Branch& ter );
private:
wptr< BasicBlock > m_dest;
};
class CondBranch
{
public:
template< typename BT, typename BF >
CondBranch( BT&& trueDest, BF&& falseDest ) :
m_trueDest( forward< BT >( trueDest ) ),
m_falseDest( forward< BF >( falseDest ) )
{
}
const auto& trueDest() const { return m_trueDest; }
const auto& falseDest() const { return m_falseDest; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex );
friend ostream& operator<<( ostream& out, const CondBranch& ter );
private:
wptr< BasicBlock > m_trueDest;
wptr< BasicBlock > m_falseDest;
};
// A special terminator that points to a basic block to use only
// during verification (containing "ghost code") and to a continuation
// basic block
class GhostBranch
{
public:
template< typename GCB, typename CB >
GhostBranch( GCB&& ghostCode, CB&& continuation ) :
m_ghostCode( forward< GCB >( ghostCode ) ),
m_continuation( forward< CB >( continuation ) )
{
}
const auto& ghostCode() const { return m_ghostCode; }
const auto& continuation() const { return m_continuation; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex );
friend ostream& operator<<( ostream& out, const GhostBranch& ter );
private:
wptr< BasicBlock > m_ghostCode;
wptr< BasicBlock > m_continuation;
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/break.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_BREAK_H
#define GOOSE_CIR_BREAK_H
namespace goose::cir
{
class Break
{
| | | | | < | > > | | > | | | | | < > | 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 |
#ifndef GOOSE_CIR_BREAK_H
#define GOOSE_CIR_BREAK_H
namespace goose::cir
{
class Break
{
public:
Break() {}
Break( uint32_t level ) :
m_level( level )
{
}
const auto& level() const { return m_level; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex ) {}
friend ostream& operator<<( ostream& out, const Break& ter );
private:
uint32_t m_level = 0;
};
} // namespace goose::cir
#endif
|
Deleted bs/cir/call.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to bs/cir/cfg.cpp.
1 2 3 4 5 6 7 |
#include "cir.h"
using namespace goose;
using namespace goose::cir;
const ptr< cir::BasicBlock >& CFG::createBB()
{
| > | < < < < | | > | > | < < < | 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 |
#include "cir.h"
using namespace goose;
using namespace goose::cir;
const ptr< cir::BasicBlock >& CFG::createBB()
{
m_basicBlocks.emplace_back(
make_shared< BasicBlock >( shared_from_this(), m_basicBlocks.size() + 1 ) );
return m_basicBlocks.back();
}
void CFG::addEdge( uint32_t srcIndex, uint32_t destIndex )
{
m_edges.emplace( srcIndex, destIndex );
}
bool CFG::canBeExecuted() const
{
for( auto&& bb : m_basicBlocks )
if( !bb->canBeExecuted() )
return false;
return true;
}
bool CFG::canBeEagerlyEvaluated() const
{
for( auto&& bb : m_basicBlocks )
if( !bb->canBeEagerlyEvaluated() )
return false;
return true;
}
optional< uint32_t > CFG::getDominatorDistance( uint32_t bbIndex1, uint32_t bbIndex2 ) const
{
assert( !m_idoms.empty() );
if( bbIndex1 == bbIndex2 )
return 0;
uint32_t dist = 1;
uint32_t currentBB = bbIndex2;
for( ;; )
{
assert( currentBB < m_idoms.size() );
uint32_t nextBB = m_idoms[currentBB];
if( nextBB == 0 )
return nullopt;
if( nextBB == bbIndex1 )
return dist;
currentBB = nextBB;
++dist;
}
}
CFG::EdgeLoopCategory CFG::GetEdgeLoopCategory( const BasicBlock& fromBB, const BasicBlock& toBB )
{
if( fromBB.isLoopHeader() )
{
return toBB.loopId() == fromBB.index() ? EdgeLoopCategory::None :
EdgeLoopCategory::LoopExitEdge;
}
if( toBB.isLoopHeader() )
{
return fromBB.loopId() == toBB.index() ? EdgeLoopCategory::LoopEdge :
EdgeLoopCategory::None;
}
return EdgeLoopCategory::None;
}
ostream& goose::cir::operator<<( ostream& out, const CFG& cfg )
{
cfg.forEachBB( [&]( auto&& bb ) { out << *bb << endl; } );
return out;
}
|
Changes to bs/cir/cfg.h.
1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_CIR_CFG_H
#define GOOSE_CIR_CFG_H
namespace goose::cir
{
class BasicBlock;
class CFG : public enable_shared_from_this< CFG >
{
| | | | < | > > | < | | | < | > | | < < | | | < < | | | | | | | | | | | > | | < | < < | | > | | | | | | | | | | | | | | > | | | | | > | | > | | | | < | | | | | < | | | | > | | | | | | | | > > | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | > < > | 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 |
#ifndef GOOSE_CIR_CFG_H
#define GOOSE_CIR_CFG_H
namespace goose::cir
{
class BasicBlock;
class CFG : public enable_shared_from_this< CFG >
{
public:
CFG( uint32_t numParams ) :
m_temporariesCount( numParams )
{
}
bool isPoisoned() const { return m_poisoned; }
void poison() { m_poisoned = true; }
const auto& entryBB() const { return m_basicBlocks.front(); }
const auto& lastBB() const { return m_basicBlocks.back(); }
const auto& currentBB() const { return m_currentBB; }
template< typename T > void setCurrentBB( T&& pBB ) { m_currentBB = forward< T >( pBB ); }
template< typename T > void emitTerminator( LocationId locId, T&& terminator )
{
if( !currentBB() || currentBB()->terminator() )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locId, "unreachable code.", 0 );
return;
}
currentBB()->setTerminator( forward< T >( terminator ) );
}
void addEdge( uint32_t srcIndex, uint32_t destIndex );
const auto& edges() const { return m_edges; }
template< typename V > void setIdoms( V&& idoms ) { m_idoms = forward< V >( idoms ); }
const auto& idoms() const { return m_idoms; }
uint32_t loopCount() const { return m_loopCount; }
void incLoopCount() { ++m_loopCount; }
// If the BB of index 1 dominates the BB of index 2, returns the distance:
// 0 if BB1 and BB2 are the same, 1 if BB1 is the immediate dominator, 2 if it is
// the dominator's dominator, and so on.
// Note: dominators needs to have been computed for the CFG before hand by
// calling ComputeDominators().
optional< uint32_t > getDominatorDistance( uint32_t bbIndex1, uint32_t bbIndex2 ) const;
enum class EdgeLoopCategory
{
None,
LoopEdge,
LoopExitEdge
};
static EdgeLoopCategory GetEdgeLoopCategory(
const BasicBlock& fromBB, const BasicBlock& toBB );
const auto& getBB( uint32_t index ) const { return m_basicBlocks[index - 1]; }
auto count() const { return m_basicBlocks.size(); }
const ptr< BasicBlock >& createBB();
// TODO: to remove: replaced by "valuecount" below
auto temporariesCount() const { return m_temporariesCount; }
void setTemporariesCount( uint32_t count ) { m_temporariesCount = count; }
uint32_t valueCount() const { return m_valueCount; }
uint32_t nextValueIndex() { return m_valueCount++; }
bool canBeExecuted() const;
bool canBeEagerlyEvaluated() const;
template< typename F > void forEachBB( F&& func )
{
for( auto&& bb : m_basicBlocks )
func( bb );
}
template< typename F > void forEachBB( F&& func ) const
{
for( auto&& bb : m_basicBlocks )
func( bb );
}
void setDataLocationModifiedByLoop(
uint32_t loopId, const eir::Term& type, const DataLocation& dloc )
{
m_loopModifiedDataLocations.emplace( make_pair( loopId, dloc ), type );
}
template< typename F >
void forEachDataLocationModifiedByLoop( uint32_t loopId, F&& func ) const
{
auto begin = m_loopModifiedDataLocations.lower_bound( {
loopId, Address{ {}, 0 }
} );
auto end = m_loopModifiedDataLocations.upper_bound( { loopId, monostate() } );
for( auto it = begin; it != end; ++it )
func( it->second, it->first.second );
}
template< typename F >
void forEachReachableBlock( const ptr< BasicBlock >& bb, F&& func ) const
{
unordered_set< uint32_t > m_visitedBlocks;
stack< uint32_t > m_stack;
m_stack.push( bb->index() );
while( !m_stack.empty() )
{
auto bbId = m_stack.top();
m_stack.pop();
const auto& bb = getBB( bbId );
bb->forEachSuccessor(
[&]( auto&& succBB )
{
assert( succBB->index() <= m_basicBlocks.size() );
if( m_visitedBlocks.find( succBB->index() ) != m_visitedBlocks.end() )
return;
m_visitedBlocks.emplace( succBB->index() );
m_stack.push( succBB->index() );
} );
func( bb );
}
}
friend ostream& operator<<( ostream& out, const CFG& cfg );
private:
vector< ptr< BasicBlock > > m_basicBlocks;
ptr< BasicBlock > m_currentBB;
// All the edges of the CFG in SrcBBIndex, DestBBIndex form
unordered_multimap< uint32_t, uint32_t > m_edges;
// For each BB, store the index of its immediate dominator.
// May be be empty if it has not (yet) been computed.
vector< uint32_t > m_idoms;
// For each loop, store all of the storage locations modified
// during that loop.
// May be be empty if it has not (yet) been computed.
multimap< pair< uint32_t, DataLocation >, eir::Term > m_loopModifiedDataLocations;
// The number of temporary indices used by this CFG.
uint32_t m_temporariesCount = 0; // TODO: remove
uint32_t m_valueCount = 0;
uint32_t m_loopCount = 0;
bool m_poisoned = false;
};
// Compute the immediate dominators for each BB in the provided CFG,
// using the Lengauer-Tarjan algorithm.
extern void ComputeDominators( const ptr< CFG >& cfg );
// Detect loops and store loop informations into the CFG.
extern void IdentifyLoops( const ptr< CFG >& cfg );
// Find which loop modifies which variable.
extern void IdentifyLoopModifiedAddrs( const ptr< CFG >& cfg );
// While we generate CIR instructions, we use a simple global unique id for each different
// temporary. This pass renumbers them and counts them, so that CIR consumers can simply store
// them in arrays.
extern void ReindexVars( const ptr< CFG >& cfg );
extern void ReindexVars( InstrSeq& is );
} // namespace goose::cir
#endif
|
Changes to bs/cir/cir.h.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
{
using namespace util;
using namespace diagnostics;
static constexpr uint32_t InvalidVarId = numeric_limits< uint32_t >::max();
class CFG;
| | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
{
using namespace util;
using namespace diagnostics;
static constexpr uint32_t InvalidVarId = numeric_limits< uint32_t >::max();
class CFG;
} // namespace goose::cir
#include "helpers.h"
#include "operation.h"
#include "datalocation.h"
#include "op-constant.h"
|
| ︙ | ︙ |
Deleted bs/cir/comparison.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted bs/cir/constant.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to bs/cir/continue.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_CONTINUE_H
#define GOOSE_CIR_CONTINUE_H
namespace goose::cir
{
class Continue
{
| | | | | < | > > | | > | | | | | < > | 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 |
#ifndef GOOSE_CIR_CONTINUE_H
#define GOOSE_CIR_CONTINUE_H
namespace goose::cir
{
class Continue
{
public:
Continue() {}
Continue( uint32_t level ) :
m_level( level )
{
}
const auto& level() const { return m_level; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex ) {}
friend ostream& operator<<( ostream& out, const Continue& ter );
private:
uint32_t m_level = 0;
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/datalocation.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include "cir/cir.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::builtins;
using namespace goose::cir;
namespace goose::eir
{
// Lifetime
const Term& Bridge< Lifetime >::Type()
{
| > | | < < > | | < | | | | < < < < < < | | < | < < > | | < < | | 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 |
#include "cir/cir.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::builtins;
using namespace goose::cir;
namespace goose::eir
{
// Lifetime
const Term& Bridge< Lifetime >::Type()
{
static auto type =
ValueToEIR( Value( TypeType(), TVEC( TSID( ct_type ), TSID( lifetime ) ) ) );
return type;
}
Value Bridge< Lifetime >::ToValue( const Lifetime& lt )
{
return Value( Type(), TERM( lt ) );
}
optional< Lifetime > Bridge< Lifetime >::FromValue( const Value& v )
{
auto result = Decompose( v.val(), Val< StringId >() );
if( !result )
return nullopt;
auto&& name = *result;
return Lifetime( name );
}
// DataPath
const Term& Bridge< DataPath >::Type()
{
static auto type =
ValueToEIR( Value( TypeType(), TVEC( TSID( ct_type ), TSID( datapath ) ) ) );
return type;
}
Value Bridge< DataPath >::ToValue( const cir::DataPath& dp )
{
return Value( Type(),
VEC( TERM( dp.lifetime() ), TERM( static_cast< uint64_t >( dp.origin() ) ),
ValueToEIR( ::ToValue( dp.path() ) ) ) )
.setLocationId( dp.locationId() );
}
optional< cir::DataPath > Bridge< DataPath >::FromValue( const Value& v )
{
auto result = Decompose( v.val(), Vec( Val< StringId >(), Val< uint64_t >(), SubTerm() ) );
if( !result )
return nullopt;
auto&& [lt, origin, path] = *result;
auto pathVal = *EIRToValue( path );
return cir::DataPath{ static_cast< uint32_t >( origin ),
*FromValue< SelectPath >( pathVal ), lt, v.locationId() };
}
// Address
const Term& Bridge< Address >::Type()
{
static auto type =
ValueToEIR( Value( TypeType(), TVEC( TSID( ct_type ), TSID( address ) ) ) );
return type;
}
Value Bridge< Address >::ToValue( const cir::Address& a )
{
return Value( Type(), TERM( a.lifetime() ) );
}
optional< cir::Address > Bridge< Address >::FromValue( const Value& v )
{
auto result = Decompose( v.val(), Val< StringId >() );
if( !result )
return nullopt;
return cir::Address{ *result, v.locationId() };
}
} // namespace goose::eir
|
Changes to bs/cir/datalocation.h.
1 2 3 4 5 | #ifndef GOOSE_CIR_DATALOCATION_H #define GOOSE_CIR_DATALOCATION_H // A data location in the CIR can be one of the following: // | | > | | > > | > | | > | | | | > | | | | < > | > > | | | | | | | | | < | > > | > | | | < < < | | | | | | | | | | < | > > | > | > | > | | | | | | | | | | | | | | | | | | < | > > | > | > | | | | | | | | | | | | | | < < < < < < | < | < | | | 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 |
#ifndef GOOSE_CIR_DATALOCATION_H
#define GOOSE_CIR_DATALOCATION_H
// A data location in the CIR can be one of the following:
//
// - A DataPath: a value index (the origin) + an array of indices indicating which sub members to
// select inside nested structs.
// - An address: a numerical address whose value is unknown at compilation time. Unlike datapaths
// they can be passed as function params and stored in memory. load/store work on both datapaths
// and addresses.
//
// A datapath can be converted into an address. (in codegen it will result in generating a llvm GEP
// instruction)
// DataPaths and Addresses both have an associated lifetime, either identified by a unique name or
// unspecified. The origin of a datapath may be either a SSA value index or a memory value index
// (details TBD)
// - A ghost function application: a ghost function call is treated during verification like a value
// whose name is constructed from a function + parameters, into which it is possible to load/store
// something. It is basically a declaration that "from this point on, consider that this function
// called with these parameters would return this". This can only be used by the verification
// backend.
namespace goose::cir
{
using SelectPath = Sequence< uint32_t >;
class Lifetime : public StringId
{
public:
Lifetime() {}
template< typename S >
Lifetime( S&& name ) :
StringId( forward< S >( name ) )
{
}
static const Term& Unspecified()
{
static auto us = ValueToEIR( ToValue( Lifetime{} ) );
return us;
}
};
class Address
{
public:
Address( Lifetime lt, LocationId loc ) :
m_lifetime( lt ),
m_loc( loc )
{
}
const auto& lifetime() const { return m_lifetime; }
auto locationId() const { return m_loc; }
bool operator<( const Address& rhs ) const { return m_lifetime < rhs.m_lifetime; }
private:
Lifetime m_lifetime;
LocationId m_loc;
};
class DataPath
{
public:
template< typename P >
DataPath( uint32_t origin, P&& path, Lifetime lt, LocationId loc ) :
m_origin( origin ),
m_path( forward< P >( path ) ),
m_lifetime( lt ),
m_loc( loc )
{
}
auto origin() const { return m_origin; }
const auto& path() const { return m_path; }
auto lifetime() const { return m_lifetime; }
auto locationId() const { return m_loc; }
bool operator<( const DataPath& rhs ) const
{
if( m_origin != rhs.m_origin )
return m_origin < rhs.m_origin;
if( m_lifetime != rhs.m_lifetime )
return m_lifetime < rhs.m_lifetime;
return m_path < rhs.m_path;
}
private:
uint32_t m_origin = 0;
SelectPath m_path;
Lifetime m_lifetime;
LocationId m_loc;
};
class GhostFuncApplication
{
public:
GhostFuncApplication( cir::Value&& func, LocationId loc ) :
m_func( move( func ) ),
m_loc( loc )
{
}
const auto& func() const { return m_func; }
const auto& args() const { return m_args; }
auto& args() { return m_args; }
auto locationId() const { return m_loc; }
bool operator<( const GhostFuncApplication& rhs ) const
{
if( m_func != rhs.m_func )
return m_func < rhs.m_func;
return m_args < rhs.m_args;
}
private:
eir::Value m_func;
llvm::SmallVector< variant< eir::Value, Address >, 4 > m_args;
LocationId m_loc;
};
using DataLocation = variant< Address, DataPath, GhostFuncApplication, monostate >;
} // namespace goose::cir
namespace goose::eir
{
template<> struct Bridge< cir::Lifetime >
{
static const Term& Type();
static Value ToValue( const cir::Lifetime& lt );
static optional< cir::Lifetime > FromValue( const Value& v );
};
template<> struct Bridge< cir::DataPath >
{
static const Term& Type();
static Value ToValue( const cir::DataPath& dp );
static optional< cir::DataPath > FromValue( const Value& v );
};
template<> struct Bridge< cir::Address >
{
static const Term& Type();
static Value ToValue( const cir::Address& a );
static optional< cir::Address > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/cir/dominators.cpp.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 |
ancestor( n + 1, 0 ),
child( n + 1, 0 ),
vertex( n + 1, 0 ),
label( n + 1, 0 ),
semi( n + 1, 0 ),
size( n + 1, 0 ),
succ( s )
| < > > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
ancestor( n + 1, 0 ),
child( n + 1, 0 ),
vertex( n + 1, 0 ),
label( n + 1, 0 ),
semi( n + 1, 0 ),
size( n + 1, 0 ),
succ( s )
{
}
vector< uint32_t > dom;
vector< uint32_t > parent;
vector< uint32_t > ancestor;
vector< uint32_t > child;
vector< uint32_t > vertex;
vector< uint32_t > label;
|
| ︙ | ︙ | |||
85 86 87 88 89 90 91 |
void Link( DomComputeState& st, uint32_t v, uint32_t w )
{
uint32_t s = w;
while( st.semi[st.label[w]] < st.semi[st.label[st.child[s]]] )
{
| | | | | 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 |
void Link( DomComputeState& st, uint32_t v, uint32_t w )
{
uint32_t s = w;
while( st.semi[st.label[w]] < st.semi[st.label[st.child[s]]] )
{
if( ( st.size[s] + st.size[st.child[st.child[s]]] ) >= 2 * st.size[st.child[s]] )
{
st.ancestor[st.child[s]] = s;
st.child[s] = st.child[st.child[s]];
}
else
{
st.size[st.child[s]] = st.size[s];
s = st.ancestor[s] = st.child[s];
}
}
st.label[s] = st.label[w];
st.size[v] += st.size[w];
if( st.size[v] < 2 * st.size[w] )
swap( s, st.child[v] );
while( s != 0 )
{
st.ancestor[s] = v;
s = st.child[s];
}
}
} // namespace
namespace goose::cir
{
void ComputeDominators( const ptr< CFG >& cfg )
{
// Check if this has already been computed.
if( !cfg->idoms().empty() )
|
| ︙ | ︙ | |||
165 166 167 168 169 170 171 |
s.dom[w] = s.dom[s.dom[w]];
}
s.dom[0] = 0;
cfg->setIdoms( move( s.dom ) );
}
| | | 166 167 168 169 170 171 172 173 |
s.dom[w] = s.dom[s.dom[w]];
}
s.dom[0] = 0;
cfg->setIdoms( move( s.dom ) );
}
} // namespace goose::cir
|
Deleted bs/cir/forall.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to bs/cir/func.cpp.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 |
if( f.body() )
out << *f.body();
else
out << "<invalid>\n";
return out;
}
| | | 14 15 16 17 18 19 20 21 |
if( f.body() )
out << *f.body();
else
out << "<invalid>\n";
return out;
}
} // namespace goose::cir
|
Changes to bs/cir/func.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_FUNC_H
#define GOOSE_CIR_FUNC_H
namespace goose::cir
{
class Func
{
| | | | | | | | | | | < | > > | | | | | | | | > | > | | | > | | | | | | | < > | 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 |
#ifndef GOOSE_CIR_FUNC_H
#define GOOSE_CIR_FUNC_H
namespace goose::cir
{
class Func
{
public:
enum class State
{
Unavailable,
Invalid,
Complete
};
template< typename I >
Func( I&& identity ) :
m_identity( forward< I >( identity ) )
{
}
template< typename I, typename B >
Func( I&& identity, B&& body ) :
m_identity( forward< I >( identity ) ),
m_body( forward< B >( body ) )
{
m_state = State::Complete;
}
const auto& identity() const { return m_identity; }
const auto& body() const { return m_body; }
auto& body() { return m_body; }
bool canBeExecuted() const;
auto state() const { return m_state; }
void setState( State s ) { m_state = s; }
bool isComplete() const { return m_state == State::Complete; }
friend ostream& operator<<( ostream& out, const Func& f );
private:
eir::Term m_identity;
ptr< CFG > m_body;
State m_state = State::Unavailable;
};
} // namespace goose::cir
#endif
|
Deleted bs/cir/ghostcall.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to bs/cir/hash.cpp.
1 2 3 4 |
#include "cir/cir.h"
namespace std
{
| | > | > | < | 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 |
#include "cir/cir.h"
namespace std
{
size_t hash< goose::cir::LowerableType >::operator()(
const goose::cir::LowerableType& lt ) const
{
return goose::util::ComputeHash( lt.type() );
}
size_t hash< goose::cir::LowerableValue >::operator()(
const goose::cir::LowerableValue& lv ) const
{
return goose::util::ComputeHash( lv.val() );
}
size_t hash< goose::cir::InstrSeq >::operator()( const goose::cir::InstrSeq& is ) const
{
auto g = goose::util::ContainerHashGenerator( is );
return llvm::hash_combine_range( g.begin(), g.end() );
}
size_t hash< goose::cir::Instruction >::operator()( const goose::cir::Instruction& x ) const
{
return goose::util::ComputeHash( x );
}
size_t hash< goose::cir::InlineCall >::operator()( const goose::cir::InlineCall& x ) const
{
auto g = goose::util::ContainerHashGenerator( x.argUids() );
return llvm::hash_combine( goose::util::ComputeHash( x.funcCIR() ),
llvm::hash_combine_range( g.begin(), g.end() ),
goose::util::ComputeHash( x.retType() ) );
}
size_t hash< goose::cir::Call >::operator()( const goose::cir::Call& x ) const
{
return goose::util::ComputeHash( x.numArgs() );
|
| ︙ | ︙ | |||
50 51 52 53 54 55 56 |
size_t hash< goose::cir::Select >::operator()( const goose::cir::Select& x ) const
{
return goose::util::ComputeHash( x.memberIndex() );
}
size_t hash< goose::cir::AllocVar >::operator()( const goose::cir::AllocVar& x ) const
{
| > | | | | > > | 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 |
size_t hash< goose::cir::Select >::operator()( const goose::cir::Select& x ) const
{
return goose::util::ComputeHash( x.memberIndex() );
}
size_t hash< goose::cir::AllocVar >::operator()( const goose::cir::AllocVar& x ) const
{
return llvm::hash_combine(
goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.index() ) );
}
size_t hash< goose::cir::Load >::operator()( const goose::cir::Load& x ) const
{
return goose::util::ComputeHash( x.type() );
}
size_t hash< goose::cir::Store >::operator()( const goose::cir::Store& x ) const
{
return goose::util::ComputeHash( x.type() );
}
size_t hash< goose::cir::Phi >::operator()( const goose::cir::Phi& x ) const
{
// Note: we don't bother hashing the incomings here, and we don't really care because we
// only really need hashing for the predicates and phi doesn't make sense there
return llvm::hash_combine( goose::util::ComputeHash( x.type() ),
goose::util::ComputeHash( x.numIncomings() ),
goose::util::ComputeHash( x.destIndex() ) );
}
size_t hash< goose::cir::Not >::operator()( const goose::cir::Not& x ) const
{
return 0;
}
|
| ︙ | ︙ | |||
207 208 209 210 211 212 213 |
size_t hash< goose::cir::Assert >::operator()( const goose::cir::Assert& x ) const
{
return 0;
}
size_t hash< goose::cir::Placeholder >::operator()( const goose::cir::Placeholder& x ) const
{
| > | | > | 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 |
size_t hash< goose::cir::Assert >::operator()( const goose::cir::Assert& x ) const
{
return 0;
}
size_t hash< goose::cir::Placeholder >::operator()( const goose::cir::Placeholder& x ) const
{
return llvm::hash_combine(
goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.name() ) );
}
size_t hash< goose::cir::PHOverrideSet >::operator()( const goose::cir::PHOverrideSet& x ) const
{
return goose::util::ComputeHash( x.name() );
}
size_t hash< goose::cir::PHOverrideClear >::operator()(
const goose::cir::PHOverrideClear& x ) const
{
return goose::util::ComputeHash( x.name() );
}
size_t hash< goose::cir::GhostCall >::operator()( const goose::cir::GhostCall& x ) const
{
return goose::util::ComputeHash( x.numArgs() );
|
| ︙ | ︙ | |||
244 245 246 247 248 249 250 |
return goose::util::ComputeHash( x.type() );
}
size_t hash< goose::cir::PushStringId >::operator()( const goose::cir::PushStringId& x ) const
{
return goose::util::ComputeHash( x.stringId() );
}
| | | 250 251 252 253 254 255 256 257 |
return goose::util::ComputeHash( x.type() );
}
size_t hash< goose::cir::PushStringId >::operator()( const goose::cir::PushStringId& x ) const
{
return goose::util::ComputeHash( x.stringId() );
}
} // namespace std
|
Changes to bs/cir/hash.h.
| ︙ | ︙ | |||
238 239 240 241 242 243 244 |
size_t operator()( const goose::cir::PushType& x ) const;
};
template<> struct hash< goose::cir::PushStringId >
{
size_t operator()( const goose::cir::PushStringId& x ) const;
};
| | | 238 239 240 241 242 243 244 245 246 247 |
size_t operator()( const goose::cir::PushType& x ) const;
};
template<> struct hash< goose::cir::PushStringId >
{
size_t operator()( const goose::cir::PushStringId& x ) const;
};
} // namespace std
#endif
|
Changes to bs/cir/helpers.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "cir/cir.h"
#include "sema/sema.h"
namespace goose::cir
{
bool IsValueConstantOrExecutable( const eir::Value& val )
{
if( val.isConstant() )
return true;
for( const auto& instr : *val.cir() )
| < < | < | < | < < < < | 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 |
#include "cir/cir.h"
#include "sema/sema.h"
namespace goose::cir
{
bool IsValueConstantOrExecutable( const eir::Value& val )
{
if( val.isConstant() )
return true;
for( const auto& instr : *val.cir() )
if( !CanBeExecuted( instr ) )
return false;
return true;
}
bool CanValueBeEagerlyEvaluated( const eir::Value& val )
{
if( val.isConstant() )
return true;
// Can't think from the top of my head whether we can legitimately come accross such a value
// that could be eager eval'd. The old scheme with the stack language didn't seem to "think"
// so, we'll see if we get a surprising rejection of compile time eval.
if( val.isIndex() )
return false;
for( const auto& instr : *val.cir() )
if( !CanBeEagerlyEvaluated( instr ) )
return false;
return true;
}
bool DoesInstrSeqHaveSideEffects( const InstrSeq& is )
{
for( auto&& instr : is )
if( HaveSideEffects( instr ) )
return true;
return false;
}
const eir::Term& LowerableType::get() const
{
if( !m_loweredType )
|
| ︙ | ︙ | |||
93 94 95 96 97 98 99 |
auto lv = LowerValue( *c, m_val );
m_loweredVal = lv ? *lv : PoisonValue();
}
return *m_loweredVal;
}
| | | 85 86 87 88 89 90 91 92 |
auto lv = LowerValue( *c, m_val );
m_loweredVal = lv ? *lv : PoisonValue();
}
return *m_loweredVal;
}
} // namespace goose::cir
|
Changes to bs/cir/helpers.h.
1 2 3 4 5 6 7 8 9 10 |
#ifndef GOOSE_CIR_HELPERS_H
#define GOOSE_CIR_HELPERS_H
namespace goose::cir
{
extern bool IsValueConstantOrExecutable( const eir::Value& val );
extern bool CanValueBeEagerlyEvaluated( const eir::Value& val );
extern bool DoesInstrSeqHaveSideEffects( const InstrSeq& is );
| | < | | | < | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | < < | < | | | > | | | | < | | | | | | | < | > > | > | | | | < < < | | < < < | | | < > | 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 |
#ifndef GOOSE_CIR_HELPERS_H
#define GOOSE_CIR_HELPERS_H
namespace goose::cir
{
extern bool IsValueConstantOrExecutable( const eir::Value& val );
extern bool CanValueBeEagerlyEvaluated( const eir::Value& val );
extern bool DoesInstrSeqHaveSideEffects( const InstrSeq& is );
template< typename T > class TempStorage
{
public:
TempStorage( size_t size = 0 ) :
m_storage( size )
{
}
template< typename TT > T& set( uint32_t index, TT&& x )
{
if( IsUid( index ) )
{
auto [it, _] = m_uidStorage.emplace( index, forward< TT >( x ) );
return it->second;
}
if( index >= m_storage.size() )
m_storage.resize( index + 1 );
m_storage[index] = forward< TT >( x );
return m_storage[index];
}
const T* get( uint32_t index ) const
{
if( IsUid( index ) )
{
auto it = m_uidStorage.find( index );
if( it == m_uidStorage.end() )
return nullptr;
return &it->second;
}
if( index < m_storage.size() )
return &m_storage[index];
return nullptr;
}
T* get( uint32_t index )
{
if( IsUid( index ) )
return nullptr;
if( index < m_storage.size() )
return &m_storage[index];
return nullptr;
}
private:
llvm::SmallVector< T, 16 > m_storage;
unordered_map< uint32_t, T > m_uidStorage;
};
// We need to lower types before consuming them from the CIR. Ideally we'd do this while
// building the CIR but as things are now it would involve enormous refactoring to store lowered
// types in a bunch of high level objects, and all the code to round trip them to EIR would have
// to deal with them, while also making sure they aren't actually participating in pattern
// matching. A significant redesign will probably allow for an elegant solution, but that's for
// the rewrite. In the mean time we use this ugly helper that lowers the type when reading it
// from the CIR, if not previously done. Since it needs the context we have to store the current
// context as a static member. This will have to do for the BS compiler...
class LowerableType
{
public:
template< typename T >
LowerableType( T&& type ) :
m_type( forward< T >( type ) )
{
}
const auto& type() const { return m_type; }
const eir::Term& get() const;
auto operator<( const LowerableType& other ) const { return m_type < other.m_type; }
auto operator==( const LowerableType& other ) const { return m_type == other.m_type; }
private:
eir::Term m_type;
mutable optional< eir::Term > m_loweredType;
};
// Same as above for values
class LowerableValue
{
public:
template< typename T >
LowerableValue( T&& val ) :
m_val( forward< T >( val ) )
{
}
const auto& val() const { return m_val; }
const eir::Value& get() const;
eir::Value& modify();
auto operator<( const LowerableValue& other ) const { return m_val < other.m_val; }
auto operator==( const LowerableValue& other ) const { return m_val == other.m_val; }
private:
eir::Value m_val;
mutable optional< eir::Value > m_loweredVal;
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/inliner.cpp.
| ︙ | ︙ | |||
29 30 31 32 33 34 35 |
bb->dirty();
// Extract the remainder of the current BB, to be re-appended to whatever
// the current BB is after inlining the call
InstrSeq remainder;
auto itNext = it;
++itNext;
| > | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
bb->dirty();
// Extract the remainder of the current BB, to be re-appended to whatever
// the current BB is after inlining the call
InstrSeq remainder;
auto itNext = it;
++itNext;
remainder.splice(
remainder.end(), bb->instructions(), itNext, bb->instructions().end() );
auto termi = move( bb->terminator() );
auto funcToInline = icInstr->funcCIR();
auto argUids = move( icInstr->argUids() );
auto retType = move( icInstr->retType() );
bb->instructions().erase( it );
|
| ︙ | ︙ | |||
59 60 61 62 63 64 65 |
// we need to restart from its beginning.
if( it == bb->instructions().end() )
it = bb->instructions().begin();
}
}
}
| | > | < < | > | | | | > | | | | | | | | | | | < | | < | | | | | | | | | | | < | | | | | < | | | | | | | | | 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 |
// we need to restart from its beginning.
if( it == bb->instructions().end() )
it = bb->instructions().begin();
}
}
}
ptr< InstrSeq > AppendCFG( CFG& dest, const CFG& src, const eir::Term& retType,
const InlineCall::ArgUidArray& argUids )
{
auto result = make_shared< InstrSeq >();
auto bbIndexOffset = dest.count() - 1;
auto fixUpBBPtr = [&]( const wptr< BasicBlock >& pBB )
{
auto index = pBB.lock()->index();
return dest.getBB( index + bbIndexOffset );
};
uint32_t retValUid = 0;
if( src.count() > 1 && retType != GetValueType< void >() )
{
retValUid = util::GenerateNewUID();
auto rtVal = *EIRToValue( retType );
auto rtLoc = rtVal.locationId();
dest.currentBB()->append( AllocVar( retType, retValUid, rtLoc ) );
AppendToInstrSeq( *result, DataPathOf{ retValUid, rtLoc }, Load{ retType, rtLoc } );
}
ptr< BasicBlock > contBB = dest.currentBB();
for( uint32_t i = 0; i < src.count() - 1; ++i )
dest.createBB();
if( src.count() > 1 )
contBB = dest.createBB();
for( uint32_t i = 0; i < src.count(); ++i )
{
const auto& srcBB = src.getBB( i + 1 );
const auto& destBB = dest.getBB( i + 1 + bbIndexOffset );
for( auto instr : srcBB->instructions() )
{
if( auto* phiInstr = get_if< Phi >( &instr ) )
{
phiInstr->forAllIncomings(
[&]( auto& pBB, auto& val )
{
pBB = fixUpBBPtr( pBB );
return true;
} );
}
/*else if( auto* gtInstr = get_if< GetTemporary >( &instr ) )
{
if( gtInstr->index() < argUids.size() )
gtInstr->setIndex( argUids[gtInstr->index()] );
}*/
destBB->instructions().emplace_back( instr );
}
assert( srcBB->terminator() );
visit(
[&]< typename T >( const T& termi )
{
if constexpr( is_same_v< T, RetVoid > )
{
if( src.count() > 1 )
destBB->setTerminator( Branch( contBB ) );
}
else if constexpr( is_same_v< T, Ret > )
{
if( src.count() > 1 )
{
destBB->append( DataPathOf( retValUid, termi.locationId() ),
Store( move( retType ), termi.locationId(), termi.locationId() ) );
destBB->setTerminator( Branch( contBB ) );
}
}
else if constexpr( is_same_v< T, Branch > )
{
destBB->setTerminator( Branch( fixUpBBPtr( termi.dest() ) ) );
}
else if constexpr( is_same_v< T, CondBranch > )
{
destBB->setTerminator( CondBranch(
fixUpBBPtr( termi.trueDest() ), fixUpBBPtr( termi.falseDest() ) ) );
}
else if constexpr( is_same_v< T, GhostBranch > )
{
destBB->setTerminator( CondBranch(
fixUpBBPtr( termi.ghostCode() ), fixUpBBPtr( termi.continuation() ) ) );
}
else
{
destBB->setTerminator( T( termi ) );
}
},
srcBB->terminator()->content() );
}
if( contBB )
dest.setCurrentBB( contBB );
return result;
}
} // namespace goose::cir
|
Changes to bs/cir/inliner.h.
1 2 3 4 5 6 7 8 9 10 11 |
#ifndef GOOSE_CIR_INLINER_H
#define GOOSE_CIR_INLINER_H
namespace goose::cir
{
// Go through a CFG and expand all InlineCall instructions encountered.
extern void ExpandInlineCalls( CFG& cfg );
// Go through a basic block and expand all InlineCall instructions encountered.
extern void ExpandInlineCalls( CFG& cfg, BasicBlock* bb );
| | | | | | < > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#ifndef GOOSE_CIR_INLINER_H
#define GOOSE_CIR_INLINER_H
namespace goose::cir
{
// Go through a CFG and expand all InlineCall instructions encountered.
extern void ExpandInlineCalls( CFG& cfg );
// Go through a basic block and expand all InlineCall instructions encountered.
extern void ExpandInlineCalls( CFG& cfg, BasicBlock* bb );
// Append the src cfg to the dest cfg, replacing all return terminators from src to branches to
// the provided continuation block (which should belong to the dest cfg). The provided callbacks
// are used to transform input instructions, and to retrieve and returned value generated by the
// src cfg.
extern ptr< InstrSeq > AppendCFG( CFG& dest, const CFG& src, const eir::Term& retType,
const InlineCall::ArgUidArray& argUids );
} // namespace goose::cir
#endif
|
Changes to bs/cir/instruction.cpp.
1 2 3 4 5 6 |
#include "cir/cir.h"
namespace goose::cir
{
bool CanBeExecuted( const Instruction& instr )
{
| > | | | | | | > | > | | | | | | > | > | | | | | | > | > | | | | | | > | | 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 |
#include "cir/cir.h"
namespace goose::cir
{
bool CanBeExecuted( const Instruction& instr )
{
return visit(
[]< typename ET >( const ET& e )
{
if constexpr( is_same_v< ET, monostate > )
return false;
else
return e.canBeExecuted();
},
instr );
}
bool CanBeEagerlyEvaluated( const Instruction& instr )
{
return visit(
[]< typename ET >( const ET& e )
{
if constexpr( is_same_v< ET, monostate > )
return false;
else
return e.canBeEagerlyEvaluated();
},
instr );
}
bool HaveSideEffects( const Instruction& instr )
{
return visit(
[]< typename ET >( const ET& e )
{
if constexpr( is_same_v< ET, monostate > )
return false;
else
return e.haveSideEffects();
},
instr );
}
ostream& operator<<( ostream& out, const Instruction& instr )
{
return visit(
[&]< typename ET >( const ET& e ) -> ostream&
{
if constexpr( is_same_v< ET, monostate > )
return out << "NIL";
else
return out << e;
},
instr );
}
ostream& operator<<( ostream& out, const Select& ins )
{
out << "SELECT(" << ins.m_memberIndex;
return out << ')';
}
|
| ︙ | ︙ | |||
73 74 75 76 77 78 79 |
return out << "PHOVERRIDECLEAR(" << ins.m_name << ')';
}
ostream& operator<<( ostream& out, const Phi& ins )
{
out << "PHI(" << ins.m_destIndex << ", " << ins.m_type.get();
| | | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
return out << "PHOVERRIDECLEAR(" << ins.m_name << ')';
}
ostream& operator<<( ostream& out, const Phi& ins )
{
out << "PHI(" << ins.m_destIndex << ", " << ins.m_type.get();
for( const auto& [bb, val] : ins.m_incomings )
out << ", " << bb.lock()->index() << ':' << val;
return out << " )";
}
bool Select::operator<( const Select& rhs ) const
{
|
| ︙ | ︙ | |||
103 104 105 106 107 108 109 |
return m_name < rhs.m_name;
}
bool PHOverrideClear::operator<( const PHOverrideClear& rhs ) const
{
return m_name < rhs.m_name;
}
| | | 111 112 113 114 115 116 117 118 |
return m_name < rhs.m_name;
}
bool PHOverrideClear::operator<( const PHOverrideClear& rhs ) const
{
return m_name < rhs.m_name;
}
} // namespace goose::cir
|
Changes to bs/cir/instruction.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_INSTRUCTION_H
#define GOOSE_CIR_INSTRUCTION_H
namespace goose::cir
{
class CFG;
| | | < < < < < < | < | < < < < | < < < < | < < < < | < < < < | < < < < < < < < < < < | < | < | < | < < | > > | | 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 |
#ifndef GOOSE_CIR_INSTRUCTION_H
#define GOOSE_CIR_INSTRUCTION_H
namespace goose::cir
{
class CFG;
using Instruction = variant< Call, InlineCall, CallCheck,
DataPathOf, DataPathToAddr,
Select,
Constant, AllocVar, Load, Store, Phi,
Not, And, Or, Xor,
Shl, LShr, AShr,
Add, Sub, Mul, UDiv, SDiv, URem, SRem,
Eq, Neq, UGT, UGE, ULT, ULE, SGT, SGE, SLT, SLE,
Assert, GhostCall, PHOverrideSet, PHOverrideClear, Placeholder,
ForAllSetup, ForAll,
Implies, IsPrefixOf,
PushType, PushStringId >;
class InstrSeq : public list< Instruction >
{
};
extern bool CanBeExecuted( const Instruction& instr );
extern bool CanBeEagerlyEvaluated( const Instruction& instr );
extern bool HaveSideEffects( const Instruction& instr );
extern ostream& operator<<( ostream& out, const Instruction& instr );
} // namespace goose::cir
#endif
|
Deleted bs/cir/load.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted bs/cir/logic.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to bs/cir/loops.cpp.
1 2 | #include "cir.h" | | | > | 1 2 3 4 5 6 7 8 9 10 11 12 |
#include "cir.h"
// Note: we only handle reducible CFGs for now. The results are undefined (ie probably a
// clusterfuck) for irreducible CFGs. We'll have to handle irreducible CFGs when (if...) we
// implement goto.
namespace goose::cir
{
void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb );
void MarkLoop( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, uint32_t loopHeaderId )
{
if( bb->index() == loopHeaderId )
|
| ︙ | ︙ | |||
34 35 36 37 38 39 40 |
for( auto&& x : bb->backEdges() )
MarkLoop( cfg, cfg->getBB( x ), loopHeaderId );
}
// Handle an outgoing edge while computing loops:
// If the target BB dominates the source BB, create a loop and invoke
// MarkLoop. Otherwise, continue the recursion through that edge.
| > | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
for( auto&& x : bb->backEdges() )
MarkLoop( cfg, cfg->getBB( x ), loopHeaderId );
}
// Handle an outgoing edge while computing loops:
// If the target BB dominates the source BB, create a loop and invoke
// MarkLoop. Otherwise, continue the recursion through that edge.
void IdentifyLoops(
const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, const ptr< BasicBlock >& destBB )
{
if( cfg->getDominatorDistance( destBB->index(), bb->index() ) )
{
// This is a natural loop whose header is destBB.
destBB->setLoopHeader();
// Remove the edge from the header's back edges, and add it to the header's
|
| ︙ | ︙ | |||
61 62 63 64 65 66 67 |
}
else
IdentifyLoops( cfg, destBB );
}
template< typename T >
void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, const T& tr )
| < > > | < < < | | 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 |
}
else
IdentifyLoops( cfg, destBB );
}
template< typename T >
void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, const T& tr )
{
}
void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, const Branch& tr )
{
IdentifyLoops( cfg, bb, tr.dest().lock() );
}
void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, const CondBranch& tr )
{
IdentifyLoops( cfg, bb, tr.trueDest().lock() );
IdentifyLoops( cfg, bb, tr.falseDest().lock() );
}
void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, const GhostBranch& tr )
{
IdentifyLoops( cfg, bb, tr.ghostCode().lock() );
IdentifyLoops( cfg, bb, tr.continuation().lock() );
}
void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb, const Terminator& tr )
{
visit( [&]( auto&& tr ) { IdentifyLoops( cfg, bb, tr ); }, tr.content() );
}
void IdentifyLoops( const ptr< CFG >& cfg, const ptr< BasicBlock >& bb )
{
if( bb->terminator() )
IdentifyLoops( cfg, bb, *bb->terminator() );
}
void IdentifyLoops( const ptr< CFG >& cfg )
{
ComputeDominators( cfg );
IdentifyLoops( cfg, cfg->entryBB() );
}
} // namespace goose::cir
|
Deleted bs/cir/not.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to bs/cir/op-allocvar.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_ALLOCVAR_H
#define GOOSE_CIR_ALLOCVAR_H
namespace goose::cir
{
class AllocVar : public Operation< 0, false >
{
| | | | | | | < | > > | > | > | | > | > | | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
#ifndef GOOSE_CIR_ALLOCVAR_H
#define GOOSE_CIR_ALLOCVAR_H
namespace goose::cir
{
class AllocVar : public Operation< 0, false >
{
public:
template< typename T >
AllocVar( T&& type, uint32_t index, LocationId loc ) :
Operation( loc ),
m_type( forward< T >( type ) ),
m_index( index )
{
}
const auto& type() const { return m_type; }
const auto& index() const { return m_index; }
void setIndex( uint32_t index ) { m_index = index; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return true; }
bool operator<( const AllocVar& rhs ) const
{
if( m_index != rhs.m_index )
return m_index < rhs.m_index;
return m_type < rhs.m_type;
}
friend ostream& operator<<( ostream& out, const AllocVar& ins )
{
return out << "ALLOCVAR(" << ins.m_index << ", " << ins.m_type.get() << ')';
}
private:
LowerableType m_type;
uint32_t m_index = 0;
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-arith.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_OP_ARITH_H
#define GOOSE_CIR_OP_ARITH_H
namespace goose::cir
{
class Add : public BinaryOp
{
| | | | < | < > | | < | | | < | < > | | < | | | < | < > | | < | | | < | < > | | < | | | < | < > | | < | | | < | < > | | < | | | < | < < < | | > | > | 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 |
#ifndef GOOSE_CIR_OP_ARITH_H
#define GOOSE_CIR_OP_ARITH_H
namespace goose::cir
{
class Add : public BinaryOp
{
public:
Add( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const Add& ins ) { return out << "ADD"; }
};
class Sub : public BinaryOp
{
public:
Sub( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const Sub& ins ) { return out << "SUB"; }
};
class Mul : public BinaryOp
{
public:
Mul( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const Mul& ins ) { return out << "MUL"; }
};
class UDiv : public BinaryOp
{
public:
UDiv( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const UDiv& ins ) { return out << "UDIV"; }
};
class SDiv : public BinaryOp
{
public:
SDiv( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const SDiv& ins ) { return out << "SDIV"; }
};
class URem : public BinaryOp
{
public:
URem( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const URem& ins ) { return out << "UREM"; }
};
class SRem : public BinaryOp
{
public:
SRem( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const SRem& ins ) { return out << "SREM"; }
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-assert.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_OP_ASSERT_H
#define GOOSE_CIR_OP_ASSERT_H
namespace goose::cir
{
class Assert : public Operation< 1, false >
{
| | | | < | > > | > | > | | | < < < | < < < < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#ifndef GOOSE_CIR_OP_ASSERT_H
#define GOOSE_CIR_OP_ASSERT_H
namespace goose::cir
{
class Assert : public Operation< 1, false >
{
public:
Assert( LocationId loc ) :
Operation( loc )
{
}
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return true; }
bool operator<( const Assert& rhs ) const { return false; }
friend ostream& operator<<( ostream& out, const Assert& ins ) { return out << "ASSERT"; }
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-binary.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_BINARYINSTR_H
#define GOOSE_CIR_BINARYINSTR_H
namespace goose::cir
{
class BinaryOp : public Operation< 2, true >
{
| | | | < | > > | > | > | | < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#ifndef GOOSE_CIR_BINARYINSTR_H
#define GOOSE_CIR_BINARYINSTR_H
namespace goose::cir
{
class BinaryOp : public Operation< 2, true >
{
public:
BinaryOp( LocationId loc ) :
Operation( loc )
{
}
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
bool haveSideEffects() const { return false; }
bool operator<( const BinaryOp& rhs ) const { return false; }
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-bitwise.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_OP_BITWISE_H
#define GOOSE_CIR_OP_BITWISE_H
namespace goose::cir
{
class Shl : public BinaryOp
{
| | | | < | < > | | < | | | < | < > | | < | | | < | < < < | | > | > | 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 |
#ifndef GOOSE_CIR_OP_BITWISE_H
#define GOOSE_CIR_OP_BITWISE_H
namespace goose::cir
{
class Shl : public BinaryOp
{
public:
Shl( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const Shl& ins ) { return out << "SHL"; }
};
class LShr : public BinaryOp
{
public:
LShr( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const LShr& ins ) { return out << "LSHR"; }
};
class AShr : public BinaryOp
{
public:
AShr( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const AShr& ins ) { return out << "ASHR"; }
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-call.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_OP_CALL_H
#define GOOSE_CIR_OP_CALL_H
namespace goose::cir
{
class Func;
| | | | | | < | > > | | > | > | | | < < < | | | | | | | | | | | | | | | < | > > | > | > | | > | | > | > | | | | | | | | | | | | | | | | | | | | | | < | > > | | > | > | | | < < < | | | | | | | | 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 |
#ifndef GOOSE_CIR_OP_CALL_H
#define GOOSE_CIR_OP_CALL_H
namespace goose::cir
{
class Func;
class Call : public VariadicOperation< 1, true > // Can pop more depending on arg count
{
public:
Call( uint32_t numArgs, LocationId loc ) :
VariadicOperation( numArgs, loc ),
m_numArgs( numArgs )
{
}
const auto& numArgs() const { return m_numArgs; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
bool haveSideEffects() const { return true; }
bool operator<( const Call& rhs ) const { return m_numArgs < rhs.m_numArgs; }
friend ostream& operator<<( ostream& out, const Call& ins )
{
return out << "CALL " << ins.m_numArgs;
}
private:
// TODO remove, its in the base class now
uint32_t m_numArgs = 0;
};
class InlineCall : public VariadicOperation< 0, true >
{
public:
using ArgUidArray = llvm::SmallVector< uint32_t, 8 >;
InlineCall( ArgUidArray&& argUids, const cir::Func* pFuncCIR, const eir::Term& retType,
LocationId loc ) :
VariadicOperation( argUids.size(), loc ),
m_pFuncCIR( pFuncCIR ),
m_argUids( move( argUids ) ),
m_retType( retType )
{
}
const auto* funcCIR() const { return m_pFuncCIR; }
const auto& argUids() const { return m_argUids; }
const auto& retType() const { return m_retType; }
auto& argUids() { return m_argUids; }
auto& retType() { return m_retType; }
bool canBeExecuted() const { return false; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return true; }
bool operator<( const InlineCall& rhs ) const
{
if( m_pFuncCIR != rhs.m_pFuncCIR )
return m_pFuncCIR < rhs.m_pFuncCIR;
if( m_retType != rhs.m_retType )
return m_retType < rhs.m_retType;
return m_argUids < rhs.m_argUids;
}
friend ostream& operator<<( ostream& out, const InlineCall& ins )
{
return out << "INLINECALL " << ins.m_pFuncCIR;
}
private:
const cir::Func* m_pFuncCIR = nullptr;
ArgUidArray m_argUids;
eir::Term m_retType;
};
class CallCheck : public VariadicOperation< 1, true > // Can pop more depending on arg count
{
public:
CallCheck( uint32_t numArgs, LocationId loc ) :
VariadicOperation( numArgs, loc ),
m_numArgs( numArgs )
{
}
const auto& numArgs() const { return m_numArgs; }
bool canBeExecuted() const { return false; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return false; }
bool operator<( const CallCheck& rhs ) const { return m_numArgs < rhs.m_numArgs; }
friend ostream& operator<<( ostream& out, const CallCheck& ins )
{
return out << "CALLCHECK " << ins.m_numArgs;
}
private:
uint32_t m_numArgs = 0;
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-comparisons.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_OP_COMPARISON_H
#define GOOSE_CIR_OP_COMPARISON_H
namespace goose::cir
{
class Eq : public BinaryOp
{
| | | | < | < > | | < | | | < | < > | | < | | | < | < > | | < | | | < | < > | | < | | | < | < > | | < | | | < | < > | | < | | | < | < > | | < | | | < | < > | | < | | | < | < > | | < | | | < | < < < | | > | > | 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 |
#ifndef GOOSE_CIR_OP_COMPARISON_H
#define GOOSE_CIR_OP_COMPARISON_H
namespace goose::cir
{
class Eq : public BinaryOp
{
public:
Eq( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const Eq& ins ) { return out << "EQ"; }
};
class Neq : public BinaryOp
{
public:
Neq( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const Neq& ins ) { return out << "NEQ"; }
};
class UGT : public BinaryOp
{
public:
UGT( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const UGT& ins ) { return out << "UGT"; }
};
class UGE : public BinaryOp
{
public:
UGE( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const UGE& ins ) { return out << "UGE"; }
};
class ULT : public BinaryOp
{
public:
ULT( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const ULT& ins ) { return out << "ULT"; }
};
class ULE : public BinaryOp
{
public:
ULE( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const ULE& ins ) { return out << "ULE"; }
};
class SGT : public BinaryOp
{
public:
SGT( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const SGT& ins ) { return out << "SGT"; }
};
class SGE : public BinaryOp
{
public:
SGE( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const SGE& ins ) { return out << "SGE"; }
};
class SLT : public BinaryOp
{
public:
SLT( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const SLT& ins ) { return out << "SLT"; }
};
class SLE : public BinaryOp
{
public:
SLE( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const SLE& ins ) { return out << "SLE"; }
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-constant.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_OP_CONSTANT_H
#define GOOSE_CIR_OP_CONSTANT_H
namespace goose::cir
{
class Constant : public Operation< 0, true >
{
| | | | | | < | > > | > | | > | > | | | < < < | | | | | | < > | 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 |
#ifndef GOOSE_CIR_OP_CONSTANT_H
#define GOOSE_CIR_OP_CONSTANT_H
namespace goose::cir
{
class Constant : public Operation< 0, true >
{
public:
template< typename V >
Constant( V&& val ) :
Operation( val.locationId() ),
m_value( forward< V >( val ) )
{
}
const auto& value() const { return m_value; }
auto& value() { return m_value; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
bool haveSideEffects() const { return false; }
bool operator<( const Constant& rhs ) const { return m_value < rhs.m_value; }
friend ostream& operator<<( ostream& out, const Constant& ins )
{
return out << "CONSTANT(" << ins.m_value.get() << ')';
}
private:
LowerableValue m_value;
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-datapath.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_DATAPATH_H
#define GOOSE_CIR_DATAPATH_H
namespace goose::cir
{
class DataPathOf : public Operation< 1, true >
{
| | | | | < | > > | > | | > | > | | | < < < | | | | | | | | | < | > > | > | > | | | < < < | | | | | | 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 |
#ifndef GOOSE_CIR_DATAPATH_H
#define GOOSE_CIR_DATAPATH_H
namespace goose::cir
{
class DataPathOf : public Operation< 1, true >
{
public:
DataPathOf( uint32_t origin, LocationId loc ) :
Operation( loc ),
m_origin( origin )
{
}
uint32_t origin() const { return m_origin; }
void setOrigin( uint32_t origin ) { m_origin = origin; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
bool haveSideEffects() const { return false; }
bool operator<( const DataPathOf& rhs ) const { return m_origin < rhs.m_origin; }
friend ostream& operator<<( ostream& out, const DataPathOf& ins )
{
return out << "DATAPATHOF(" << ins.m_origin << ')';
}
private:
uint32_t m_origin = 0;
};
class DataPathToAddr : public Operation< 1, true >
{
public:
DataPathToAddr( LocationId loc ) :
Operation( loc )
{
}
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
bool haveSideEffects() const { return false; }
bool operator<( const DataPathToAddr& rhs ) const { return false; }
friend ostream& operator<<( ostream& out, const DataPathToAddr& ins )
{
return out << "DATAPATHTOADDR";
}
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-forall.h.
1 2 3 4 5 |
#ifndef GOOSE_CIR_OP_FORALL_H
#define GOOSE_CIR_OP_FORALL_H
namespace goose::cir
{
| | | | | | < | > > | | | | > | > | | | < < < | | | | | | | | | < | > > | | | > | > | | | < < < | < < < < > | 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 |
#ifndef GOOSE_CIR_OP_FORALL_H
#define GOOSE_CIR_OP_FORALL_H
namespace goose::cir
{
class ForAllSetup : public Operation< 0, false > // Can pop more depending on arg count
{
public:
ForAllSetup( uint32_t numArgs, LocationId loc ) :
Operation( loc ),
m_numArgs( numArgs )
{
}
const auto& numArgs() const { return m_numArgs; }
// This can't be executed, it's only meant to appear
// in expressions handled by the verifier.
bool canBeExecuted() const { return false; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return false; }
bool operator<( const ForAllSetup& rhs ) const { return m_numArgs < rhs.m_numArgs; }
friend ostream& operator<<( ostream& out, const ForAllSetup& ins )
{
return out << "FORALLSETUP(" << ins.m_numArgs << ')';
}
private:
uint32_t m_numArgs = 0;
};
class ForAll : public Operation< 1, true >
{
public:
ForAll( LocationId loc ) :
Operation( loc )
{
}
// This can't be executed, it's only meant to appear
// in expressions handled by the verifier.
bool canBeExecuted() const { return false; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return false; }
bool operator<( const ForAll& rhs ) const { return false; }
friend ostream& operator<<( ostream& out, const ForAll& ins ) { return out << "FORALL"; }
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-ghostcall.h.
1 2 3 4 5 |
#ifndef GOOSE_CIR_OP_GHOSTCALL_H
#define GOOSE_CIR_OP_GHOSTCALL_H
namespace goose::cir
{
| | | | | | < | > > | | > | > | | | < < < | | | | | | | | 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 |
#ifndef GOOSE_CIR_OP_GHOSTCALL_H
#define GOOSE_CIR_OP_GHOSTCALL_H
namespace goose::cir
{
class GhostCall : public Operation< 1, true > // Can pop more depending on arg count
{
public:
GhostCall( uint32_t numArgs, LocationId loc ) :
Operation( loc ),
m_numArgs( numArgs )
{
}
const auto& numArgs() const { return m_numArgs; }
bool canBeExecuted() const { return false; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return true; }
bool operator<( const GhostCall& rhs ) const { return m_numArgs < rhs.m_numArgs; }
friend ostream& operator<<( ostream& out, const GhostCall& ins )
{
return out << "GHOSTCALL " << ins.m_numArgs;
}
private:
uint32_t m_numArgs = 0;
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-load.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_OP_LOAD_H
#define GOOSE_CIR_OP_LOAD_H
namespace goose::cir
{
class Load : public Operation< 1, true >
{
| | | | | | < | > > | | > | > | | | | | < > | 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 |
#ifndef GOOSE_CIR_OP_LOAD_H
#define GOOSE_CIR_OP_LOAD_H
namespace goose::cir
{
class Load : public Operation< 1, true >
{
public:
template< typename T >
Load( T&& type, LocationId loc ) :
Operation( loc ),
m_type( forward< T >( type ) )
{
}
const auto& type() const { return m_type; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
bool haveSideEffects() const { return false; }
bool operator<( const Load& rhs ) const;
friend ostream& operator<<( ostream& out, const Load& ins );
private:
LowerableType m_type;
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-logic.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_OP_LOGIC_H
#define GOOSE_CIR_OP_LOGIC_H
namespace goose::cir
{
class And : public BinaryOp
{
| | | | < | < > | | < | | | < | < > | | < | | | < | < < < | | > | > | 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 |
#ifndef GOOSE_CIR_OP_LOGIC_H
#define GOOSE_CIR_OP_LOGIC_H
namespace goose::cir
{
class And : public BinaryOp
{
public:
And( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const And& ins ) { return out << "AND"; }
};
class Or : public BinaryOp
{
public:
Or( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const Or& ins ) { return out << "OR"; }
};
class Xor : public BinaryOp
{
public:
Xor( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const Xor& ins ) { return out << "XOR"; }
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-not.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_OP_NOT_H
#define GOOSE_CIR_OP_NOT_H
namespace goose::cir
{
class Not : public Operation< 1, true >
{
| | | | < | > > | > | > | | | < < < | < < < < > | 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 |
#ifndef GOOSE_CIR_OP_NOT_H
#define GOOSE_CIR_OP_NOT_H
namespace goose::cir
{
class Not : public Operation< 1, true >
{
public:
Not( LocationId loc ) :
Operation( loc )
{
}
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
bool haveSideEffects() const { return false; }
bool operator<( const Not& rhs ) const { return false; }
friend ostream& operator<<( ostream& out, const Not& ins ) { return out << "NOT"; }
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-phi.h.
1 2 3 | #ifndef GOOSE_CIR_OP_PHI_H #define GOOSE_CIR_OP_PHI_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 73 74 75 |
#ifndef GOOSE_CIR_OP_PHI_H
#define GOOSE_CIR_OP_PHI_H
// TODO: turn this into a bunch of arrays stored directly in basic block, it's just unwieldy to have
// this as an instruction that needs to always come first
namespace goose::cir
{
class BasicBlock;
class Phi : public Operation< 0, false >
{
public:
template< typename T >
Phi( T&& type, uint32_t numIncomings, uint32_t destIndex, LocationId loc ) :
Operation( loc ),
m_type( forward< T >( type ) ),
m_destIndex( destIndex )
{
m_incomings.reserve( numIncomings );
}
const auto& type() const { return m_type; }
uint32_t numIncomings() const { return m_incomings.size(); }
const auto& destIndex() const { return m_destIndex; }
void setDestIndex( uint32_t index ) { m_destIndex = index; }
void setIncoming( const ptr< BasicBlock >& bb, const eir::Value& val )
{
m_incomings.emplace_back( bb, val );
}
template< typename F > void forAllIncomings( F&& func ) const
{
for( auto&& [bb, val] : m_incomings )
if( !func( bb.lock(), val ) )
return;
}
template< typename F > void forAllIncomings( F&& func )
{
for( auto& [bb, val] : m_incomings )
if( !func( bb, val ) )
return;
}
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
bool haveSideEffects() const { return true; }
bool operator<( const Phi& rhs ) const
{
if( m_type != rhs.m_type )
return m_type < rhs.m_type;
return m_destIndex < rhs.m_destIndex;
}
friend ostream& operator<<( ostream& out, const Phi& ins );
private:
LowerableType m_type;
uint32_t m_destIndex = 0;
using incInfo = pair< wptr< BasicBlock >, eir::Value >;
llvm::SmallVector< incInfo, 2 > m_incomings;
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-phoverride.h.
1 2 3 4 5 6 7 8 |
#ifndef GOOSE_CIR_OP_PHOVERRIDE_H
#define GOOSE_CIR_OP_PHOVERRIDE_H
namespace goose::cir
{
// A pseudo instruction that defines an override value for a placeholder
class PHOverrideSet : public Operation< 1, false >
{
| | | | | | < | > > | | | | > | > | | | | | | | | | | < | > > | | | | > | > | | | | | < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
#ifndef GOOSE_CIR_OP_PHOVERRIDE_H
#define GOOSE_CIR_OP_PHOVERRIDE_H
namespace goose::cir
{
// A pseudo instruction that defines an override value for a placeholder
class PHOverrideSet : public Operation< 1, false >
{
public:
template< typename S >
PHOverrideSet( S&& name, LocationId loc ) :
Operation( loc ),
m_name( forward< S >( name ) )
{
}
const auto& name() const { return m_name; }
// This can't be executed, it's only meant to appear
// in expressions handled by the verifier.
bool canBeExecuted() const { return false; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return false; }
bool operator<( const PHOverrideSet& rhs ) const;
friend ostream& operator<<( ostream& out, const PHOverrideSet& ins );
private:
StringId m_name;
};
// A pseudo instruction that clears an override value for a placeholder
class PHOverrideClear : public Operation< 0, 0 >
{
public:
template< typename S >
PHOverrideClear( S&& name, LocationId loc ) :
Operation( loc ),
m_name( forward< S >( name ) )
{
}
const auto& name() const { return m_name; }
// This can't be executed, it's only meant to appear
// in expressions handled by the verifier.
bool canBeExecuted() const { return false; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return false; }
bool operator<( const PHOverrideClear& rhs ) const;
friend ostream& operator<<( ostream& out, const PHOverrideClear& ins );
private:
StringId m_name;
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-placeholder.h.
1 2 3 4 5 6 7 8 9 10 |
#ifndef GOOSE_CIR_OP_PLACEHOLDER_H
#define GOOSE_CIR_OP_PLACEHOLDER_H
namespace goose::cir
{
// A placeholder value with a name, to be replaced with something else.
// Intended to represent the @ return value placeholder in functions'
// post conditions.
class Placeholder : public Operation< 0, true >
{
| | | | | | | < | > > | > | | | | > | > | | | | | | | | | | | | | | | | 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 |
#ifndef GOOSE_CIR_OP_PLACEHOLDER_H
#define GOOSE_CIR_OP_PLACEHOLDER_H
namespace goose::cir
{
// A placeholder value with a name, to be replaced with something else.
// Intended to represent the @ return value placeholder in functions'
// post conditions.
class Placeholder : public Operation< 0, true >
{
public:
template< typename T, typename S >
Placeholder( T&& type, S&& name, LocationId loc ) :
Operation( loc ),
m_type( forward< T >( type ) ),
m_name( forward< S >( name ) )
{
}
const auto& type() const { return m_type; }
const auto& name() const { return m_name; }
// This can't be executed, it's only meant to appear in expressions handled by the
// verifier.
bool canBeExecuted() const { return false; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return false; }
bool operator<( const Placeholder& rhs ) const
{
if( m_name != rhs.m_name )
return m_name < rhs.m_name;
return m_type < rhs.m_type;
}
friend ostream& operator<<( ostream& out, const Placeholder& ins )
{
return out << "PLACEHOLDER(" << ins.m_name << ", " << ins.m_type.get() << ')';
}
private:
LowerableType m_type;
StringId m_name;
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-select.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_OP_SELECT_H
#define GOOSE_CIR_OP_SELECT_H
namespace goose::cir
{
class Select : public Operation< 1, true >
{
| | | | | < | < > | | | < | > | > | | | | | | | 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 |
#ifndef GOOSE_CIR_OP_SELECT_H
#define GOOSE_CIR_OP_SELECT_H
namespace goose::cir
{
class Select : public Operation< 1, true >
{
public:
Select( uint32_t memberIndex, LocationId loc ) :
Operation( loc ),
m_memberIndex( memberIndex )
{
}
uint32_t memberIndex() const { return m_memberIndex; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return false; }
bool operator<( const Select& rhs ) const;
friend ostream& operator<<( ostream& out, const Select& ins );
private:
uint32_t m_memberIndex = 0;
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-store.h.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
public:
template< typename T >
Store( T&& type, LocationId srcLocId, LocationId destLocId ) :
Operation( destLocId ),
m_type( forward< T >( type ) ),
m_srcLocId( srcLocId ),
m_destLocId( destLocId )
| < | > > > > > < < | | | 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 |
public:
template< typename T >
Store( T&& type, LocationId srcLocId, LocationId destLocId ) :
Operation( destLocId ),
m_type( forward< T >( type ) ),
m_srcLocId( srcLocId ),
m_destLocId( destLocId )
{
}
const auto& type() const { return m_type; }
const auto& srcLocId() const { return m_srcLocId; }
const auto& destLocId() const { return m_destLocId; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
bool haveSideEffects() const { return true; }
bool operator<( const Store& rhs ) const;
friend ostream& operator<<( ostream& out, const Store& ins );
private:
LowerableType m_type;
LocationId m_srcLocId = 0;
LocationId m_destLocId = 0;
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-stringid.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_OP_STRINGID_H
#define GOOSE_CIR_OP_STRINGID_H
namespace goose::cir
{
class PushStringId : public Operation< 0, true >
{
| | | | | < | > > | | > | > | | | < < < | | | | | | | | 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 |
#ifndef GOOSE_CIR_OP_STRINGID_H
#define GOOSE_CIR_OP_STRINGID_H
namespace goose::cir
{
class PushStringId : public Operation< 0, true >
{
public:
PushStringId( util::StringId sid, LocationId loc ) :
Operation( loc ),
m_stringId( sid )
{
}
const auto& stringId() const { return m_stringId; }
bool canBeExecuted() const { return false; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return false; }
bool operator<( const PushStringId& rhs ) const { return m_stringId < rhs.m_stringId; }
friend ostream& operator<<( ostream& out, const PushStringId& ins )
{
return out << "PUSHSTRINGID(" << ins.m_stringId << ')';
}
private:
util::StringId m_stringId;
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-type.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_OP_TYPE_H
#define GOOSE_CIR_OP_TYPE_H
namespace goose::cir
{
class PushType : public Operation< 0, true >
{
| | | | | | < | > > | | > | > | | | < < < | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#ifndef GOOSE_CIR_OP_TYPE_H
#define GOOSE_CIR_OP_TYPE_H
namespace goose::cir
{
class PushType : public Operation< 0, true >
{
public:
template< typename T >
PushType( T&& type, LocationId loc ) :
Operation( loc ),
m_type( forward< T >( type ) )
{
}
const auto& type() const { return m_type; }
bool canBeExecuted() const { return false; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return false; }
bool operator<( const PushType& rhs ) const { return m_type < rhs.m_type; }
friend ostream& operator<<( ostream& out, const PushType& ins )
{
return out << "PUSHTYPE(" << ins.m_type << ')';
}
private:
eir::Term m_type;
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/op-verification.h.
1 2 3 4 5 6 7 8 9 10 |
#ifndef GOOSE_CIR_OP_VERIFICATION_H
#define GOOSE_CIR_OP_VERIFICATION_H
// Misc verification-only instructions
namespace goose::cir
{
// Logical implication
class Implies : public BinaryOp
{
| | | | < | < > | | < | | | < | > > | | | | < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#ifndef GOOSE_CIR_OP_VERIFICATION_H
#define GOOSE_CIR_OP_VERIFICATION_H
// Misc verification-only instructions
namespace goose::cir
{
// Logical implication
class Implies : public BinaryOp
{
public:
Implies( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const Implies& ins ) { return out << "IMPLIES"; }
};
// Tests if a sequence is a prefix of another
class IsPrefixOf : public BinaryOp
{
public:
IsPrefixOf( LocationId loc ) :
BinaryOp( loc )
{
}
friend ostream& operator<<( ostream& out, const IsPrefixOf& ins )
{
return out << "ISPREFIXOF";
}
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/operation.h.
1 2 3 4 5 |
#ifndef GOOSE_CIR_OPERATION_H
#define GOOSE_CIR_OPERATION_H
namespace goose::cir
{
| | < < | > | | < | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | < | < | < | | | < | > | | | | < | | < | < | | | < | | | | | | | | < | > > | > | | | | | | | | | | | | | | | | | | | | | > | | | | | | | < | | | < | | | < > | 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 |
#ifndef GOOSE_CIR_OPERATION_H
#define GOOSE_CIR_OPERATION_H
namespace goose::cir
{
template< bool enable > struct OpResult
{
uint32_t index = ~0;
};
template<> struct OpResult< false >
{
};
template< size_t operandCount, bool haveResult > class Operation
{
public:
Operation( LocationId loc ) :
m_loc( loc )
{
fill( m_operandIndices.begin(), m_operandIndices.end(), 0 );
}
static constexpr size_t OperandCount = operandCount;
static constexpr bool HaveResult = haveResult;
uint32_t operandIndex( uint32_t operand ) const
{
assert( operand < operandCount );
return m_operandIndices[operand];
}
void setOperandIndex( uint32_t operand, uint32_t index )
{
assert( operand < operandCount );
m_operandIndices[operand] = index;
}
const auto& operandIndices() const { return m_operandIndices; }
auto& operandIndices() { return m_operandIndices; }
int32_t resultIndex() const
{
if constexpr( haveResult )
{
return m_result.index;
}
else
{
assert( false );
return 0;
}
}
void setResultIndex( uint32_t index )
{
if constexpr( haveResult )
m_result.index = index;
else
assert( false );
}
void setLocation( LocationId loc ) { m_loc = loc; }
auto locationId() const { return m_loc; }
void printOperands( ostream& out ) const
{
if constexpr( operandCount > 0 )
out << format( " ({:n})", m_operandIndices );
if constexpr( haveResult )
out << " -> " << m_result.index;
}
private:
LocationId m_loc;
array< uint32_t, operandCount > m_operandIndices;
OpResult< haveResult > m_result;
};
template< size_t minOperandCount, bool haveResult >
class VariadicOperation : public Operation< minOperandCount, haveResult >
{
using super = Operation< minOperandCount, haveResult >;
public:
VariadicOperation( uint32_t extraOperandsCount, LocationId loc ) :
super( loc ),
m_extraOperands( extraOperandsCount, 0 )
{
}
const auto& extraOperandIndices() const { return m_extraOperands; }
auto& extraOperandIndices() { return m_extraOperands; }
uint32_t operandIndex( uint32_t operand ) const
{
if( operand < minOperandCount )
return super::operandIndex( operand );
operand -= minOperandCount;
assert( operand < m_extraOperands.size() );
return m_extraOperands[operand];
}
void setOperandIndex( uint32_t operand, uint32_t index )
{
if( operand < minOperandCount )
super::setOperandIndex( operand, index );
operand -= minOperandCount;
assert( operand < m_extraOperands.size() );
m_extraOperands[operand] = index;
}
void printOperands( ostream& out ) const
{
if constexpr( minOperandCount > 0 )
{
out << format(
"({:n} {:n})", super::operandIndices(), views::all( m_extraOperands ) );
}
else
{
out << format( "({:n})", views::all( m_extraOperands ) );
}
if constexpr( haveResult )
out << " -> " << super::resultIndex();
}
private:
// TODO replace with llvm smallvector once we pull llvm in
vector< uint32_t > m_extraOperands;
};
} // namespace goose::cir
#endif
|
Deleted bs/cir/phi.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted bs/cir/phoverride.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted bs/cir/placeholder.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to bs/cir/reindexvars.cpp.
1 2 3 4 5 6 7 |
#include "cir.h"
#include "builtins/builtins.h"
// TODO: we probably won't need this anymore
namespace goose::cir
{
| | | | | | > | | | | < | > | | | | | | | | | | | | | | | | > | | | | | | | > | | | > | | | | | | | 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 |
#include "cir.h"
#include "builtins/builtins.h"
// TODO: we probably won't need this anymore
namespace goose::cir
{
// While we generate CIR instructions, we use a simple global unique id for each different var
// and temporary. This pass renumbers them and counts them, so that CIR consumers can simply
// store them in arrays.
template< typename F > void ReindexVarsInValue( Value& v, F& getOrCreateIndex )
{
if( !v.isConstant() )
{
ReindexVars( *v.cir(), getOrCreateIndex );
return;
}
if( builtins::IsTuple( v ) )
{
builtins::ForEachInTuple( v,
[&]( auto&& v )
{
ReindexVarsInValue( v, getOrCreateIndex );
return true;
} );
}
}
template< typename F > void ReindexVars( InstrSeq& is, F& getOrCreateIndex )
{
for( auto& x : is )
{
visit(
[&]< typename T >( T& instr )
{
if constexpr( is_same_v< T, Constant > )
{
// Constants can be tuples, and constant tuples can contain computed values,
// so we need to recurse into these.
ReindexVarsInValue( instr.value().modify(), getOrCreateIndex );
}
else if constexpr( is_same_v< T, AllocVar > )
{
instr.setIndex( getOrCreateIndex( instr.index() ) );
}
else if constexpr( is_same_v< T, Phi > )
{
instr.setDestIndex( getOrCreateIndex( instr.destIndex() ) );
instr.forAllIncomings(
[&]( auto&&, auto&& val )
{
if( val.isConstant() )
return true;
ReindexVars( *val.cir(), getOrCreateIndex );
return true;
} );
}
},
x );
}
}
void ReindexVars( const ptr< CFG >& cfg )
{
unordered_map< uint32_t, uint32_t > uidToIndex;
uint32_t count = cfg->temporariesCount();
auto getOrCreateIndex = [&]( uint32_t uid )
{
// The indices of function parameters are directly set as indices,
// not UIDs, and we don't want to modify them as they are assumed to
// start at 0 in the param order.
if( !IsUid( uid ) )
return uid;
auto it = uidToIndex.find( uid );
if( it == uidToIndex.end() )
{
auto&& [newIt, _] = uidToIndex.emplace( uid, count++ );
it = newIt;
}
return it->second;
};
cfg->forEachBB(
[&]( auto&& bb )
{
ReindexVars( bb->instructions(), getOrCreateIndex );
bb->dirty();
} );
cfg->setTemporariesCount( count );
}
void ReindexVars( InstrSeq& is )
{
unordered_map< uint32_t, uint32_t > uidToIndex;
uint32_t count = 0;
auto getOrCreateIndex = [&]( uint32_t uid )
{
// The indices of function parameters are directly set as indices,
// not UIDs, and we don't want to modify them as they are assumed to
// start at 0 in the param order.
if( !IsUid( uid ) )
return uid;
auto it = uidToIndex.find( uid );
if( it == uidToIndex.end() )
{
auto&& [newIt, _] = uidToIndex.emplace( uid, count++ );
it = newIt;
}
return it->second;
};
ReindexVars( is, getOrCreateIndex );
}
} // namespace goose::cir
|
Changes to bs/cir/ret.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_RET_H
#define GOOSE_CIR_RET_H
namespace goose::cir
{
class RetVoid
{
| | | | < | > > | > | | | | | | | | | < | > > | > | | | | | | < > | 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 |
#ifndef GOOSE_CIR_RET_H
#define GOOSE_CIR_RET_H
namespace goose::cir
{
class RetVoid
{
public:
RetVoid( LocationId loc ) :
m_loc( loc )
{
}
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex ) {}
const auto& locationId() const { return m_loc; }
friend ostream& operator<<( ostream& out, const RetVoid& ter );
private:
LocationId m_loc;
};
class Ret
{
public:
Ret( LocationId loc ) :
m_loc( loc )
{
}
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex ) {}
const auto& locationId() const { return m_loc; }
friend ostream& operator<<( ostream& out, const Ret& ter );
private:
LocationId m_loc;
};
} // namespace goose::cir
#endif
|
Deleted bs/cir/select.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to bs/cir/seqbuilder.cpp.
| ︙ | ︙ | |||
71 72 73 74 75 76 77 |
}
void SeqBuilder::pushResult()
{
if( m_instrSeq.empty() )
return;
| | < < < < > | 71 72 73 74 75 76 77 78 79 80 |
}
void SeqBuilder::pushResult()
{
if( m_instrSeq.empty() )
return;
visit( [&]( const auto& op ) { pushResult( op ); }, m_instrSeq.back() );
}
} // namespace goose::cir
|
Changes to bs/cir/seqbuilder.h.
1 2 3 4 5 6 7 8 9 10 11 |
#ifndef GOOSE_CIR_SEQBUILDER_H
#define GOOSE_CIR_SEQBUILDER_H
namespace goose::cir
{
extern void AppendInstrSeq( InstrSeq& is, InstrSeq&& isToAppend );
extern void AppendInstrSeq( InstrSeq& is, const InstrSeq& isToAppend );
extern void AppendValue( InstrSeq& is, Value&& v );
extern void AppendValue( InstrSeq& is, const Value& v );
| < | < < < < < < | < | | | < | > | | | < | | | | | | > | | < < < | | | | | | | | | < | | | < > | 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 |
#ifndef GOOSE_CIR_SEQBUILDER_H
#define GOOSE_CIR_SEQBUILDER_H
namespace goose::cir
{
extern void AppendInstrSeq( InstrSeq& is, InstrSeq&& isToAppend );
extern void AppendInstrSeq( InstrSeq& is, const InstrSeq& isToAppend );
extern void AppendValue( InstrSeq& is, Value&& v );
extern void AppendValue( InstrSeq& is, const Value& v );
template< typename I > void AppendToInstrSeq( InstrSeq& is, I&& instr )
{
using II = remove_cvref_t< I >;
if constexpr( is_same_v< II, InstrSeq > )
AppendInstrSeq( is, forward< I >( instr ) );
else if constexpr( is_same_v< II, Value > )
AppendValue( is, forward< I >( instr ) );
else
is.emplace_back( Instruction( forward< I >( instr ) ) );
}
static inline void AppendToInstrSeq( InstrSeq& is, const ptr< InstrSeq >& instr )
{
AppendToInstrSeq( is, *instr );
}
static inline void AppendToInstrSeq( InstrSeq& is, ptr< InstrSeq >&& instr )
{
AppendToInstrSeq( is, move( *instr ) );
}
template< typename HI, typename... TI >
void AppendToInstrSeq( InstrSeq& is, HI&& headInstr, TI&&... tailInstrs )
{
AppendToInstrSeq( is, forward< HI >( headInstr ) );
AppendToInstrSeq( is, forward< TI >( tailInstrs )... );
}
template< typename T, typename... I > auto BuildComputedValue( T&& type, I&&... instrs )
{
auto is = make_shared< InstrSeq >();
AppendToInstrSeq( *is, forward< I >( instrs )... );
return eir::Value( forward< T >( type ), move( is ) );
}
class SeqBuilder
{
public:
SeqBuilder( const ptr< CFG >& cfg ) :
m_cfg( cfg )
{
}
template< typename O > void append( O&& op );
template< typename HI, typename... TI > void append( HI&& headInstr, TI&&... tailInstrs );
void append( uint32_t index );
void append( const InstrSeq& is );
void append( InstrSeq&& is );
void append( Value&& v );
void append( const Value& v );
bool isComplete() const { return m_stack.size() <= 1; }
bool haveResult() const { return m_stack.size() == 1; }
uint32_t getResultIndex() const { return m_stack.top(); }
auto&& grabInstrSeq() &&
{
stack< uint32_t > gg;
swap( gg, m_stack );
return move( m_instrSeq );
}
private:
void pushResult();
template< typename O > void pushResult( const O& op );
ptr< CFG > m_cfg;
InstrSeq m_instrSeq;
stack< uint32_t > m_stack;
};
} // namespace goose::cir
#endif
|
Changes to bs/cir/seqbuilder.inl.
1 2 3 4 5 |
#ifndef GOOSE_CIR_SEQBUILDER_INL
#define GOOSE_CIR_SEQBUILDER_INL
namespace goose::cir
{
| | < | < < | | | 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 |
#ifndef GOOSE_CIR_SEQBUILDER_INL
#define GOOSE_CIR_SEQBUILDER_INL
namespace goose::cir
{
template< typename O > void SeqBuilder::append( O&& op )
{
using T = remove_cvref_t< O >;
if constexpr( is_same_v< Value, T > )
{
visit( [&]< typename PT >( PT&& payload ) { append( forward< PT >( payload ) ); },
op.payload() );
}
else
{
for( auto& index : op.operandIndices() | views::reverse )
{
assert( !m_stack.empty() );
index = m_stack.top();
m_stack.pop();
}
if constexpr( requires { op.extraOperandIndices(); } )
{
for( auto& index : op.extraOperandIndices() | views::reverse )
{
assert( !m_stack.empty() );
index = m_stack.top();
m_stack.pop();
}
|
| ︙ | ︙ | |||
47 48 49 50 51 52 53 |
template< typename HI, typename... TI >
void SeqBuilder::append( HI&& headInstr, TI&&... tailInstrs )
{
append( forward< HI >( headInstr ) );
append( forward< TI >( tailInstrs )... );
}
| < | | | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
template< typename HI, typename... TI >
void SeqBuilder::append( HI&& headInstr, TI&&... tailInstrs )
{
append( forward< HI >( headInstr ) );
append( forward< TI >( tailInstrs )... );
}
template< typename O > void SeqBuilder::pushResult( const O& op )
{
if constexpr( O::HaveResult )
{
auto rindex = op.resultIndex();
if( rindex != ~0 )
m_stack.push( op.resultIndex() );
}
}
} // namespace goose::cir
#endif
|
Deleted bs/cir/store.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted bs/cir/stringid.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to bs/cir/terminator.cpp.
1 2 3 4 5 6 7 |
#include "cir/cir.h"
using namespace goose;
using namespace goose::cir;
bool Terminator::canBeExecuted() const
{
| | < < < | < < < | < < < | 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 |
#include "cir/cir.h"
using namespace goose;
using namespace goose::cir;
bool Terminator::canBeExecuted() const
{
return visit( []( auto&& e ) { return e.canBeExecuted(); }, m_content );
}
bool Terminator::canBeEagerlyEvaluated() const
{
return visit( []( auto&& e ) { return e.canBeEagerlyEvaluated(); }, m_content );
}
void Terminator::addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex )
{
visit( [&]( auto&& e ) { e.addCFGEdges( cfg, srcBBIndex ); }, m_content );
}
void Branch::addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex )
{
assert( m_dest.lock() );
cfg->addEdge( srcBBIndex, m_dest.lock()->index() );
|
| ︙ | ︙ | |||
49 50 51 52 53 54 55 |
void GhostBranch::addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex )
{
assert( m_ghostCode.lock() );
assert( m_continuation.lock() );
cfg->addEdge( srcBBIndex, m_ghostCode.lock()->index() );
cfg->addEdge( srcBBIndex, m_continuation.lock()->index() );
| | < | | | | < < | | | 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 |
void GhostBranch::addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex )
{
assert( m_ghostCode.lock() );
assert( m_continuation.lock() );
cfg->addEdge( srcBBIndex, m_ghostCode.lock()->index() );
cfg->addEdge( srcBBIndex, m_continuation.lock()->index() );
// HACK: we don't include the edge going directly to the continuation block because the verifier
// needs to ignore that edge, and no one else save the loop identification code uses those back
// edges. And when it comes to loops, using a GhostBranch as a looping instruction makes no
// sense anyway. I'll do something better when it inevitably bites me in the ass some day
// because I need the back edges for something else
m_ghostCode.lock()->addBackEdge( srcBBIndex );
}
namespace goose::cir
{
ostream& operator<<( ostream& out, const Terminator& ter )
{
return visit(
[&]< typename ET >( const ET& e ) -> ostream& { return out << e; }, ter.content() );
}
ostream& operator<<( ostream& out, const RetVoid& ter )
{
return out << "RETVOID";
}
|
| ︙ | ︙ | |||
107 108 109 110 111 112 113 |
return out << "BREAK " << ter.level();
}
ostream& operator<<( ostream& out, const Continue& ter )
{
return out << "CONTINUE " << ter.level();
}
| | | 95 96 97 98 99 100 101 102 |
return out << "BREAK " << ter.level();
}
ostream& operator<<( ostream& out, const Continue& ter )
{
return out << "CONTINUE " << ter.level();
}
} // namespace goose::cir
|
Changes to bs/cir/terminator.h.
1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_CIR_TERMINATOR_H
#define GOOSE_CIR_TERMINATOR_H
namespace goose::cir
{
class BasicBlock;
class Terminator
{
| | | | < | > > | | < | > > | | < | > > | | < | < < > | > > | > > | | < | > > | | < | < > | < < < < | < < < | | | | | | | | | 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 |
#ifndef GOOSE_CIR_TERMINATOR_H
#define GOOSE_CIR_TERMINATOR_H
namespace goose::cir
{
class BasicBlock;
class Terminator
{
public:
Terminator( RetVoid&& r ) :
m_content( move( r ) )
{
}
Terminator( Ret&& r ) :
m_content( move( r ) )
{
}
Terminator( Branch&& b ) :
m_content( move( b ) )
{
}
Terminator( CondBranch&& cb ) :
m_content( move( cb ) )
{
}
Terminator( GhostBranch&& gcb ) :
m_content( move( gcb ) )
{
}
Terminator( Break&& b ) :
m_content( move( b ) )
{
}
Terminator( Continue&& c ) :
m_content( move( c ) )
{
}
using Content = variant< RetVoid, Ret, Branch, CondBranch, GhostBranch, Break, Continue >;
const auto& content() const { return m_content; }
bool canBeExecuted() const;
bool canBeEagerlyEvaluated() const;
void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex );
friend ostream& operator<<( ostream& out, const Terminator& inst );
private:
Content m_content;
};
} // namespace goose::cir
#endif
|
Deleted bs/cir/type.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted bs/cir/verification.h.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to bs/cir/verifinstrfilter.cpp.
| ︙ | ︙ | |||
198 199 200 201 202 203 204 |
return false;
count += abs( c );
push( -count );
return true;
}
| < | | 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
return false;
count += abs( c );
push( -count );
return true;
}
template< typename T > bool VerifInstrFilter::processInstr( const T& instr )
{
size_t count = 1;
bool isVerifCode = false;
for( uint32_t i = 0; i < T::OperandCount; ++i )
{
auto c = pop();
|
| ︙ | ︙ | |||
226 227 228 229 230 231 232 |
return true;
}
bool VerifInstrFilter::processInstr( const Instruction& instr )
{
m_pendingInstrs.emplace_back( m_instrCount++ );
| > | | | | | | < | > | > | 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
return true;
}
bool VerifInstrFilter::processInstr( const Instruction& instr )
{
m_pendingInstrs.emplace_back( m_instrCount++ );
return visit(
[&]< typename T >( const T& instr )
{
if constexpr( is_same_v< T, monostate > )
return false;
else
return VerifInstrFilter::processInstr( instr );
},
instr );
}
} // namespace goose::cir
|
Changes to bs/cir/verifinstrfilter.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_VERIFINSTRFILTER_H
#define GOOSE_CIR_VERIFINSTRFILTER_H
namespace goose::cir
{
struct VerifInstrFilter
{
| | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | | | | < | < < | | 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_CIR_VERIFINSTRFILTER_H
#define GOOSE_CIR_VERIFINSTRFILTER_H
namespace goose::cir
{
struct VerifInstrFilter
{
public:
VerifInstrFilter( size_t inputInstrCount ) :
m_instrDiscardFlags( inputInstrCount )
{
m_pendingInstrs.reserve( inputInstrCount );
}
bool processInstr( const Instruction& instr );
template< typename T > void filter( const InstrSeq& input, T& output )
{
assert( input.size() == m_instrDiscardFlags.size() );
output.reserve( input.size() - m_discardCount );
auto fit = m_instrDiscardFlags.begin();
for( const auto& instr : input )
{
if( !*fit )
output.emplace_back( instr );
++fit;
}
}
private:
void push( int32_t instrCount );
int32_t pop();
void retire();
bool processInstr( const Call& instr );
bool processInstr( const CallCheck& instr );
bool processInstr( const GhostCall& instr );
bool processInstr( const Store& instr );
bool processInstr( const Assert& instr );
bool processInstr( const Placeholder& instr );
bool processInstr( const PHOverrideSet& instr );
bool processInstr( const PHOverrideClear& instr );
bool processInstr( const ForAllSetup& instr );
bool processInstr( const ForAll& instr );
template< typename T > bool processInstr( const T& instr );
llvm::SmallVector< size_t, 16 > m_pendingInstrs;
vector< bool > m_instrDiscardFlags;
stack< int32_t > m_stack;
size_t m_instrCount = 0;
size_t m_discardCount = 0;
};
template< typename T > bool FilterVerificationInstructions( const InstrSeq& is, T& destination )
{
VerifInstrFilter f( is.size() );
for( const auto& instr : is )
if( !f.processInstr( instr ) )
return false;
f.filter( is, destination );
return true;
}
} // namespace goose::cir
#endif
|
Changes to bs/codegen/address.cpp.
1 2 3 4 5 6 7 | #include "codegen.h" #include "builtins/builtins.h" using namespace goose; using namespace goose::codegen; using 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 |
#include "codegen.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;
ptr< codegen::Value > codegen::AddressToGEP(
llvm::IRBuilder<>& builder, const codegen::Address& addr )
{
if( addr.path().empty() )
return addr.originAddr();
llvm::SmallVector< llvm::Value*, 8 > idxs;
idxs.push_back( llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), 0 ) );
for( auto&& index : addr.path() )
idxs.push_back(
llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), index ) );
llvm::Type* pointedType = nullptr;
if( addr.originAddr()->cgType()->isPointer() )
pointedType = *static_cast< const codegen::PointerType* >( addr.originAddr()->cgType() )
->pointedType();
else
pointedType = *addr.originAddr()->cgType();
// This value represents an address, and so it has no type because the only things that it's
// good for is to be loaded form or stored to, and load/store know what type they're accessing
// anyway
// It does mean that invalid CIR (which can be created by compile time user code) will crash the
// compiler. In the self hosted rewrite, if we mark the type as optional (which we'll have to if
// we want to omit it) the compiler should never allow us to write code that access it without
// having tested it first, so it'll be ok because we'll be forced to handle those errors
// gracefully.
return codegen::Value::Get(
nullptr, builder.CreateGEP( pointedType, *addr.originAddr(), idxs ) );
}
bool Module::buildInstruction( State& st, const VarAddr& va )
{
auto* ppVal = st.temporaries->get( va.varIndex() );
assert( ppVal );
|
| ︙ | ︙ | |||
77 78 79 80 81 82 83 |
auto baseAddr = st.stack.pop();
if( !baseAddr )
return false;
if( holds_alternative< Address >( *baseAddr ) )
st.stack.push( Address::Select( move( get< Address >( *baseAddr ) ), s.memberIndex() ) );
else
| > | | 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
auto baseAddr = st.stack.pop();
if( !baseAddr )
return false;
if( holds_alternative< Address >( *baseAddr ) )
st.stack.push( Address::Select( move( get< Address >( *baseAddr ) ), s.memberIndex() ) );
else
st.stack.push( Address::Select(
Address( get< ptr< codegen::Value > >( *baseAddr ) ), s.memberIndex() ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::GhostCall& gc )
{
// We should never encounter this during codegen, since it's a construct only
// intended to be used in verification expressions.
return false;
}
|
Changes to bs/codegen/address.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CODEGEN_ADDRESS_H
#define GOOSE_CODEGEN_ADDRESS_H
namespace goose::codegen
{
class Address
{
| | | | < | > > | > | | | | | | | | | | < > > | 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 |
#ifndef GOOSE_CODEGEN_ADDRESS_H
#define GOOSE_CODEGEN_ADDRESS_H
namespace goose::codegen
{
class Address
{
public:
Address( const ptr< codegen::Value >& originAddr ) :
m_originAddr( originAddr )
{
}
const auto& originAddr() const { return m_originAddr; }
const auto& path() const { return m_path; }
static Address Select( Address&& baseAddr, uint32_t index )
{
baseAddr.m_path.emplace_back( index );
return baseAddr;
}
private:
SelectPath m_path;
ptr< codegen::Value > m_originAddr;
};
extern ptr< codegen::Value > AddressToGEP(
llvm::IRBuilder<>& builder, const codegen::Address& addr );
} // namespace goose::codegen
#endif
|
Changes to bs/codegen/arithops.cpp.
1 2 3 4 5 6 7 8 9 |
#include "codegen.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;
bool Module::buildInstruction( State& st, const cir::Add& bo )
{
| | < < | < | < < | < | < < | < | < < | < | < < | < | < < | < | < < | < | 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 |
#include "codegen.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;
bool Module::buildInstruction( State& st, const cir::Add& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateAdd( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::Sub& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateSub( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::Mul& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateMul( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::UDiv& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateUDiv( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::SDiv& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateSDiv( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::URem& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateURem( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::SRem& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateSRem( *lhs, *rhs ); } );
}
|
Changes to bs/codegen/basicblock.cpp.
| ︙ | ︙ | |||
22 23 24 25 26 27 28 |
if( pLLVMBB->getTerminator() || pBB->codeGenStarted() )
return pLLVMBB;
m_llvmBuilder.SetInsertPoint( pLLVMBB );
auto pCurrentBB = pBB;
| | < < | 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 |
if( pLLVMBB->getTerminator() || pBB->codeGenStarted() )
return pLLVMBB;
m_llvmBuilder.SetInsertPoint( pLLVMBB );
auto pCurrentBB = pBB;
for( ;; )
{
pCurrentBB->setCodeGenStarted();
const auto* pInstrs = pCurrentBB->runnableInstructions();
if( !pInstrs )
return nullptr;
for( auto&& instr : *pInstrs )
if( !buildInstruction( st, instr ) )
return nullptr;
// If the terminator is a ghostbranch, directly
// append the content of the continuation block
// to avoid unecessary basic blocks splits whenever
// some ghost code is present
if( !pCurrentBB->terminator() )
break;
|
| ︙ | ︙ | |||
59 60 61 62 63 64 65 |
pCurrentBB->setCodeGenStarted( false );
return pLLVMBB;
}
bool Module::buildTerminator( State& st, const cir::Terminator& terminator )
{
| | < < < | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
pCurrentBB->setCodeGenStarted( false );
return pLLVMBB;
}
bool Module::buildTerminator( State& st, const cir::Terminator& terminator )
{
return visit( [&]( auto&& e ) { return buildTerminator( st, e ); }, terminator.content() );
}
bool Module::buildTerminator( State& st, const cir::RetVoid& r )
{
m_llvmBuilder.CreateRetVoid();
return true;
}
|
| ︙ | ︙ |
Changes to bs/codegen/codegen.cpp.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 |
{
DiagnosticsManager::GetInstance().emitErrorMessage(
t.locationId(), "codegen: unable to lower this type.", 0 );
}
return cgType;
}
| | | 16 17 18 19 20 21 22 23 |
{
DiagnosticsManager::GetInstance().emitErrorMessage(
t.locationId(), "codegen: unable to lower this type.", 0 );
}
return cgType;
}
} // namespace goose::codegen
|
Changes to bs/codegen/codegen.h.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 |
#include "sema/sema.h"
namespace goose::builtins
{
class Func;
class FuncType;
struct PointerType;
| | | | | | | | 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 |
#include "sema/sema.h"
namespace goose::builtins
{
class Func;
class FuncType;
struct PointerType;
} // namespace goose::builtins
namespace goose::codegen
{
using namespace eir;
using namespace cir;
using namespace sema;
class Type;
extern llvm::LLVMContext& GetLLVMContext();
extern optional< string > Mangle( const Term& identity );
extern const codegen::Type* GetCodegenType( const eir::Value& t );
} // namespace goose::codegen
#include "llvmwrappers.h"
// TODO_SSA reenable
// #include "address.h"
// #include "stack.h"
// #include "module.h"
// #include "module.inl"
#endif
|
Changes to bs/codegen/compareops.cpp.
1 2 3 4 5 6 7 8 9 |
#include "codegen.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;
bool Module::buildInstruction( State& st, const cir::Eq& bo )
{
| | < < | < | < < | < | < < | < | < < | < | < < | < | < < | < | < < | < | < < | < | < < | < | < < | < | 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 |
#include "codegen.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;
bool Module::buildInstruction( State& st, const cir::Eq& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpEQ( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::Neq& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpNE( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::UGT& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpUGT( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::UGE& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpUGE( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::ULT& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpULT( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::ULE& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpULE( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::SGT& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpSGT( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::SGE& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpSGE( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::SLT& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpSLT( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::SLE& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateICmpSLE( *lhs, *rhs ); } );
}
|
Changes to bs/codegen/func.cpp.
1 2 3 4 5 6 | #include "codegen.h" #include "builtins/builtins.h" using namespace goose; using namespace goose::codegen; | | | | | | 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 "codegen.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::codegen;
ptr< codegen::Function > Module::buildFuncProto( const Context& c, const builtins::Func& func,
const string& name, llvm::Function::LinkageTypes linkageType )
{
auto* pllvmFuncType = GetFuncCodegenType( func.type() );
if( !pllvmFuncType )
return {};
return codegen::Function::Get( pllvmFuncType, linkageType, name, m_llvmModule );
}
ptr< codegen::Function > Module::getOrCreateFunc( const Context& c, const builtins::Func& func )
{
if( func.isExternal() )
return getOrCreateFunc( c, func, *func.symbol(), llvm::Function::ExternalLinkage );
auto name = Mangle( func.cir()->identity() );
if( !name )
return {};
return getOrCreateFunc( c, func, *name, llvm::Function::PrivateLinkage );
}
ptr< codegen::Function > Module::getOrCreateFunc( const Context& c, const builtins::Func& func,
const string& name, llvm::Function::LinkageTypes linkageType )
{
auto pCGFunc = codegen::Function::Get( name );
if( pCGFunc )
return pCGFunc;
pCGFunc = buildFuncProto( c, func, name, linkageType );
if( !pCGFunc )
|
| ︙ | ︙ | |||
70 71 72 73 74 75 76 |
if( !buildCFG( st, pllvmFunc, func.cir()->body() ) )
return {};
llvm::verifyFunction( *pllvmFunc );
return pCGFunc;
}
| > | | 70 71 72 73 74 75 76 77 78 79 80 81 |
if( !buildCFG( st, pllvmFunc, func.cir()->body() ) )
return {};
llvm::verifyFunction( *pllvmFunc );
return pCGFunc;
}
llvm::BasicBlock* Module::buildCFG(
State& st, llvm::Function* llvmFunc, const ptr< cir::CFG >& pCFG )
{
return buildBasicBlock( st, pCFG->entryBB() );
}
|
Changes to bs/codegen/instructions.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "codegen.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;
bool Module::buildInstruction( State& st, const cir::InstrSeq& is )
{
for( auto&& instr : is )
| < < | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#include "codegen.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;
bool Module::buildInstruction( State& st, const cir::InstrSeq& is )
{
for( auto&& instr : is )
if( !buildInstruction( st, instr ) )
return false;
return true;
}
bool Module::buildInstruction( State& st, const cir::Instruction& instr )
{
return visit( [&]< typename T >( T&& inst ) { return buildInstruction( st, inst ); }, instr );
}
bool Module::buildInstruction( State& st, const monostate& )
{
return false;
}
|
| ︙ | ︙ | |||
45 46 47 48 49 50 51 |
auto parg = st.stack.pop( m_llvmBuilder );
if( !parg )
return false;
args[call.numArgs() - i - 1] = **parg;
}
| | < | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
auto parg = st.stack.pop( m_llvmBuilder );
if( !parg )
return false;
args[call.numArgs() - i - 1] = **parg;
}
st.stack.push( codegen::Value::Get( cgFunc->cgFuncType()->returnType(),
m_llvmBuilder.CreateCall( llvm::FunctionCallee( cgFunc->llvmFunc() ), args ) ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::Constant& ct )
{
auto val = buildValue( st, ct.value().get() );
|
| ︙ | ︙ | |||
116 117 118 119 120 121 122 |
if( !ptrVal )
return false;
auto* cgType = GetCodegenType( *EIRToValue( load.type().get() ) );
if( !cgType )
return false;
| | | | | | 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 |
if( !ptrVal )
return false;
auto* cgType = GetCodegenType( *EIRToValue( load.type().get() ) );
if( !cgType )
return false;
st.stack.push( codegen::Value::Get(
cgType, m_llvmBuilder.CreateLoad( *cgType, ( *ptrVal )->llvmValue() ) ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::Store& store )
{
auto ptrVal = st.stack.pop( m_llvmBuilder );
if( !ptrVal )
return false;
auto val = st.stack.pop( m_llvmBuilder );
if( !val )
return false;
if( llvm::isa< llvm::ConstantAggregate >( ( *val )->llvmValue() ) )
{
stringstream name;
name << ".constaggr" << hex << m_nextAggregateID++;
auto* llvmValue = ( *val )->llvmValue();
auto pGlob = new llvm::GlobalVariable( m_llvmModule, ( *val )->llvmValue()->getType(), true,
llvm::GlobalValue::LinkageTypes::PrivateLinkage,
static_cast< llvm::Constant* >( llvmValue ), name.str() );
auto size = m_dataLayout.getTypeAllocSize( llvmValue->getType() );
m_llvmBuilder.CreateMemCpy( **ptrVal, nullopt, pGlob, nullopt,
llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), size ) );
return true;
|
| ︙ | ︙ | |||
166 167 168 169 170 171 172 |
auto* pPHI = m_llvmBuilder.CreatePHI( *cgType, p.numIncomings() );
if( !pPHI )
return false;
{
llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );
| | > | | | | | | | | | | | | | | | | | 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 |
auto* pPHI = m_llvmBuilder.CreatePHI( *cgType, p.numIncomings() );
if( !pPHI )
return false;
{
llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );
p.forAllIncomings(
[&]( auto&& bb, auto&& val )
{
auto pIncomingBB = buildBasicBlock( st, bb );
if( !pIncomingBB )
{
pPHI = nullptr;
return false;
}
auto pVal = buildValue( st, val );
if( !pVal )
{
pPHI = nullptr;
return false;
}
pPHI->addIncoming( *pVal, pIncomingBB );
return true;
} );
}
if( !pPHI )
return false;
st.temporaries->set( p.destIndex(), codegen::Value::Get( cgType, pPHI ) );
return true;
|
| ︙ | ︙ |
Changes to bs/codegen/llvmwrappers.cpp.
| ︙ | ︙ | |||
38 39 40 41 42 43 44 |
codegen::PointerType::PointerType( const Type* pointedType ) :
Type( llvm::PointerType::get( GetLLVMContext(), 0 ) ),
m_pointedType( pointedType )
{
m_isPointer = true;
}
| | > | < | < < < < | | < | 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 |
codegen::PointerType::PointerType( const Type* pointedType ) :
Type( llvm::PointerType::get( GetLLVMContext(), 0 ) ),
m_pointedType( pointedType )
{
m_isPointer = true;
}
const FunctionType* FunctionType::Get(
const codegen::Type* rt, const ParamTypes& paramTypes, bool isVarArg )
{
return new FunctionType( rt, paramTypes, isVarArg );
}
FunctionType::FunctionType( const codegen::Type* rt, const ParamTypes& paramTypes, bool isVarArg ) :
m_returnType( rt ),
m_paramTypes( paramTypes )
{
llvm::SmallVector< llvm::Type*, 8 > pt;
pt.reserve( paramTypes.size() );
for( auto&& t : paramTypes )
pt.emplace_back( *t );
m_llvmType = llvm::FunctionType::get( *rt, pt, isVarArg );
}
ptr< Function > Function::Get( const codegen::FunctionType* pType,
llvm::Function::LinkageTypes linkageType, const string& name, llvm::Module& llvmModule )
{
auto pFunc = make_shared< Function >( pType, linkageType, name, llvmModule );
ms_funcs.emplace( name, pFunc );
return pFunc;
}
ptr< Function > Function::Get( const string& name )
{
auto it = ms_funcs.find( name );
if( it == ms_funcs.end() )
return {};
return it->second;
}
Function::Function( const codegen::FunctionType* pType, llvm::Function::LinkageTypes linkageType,
const string& name, llvm::Module& llvmModule ) :
Value( pType, llvm::Function::Create( pType->llvmFuncType(), linkageType, name, llvmModule ) )
{
m_isFunc = true;
uint32_t paramCount = pType->paramTypes().size();
m_args.reserve( paramCount );
auto* lf = llvmFunc();
uint32_t i = 0;
for( auto&& llvmArg : lf->args() )
m_args.emplace_back( codegen::Value::Get( pType->paramTypes()[i++], &llvmArg ) );
}
|
Changes to bs/codegen/llvmwrappers.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CODEGEN_LLVMWRAPPERS_H
#define GOOSE_CODEGEN_LLVMWRAPPERS_H
namespace goose::codegen
{
// Since llvm 15, llvm pointer types don't know their pointed types anymore.
// The llvm documentation says:
| | | | | | | | | | | > | | > | | | > | > | > > | | | | | | | | | | > | | > > > > | > | | | | | | | | | | | | < | | | | | < | > > | > | | > | > | | | | | | | | | < | < | < < | | | > | | > > | > | | | | | | 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 |
#ifndef GOOSE_CODEGEN_LLVMWRAPPERS_H
#define GOOSE_CODEGEN_LLVMWRAPPERS_H
namespace goose::codegen
{
// Since llvm 15, llvm pointer types don't know their pointed types anymore.
// The llvm documentation says:
// "Frontends need to be adjusted to track pointee types independently of LLVM, insofar as they
// are necessary for lowering".
// Here's the sanest way I found to do this "adjustment": wrap every single llvm struct that we
// use with wrappers that work exactly the same way, with the exception that our pointer type
// wrapper still tracks the pointed type.
// So basically this is a whole layer of shit on top of llvm that pretends that things still
// works the old way.
class Type
{
public:
static const Type* Get( llvm::Type* llvmType );
operator llvm::Type*() const { return m_llvmType; }
operator llvm::Type*() { return m_llvmType; }
auto* llvmType() const { return m_llvmType; }
bool isPointer() const { return m_isPointer; }
protected:
Type() {}
Type( llvm::Type* llvmType ) :
m_llvmType( llvmType )
{
}
llvm::Type* m_llvmType = nullptr;
bool m_isPointer = false;
};
class PointerType : public Type
{
public:
static const PointerType* Get( const Type* pointedType );
const Type* pointedType() const { return m_pointedType; }
private:
PointerType( const Type* pointedType );
const Type* m_pointedType = nullptr;
};
class FunctionType : public Type
{
public:
using ParamTypes = llvm::SmallVector< const codegen::Type*, 8 >;
static const FunctionType* Get(
const codegen::Type* rt, const ParamTypes& paramTypes, bool isVarArg );
llvm::FunctionType* llvmFuncType() const
{
return static_cast< llvm::FunctionType* >( m_llvmType );
}
const Type* returnType() const { return m_returnType; }
const auto& paramTypes() const { return m_paramTypes; }
private:
FunctionType( const codegen::Type* rt, const ParamTypes& paramTypes, bool isVarArg );
const Type* m_returnType = nullptr;
ParamTypes m_paramTypes;
};
class Value
{
public:
static ptr< Value > Get( const codegen::Type* cgType, llvm::Value* llvmValue )
{
return make_shared< Value >( cgType, llvmValue );
}
static ptr< Value > Get( llvm::Value* llvmValue )
{
return make_shared< Value >( codegen::Type::Get( llvmValue->getType() ), llvmValue );
}
Value( const codegen::Type* cgType, llvm::Value* llvmValue ) :
m_cgType( cgType ),
m_llvmValue( llvmValue )
{
}
operator llvm::Value*() const { return m_llvmValue; }
operator llvm::Value*() { return m_llvmValue; }
auto* llvmValue() const { return m_llvmValue; }
const auto* cgType() const { return m_cgType; }
bool isFunc() const { return m_isFunc; }
private:
const codegen::Type* m_cgType = nullptr;
llvm::Value* m_llvmValue = nullptr;
protected:
bool m_isFunc = false;
};
class Function : public codegen::Value
{
public:
static ptr< Function > Get( const string& name );
static ptr< Function > Get( const codegen::FunctionType* pType,
llvm::Function::LinkageTypes linkaGeType, const string& name,
llvm::Module& llvmModule );
Function( const codegen::FunctionType* pType, llvm::Function::LinkageTypes linkaGeType,
const string& name, llvm::Module& llvmModule );
const FunctionType* cgFuncType() const
{
return static_cast< const FunctionType* >( cgType() );
}
llvm::Function* llvmFunc() const { return static_cast< llvm::Function* >( llvmValue() ); }
const auto& args() const { return m_args; }
private:
llvm::SmallVector< ptr< codegen::Value >, 8 > m_args;
static unordered_map< string, ptr< Function > > ms_funcs;
};
} // namespace goose::codegen
#endif
|
Changes to bs/codegen/logicops.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "codegen.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;
bool Module::buildInstruction( State& st, const cir::Not& uo )
{
return buildUnaryInstruction( st,
| | < > | | < < | < | < < | < | < < | < | < < | < | < < | < | < < | < | 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 "codegen.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;
bool Module::buildInstruction( State& st, const cir::Not& uo )
{
return buildUnaryInstruction( st,
[&]( auto&& operand ) {
return m_llvmBuilder.CreateXor(
*operand, llvm::ConstantInt::getTrue( GetLLVMContext() ) );
} );
}
bool Module::buildInstruction( State& st, const cir::And& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateAnd( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::Or& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateOr( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::Xor& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateXor( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::Shl& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateShl( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::LShr& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateLShr( *lhs, *rhs ); } );
}
bool Module::buildInstruction( State& st, const cir::AShr& bo )
{
return buildBinaryInstruction(
st, [&]( auto&& lhs, auto&& rhs ) { return m_llvmBuilder.CreateAShr( *lhs, *rhs ); } );
}
|
Changes to bs/codegen/mangle.cpp.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 | // Literal elements: // i: integer, followed by its value // S: string, followed by its length, followed by ':', followed by the string itself. // n: anonymous StringId, followed by its unique id // s: StringId, followed by its length, followed by ':', followed by the string itself. // _ followed by an hex number: vector, whose length is that number, // followed by its contained elements, serialized recursively. | | | > | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | // Literal elements: // i: integer, followed by its value // S: string, followed by its length, followed by ':', followed by the string itself. // n: anonymous StringId, followed by its unique id // s: StringId, followed by its length, followed by ':', followed by the string itself. // _ followed by an hex number: vector, whose length is that number, // followed by its contained elements, serialized recursively. // @ same as above, but the vector is of variable length and is followed by the repetition // element. p, P: a void* or ptr< void > term (unfortunately they have legitimate uses in some // type identitiers // that we do need to be able to mangle) // x: an eir term placeholder. // X: a bigint, followed by its value // // $: compressed string id: it is followed by the 0 based index of a previously encountered // StringId literal, in the order they were encountered. // |
| ︙ | ︙ | |||
57 58 59 60 61 62 63 |
// unecessary parts. This would require more intelligence to build an identity, although it might
// not necessarily be more complicated than the rule system idea above.
namespace
{
class Mangler
{
| | < < | | < | | | < < < | | | | | | | | | | | | | | | < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | < | | | | | | | | | | | | | | | | | | < | < < < | | < < < | | | | | | 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 |
// unecessary parts. This would require more intelligence to build an identity, although it might
// not necessarily be more complicated than the rule system idea above.
namespace
{
class Mangler
{
public:
Mangler() { m_mangled << hex; }
bool mangle( const Term& t )
{
return visit( [&]( auto&& t ) { return mangle( t ); }, t );
}
bool mangle( uint64_t integer )
{
m_mangled << 'i' << integer;
return true;
}
bool mangle( const BigInt& bi )
{
m_mangled << 'X';
bi.toHex( m_mangled );
return true;
}
// Locations shouldn't be included in the mangling, but
// should not cause it to fail either.
bool mangle( LocationId loc ) { return true; }
bool mangle( const string& str )
{
m_mangled << 'S' << str.size() << ':' << str;
return true;
}
bool mangle( const ptr< void >& pv )
{
m_mangled << 'P';
return true;
}
bool mangle( void* pv )
{
m_mangled << 'p';
return true;
}
bool mangle( StringId strId )
{
// Some hard coded abbreviations for super recurrent stuff that ends up being
// present every time. Not strictly necessary, but makes for shorter symbols.
if( strId == "value"_sid )
{
m_mangled << 'V';
return true;
}
if( strId == "constant"_sid )
{
// Use o because we can't use a letter that also counts as an hex digit
// if we want to be able to unmangle it
m_mangled << 'o';
return true;
}
if( strId == "void"_sid )
{
m_mangled << 'v';
return true;
}
if( strId == "type"_sid )
{
m_mangled << 't';
return true;
}
if( strId == "rt_type"_sid )
{
m_mangled << 'T';
return true;
}
if( strId == "quote"_sid )
{
m_mangled << 'q';
return true;
}
if( strId == "func"_sid )
{
m_mangled << 'u';
return true;
}
if( strId == "param"_sid )
{
m_mangled << 'r';
return true;
}
if( strId == "predicates"_sid )
{
m_mangled << 'R';
return true;
}
if( strId == "anykind"_sid )
{
m_mangled << 'n';
return true;
}
if( strId == "bool"_sid )
{
m_mangled << 'l';
return true;
}
if( strId == "ct_type"_sid )
{
m_mangled << 'y';
return true;
}
if( strId == "access_level"_sid )
{
m_mangled << 'z';
return true;
}
if( strId == "integer"_sid )
{
m_mangled << 'I';
return true;
}
if( strId == "reference"_sid )
{
m_mangled << 'N';
return true;
}
// Generic compression scheme to avoid storing the same stringId
// literals multiple times.
if( auto it = m_dictionary.find( strId ); it != m_dictionary.end() )
{
m_mangled << '$' << it->second;
return true;
}
m_dictionary.emplace( strId, m_dictionary.size() );
m_mangled << 's' << strId.str().size();
if( strId.isNumerical() )
m_mangled << '#' << strId.id() << '#';
else
m_mangled << ':' << strId.str();
return true;
}
bool mangle( const pvec& vec )
{
if( vec->repetitionTerm() )
m_mangled << '@';
else
m_mangled << '_';
m_mangled << vec->terms().size();
for( auto&& t : vec->terms() )
if( !mangle( t ) )
return false;
if( vec->repetitionTerm() && !mangle( *vec->repetitionTerm() ) )
return false;
return true;
}
bool mangle( const AnyTerm& a )
{
m_mangled << 'x';
return true;
}
bool mangle( const Hole& h )
{
m_mangled << 'h';
mangle( h.name() );
mangle( h.kind() );
mangle( static_cast< uint64_t >( h.behavior() ) );
return true;
}
template< typename T > bool mangle( const T& t ) { return false; }
auto mangled() const { return m_mangled.str(); }
private:
stringstream m_mangled;
unordered_map< StringId, size_t > m_dictionary;
};
} // namespace
namespace goose::codegen
{
optional< string > Mangle( const Term& identity )
{
Mangler m;
if( !m.mangle( builtins::StripPredicateHashesFromIdentity( identity ) ) )
return nullopt;
return m.mangled();
}
} // namespace goose::codegen
|
Changes to bs/codegen/module.cpp.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 |
using namespace goose;
using namespace goose::codegen;
Module::Module( const string& name ) :
m_llvmModule( name, GetLLVMContext() ),
m_dataLayout( &m_llvmModule ),
m_llvmBuilder( GetLLVMContext() )
| < > > | | | 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 |
using namespace goose;
using namespace goose::codegen;
Module::Module( const string& name ) :
m_llvmModule( name, GetLLVMContext() ),
m_dataLayout( &m_llvmModule ),
m_llvmBuilder( GetLLVMContext() )
{
}
void Module::dumpAsLlvmIr( const string& filename ) const
{
error_code err;
llvm::raw_fd_ostream out( filename, err );
if( err )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
0, format( "Couln't open file '{}': {}.", filename, err.message() ) );
return;
}
m_llvmModule.print( out, nullptr );
out.flush();
}
|
| ︙ | ︙ | |||
55 56 57 58 59 60 61 |
{
llvm::LoopAnalysisManager loopAnalysisManager;
llvm::FunctionAnalysisManager functionAnalysisManager;
llvm::CGSCCAnalysisManager cGSCCAnalysisManager;
llvm::ModuleAnalysisManager moduleAnalysisManager;
llvm::PassBuilder passBuilder( m_targetMachine );
| | | | | > | | < | | | | | 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 |
{
llvm::LoopAnalysisManager loopAnalysisManager;
llvm::FunctionAnalysisManager functionAnalysisManager;
llvm::CGSCCAnalysisManager cGSCCAnalysisManager;
llvm::ModuleAnalysisManager moduleAnalysisManager;
llvm::PassBuilder passBuilder( m_targetMachine );
passBuilder.registerModuleAnalyses( moduleAnalysisManager );
passBuilder.registerCGSCCAnalyses( cGSCCAnalysisManager );
passBuilder.registerFunctionAnalyses( functionAnalysisManager );
passBuilder.registerLoopAnalyses( loopAnalysisManager );
passBuilder.crossRegisterProxies(
loopAnalysisManager, functionAnalysisManager, cGSCCAnalysisManager, moduleAnalysisManager );
auto optimPipeline = passBuilder.buildPerModuleDefaultPipeline( llvm::OptimizationLevel::O3 );
optimPipeline.run( m_llvmModule, moduleAnalysisManager );
}
bool Module::emitToFile( const string& filename, llvm::CodeGenFileType type )
{
error_code err;
llvm::raw_fd_ostream out( filename.c_str(), err );
if( err )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
0, format( "Couln't open file '{}': {}.", filename, err.message() ) );
return false;
}
llvm::legacy::PassManager passMan;
if( m_targetMachine->addPassesToEmitFile( passMan, out, nullptr, type ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
0, "TargetMachine can't emit a file of this type." );
return false;
}
passMan.run( m_llvmModule );
out.flush();
return true;
}
|
Changes to bs/codegen/module.h.
1 2 3 4 5 6 7 8 9 10 11 12 |
#ifndef GOOSE_CODEGEN_MODULE_H
#define GOOSE_CODEGEN_MODULE_H
namespace llvm
{
class TargetMachine;
}
namespace goose::codegen
{
class Module
{
| | | | | | | | | | | | | | | | | < > > | | | | | | | | | | | | | | | | > | | | | | > < | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | < < < | | | | | | | < > | 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 |
#ifndef GOOSE_CODEGEN_MODULE_H
#define GOOSE_CODEGEN_MODULE_H
namespace llvm
{
class TargetMachine;
}
namespace goose::codegen
{
class Module
{
public:
Module( const string& name );
bool setupTarget();
void dumpAsLlvmIr( const string& filename ) const;
ptr< codegen::Function > getOrCreateFunc( const Context& c, const builtins::Func& func );
ptr< codegen::Function > getOrCreateFunc( const Context& c, const builtins::Func& func,
const string& name, llvm::Function::LinkageTypes linkageType );
void runOptimizationPasses();
bool emitToFile( const string& filename, llvm::CodeGenFileType type );
private:
struct State
{
State( const Context& c, size_t numVars, llvm::Function* f ) :
context( c ),
llvmFunc( f ),
temporaries( make_shared< storage_type >( numVars ) )
{
}
const Context& context;
llvm::Function* llvmFunc = nullptr;
llvm::BasicBlock* allocaBasicBlock = nullptr;
llvm::AllocaInst* lastEmittedAlloca = nullptr;
using storage_type = cir::TempStorage< ptr< codegen::Value > >;
ptr< storage_type > temporaries;
unordered_map< const cir::BasicBlock*, llvm::BasicBlock* > bbMap;
Stack stack;
};
static llvm::BasicBlock* GetOrCreateLLVMBB( State& st, const ptr< cir::BasicBlock >& pBB );
ptr< codegen::Function > buildFuncProto( const Context& c, const builtins::Func& func,
const string& name, llvm::Function::LinkageTypes linkageType );
llvm::Function* getCurrentFunction() const
{
return m_llvmBuilder.GetInsertBlock()->getParent();
}
llvm::BasicBlock* buildCFG(
State& st, llvm::Function* llvmFunc, const ptr< cir::CFG >& pCFG );
llvm::BasicBlock* buildBasicBlock( State& st, const ptr< cir::BasicBlock >& pBB );
ptr< codegen::Value > buildValue( State& st, const eir::Value& val );
ptr< codegen::Value > buildConstant( State& st, const eir::Value& val );
ptr< codegen::Value > buildConstantPointer(
State& st, const builtins::PointerType& ptType, const eir::Value& v );
template< typename F > bool buildUnaryInstruction( State& st, F&& impl );
template< typename F > bool buildBinaryInstruction( State& st, F&& impl );
bool buildInstruction( State& st, const cir::InstrSeq& is );
bool buildInstruction( State& st, const cir::Instruction& instr );
bool buildInstruction( State& st, const monostate& );
bool buildInstruction( State& st, const cir::Call& call );
bool buildInstruction( State& st, const cir::Constant& ct );
bool buildInstruction( State& st, const cir::VarAddr& va );
bool buildInstruction( State& st, const cir::TempAddr& ta );
bool buildInstruction( State& st, const cir::Select& s );
bool buildInstruction( State& st, const cir::CreateTemporary& ct );
bool buildInstruction( State& st, const cir::GetTemporary& gt );
bool buildInstruction( State& st, const cir::AllocVar& av );
bool buildInstruction( State& st, const cir::Load& load );
bool buildInstruction( State& st, const cir::Store& store );
bool buildInstruction( State& st, const cir::Phi& p );
bool buildInstruction( State& st, const cir::Not& uo );
bool buildInstruction( State& st, const cir::And& bo );
bool buildInstruction( State& st, const cir::Or& bo );
bool buildInstruction( State& st, const cir::Xor& bo );
bool buildInstruction( State& st, const cir::Implies& bo );
bool buildInstruction( State& st, const cir::IsPrefixOf& bo );
bool buildInstruction( State& st, const cir::Shl& bo );
bool buildInstruction( State& st, const cir::LShr& bo );
bool buildInstruction( State& st, const cir::AShr& bo );
bool buildInstruction( State& st, const cir::Add& bo );
bool buildInstruction( State& st, const cir::Sub& bo );
bool buildInstruction( State& st, const cir::Mul& bo );
bool buildInstruction( State& st, const cir::UDiv& bo );
bool buildInstruction( State& st, const cir::SDiv& bo );
bool buildInstruction( State& st, const cir::URem& bo );
bool buildInstruction( State& st, const cir::SRem& bo );
bool buildInstruction( State& st, const cir::Eq& bo );
bool buildInstruction( State& st, const cir::Neq& bo );
bool buildInstruction( State& st, const cir::UGT& bo );
bool buildInstruction( State& st, const cir::UGE& bo );
bool buildInstruction( State& st, const cir::ULT& bo );
bool buildInstruction( State& st, const cir::ULE& bo );
bool buildInstruction( State& st, const cir::SGT& bo );
bool buildInstruction( State& st, const cir::SGE& bo );
bool buildInstruction( State& st, const cir::SLT& bo );
bool buildInstruction( State& st, const cir::SLE& bo );
bool buildInstruction( State& st, const cir::Assert& );
bool buildInstruction( State& st, const cir::Placeholder& );
bool buildInstruction( State& st, const cir::PHOverrideSet& );
bool buildInstruction( State& st, const cir::PHOverrideClear& );
bool buildInstruction( State& st, const cir::GhostCall& );
bool buildInstruction( State& st, const cir::ForAllSetup& );
bool buildInstruction( State& st, const cir::ForAll& );
bool buildInstruction( State& st, const cir::PushType& );
bool buildInstruction( State& st, const cir::PushStringId& );
bool buildInstruction( State& st, const cir::CallCheck& );
bool buildInstruction( State& st, const cir::InlineCall& );
bool buildTerminator( State& st, const cir::Terminator& terminator );
bool buildTerminator( State& st, const cir::RetVoid& r );
bool buildTerminator( State& st, const cir::Ret& r );
bool buildTerminator( State& st, const cir::Branch& b );
bool buildTerminator( State& st, const cir::CondBranch& cb );
bool buildTerminator( State& st, const cir::GhostBranch& gb );
template< typename T > bool buildTerminator( State& st, const T& t ) { return false; }
ptr< codegen::Value > buildAlloca( State& st, const codegen::Type* type );
llvm::Module m_llvmModule;
llvm::DataLayout m_dataLayout;
llvm::IRBuilder<> m_llvmBuilder;
llvm::TargetMachine* m_targetMachine = nullptr;
unordered_map< string, llvm::GlobalVariable* > m_strings;
uint32_t m_nextAggregateID = 0;
};
} // namespace goose::codegen
#endif
|
Changes to bs/codegen/module.inl.
1 2 3 4 5 |
#ifndef GOOSE_CODEGEN_MODULE_INL
#define GOOSE_CODEGEN_MODULE_INL
namespace goose::codegen
{
| < | < | | | 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 |
#ifndef GOOSE_CODEGEN_MODULE_INL
#define GOOSE_CODEGEN_MODULE_INL
namespace goose::codegen
{
template< typename F > bool Module::buildUnaryInstruction( State& st, F&& impl )
{
auto operand = st.stack.pop( m_llvmBuilder );
if( !operand )
return false;
st.stack.push( codegen::Value::Get( ( *operand )->cgType(), impl( *operand ) ) );
return true;
}
template< typename F > bool Module::buildBinaryInstruction( State& st, F&& impl )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( codegen::Value::Get( ( *lhs )->cgType(), impl( *lhs, *rhs ) ) );
return true;
}
} // namespace goose::codegen
#endif
|
Changes to bs/codegen/stack.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CODEGEN_STACK_H
#define GOOSE_CODEGEN_STACK_H
namespace goose::codegen
{
class Stack
{
| | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | < < < | | > | | | | > | > | | | | < > | 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 |
#ifndef GOOSE_CODEGEN_STACK_H
#define GOOSE_CODEGEN_STACK_H
namespace goose::codegen
{
class Stack
{
public:
using Slot = variant< ptr< codegen::Value >, codegen::Address >;
template< typename T = ptr< codegen::Value > >
optional< T > pop( llvm::IRBuilder<>& builder )
{
static_assert(
is_same_v< T, ptr< codegen::Value > > || is_same_v< T, codegen::Address > );
if( m_stack.empty() )
return nullopt;
auto result = m_stack.top();
m_stack.pop();
if constexpr( is_same_v< T, codegen::Address > )
{
if( !holds_alternative< codegen::Address >( result ) )
return nullopt;
return get< codegen::Address >( result );
}
else if constexpr( is_same_v< T, ptr< codegen::Value > > )
{
if( holds_alternative< codegen::Address >( result ) )
return AddressToGEP( builder, get< codegen::Address >( result ) );
else
return InsertLoadIfNeeded( builder, get< ptr< codegen::Value > >( result ) );
}
return nullopt;
}
optional< Slot > pop()
{
if( m_stack.empty() )
return nullopt;
auto result = m_stack.top();
m_stack.pop();
return result;
}
template< typename T > void push( T&& v ) { m_stack.push( forward< T >( v ) ); }
private:
static ptr< codegen::Value > InsertLoadIfNeeded(
llvm::IRBuilder<>& builder, const ptr< codegen::Value >& v )
{
if( llvm::isa< llvm::AllocaInst >( v->llvmValue() ) )
{
return codegen::Value::Get( v->cgType(),
builder.CreateLoad(
llvm::cast< llvm::AllocaInst >( v->llvmValue() )->getAllocatedType(),
*v ) );
}
return v;
}
stack< Slot > m_stack;
};
} // namespace goose::codegen
#endif
|
Changes to bs/codegen/value.cpp.
| ︙ | ︙ | |||
27 28 29 30 31 32 33 |
{
G_VAL_ASSERT( val, val.isConstant() );
auto type = *EIRToValue( val.type() );
if( auto b = FromValue< bool >( val ) )
{
| | < | | | | > | | | | | | | | | | | | | | | | > | | | | | < | | | 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 |
{
G_VAL_ASSERT( val, val.isConstant() );
auto type = *EIRToValue( val.type() );
if( auto b = FromValue< bool >( val ) )
{
return codegen::Value::Get( *b ? llvm::ConstantInt::getTrue( GetLLVMContext() ) :
llvm::ConstantInt::getFalse( GetLLVMContext() ) );
}
else if( auto intVal = FromValue< APSInt >( val ) )
{
return codegen::Value::Get(
llvm::ConstantInt::get( *GetCodegenType( type ), *FromValue< APSInt >( val ) ) );
}
else if( auto ptType = FromValue< builtins::PointerType >( type ) )
{
return buildConstantPointer( st, *ptType, val );
}
else if( IsTupleType( type ) )
{
llvm::SmallVector< llvm::Constant*, 8 > members;
bool success = true;
ForEachInTuple( val,
[&]( auto&& v )
{
auto pMemberVal = buildConstant( st, v );
if( !pMemberVal )
{
success = false;
return false;
}
members.push_back( static_cast< llvm::Constant* >( pMemberVal->llvmValue() ) );
return true;
} );
if( !success )
return {};
return codegen::Value::Get( llvm::ConstantStruct::get(
static_cast< llvm::StructType* >( GetCodegenType( type )->llvmType() ), members ) );
}
else if( auto funcType = FromValue< FuncType >( type ) )
{
if( IsBuiltinFunc( val ) || IsBuiltinIntrinsicFunc( val ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
val.locationId(), "builtin functions can't be called at runtime." );
return {};
}
// If it is a plain function, we just need to generate or retrieve it.
if( val.isConstant() )
{
auto f = FromValue< builtins::Func >( val );
if( !f )
return {};
llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );
return getOrCreateFunc( st.context, *f );
}
else
{
// TODO: function pointers
assert( false );
return {};
}
}
DiagnosticsManager::GetInstance().emitErrorMessage( val.locationId(),
"constants with compile-time types are not supported by code generation.", 0 );
return {};
}
ptr< codegen::Value > Module::buildConstantPointer(
State& st, const builtins::PointerType& ptType, const eir::Value& v )
{
// We currently have two types of pointer constants: nullptr,
// and pointers to string literals.
if( auto str = get_if< string >( &v.val() ) )
{
llvm::GlobalVariable* pGlob = nullptr;
auto it = m_strings.find( *str );
if( it == m_strings.end() )
{
stringstream name;
name << ".str" << hex << m_strings.size();
auto* arType = llvm::ArrayType::get(
llvm::IntegerType::getInt8Ty( GetLLVMContext() ), str->size() + 1 );
pGlob = new llvm::GlobalVariable( m_llvmModule, arType, true,
llvm::GlobalValue::LinkageTypes::PrivateLinkage,
llvm::ConstantDataArray::getString( GetLLVMContext(), *str ), name.str() );
m_strings.emplace( *str, pGlob );
}
else
pGlob = it->second;
return codegen::Value::Get( pGlob );
}
return codegen::Value::Get( llvm::ConstantPointerNull::get( static_cast< llvm::PointerType* >(
GetCodegenType( *EIRToValue( v.type() ) )->llvmType() ) ) );
}
|
Changes to bs/compile/compiler.cpp.
1 2 3 4 5 | #include "compiler.h" #include "builtins/builtins.h" #include "g0api/g0api.h" #include "sema/sema.h" // TODO_SSA reenable | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 | #include "compiler.h" #include "builtins/builtins.h" #include "g0api/g0api.h" #include "sema/sema.h" // TODO_SSA reenable // #include "verify/verify.h" #include "execute/execute.h" #include "diagnostics/diagnostics.h" #include "llvm/InitializePasses.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/TargetSelect.h" |
| ︙ | ︙ | |||
34 35 36 37 38 39 40 |
cmdArgs = cmdArgs + argv[i];
}
SetupBuiltins( *m_pEnv );
parse::SetupParsingBuiltins( *m_pEnv );
g0api::SetupG0Api( *m_pEnv );
| | < | < < < | | < < < | > | | | | | | | | | < | | | 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 |
cmdArgs = cmdArgs + argv[i];
}
SetupBuiltins( *m_pEnv );
parse::SetupParsingBuiltins( *m_pEnv );
g0api::SetupG0Api( *m_pEnv );
RegisterBuiltinFunc< string() >( *m_pEnv, "Args"_sid, [cmdArgs]() { return cmdArgs; } );
RegisterBuiltinFunc< void( string ) >(
*m_pEnv, "Print"_sid, []( const string& str ) { cout << str; } );
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
}
uint32_t Compiler::execute( const string& filename )
{
auto locVarsIdentity = AppendToVectorTerm( builtins::RootG0Identity(), TSID( 0_locvars ) );
m_pEnv->addVisibilityRule( builtins::RootG0Identity(), locVarsIdentity );
auto result = LoadAndExecuteFile( m_pEnv, filename, locVarsIdentity,
GetValueType< uint32_t >(), ToValue< uint32_t >( 1 ) );
if( DiagnosticsManager::GetInstance().errorsWereEmitted() )
return 1;
if( !result )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
0, format( "{}: could not be executed.", filename ) );
return 1;
}
if( result->isPoison() )
return 1;
return *FromValue< uint32_t >( *result );
}
optional< Value > Compiler::LoadAndExecuteFile( const ptr< Env >& e, const string& filename,
const Term& identity, const Term& returnType, optional< Value > defRetVal )
{
ProfileZoneScoped;
ProfileZoneName( filename.c_str(), filename.size() );
auto cfg = LoadAndParseFile( e, filename, identity, returnType, defRetVal );
if( !cfg || cfg->isPoisoned() )
return PoisonValue();
if( !cfg->entryBB()->canBeExecuted() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
0, format( "{}: can not be executed.", filename ) );
return PoisonValue();
}
sema::Context c( e, identity, returnType );
Context::DefaultContextGuard dfg( c );
execute::VM vm;
return vm.execute( *cfg );
}
ptr< cir::CFG > Compiler::LoadAndParseFile( const ptr< Env >& e, const string& filename,
const Term& identity, const Term& returnType, optional< Value > defRetVal )
{
ProfileZoneScoped;
ProfileZoneName( filename.c_str(), filename.size() );
auto& dm = DiagnosticsManager::GetInstance();
assert( returnType == GetValueType< void >() || defRetVal );
ifstream sourcefile( filename.c_str() );
if( !sourcefile.good() )
{
dm.emitErrorMessage( 0, format( "can't open '{}'.", filename ) );
return nullptr;
}
sema::Context c( e, identity, returnType );
Context::DefaultContextGuard dfg( c );
DiagnosticsContext dc( 0, true );
VerbosityContext vc( Verbosity::Normal, true );
auto cfg = make_shared< cir::CFG >( 0 );
cfg->createBB();
cfg->setCurrentBB( cfg->entryBB() );
auto cb = make_shared< CodeBuilder >( cfg );
c.setBuilder( ToValue( TypeWrapper< ptr< CodeBuilder > >( cb ) ) );
auto r =
make_shared< parse::Resolver >( make_shared< lex::Lexer >( sourcefile, filename ), c );
parse::Parser p( r );
p.parseSequence();
if( cfg->isPoisoned() )
return cfg;
|
| ︙ | ︙ | |||
162 163 164 165 166 167 168 |
{
auto converted = ConvertValueToType( c, *defRetVal, returnType );
if( holds_alternative< ValUnifyError >( converted ) )
{
switch( get< ValUnifyError >( converted ) )
{
case ValUnifyError::NoSolution:
| > | | > | | | 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 |
{
auto converted = ConvertValueToType( c, *defRetVal, returnType );
if( holds_alternative< ValUnifyError >( converted ) )
{
switch( get< ValUnifyError >( converted ) )
{
case ValUnifyError::NoSolution:
dm.emitErrorMessage(
defRetVal->locationId(), "default return value type mismatch." );
break;
case ValUnifyError::Ambiguous:
dm.emitErrorMessage( defRetVal->locationId(),
"ambiguous default return value conversion." );
break;
}
return nullptr;
}
cfg->currentBB()->append( get< Value >( converted ) );
cfg->emitTerminator( r->currentLocation(), cir::Ret( r->currentLocation() ) );
}
}
// TODO_SSA reenable or not
// ReindexVars( cfg );
// TODO_SSA reenable
/*verify::Func fv( c, cfg, returnType );
if( !fv.verify() )
return nullptr;*/
return cfg;
}
} // namespace goose::compile
|
Changes to bs/compile/compiler.h.
1 2 3 4 5 6 7 8 9 10 11 |
#ifndef GOOSE_COMPILER_H
#define GOOSE_COMPILER_H
#include "sema/sema.h"
namespace goose::compile
{
using namespace std;
class Compiler
{
| | | | | | > | | > | | < > | 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 |
#ifndef GOOSE_COMPILER_H
#define GOOSE_COMPILER_H
#include "sema/sema.h"
namespace goose::compile
{
using namespace std;
class Compiler
{
public:
Compiler( int argc, char** argv );
uint32_t execute( const string& filename );
static optional< eir::Value > LoadAndExecuteFile( const util::ptr< sema::Env >& e,
const string& filename, const eir::Term& identity, const eir::Term& returnType,
optional< eir::Value > defRetVal );
static util::ptr< cir::CFG > LoadAndParseFile( const util::ptr< sema::Env >& e,
const string& filename, const eir::Term& identity, const eir::Term& returnType,
optional< eir::Value > defRetVal );
private:
util::ptr< sema::Env > m_pEnv;
};
} // namespace goose::compile
#endif
|
Changes to bs/diagnostics/diagnosticscontext.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_DIAGNOSTICS_DISAGNOSTICSCONTEXT_H
#define GOOSE_DIAGNOSTICS_DISAGNOSTICSCONTEXT_H
namespace goose::diagnostics
{
class DiagnosticsContext
{
| | | | | | | | | | | | | | < < | < < > | 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 |
#ifndef GOOSE_DIAGNOSTICS_DISAGNOSTICSCONTEXT_H
#define GOOSE_DIAGNOSTICS_DISAGNOSTICSCONTEXT_H
namespace goose::diagnostics
{
class DiagnosticsContext
{
public:
template< typename S >
DiagnosticsContext( LocationId location, S&& desc, bool rootContext = false )
{
DiagnosticsManager::GetInstance().pushDiagnosticsContext(
make_shared< ContextInfos >( forward< S >( desc ), location, rootContext ) );
}
DiagnosticsContext( LocationId location, bool rootContext = false )
{
DiagnosticsManager::GetInstance().pushDiagnosticsContext(
make_shared< ContextInfos >( "", location, rootContext ) );
}
DiagnosticsContext( const DiagnosticsContext& ) = delete;
~DiagnosticsContext() { DiagnosticsManager::GetInstance().popDiagnosticsContext(); }
};
} // namespace goose::diagnostics
#endif
|
Changes to bs/diagnostics/diagnosticsmanager.cpp.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
{
if( m_vContextStack.empty() )
return;
m_vContextStack.back()->setVerbosity( v );
}
| | > > | | > | < | > > | > | | 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 |
{
if( m_vContextStack.empty() )
return;
m_vContextStack.back()->setVerbosity( v );
}
void DiagnosticsManager::emitErrorMessage(
LocationId locationId, const string& message, uint32_t contextLevels )
{
if( !m_vContextStack.empty() && m_vContextStack.back()->verbosity() != Verbosity::Normal )
return;
emitError( locationId, message, contextLevels );
}
void DiagnosticsManager::emitErrorMessage(
LocationId locationId, StringId errId, const string& message, uint32_t contextLevels )
{
if( !m_vContextStack.empty() && m_vContextStack.back()->verbosity() != Verbosity::Normal )
return;
auto it = m_customDiagnostics.find( make_pair( locationId, errId ) );
if( it == m_customDiagnostics.end() )
emitError( locationId, message, contextLevels );
else
{
auto&& [msg, contexts] = it->second;
for( auto it = contexts.rbegin(); it != contexts.rend(); ++it )
m_contextStack.emplace_back( *it );
emitError( locationId, msg.empty() ? message : msg, contextLevels );
m_contextStack.resize( m_contextStack.size() - contexts.size() );
}
}
void DiagnosticsManager::emitSyntaxErrorMessage(
LocationId locationId, const string& message, uint32_t contextLevels )
{
emitErrorMessage( locationId, message, contextLevels );
setCurrentVerbosityLevel( Verbosity::Silent );
}
void DiagnosticsManager::emitLexerErrorMessage( LocationId locationId, const string& message )
{
emitErrorMessage( locationId, message, 0 );
}
void DiagnosticsManager::emitTraceMessage( LocationId locationId, const string& message )
{
Renderer r( llvm::outs() );
r.addContext( locationId, Renderer::Squiggles, message, Renderer::Colors::Trace );
}
void DiagnosticsManager::defineCustomDiagnostic(
LocationId locationId, StringId errId, const string& message )
{
vector< ptr< ContextInfos > > contexts;
contexts.reserve( m_contextStack.size() );
for( auto it = m_contextStack.rbegin(); it != m_contextStack.rend(); ++it )
{
bool isRootContext = get< 2 >( **it );
get< 2 >( **it ) = false;
contexts.emplace_back( *it );
if( isRootContext )
break;
}
m_customDiagnostics.emplace(
make_pair( locationId, errId ), make_pair( message, move( contexts ) ) );
}
void DiagnosticsManager::emitError(
LocationId locationId, const string& message, uint32_t contextLevels )
{
if( m_usedUpErrorLocations.find( locationId ) != m_usedUpErrorLocations.end() )
return;
m_usedUpErrorLocations.emplace( locationId );
if( m_emittedFirstError )
llvm::errs() << '\n';
|
| ︙ | ︙ | |||
118 119 120 121 122 123 124 |
if( contextLevels == 0 )
break;
auto&& [desc, location, isRootContext] = **it;
// Highlight all context as error until the first one that have a valid location.
// This is because sometimes the error originates someplace abstract, such as resolving
| | | | | < | 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 |
if( contextLevels == 0 )
break;
auto&& [desc, location, isRootContext] = **it;
// Highlight all context as error until the first one that have a valid location.
// This is because sometimes the error originates someplace abstract, such as resolving
// an internally created overload set for an operator and we only get a source code
// position in the parent context, and we want that, as the place where the actual
// faulty code has been written, to be displayed in red.
if( locationId.invalid() && !location.invalid() )
{
locationId = location;
r.addContext( location, Renderer::Caret | Renderer::Squiggles | Renderer::Highlight,
desc, Renderer::Colors::Error );
}
else
{
r.addContext( location, Renderer::Squiggles, desc, Renderer::Colors::Context );
}
if( isRootContext )
break;
if( contextLevels != ~0U )
--contextLevels;
|
| ︙ | ︙ | |||
165 166 167 168 169 170 171 |
m_vContextStack.emplace_back( vc );
}
void DiagnosticsManager::popVerbosityContext()
{
m_vContextStack.resize( m_vContextStack.size() - 1 );
}
| | | 169 170 171 172 173 174 175 176 |
m_vContextStack.emplace_back( vc );
}
void DiagnosticsManager::popVerbosityContext()
{
m_vContextStack.resize( m_vContextStack.size() - 1 );
}
} // namespace goose::diagnostics
|
Changes to bs/diagnostics/diagnosticsmanager.h.
1 2 3 4 5 6 7 8 9 10 |
#ifndef GOOSE_DIAGNOSTICS_DIAGNOSTICSMANAGER_H
#define GOOSE_DIAGNOSTICS_DIAGNOSTICSMANAGER_H
namespace goose::diagnostics
{
class DiagnosticsContext;
class VerbosityContext;
enum class Verbosity
{
| | | > | | > | | > | | | > | | | | > | | | | < < < | | | < < < | | | < | | | | | | | < | | > | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | < | 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 |
#ifndef GOOSE_DIAGNOSTICS_DIAGNOSTICSMANAGER_H
#define GOOSE_DIAGNOSTICS_DIAGNOSTICSMANAGER_H
namespace goose::diagnostics
{
class DiagnosticsContext;
class VerbosityContext;
enum class Verbosity
{
Normal, // Display all errors normally
Silent // Stop displaying errors for the current context and all non-root sub child
// contexts.
};
using ContextInfos = tuple< string, LocationId, bool >;
class DiagnosticsManager
{
friend class DiagnosticsContext;
friend class VerbosityContext;
public:
static DiagnosticsManager& GetInstance();
void emitErrorMessage(
LocationId locationId, const string& message, uint32_t contextLevels = ~0 );
void emitErrorMessage( LocationId locationId, StringId errId, const string& message,
uint32_t contextLevels = ~0 );
// This is similar to standard error message, but it also set the verbosity level for
// the current context to "Silent", as syntax errors are likely to cause cascading
// errors for the rest of the scope.
void emitSyntaxErrorMessage(
LocationId locationId, const string& message, uint32_t contextLevels = ~0 );
// This is similar to emitErrorMessage, except it nevers display any context.
void emitLexerErrorMessage( LocationId locationId, const string& message );
void emitTraceMessage( LocationId locationId, const string& message );
void defineCustomDiagnostic(
LocationId locationId, StringId errId, const string& message = ""s );
// When enabled, all diagnostics contexts will be pretty printed on the standard output.
void setTraceMode( bool enable ) { m_traceMode = enable; }
bool forceColors() const { return m_forceColors; }
void setForceColors( bool enable ) { m_forceColors = enable; }
void setCurrentVerbosityLevel( Verbosity v );
bool errorsWereEmitted() const { return m_emittedFirstError; }
template< typename C > void pushDiagnosticsContext( C&& ci )
{
m_contextStack.emplace_back( forward< C >( ci ) );
if( m_traceMode )
{
Renderer r( llvm::outs() );
auto&& [desc, location, isRootContext] = *ci;
r.addContext( location, Renderer::Squiggles, desc, Renderer::Colors::Trace );
}
}
void popDiagnosticsContext();
private:
void emitError( LocationId locationId, const string& message, uint32_t contextLevels );
void pushVerbosityContext( VerbosityContext* vc );
void popVerbosityContext();
vector< ptr< ContextInfos > > m_contextStack;
vector< VerbosityContext* > m_vContextStack;
using CustomDiagIdentifier = pair< LocationId, StringId >;
using CustomDiag = pair< string, vector< ptr< ContextInfos > > >;
unordered_map< CustomDiagIdentifier, CustomDiag > m_customDiagnostics;
// Keep track of which location IDs have already been used to emit an error, to avoid
// duplicate error messages (for situations such as template where we parse the same
// piece of code several times, for instance)
unordered_set< LocationId > m_usedUpErrorLocations;
bool m_emittedFirstError = false;
bool m_forceColors = false;
bool m_traceMode = false;
};
} // namespace goose::diagnostics
#define G_ERROR( message ) \
{ \
diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( \
0, format( "internal: {}:{}: {}", __FILE__, __LINE__, message ) ); \
abort(); \
}
#define G_VAL_ERROR( val, message ) \
{ \
diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( \
( val ).locationId(), format( "internal: {}:{}: {}", __FILE__, __LINE__, message ) ); \
abort(); \
}
#define G_VAL_ASSERT( val, cond ) \
if( !( cond ) ) \
{ \
stringstream sstr; \
sstr << ( val ); \
diagnostics::DiagnosticsContext dc( \
( val ).locationId(), format( "Value: {}", sstr.str() ) ); \
diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( ( val ).locationId(), \
format( "internal: {}:{}: assertion " #cond " failed", __FILE__, __LINE__ ) ); \
abort(); \
}
#define G_LOC_ERROR( loc, message ) \
{ \
diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( \
( loc ), format( "internal: {}:{}: {}", __FILE__, __LINE__, message ) ); \
abort(); \
}
#define G_LOC_ASSERT( loc, cond ) \
if( !( cond ) ) \
{ \
diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( ( loc ), \
format( "internal: {}:{}: assertion " #cond " failed", __FILE__, __LINE__ ) ); \
abort(); \
}
#define G_TRACE_VAL( val ) \
{ \
stringstream sstr; \
sstr << val; \
diagnostics::DiagnosticsManager::GetInstance().emitTraceMessage( \
( val ).locationId(), sstr.str() ); \
}
#define G_TRACE( loc, message ) \
{ \
diagnostics::DiagnosticsManager::GetInstance().emitTraceMessage( loc, message ); \
}
#endif
|
Changes to bs/diagnostics/renderer.cpp.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
const char* Renderer::Colors::Context = "\x1b[0m\x1b[38;5;128m";
const char* Renderer::Colors::Trace = "\x1b[0m\x1b[38;5;46m";
const char* Renderer::Colors::Error = "\x1b[0m\x1b[1;38;5;197m";
const char* Renderer::Colors::Source = "\x1b[0m\x1b[38;5;253m";
bool Renderer::useColors() const
{
| | < > | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
const char* Renderer::Colors::Context = "\x1b[0m\x1b[38;5;128m";
const char* Renderer::Colors::Trace = "\x1b[0m\x1b[38;5;46m";
const char* Renderer::Colors::Error = "\x1b[0m\x1b[1;38;5;197m";
const char* Renderer::Colors::Source = "\x1b[0m\x1b[38;5;253m";
bool Renderer::useColors() const
{
return DiagnosticsManager::GetInstance().forceColors() || m_output.has_colors();
}
void Renderer::addContext( LocationId location, uint8_t styleFlags, const char* color )
{
addItem( { "", location, true, styleFlags, color } );
}
void Renderer::addContext(
LocationId location, uint8_t styleFlags, const string& message, const char* color )
{
addItem( { message, location, true, styleFlags, color } );
}
void Renderer::addItem( Item&& item )
{
auto loc = Location::Get( item.location );
|
| ︙ | ︙ | |||
48 49 50 51 52 53 54 |
m_lastLineNumber = loc->line();
}
m_output << item.message << '\n';
return;
}
| | | > | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
m_lastLineNumber = loc->line();
}
m_output << item.message << '\n';
return;
}
// Try to merge the context information with the pending context information, so that it is
// displayed on the same line. In case of conflict, flush what we have and started a new
// context line.
bool needFlush = false;
// If there is no pending filename, it means that our pending context have no location.
// So this shouldn't be a factor against merging it.
if( !m_pendingContextFilename.empty() )
{
// We can only merge context that are for the same line and file.
|
| ︙ | ︙ | |||
125 126 127 128 129 130 131 |
if( needFlush )
flushContext();
addToPendingContext( loc, item.styleFlags, item.color, item.message );
}
// Add the provided context information to the pending context information.
| | > | > | > | | < < < | 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 |
if( needFlush )
flushContext();
addToPendingContext( loc, item.styleFlags, item.color, item.message );
}
// Add the provided context information to the pending context information.
void Renderer::addToPendingContext( const optional< Location >& loc, uint8_t styleFlags,
const char* pColor, const string& message )
{
// Pending color is set to null to indicate that the pending context is clear.
if( !m_pendingContextMessageColor )
{
if( m_pendingContextMessage.empty() )
m_pendingContextMessage = message;
m_pendingContextMessageColor = pColor;
m_pendingCaret = false;
}
m_pendingCaret = m_pendingCaret || ( styleFlags & Style::Caret );
if( !loc )
return;
if( m_pendingContextFilename.empty() )
{
m_pendingContextFilename = loc->filename();
m_pendingContextOffset =
loc->isOffsetValid() ? ( loc->offset() - loc->column() + 1 ) : ~0U;
m_pendingContextLineNumber = loc->line();
m_pendingContextMessageXPosStart = loc->column() - 1;
m_pendingContextMessageXPosEnd = loc->column() - 1 + loc->length();
m_pendingContextMessageColor = pColor;
}
m_pendingHighlightSpans.emplace_back( SpanVertex{ loc->column(), styleFlags, pColor } );
m_pendingHighlightSpans.emplace_back(
SpanVertex{ loc->column() + loc->length(), styleFlags, nullptr } );
sort( m_pendingHighlightSpans.begin(), m_pendingHighlightSpans.end(),
[]( auto&& a, auto&& b ) { return a.column < b.column; } );
}
// Render the pending context line and clear the pending context state.
void Renderer::flushContext()
{
if( !m_pendingContextMessageColor )
return;
|
| ︙ | ︙ | |||
192 193 194 195 196 197 198 |
m_output << applyColor( Colors::Location );
// Fetch the line from the file if we can, otherwise
// just describe the location
ifstream sourcefile( m_pendingContextFilename );
if( m_pendingContextOffset == ~0U || !sourcefile.good() )
{
| | > | > | 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 |
m_output << applyColor( Colors::Location );
// Fetch the line from the file if we can, otherwise
// just describe the location
ifstream sourcefile( m_pendingContextFilename );
if( m_pendingContextOffset == ~0U || !sourcefile.good() )
{
m_output << format( "{}:{}:{}: ", m_pendingContextFilename, m_pendingContextLineNumber,
m_pendingContextMessageXPosStart + 1 );
m_output << applyColor( m_pendingContextMessageColor );
m_output << m_pendingContextMessage << '\n' << applyColor( Colors::Reset );
}
else
{
if( m_pendingContextFilename != m_lastFilename )
m_output << format( "{}:\n", m_pendingContextFilename );
else if( m_pendingContextLineNumber != m_lastLineNumber
&& m_pendingContextLineNumber != ( m_lastLineNumber + 1 ) )
m_output << format( " ...\n" );
m_lastFilename = m_pendingContextFilename;
m_lastLineNumber = m_pendingContextLineNumber;
m_output << format( "{:>5} | ", m_pendingContextLineNumber );
m_output << applyColor( Colors::Source );
|
| ︙ | ︙ | |||
227 228 229 230 231 232 233 |
uint32_t start = 0;
for( auto&& sv : m_pendingHighlightSpans )
{
// Output the span of the source string up to that highlight vertex
m_output << string( sview.substr( start, sv.column - 1 - start ) );
| > | | 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
uint32_t start = 0;
for( auto&& sv : m_pendingHighlightSpans )
{
// Output the span of the source string up to that highlight vertex
m_output << string( sview.substr( start, sv.column - 1 - start ) );
uint32_t count = min(
sv.column - size_t{ 1 } - start, max( sview.size(), size_t{ 1 } ) - start );
// Generate decorations
while( count-- )
decoration << ( inCaretSpan ? '^' : decChar );
start = sv.column - 1;
|
| ︙ | ︙ | |||
284 285 286 287 288 289 290 |
if( start <= sview.size() )
m_output << string( sview.substr( start, sview.size() - start ) ) << '\n';
else
m_output << '\n';
auto decStr = decoration.str();
| | | > | > > | > | | 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 |
if( start <= sview.size() )
m_output << string( sview.substr( start, sview.size() - start ) ) << '\n';
else
m_output << '\n';
auto decStr = decoration.str();
if( !decStr.empty() )
m_output << applyColor( Colors::Location ) << " | "
<< applyColor( Colors::Reset ) << decStr << '\n';
if( !m_pendingContextMessage.empty() )
{
m_output << applyColor( Colors::Location ) << " | "
<< applyColor( m_pendingContextMessageColor );
int32_t maxpos =
min( m_pendingContextMessageXPosEnd, static_cast< uint32_t >( line.size() ) );
int32_t xpos = maxpos - m_pendingContextMessageXPosStart;
xpos -= m_pendingContextMessage.size();
xpos /= 2;
uint32_t count =
max( xpos + static_cast< int32_t >( m_pendingContextMessageXPosStart ), 0 );
while( count-- )
m_output << ' ';
m_output << m_pendingContextMessage << '\n';
m_output << applyColor( Colors::Reset );
}
}
|
| ︙ | ︙ | |||
318 319 320 321 322 323 324 |
const char* Renderer::applyColor( const char* pColor )
{
if( useColors() )
return pColor;
return "";
}
| | | 326 327 328 329 330 331 332 333 |
const char* Renderer::applyColor( const char* pColor )
{
if( useColors() )
return pColor;
return "";
}
} // namespace goose::diagnostics
|
Changes to bs/diagnostics/renderer.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_DIAGNOSTICS_RENDERER_H
#define GOOSE_DIAGNOSTICS_RENDERER_H
namespace goose::diagnostics
{
class Renderer
{
| | | | < | < > | | | < | | | | | | | | | | | | | | | | | | | < > | | > | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 |
#ifndef GOOSE_DIAGNOSTICS_RENDERER_H
#define GOOSE_DIAGNOSTICS_RENDERER_H
namespace goose::diagnostics
{
class Renderer
{
public:
Renderer( llvm::raw_ostream& output ) :
m_output( output )
{
}
~Renderer() { flushContext(); }
enum Style
{
None = 0,
Caret = 1 << 0,
Squiggles = 1 << 1,
Highlight = 1 << 2
};
struct Colors
{
static const char* Reset;
static const char* Location; // File name, line number gutter
static const char* Context; // Squiggles and messages providing contextual informations
static const char* Trace; // Squiggles and messages displayed during tracing
static const char* Error; // Error highlight and "error" keyword
static const char* Source; // Source code
};
template< typename... A >
void addMessage( LocationId location, const string& message, A&&... args );
template< typename... A > void addContext( const string& message, A&&... args );
void addContext(
LocationId location, uint8_t styleFlags, const char* color = Colors::Reset );
void addContext( LocationId location, uint8_t styleFlags, const string& message,
const char* color = Colors::Reset );
private:
bool useColors() const;
void addToPendingContext( const optional< Location >& loc, uint8_t styleFlags,
const char* pColor, const string& message );
void flushContext();
llvm::raw_ostream& m_output;
struct Item
{
string message;
LocationId location = 0;
// The following values are only relevant if the location is valid.
bool quoteSource = false;
uint8_t styleFlags = 0;
// Color is only relevant if styleFlags != 0
const char* color = "";
};
void addItem( Item&& item );
const char* applyColor( const char* pColor );
string m_lastFilename;
uint32_t m_lastLineNumber = 0;
// The pending context line informations.
string m_pendingContextFilename;
uint32_t m_pendingContextOffset = 0;
uint32_t m_pendingContextLineNumber = 0;
// Describe the starting or ending point of a span to be
// highlighted. color = nullptr means it's an ending point.
struct SpanVertex
{
uint32_t column = 0;
uint8_t styleFlags = 0;
const char* color = nullptr;
};
// The pending highlighting vertices.
llvm::SmallVector< SpanVertex, 8 > m_pendingHighlightSpans;
// The pending context message.
string m_pendingContextMessage;
// The color and position of the pending context message.
const char* m_pendingContextMessageColor = nullptr;
uint32_t m_pendingContextMessageXPosStart = 0;
uint32_t m_pendingContextMessageXPosEnd = 0;
// True if there is a pending caret highlighting.
// (we only allow one per displayed context line)
bool m_pendingCaret = false;
};
} // namespace goose::diagnostics
#endif
|
Changes to bs/diagnostics/renderer.inl.
1 2 3 4 5 |
#ifndef GOOSE_DIAGNOSTICS_RENDERER_INL
#define GOOSE_DIAGNOSTICS_RENDERER_INL
namespace goose::diagnostics
{
| | | < < | | < < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#ifndef GOOSE_DIAGNOSTICS_RENDERER_INL
#define GOOSE_DIAGNOSTICS_RENDERER_INL
namespace goose::diagnostics
{
template< typename... A >
void Renderer::addMessage( LocationId location, const string& message, A&&... args )
{
addItem( { format( message, forward< A >( args )... ), location, false, Style::None } );
}
template< typename... A > void Renderer::addContext( const string& message, A&&... args )
{
addItem( { format( message, forward< A >( args )... ), 0, false, Style::None } );
}
} // namespace goose::diagnostics
#endif
|
Changes to bs/diagnostics/verbositycontext.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_DIAGNOSTICS_VERBOSITYCONTEXT_H
#define GOOSE_DIAGNOSTICS_VERBOSITYCONTEXT_H
namespace goose::diagnostics
{
class VerbosityContext
{
| | | | | | | | < < | | < | | > | | | | | | | | < > | 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 |
#ifndef GOOSE_DIAGNOSTICS_VERBOSITYCONTEXT_H
#define GOOSE_DIAGNOSTICS_VERBOSITYCONTEXT_H
namespace goose::diagnostics
{
class VerbosityContext
{
public:
VerbosityContext( Verbosity v, bool rootContext = false ) :
m_verbosity( v ),
m_isRootContext( rootContext )
{
DiagnosticsManager::GetInstance().pushVerbosityContext( this );
}
~VerbosityContext() { DiagnosticsManager::GetInstance().popVerbosityContext(); }
bool isRootContext() const { return m_isRootContext; }
auto verbosity() const { return m_verbosity; }
void setVerbosity( Verbosity v )
{
if( v > m_verbosity )
m_verbosity = v;
}
private:
Verbosity m_verbosity = Verbosity::Normal;
bool m_isRootContext = false;
};
} // namespace goose::diagnostics
#endif
|
Changes to bs/eir/anyterm.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_EIR_ANYTERM_H
#define GOOSE_EIR_ANYTERM_H
namespace goose::eir
{
class AnyTerm
{
| | | | < | > > | > | | | < < < | | | < > | | 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 |
#ifndef GOOSE_EIR_ANYTERM_H
#define GOOSE_EIR_ANYTERM_H
namespace goose::eir
{
class AnyTerm
{
public:
AnyTerm( StringId varName ) :
m_varName( varName )
{
}
const auto& varName() const { return m_varName; }
friend ostream& ToString( ostream& out, const AnyTerm& v );
friend ostream& operator<<( ostream& out, const AnyTerm& x ) { return out << x.m_varName; }
auto operator<=>( const AnyTerm& ) const = default;
private:
StringId m_varName;
};
} // namespace goose::eir
namespace std
{
template<> struct hash< goose::eir::AnyTerm >
{
size_t operator()( const goose::eir::AnyTerm& x ) const
{
return hash< goose::util::StringId >()( x.varName() );
}
};
} // namespace std
#endif
|
Changes to bs/eir/bridge.h.
1 2 3 4 5 |
#ifndef GOOSE_EIR_BRIDGE_H
#define GOOSE_EIR_BRIDGE_H
namespace goose::eir
{
| | < > | | < | < | < | < | < | < | | 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 |
#ifndef GOOSE_EIR_BRIDGE_H
#define GOOSE_EIR_BRIDGE_H
namespace goose::eir
{
template< typename T > struct Bridge
{
};
template< typename T, typename... P > auto GetValueType( P&&... params )
{
return Bridge< T >::Type( forward< P >( params )... );
}
template< typename T, typename... ARGS > Value ToDeclValue( T&& x, ARGS&&... args )
{
using TT = remove_cvref_t< T >;
return Bridge< TT >::ToDeclValue( forward< T >( x ), forward< ARGS >( args )... );
}
template< typename TT, typename... T > Value ToDeclValue( T&&... x )
{
return Bridge< TT >::ToDeclValue( forward< T >( x )... );
}
template< typename T, typename... ARGS > Value ToValue( T&& x, ARGS&&... args )
{
using TT = remove_cvref_t< T >;
return Bridge< TT >::ToValue( forward< T >( x ), forward< ARGS >( args )... );
}
template< typename TT, typename... T > Value ToValue( T&&... x )
{
return Bridge< TT >::ToValue( forward< T >( x )... );
}
template< typename T > auto FromValue( const Value& val )
{
return Bridge< T >::FromValue( val );
}
} // namespace goose::eir
#endif
|
Changes to bs/eir/compare.cpp.
1 2 3 4 5 6 7 8 9 |
#include "eir.h"
namespace goose::eir
{
bool operator==( const Term& lhs, const Term& rhs )
{
if( lhs.index() != rhs.index() )
return false;
| > | | | | | | | | | | | | | > | | > | | < | < < < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
#include "eir.h"
namespace goose::eir
{
bool operator==( const Term& lhs, const Term& rhs )
{
if( lhs.index() != rhs.index() )
return false;
return visit(
[&]< typename T >( const T& l )
{
const auto& r = get< T >( rhs );
if constexpr( is_same_v< pvec, T > )
return *l == *r;
else if constexpr( is_same_v< LocationId, T > )
return true;
else if constexpr( is_same_v< ptr< void >, T > )
return true;
else if constexpr( is_same_v< void*, T > )
return true;
else if constexpr( is_same_v< APSInt, T > )
return l.isSigned() == r.isSigned() && l.getBitWidth() == r.getBitWidth()
&& l == r;
else
return l == r;
},
lhs );
}
bool operator!=( const Term& lhs, const Term& rhs )
{
return !( lhs == rhs );
}
bool Vector::operator==( const Vector& rhs ) const
{
return m_repetitionTerm == rhs.m_repetitionTerm && m_terms == rhs.m_terms;
}
bool Hole::operator<( const Hole& rhs ) const
{
if( name() != rhs.name() )
return name() < rhs.name();
if( behavior() != rhs.behavior() )
return behavior() < rhs.behavior();
return kind() < rhs.kind();
}
bool Hole::operator==( const Hole& rhs ) const
{
return name() == rhs.name() && behavior() == rhs.behavior() && kind() == rhs.kind();
}
} // namespace goose::eir
|
Changes to bs/eir/decompose.h.
1 2 3 4 5 |
#ifndef GOOSE_EIR_DECOMPOSE_H
#define GOOSE_EIR_DECOMPOSE_H
namespace goose::eir
{
| | < | < > > > | < | < | < > | < | | | | < > | < > > | < < | | < > > | 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 |
#ifndef GOOSE_EIR_DECOMPOSE_H
#define GOOSE_EIR_DECOMPOSE_H
namespace goose::eir
{
template< typename T > struct LiteralSpec
{
using return_type = bool;
template< typename TT >
LiteralSpec( TT&& val ) :
m_val( forward< TT >( val ) )
{
}
T m_val;
};
template< typename T > auto Lit( T&& val );
template< typename T > struct Val
{
using return_type = T;
};
struct SubTerm
{
using return_type = Term;
};
template< typename... T > struct VecDecompositionReturnTypeBuilder
{
};
template< typename... TU > struct VecDecompositionReturnTypeBuilder< tuple< TU... > >
{
using return_type = tuple< TU... >;
};
template< typename... TU, typename HS, typename... TS >
struct VecDecompositionReturnTypeBuilder< tuple< TU... >, HS, TS... >
{
using return_type = conditional_t< is_same_v< bool, typename HS::return_type >,
typename VecDecompositionReturnTypeBuilder< tuple< TU... >, TS... >::return_type,
typename VecDecompositionReturnTypeBuilder< tuple< TU..., typename HS::return_type >,
TS... >::return_type >;
};
template< typename... S > struct VectorSpec
{
using return_type =
typename VecDecompositionReturnTypeBuilder< tuple<>, S... >::return_type;
template< typename... T >
VectorSpec( T&&... specs ) :
m_specs( forward< T >( specs )... )
{
}
tuple< S... > m_specs;
};
template< typename... S > auto Vec( S&&... specs );
template< typename T > bool Decompose( const Term& t, const LiteralSpec< T >& spec );
template< typename T >
optional< reference_wrapper< const T > > Decompose( const Term& t, const Val< T >& spec );
static inline const Term& Decompose( const Term& t, const SubTerm& spec );
template< typename... S >
optional< typename VectorSpec< S... >::return_type > Decompose(
const Term& t, const VectorSpec< S... >& spec );
} // namespace goose::eir
#endif
|
Changes to bs/eir/decompose.inl.
1 2 3 4 5 |
#ifndef GOOSE_EIR_DECOMPOSE_INL
#define GOOSE_EIR_DECOMPOSE_INL
namespace goose::eir
{
| | < | < < | | 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_EIR_DECOMPOSE_INL
#define GOOSE_EIR_DECOMPOSE_INL
namespace goose::eir
{
template< typename T > auto Lit( T&& val )
{
return LiteralSpec< T >( forward< T >( val ) );
}
template< typename... S > auto Vec( S&&... specs )
{
return VectorSpec< S... >( forward< S >( specs )... );
}
template< typename T > bool Decompose( const Term& t, const LiteralSpec< T >& spec )
{
const auto* pContent = get_if< T >( &t );
if( !pContent )
return false;
return *pContent == spec.m_val;
}
|
| ︙ | ︙ | |||
37 38 39 40 41 42 43 |
const Term& Decompose( const Term& t, const SubTerm& spec )
{
return t;
}
template< size_t IR, size_t IS, typename... S, typename... T >
| | > | | | | > | | 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 |
const Term& Decompose( const Term& t, const SubTerm& spec )
{
return t;
}
template< size_t IR, size_t IS, typename... S, typename... T >
bool Decompose( const Vector::container_type< Term >& terms, const tuple< S... >& specs,
tuple< T... >& result )
{
auto res = Decompose( terms[IS], get< IS >( specs ) );
if constexpr( is_same_v< decltype( res ), bool > )
{
if( !res )
return false;
// For specs that are just predicates, we have no result to insert
// so don't increment the result index.
if constexpr( IS < ( sizeof...( S ) - 1 ) )
return Decompose< IR, IS + 1 >( terms, specs, result );
else
return true;
}
else if constexpr( is_optional_v< decltype( res ) > )
{
if( !res )
return false;
get< IR >( result ) = move( *res );
if constexpr( IS < ( sizeof...( S ) - 1 ) )
return Decompose< IR + 1, IS + 1 >( terms, specs, result );
else
return true;
}
else
{
get< IR >( result ) = move( res );
if constexpr( IS < ( sizeof...( S ) - 1 ) )
return Decompose< IR + 1, IS + 1 >( terms, specs, result );
return true;
}
}
template< typename... S >
optional< typename VectorSpec< S... >::return_type > Decompose(
const Term& t, const VectorSpec< S... >& spec )
{
const auto* ppVec = get_if< pvec >( &t );
if( !ppVec )
return nullopt;
const auto& terms = ( *ppVec )->terms();
if( terms.size() != sizeof...( S ) )
return nullopt;
typename VectorSpec< S... >::return_type result;
if( !Decompose< 0, 0 >( terms, spec.m_specs, result ) )
return nullopt;
return result;
}
} // namespace goose::eir
#endif
|
Changes to bs/eir/eir.h.
1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_EIR_H
#define GOOSE_EIR_H
#include "util/util.h"
namespace goose::eir
{
using namespace util;
| | | < < > | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#ifndef GOOSE_EIR_H
#define GOOSE_EIR_H
#include "util/util.h"
namespace goose::eir
{
using namespace util;
struct EmptyPayload
{
};
template< typename U = EmptyPayload > class Trie;
} // namespace goose::eir
#include "anyterm.h"
#include "vecoflength.h"
#include "term.h"
#include "trie.h"
#include "tostring.h"
#include "vector.h"
|
| ︙ | ︙ |
Changes to bs/eir/enumerate.cpp.
1 2 3 4 5 6 |
#include "eir.h"
namespace goose::eir
{
Generator< Term > Enumerate( const Trie<>& trie )
{
| | | | 1 2 3 4 5 6 7 8 9 10 |
#include "eir.h"
namespace goose::eir
{
Generator< Term > Enumerate( const Trie<>& trie )
{
for( auto&& [t, p] : Enumerate<>( trie ) )
co_yield move( t );
}
} // namespace goose::eir
|
Changes to bs/eir/enumerate.inl.
1 2 3 4 5 6 7 8 9 10 11 |
#ifndef GOOSE_EIR_ENUMERATE_INL
#define GOOSE_EIR_ENUMERATE_INL
namespace goose::eir
{
//--------------------------------------------------------------------------------------------
// Primitive types
//--------------------------------------------------------------------------------------------
template< typename T, typename U >
static Generator< pair< Term, const U& > > Enumerate( const ValueTrieNode< T, U >& trie )
{
| | | > | > | | | | | | | 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 |
#ifndef GOOSE_EIR_ENUMERATE_INL
#define GOOSE_EIR_ENUMERATE_INL
namespace goose::eir
{
//--------------------------------------------------------------------------------------------
// Primitive types
//--------------------------------------------------------------------------------------------
template< typename T, typename U >
static Generator< pair< Term, const U& > > Enumerate( const ValueTrieNode< T, U >& trie )
{
for( auto&& [k, v] : trie.m_branches )
co_yield { TERM( k ), v };
}
//--------------------------------------------------------------------------------------------
// Hole
//--------------------------------------------------------------------------------------------
template< typename U >
static Generator< pair< Hole::Behavior, const U& > > Enumerate(
const HoleBehaviorNode< U >& rdn )
{
if( rdn.m_standardBhvBranch )
co_yield { Hole::Behavior::Standard, *rdn.m_standardBhvBranch };
if( rdn.m_packBhvBranch )
co_yield { Hole::Behavior::Pack, *rdn.m_packBhvBranch };
if( rdn.m_anyBhvBranch )
co_yield { Hole::Behavior::Any, *rdn.m_anyBhvBranch };
}
template< typename U >
static Generator< pair< StringId, const HoleBehaviorNode< U >& > > Enumerate(
const HoleKindNode< U >& hkn )
{
for( auto&& [kind, v] : hkn.m_branches )
co_yield { kind, *v };
if( hkn.m_anyKindBranch )
co_yield { ""_sid, *hkn.m_anyKindBranch };
}
template< typename U >
static Generator< pair< Term, const U& > > Enumerate( const TrieNode< Hole, U >& trie )
{
for( auto&& [name, hkn] : trie.m_branches )
for( auto&& [kind, bhvn] : Enumerate( *hkn ) )
for( auto&& [bhv, v] : Enumerate( bhvn ) )
co_yield { HOLE( move( name ), move( kind ), bhv ), v };
if( trie.m_anyNameBranch )
for( auto&& [kind, bhvn] : Enumerate( *trie.m_anyNameBranch ) )
for( auto&& [bhv, v] : Enumerate( bhvn ) )
co_yield { HOLE( ""_sid, move( kind ), bhv ), v };
}
//--------------------------------------------------------------------------------------------
// LocationId
//--------------------------------------------------------------------------------------------
template< typename U >
|
| ︙ | ︙ | |||
80 81 82 83 84 85 86 |
co_yield { static_cast< void* >( nullptr ), trie.m_next };
}
//--------------------------------------------------------------------------------------------
// Containers
//--------------------------------------------------------------------------------------------
template< typename U >
| | > | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
co_yield { static_cast< void* >( nullptr ), trie.m_next };
}
//--------------------------------------------------------------------------------------------
// Containers
//--------------------------------------------------------------------------------------------
template< typename U >
static Generator< pair< Term, const U& > > Enumerate(
Vector&& vec, const TrieContainerBranch_t& contBranch )
{
if( contBranch.index() == 0 )
{
for( auto&& [t, payload] : Enumerate( get< 0 >( contBranch )->m_trie ) )
{
auto newVec = Vector::MakeAppend( vec, t );
co_yield Enumerate< U >( move( newVec ), payload );
|
| ︙ | ︙ | |||
144 145 146 147 148 149 150 |
}
template< typename U >
static Generator< pair< Term, const U& > > Enumerate( const Trie< U >& trie )
{
return Enumerate< 0 >( trie );
}
| | | 147 148 149 150 151 152 153 154 155 156 |
}
template< typename U >
static Generator< pair< Term, const U& > > Enumerate( const Trie< U >& trie )
{
return Enumerate< 0 >( trie );
}
} // namespace goose::eir
#endif
|
Changes to bs/eir/graphviz.cpp.
1 2 3 4 5 6 7 8 |
#include "eir.h"
namespace goose::eir
{
void GraphVizDump( const char* pFilename, const Trie<>& trie )
{
GraphVizDump<>( pFilename, trie );
}
| | | 1 2 3 4 5 6 7 8 9 |
#include "eir.h"
namespace goose::eir
{
void GraphVizDump( const char* pFilename, const Trie<>& trie )
{
GraphVizDump<>( pFilename, trie );
}
} // namespace goose::eir
|
Changes to bs/eir/graphviz.h.
1 2 3 4 5 |
#ifndef GOOSE_EIR_GRAPHVIZ_H
#define GOOSE_EIR_GRAPHVIZ_H
namespace goose::eir
{
| < | | 1 2 3 4 5 6 7 8 9 10 11 |
#ifndef GOOSE_EIR_GRAPHVIZ_H
#define GOOSE_EIR_GRAPHVIZ_H
namespace goose::eir
{
template< typename U > static void GraphVizDump( const char* pFilename, const Trie< U >& trie );
extern void GraphVizDump( const char* pFilename, const Trie<>& trie );
}
#endif
|
Changes to bs/eir/graphviz.inl.
1 2 3 4 5 6 7 8 9 10 11 12 |
#ifndef GOOSE_EIR_GRAPHVIZ_INL
#define GOOSE_EIR_GRAPHVIZ_INL
namespace goose::eir
{
template< typename U, typename F >
static void GraphVizDump( GraphVizBuilder& builder, const Trie< U >& trie, F&& next );
//--------------------------------------------------------------------------------------------
// Primitive types
//--------------------------------------------------------------------------------------------
template< typename T, typename U, typename F >
| > | | | > > | | 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 |
#ifndef GOOSE_EIR_GRAPHVIZ_INL
#define GOOSE_EIR_GRAPHVIZ_INL
namespace goose::eir
{
template< typename U, typename F >
static void GraphVizDump( GraphVizBuilder& builder, const Trie< U >& trie, F&& next );
//--------------------------------------------------------------------------------------------
// Primitive types
//--------------------------------------------------------------------------------------------
template< typename T, typename U, typename F >
static void GraphVizDump(
GraphVizBuilder& builder, const char* pLabel, const ValueTrieNode< T, U >& trie, F&& next )
{
GraphVizBuilder::Node n( builder, &trie, ( pLabel + " node"s ).c_str() );
for( auto&& [k, v] : trie.m_branches )
{
GraphVizBuilder::Row row( builder );
{
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
ToString( builder.output(), k );
}
next( v );
}
}
//--------------------------------------------------------------------------------------------
// LocationId
//--------------------------------------------------------------------------------------------
template< typename U, typename F >
static void GraphVizDump( GraphVizBuilder& builder, const char* pLabel,
const TrieNode< LocationId, U >& trie, F&& next )
{
GraphVizBuilder::Node n( builder, &trie, ( pLabel + " node"s ).c_str() );
GraphVizBuilder::Row row( builder );
{
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
builder.output() << "next";
}
next( trie.m_next );
}
//--------------------------------------------------------------------------------------------
// Holes
//--------------------------------------------------------------------------------------------
template< typename U, typename F >
static void GraphVizDump(
GraphVizBuilder& builder, const HoleBehaviorNode< U >& trie, F&& next )
{
GraphVizBuilder::Node n( builder, &trie, "Hole behavior node" );
if( trie.m_standardBhvBranch )
{
auto id = builder.getNodeId( trie.m_standardBhvBranch.get() );
GraphVizBuilder::Row row( builder );
|
| ︙ | ︙ | |||
100 101 102 103 104 105 106 |
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );
builder.output() << "<any>";
builder.addEdge( builder.currentNodeId(), id );
| | < | < | | < < < > | | < | < | | < < < | > > | > | | < < < > | | < | < > | | | | > | | | | | | | < | < > | | < | < | | | | | | | | | | | | | < | | > | < | 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 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );
builder.output() << "<any>";
builder.addEdge( builder.currentNodeId(), id );
builder.queueWork(
[&, next] { GraphVizDump( builder, *trie.m_anyKindBranch, next ); } );
}
for( auto&& [k, v] : trie.m_branches )
{
auto id = builder.getNodeId( v.get() );
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );
ToString( builder.output(), k );
builder.addEdge( builder.currentNodeId(), id );
const auto& nextNode = *v;
builder.queueWork( [&, next] { GraphVizDump( builder, nextNode, next ); } );
}
}
template< typename U, typename F >
static void GraphVizDump(
GraphVizBuilder& builder, const char* pLabel, const TrieNode< Hole, U >& trie, F&& next )
{
GraphVizBuilder::Node n( builder, &trie, ( pLabel + " node"s ).c_str() );
if( trie.m_anyNameBranch )
{
auto id = builder.getNodeId( trie.m_anyNameBranch.get() );
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );
builder.output() << "<any>";
builder.addEdge( builder.currentNodeId(), id );
builder.queueWork(
[&, next] { GraphVizDump( builder, *trie.m_anyNameBranch, next ); } );
}
for( auto&& [k, v] : trie.m_branches )
{
auto id = builder.getNodeId( v.get() );
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );
ToString( builder.output(), k );
builder.addEdge( builder.currentNodeId(), id );
const auto& nextNode = *v;
builder.queueWork( [&, next] { GraphVizDump( builder, nextNode, next ); } );
}
}
//--------------------------------------------------------------------------------------------
// ptr< void >
//--------------------------------------------------------------------------------------------
template< typename U, typename F >
static void GraphVizDump( GraphVizBuilder& builder, const char* pLabel,
const TrieNode< ptr< void >, U >& trie, F&& next )
{
GraphVizBuilder::Node n( builder, &trie, ( pLabel + " node"s ).c_str() );
GraphVizBuilder::Row row( builder );
{
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
builder.output() << "next";
}
next( trie.m_next );
}
//--------------------------------------------------------------------------------------------
// void*
//--------------------------------------------------------------------------------------------
template< typename U, typename F >
static void GraphVizDump(
GraphVizBuilder& builder, const char* pLabel, const TrieNode< void*, U >& trie, F&& next )
{
GraphVizBuilder::Node n( builder, &trie, ( pLabel + " node"s ).c_str() );
GraphVizBuilder::Row row( builder );
{
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
builder.output() << "next";
}
next( trie.m_next );
}
//--------------------------------------------------------------------------------------------
// Containers
//--------------------------------------------------------------------------------------------
template< typename U, typename F >
static void GraphVizDump(
GraphVizBuilder& builder, const TrieContainerNode& contNode, F&& next )
{
auto id = builder.getNodeId( &contNode.m_trie );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );
builder.output() << id;
builder.addEdge( builder.currentNodeId(), id );
builder.queueWork( [&, next] { GraphVizDump( builder, contNode.m_trie, next ); } );
}
template< typename U, typename F >
static void GraphVizDump(
GraphVizBuilder& builder, const TrieContainerBranch_t& contBranch, F&& next )
{
if( contBranch.index() == 0 )
{
function< void( const TrieContainerBranch_t& ) > f(
[&, next]( auto&& payload ) { GraphVizDump< U >( builder, payload, next ); } );
GraphVizDump< U >( builder, *get< 0 >( contBranch ), move( f ) );
}
else
{
const auto& contEnd = get< 1 >( contBranch );
next( *any_cast< U >( &contEnd.m_payload ) );
}
}
template< typename U, typename F >
static void GraphVizDump(
GraphVizBuilder& builder, const char* pLabel, const TrieNode< pvec, U >& trie, F&& next )
{
GraphVizBuilder::Node n( builder, &trie, "Vector node" );
for( auto&& [k, contBranch] : trie.m_fixedLengthBranches )
{
GraphVizBuilder::Row row( builder );
{
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
builder.output() << "length " << k;
}
GraphVizDump< U >( builder, contBranch, next );
}
for( auto&& [k, contBranch] : trie.m_variableLengthBranches )
{
GraphVizBuilder::Row row( builder );
{
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
builder.output() << "length " << k << '+';
}
using rpt_node = ptr< TrieContainerRepetitionNode< U > >;
GraphVizDump< rpt_node >( builder, contBranch,
[&, next]( auto&& payload )
{
auto id = builder.getNodeId( &payload->m_repetition );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );
builder.output() << id;
builder.addEdge( builder.currentNodeId(), id );
builder.queueWork( [&, payload, next]
{ GraphVizDump( builder, payload->m_repetition, next ); } );
} );
}
}
//--------------------------------------------------------------------------------------------
// Terms
//--------------------------------------------------------------------------------------------
template< typename T, typename F >
static void GraphVizDumpTermRow(
GraphVizBuilder& builder, const char* pLabel, const T& ptr, F&& next )
{
if( !ptr )
return;
GraphVizBuilder::Row row( builder );
{
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
builder.output() << pLabel;
}
{
auto id = builder.getNodeId( ptr.get() );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );
builder.output() << id;
builder.addEdge( builder.currentNodeId(), id );
builder.queueWork(
[&, pLabel, ptr, next] { GraphVizDump( builder, pLabel, *ptr, next ); } );
}
}
template< typename U, typename F >
static void GraphVizDump( GraphVizBuilder& builder, const Trie< U >& trie, F&& next )
{
GraphVizBuilder::Node n( builder, &trie, "Term node" );
GraphVizDumpTermRow( builder, "uint32_t", get< 0 >( trie.branches() ), next );
GraphVizDumpTermRow( builder, "LocationId", get< 1 >( trie.branches() ), next );
GraphVizDumpTermRow( builder, "string", get< 2 >( trie.branches() ), next );
GraphVizDumpTermRow( builder, "StringId", get< 3 >( trie.branches() ), next );
GraphVizDumpTermRow( builder, "Delimiter", get< 4 >( trie.branches() ), next );
GraphVizDumpTermRow( builder, "Hole", get< 5 >( trie.branches() ), next );
GraphVizDumpTermRow( builder, "AnyTerm", get< 6 >( trie.branches() ), next );
GraphVizDumpTermRow( builder, "VecOfLength", get< 7 >( trie.branches() ), next );
GraphVizDumpTermRow( builder, "ptr< void >", get< 8 >( trie.branches() ), next );
GraphVizDumpTermRow( builder, "void*", get< 9 >( trie.branches() ), next );
GraphVizDumpTermRow( builder, "Vector", get< 10 >( trie.branches() ), next );
GraphVizDumpTermRow( builder, "BigInt", get< 11 >( trie.branches() ), next );
GraphVizDumpTermRow( builder, "APSInt", get< 12 >( trie.branches() ), next );
}
template< typename U > static void GraphVizDump( const char* pFilename, const Trie< U >& trie )
{
ofstream file( pFilename );
GraphVizBuilder builder( file );
GraphVizDump( builder, trie, []( auto&& ) {} );
}
} // namespace goose::eir
#endif
|
Changes to bs/eir/hash.cpp.
1 2 3 4 5 6 7 |
#include "eir.h"
#include "cir/cir.h"
namespace std
{
size_t hash< goose::eir::Hole >::operator()( const goose::eir::Hole& x ) const
{
| | < | < < > | | | | | | > | | | > | | | | > | | | | > | | > | > | > | | | 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 |
#include "eir.h"
#include "cir/cir.h"
namespace std
{
size_t hash< goose::eir::Hole >::operator()( const goose::eir::Hole& x ) const
{
return llvm::hash_combine( goose::util::ComputeHash( x.name() ),
goose::util::ComputeHash( x.kind() ), goose::util::ComputeHash( x.behavior() ) );
}
size_t hash< goose::eir::Vector >::operator()( const goose::eir::Vector& v ) const
{
auto g = ContainerHashGenerator( v.terms() );
return llvm::hash_combine_range( g.begin(), g.end() );
}
size_t hash< goose::eir::Term >::operator()( const goose::eir::Term& x ) const
{
return visit(
[&]< typename T >( T&& t )
{
using TT = remove_cvref_t< T >;
if constexpr( is_same_v< TT, goose::util::ptr< void > > || is_same_v< TT, void* > )
{
return llvm::hash_combine( goose::util::ComputeHash( x.index() ),
goose::util::ComputeHash( nullptr ) );
}
else if constexpr( is_same_v< TT, goose::eir::pvec > )
{
return llvm::hash_combine(
goose::util::ComputeHash( x.index() ), goose::util::ComputeHash( *t ) );
}
else if constexpr( is_same_v< TT, goose::cir::LocationId > )
{
return llvm::hash_combine(
goose::util::ComputeHash( x.index() ), goose::util::ComputeHash( 0UL ) );
}
else
{
return llvm::hash_combine(
goose::util::ComputeHash( x.index() ), goose::util::ComputeHash( t ) );
}
},
x );
}
size_t hash< goose::eir::Value >::operator()( const goose::eir::Value& x ) const
{
if( x.isConstant() )
return llvm::hash_combine(
goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.val() ) );
else
return llvm::hash_combine(
goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( *x.cir() ) );
}
} // namespace std
|
Changes to bs/eir/hash.h.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 |
size_t operator()( const goose::eir::Term& x ) const;
};
template<> struct hash< goose::eir::Value >
{
size_t operator()( const goose::eir::Value& x ) const;
};
| | | 18 19 20 21 22 23 24 25 26 27 |
size_t operator()( const goose::eir::Term& x ) const;
};
template<> struct hash< goose::eir::Value >
{
size_t operator()( const goose::eir::Value& x ) const;
};
} // namespace std
#endif
|
Changes to bs/eir/helpers.cpp.
| ︙ | ︙ | |||
43 44 45 46 47 48 49 |
TermGen ForEachInVectorTerm( const Term& vectorTerm )
{
const auto& vec = *get< pvec >( vectorTerm );
for( auto&& t : vec.terms() )
co_yield &t;
}
| | | 43 44 45 46 47 48 49 50 |
TermGen ForEachInVectorTerm( const Term& vectorTerm )
{
const auto& vec = *get< pvec >( vectorTerm );
for( auto&& t : vec.terms() )
co_yield &t;
}
} // namespace goose::eir
|
Changes to bs/eir/helpers.h.
1 2 3 4 5 6 7 8 9 10 |
#ifndef GOOSE_EIR_HELPERS_H
#define GOOSE_EIR_HELPERS_H
namespace goose::eir
{
static inline auto VecSize( const Term& vectorTerm )
{
return get< pvec >( vectorTerm )->terms().size();
}
| < | < | < | | | 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 |
#ifndef GOOSE_EIR_HELPERS_H
#define GOOSE_EIR_HELPERS_H
namespace goose::eir
{
static inline auto VecSize( const Term& vectorTerm )
{
return get< pvec >( vectorTerm )->terms().size();
}
template< typename... T > Term AppendToVectorTerm( const Term& vectorTerm, T&&... t );
template< typename... T > Term PrependToVectorTerm( const Term& vectorTerm, T&&... t );
extern Term ConcatenateVectorTerms( const Term& vector1, const Term& vector2 );
extern Term TakeVectorTerm( const Term& vectorTerm, size_t n );
extern Term DropVectorTerm( const Term& vectorTerm, size_t n );
extern Term DuplicateVectorTerm( const Term& vectorTerm );
template< typename F > void ForEachInVectorTerm( const Term& vectorTerm, F&& func );
template< typename F >
bool ForEachInVectorTerms( const Term& vectorTerm1, const Term& vectorTerm2, F&& func );
using TermGen = Generator< const Term* >;
extern TermGen ForEachInVectorTerm( const Term& vectorTerm );
extern bool AreVecLengthsCompatible( const VecLength& vl1, const VecLength& vl2 );
} // namespace goose::eir
#endif
|
Changes to bs/eir/helpers.inl.
1 2 3 4 5 |
#ifndef GOOSE_EIR_HELPERS_INL
#define GOOSE_EIR_HELPERS_INL
namespace goose::eir
{
| < | < | < | < < | | < | | | < < | | 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 |
#ifndef GOOSE_EIR_HELPERS_INL
#define GOOSE_EIR_HELPERS_INL
namespace goose::eir
{
template< typename... T > Term AppendToVectorTerm( const Term& vectorTerm, T&&... t )
{
const auto& vec = *get< pvec >( vectorTerm );
auto newVec = Vector::MakeAppend( vec, forward< T >( t )... );
return TERM( make_shared< Vector >( move( newVec ) ) );
}
template< typename... T > Term PrependToVectorTerm( const Term& vectorTerm, T&&... t )
{
const auto& vec = *get< pvec >( vectorTerm );
auto newVec = Vector::MakePrepend( vec, forward< T >( t )... );
return TERM( make_shared< Vector >( move( newVec ) ) );
}
template< typename F > void ForEachInVectorTerm( const Term& vectorTerm, F&& func )
{
auto& vec = *get< pvec >( vectorTerm );
for( auto&& t : vec.terms() )
if( !func( t ) )
break;
}
template< typename F >
bool ForEachInVectorTerms( const Term& vectorTerm1, const Term& vectorTerm2, F&& func )
{
// We allow the lambda to mutate the content even though we are taking constrefs to the
// terms: I tried to make a diff overload with mutable refs and somehow it doesn't resolve
// to it and c++ is a pain in the ass In goose I could just capture the constness of the
// inputs refs as a template param and apply it to those inner refs. It's probably possible
// in C++ through some clown show ref type wrapper but fuck that
auto& vec1 = *get< pvec >( vectorTerm1 );
auto& vec2 = *get< pvec >( vectorTerm2 );
if( vec1.terms().size() != vec2.terms().size() )
return false;
for( size_t i = 0; i < vec1.terms().size(); ++i )
if( !func( vec1[i], vec2[i] ) )
break;
return true;
}
} // namespace goose::eir
#endif
|
Changes to bs/eir/match.cpp.
| ︙ | ︙ | |||
13 14 15 16 17 18 19 |
void MatchSolution::setupVars()
{
if( !m_pVars )
m_pVars = make_shared< unordered_map< StringId, any > >();
else if( m_pVars.use_count() > 1 )
m_pVars = make_shared< unordered_map< StringId, any > >( *m_pVars );
}
| | | 13 14 15 16 17 18 19 20 |
void MatchSolution::setupVars()
{
if( !m_pVars )
m_pVars = make_shared< unordered_map< StringId, any > >();
else if( m_pVars.use_count() > 1 )
m_pVars = make_shared< unordered_map< StringId, any > >( *m_pVars );
}
} // namespace goose::eir
|
Changes to bs/eir/match.h.
| ︙ | ︙ | |||
23 24 25 26 27 28 29 |
return m_numVars < rhs.m_numVars;
}
};
class MatchSolution
{
| | | > | > > | | | | | | | | | | | | | | | | | < | | | | | | | | | | | | | | | | | | < | | | | | | | | | | | | | | | | | | | < > > | 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 |
return m_numVars < rhs.m_numVars;
}
};
class MatchSolution
{
public:
size_t complexity() const { return m_complexity; }
size_t numVars() const;
MatchScore score() const
{
return MatchScore{ m_complexity, m_pVars ? m_pVars->size() : 0 };
}
template< typename T > const T* getVar( StringId name ) const
{
if( name == "_"_sid )
return nullptr;
if( !m_pVars )
return nullptr;
auto it = m_pVars->find( name );
if( it == m_pVars->end() )
return nullptr;
return any_cast< T >( &it->second );
}
// Tries to set the variable. If it already exists but have a different
// value, returns false.
template< typename T > bool setVar( StringId name, T&& val )
{
if( name == "_"_sid )
return true;
if( m_pVars )
{
auto it = m_pVars->find( name );
if( it != m_pVars->end() )
{
using TT = remove_cvref_t< T >;
if( const auto* pExistingVal = any_cast< TT >( &it->second ) )
{
if constexpr( is_same_v< TT, VecLength > )
{
if( !AreVecLengthsCompatible( *pExistingVal, val ) )
return false;
it->second =
VecLength( max( pExistingVal->minLength(), val.minLength() ),
pExistingVal->isVariable() && val.isVariable() );
return true;
}
else
return *pExistingVal == val;
}
return false;
}
}
setupVars();
m_pVars->emplace( name, forward< T >( val ) );
return true;
}
void addComplexity( uint32_t n ) { m_complexity += n; }
private:
void setupVars();
ptr< unordered_map< StringId, any > > m_pVars;
uint32_t m_complexity = 0; // Each matched structural element of the pattern (vector)
// adds 1, each matched literal element of the pattern adds 2
};
template< typename U >
static Generator< pair< MatchSolution, const U& > > Match(
const Term& expression, const Trie< U >& patterns );
} // namespace goose::eir
#endif
|
Changes to bs/eir/match.inl.
1 2 3 4 5 6 |
#ifndef GOOSE_EIR_MATCH_INL
#define GOOSE_EIR_MATCH_INL
namespace goose::eir
{
template< typename U >
| | > | > | > | > | > | > | 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 |
#ifndef GOOSE_EIR_MATCH_INL
#define GOOSE_EIR_MATCH_INL
namespace goose::eir
{
template< typename U >
static Generator< pair< MatchSolution, const U& > > Match(
MatchSolution&& s, const Term& expression, const Trie< U >& patterns );
//--------------------------------------------------------------------------------------------
// AnyTerm
//--------------------------------------------------------------------------------------------
// Term/Trie
template< typename U >
static Generator< pair< MatchSolution, const U& > > Match(
MatchSolution&& s, const Term& expression, const ValueTrieNode< AnyTerm, U >& trie )
{
for( auto&& [at, payload] : trie.m_branches )
{
auto sol = s;
if( sol.setVar( at.varName(), expression ) )
co_yield { move( sol ), payload };
}
}
//--------------------------------------------------------------------------------------------
// VecOfLength
//--------------------------------------------------------------------------------------------
// Term/Trie
template< typename U >
static Generator< pair< MatchSolution, const U& > > Match(
MatchSolution&& s, const pvec& vec, const ValueTrieNode< VecOfLength, U >& trie )
{
auto vecLen = vec->length();
s.addComplexity( 1 );
for( auto&& [vol, payload] : trie.m_branches )
{
auto sol = s;
if( sol.setVar( vol.varName(), vecLen ) )
co_yield { move( sol ), payload };
}
}
//--------------------------------------------------------------------------------------------
// Primitive types
//--------------------------------------------------------------------------------------------
// Term/Trie
template< typename T, typename U >
static Generator< pair< MatchSolution, const U& > > Match(
MatchSolution&& s, const T& val, const ValueTrieNode< T, U >& trie )
{
auto it = trie.m_branches.find( val );
if( it != trie.m_branches.end() )
{
s.addComplexity( 2 );
co_yield { move( s ), it->second };
}
}
template< typename U >
static Generator< pair< MatchSolution, const U& > > Match(
MatchSolution&& s, const APSInt& val, const ValueTrieNode< FixedInt, U >& trie )
{
auto it = trie.m_branches.find( val );
if( it != trie.m_branches.end() )
{
s.addComplexity( 2 );
co_yield { move( s ), it->second };
}
}
//--------------------------------------------------------------------------------------------
// Hole
//--------------------------------------------------------------------------------------------
// Term/Trie
template< typename U >
static Generator< pair< MatchSolution, const U& > > Match(
MatchSolution&& s, const Hole& h, const HoleBehaviorNode< U >& trie )
{
switch( h.behavior() )
{
case Hole::Behavior::Standard:
{
if( !trie.m_standardBhvBranch )
break;
|
| ︙ | ︙ | |||
116 117 118 119 120 121 122 |
{
s.addComplexity( 1 );
co_yield { move( s ), *trie.m_anyBhvBranch };
}
}
template< typename U >
| | > | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
{
s.addComplexity( 1 );
co_yield { move( s ), *trie.m_anyBhvBranch };
}
}
template< typename U >
static Generator< pair< MatchSolution, const U& > > Match(
MatchSolution&& s, const Hole& h, const HoleKindNode< U >& trie )
{
if( h.kind() == ""_sid )
{
s.addComplexity( 1 );
for( auto&& x : trie.m_branches )
{
|
| ︙ | ︙ | |||
150 151 152 153 154 155 156 |
{
s.addComplexity( 1 );
co_yield Match( move( s ), h, *trie.m_anyKindBranch );
}
}
template< typename U >
| | > | 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
{
s.addComplexity( 1 );
co_yield Match( move( s ), h, *trie.m_anyKindBranch );
}
}
template< typename U >
static Generator< pair< MatchSolution, const U& > > Match(
MatchSolution&& s, const Hole& h, const TrieNode< Hole, U >& trie )
{
if( h.name() == ""_sid )
{
s.addComplexity( 1 );
for( auto&& x : trie.m_branches )
{
|
| ︙ | ︙ | |||
188 189 190 191 192 193 194 |
}
//--------------------------------------------------------------------------------------------
// LocationId
//--------------------------------------------------------------------------------------------
// Term/Trie
template< typename U >
| | > | > | > | | < < | > > | | > | > | | | > | < < < | > | > | | 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 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
}
//--------------------------------------------------------------------------------------------
// LocationId
//--------------------------------------------------------------------------------------------
// Term/Trie
template< typename U >
static Generator< pair< MatchSolution, const U& > > Match(
MatchSolution&& s, const LocationId& val, const TrieNode< LocationId, U >& trie )
{
auto sol = s;
co_yield { move( sol ), trie.m_next };
}
//--------------------------------------------------------------------------------------------
// ptr< void >
//--------------------------------------------------------------------------------------------
// Term/Trie
template< typename U >
static Generator< pair< MatchSolution, const U& > > Match(
MatchSolution&& s, const ptr< void >& val, const TrieNode< ptr< void >, U >& trie )
{
auto sol = s;
co_yield { move( sol ), trie.m_next };
}
//--------------------------------------------------------------------------------------------
// void*
//--------------------------------------------------------------------------------------------
// Term/Trie
template< typename U >
static Generator< pair< MatchSolution, const U& > > Match(
MatchSolution&& s, const void* val, const TrieNode< void*, U >& trie )
{
auto sol = s;
co_yield { move( sol ), trie.m_next };
}
//--------------------------------------------------------------------------------------------
// Containers
//--------------------------------------------------------------------------------------------
template< typename U >
Generator< pair< MatchSolution, const U& > > MatchRepetition(
MatchSolution&& s, VecGenerator exprCont, const TrieContainerRepetitionNode< U >& rptNode )
{
if( exprCont.finished() )
co_yield { move( s ), rptNode.m_next };
else
for( auto&& [s, payload] : Match( move( s ), exprCont(), rptNode.m_repetition ) )
co_yield MatchRepetition( move( s ), exprCont, rptNode );
}
template< typename U >
Generator< tuple< MatchSolution, const U&, VecGenerator > > Match(
MatchSolution&& s, VecGenerator exprCont, const TrieContainerBranch_t& contBranch )
{
if( contBranch.index() == 0 )
{
for( auto&& [s, payload] :
Match( move( s ), exprCont(), get< 0 >( contBranch )->m_trie ) )
co_yield Match< U >( move( s ), exprCont, payload );
}
else
{
const auto& contEnd = get< 1 >( contBranch );
co_yield { move( s ), *any_cast< U >( &contEnd.m_payload ), exprCont };
}
}
// Term/Trie
template< typename U >
static Generator< pair< MatchSolution, const U& > > Match(
MatchSolution&& s, const pvec& vec, const TrieNode< pvec, U >& trie )
{
auto exprLen = vec->length();
// Ignore variable length vectors in expressions. They can't be matched.
// We don't need it and it's much simpler this way.
if( exprLen.isVariable() )
co_return;
s.addComplexity( 1 );
if( auto it = trie.m_fixedLengthBranches.find( exprLen.minLength() );
it != trie.m_fixedLengthBranches.end() )
{
auto sol = s;
VecGenerator gen( *vec );
for( auto&& [s, content, g] : Match< U >( move( sol ), gen, it->second ) )
co_yield { s, content };
}
using rpt_node = ptr< TrieContainerRepetitionNode< U > >;
auto it = trie.m_variableLengthBranches.begin();
auto end = trie.m_variableLengthBranches.upper_bound( exprLen.minLength() );
while( it != end )
{
auto sol = s;
VecGenerator gen( *vec );
for( auto&& [s, pRptNode, g] : Match< rpt_node >( move( sol ), gen, it->second ) )
co_yield MatchRepetition( move( s ), g, *pRptNode );
++it;
}
}
//--------------------------------------------------------------------------------------------
// Terms
//--------------------------------------------------------------------------------------------
// Term/Trie
template< typename U >
static Generator< pair< MatchSolution, const U& > > Match(
MatchSolution&& s, const Term& expression, const Trie< U >& patterns )
{
return visit( [&]( auto&& t ) { return Match( move( s ), t, patterns ); }, expression );
}
template< typename T, typename U >
static Generator< pair< MatchSolution, const U& > > Match(
MatchSolution&& s, const T& t, const Trie< U >& patterns )
{
static_assert( !is_same_v< T, Term > );
if( auto pBranch = get< ptr< TrieNode< T, U > > >( patterns.branches() ) )
co_yield Match( MatchSolution( s ), t, *pBranch );
if constexpr( is_same_v< T, pvec > )
{
if( auto pBranch = get< ptr< TrieNode< VecOfLength, U > > >( patterns.branches() ) )
co_yield Match( MatchSolution( s ), t, *pBranch );
}
if constexpr( !is_same_v< T, AnyTerm > )
{
if( auto pBranch = get< ptr< TrieNode< AnyTerm, U > > >( patterns.branches() ) )
co_yield Match( move( s ), t, *pBranch );
}
}
template< typename U >
static Generator< pair< MatchSolution, const U& > > Match(
const Term& expression, const Trie< U >& patterns )
{
MatchSolution s;
co_yield Match( move( s ), expression, patterns );
}
} // namespace goose::eir
#endif
|
Changes to bs/eir/merge.cpp.
1 2 3 4 5 6 |
#include "eir.h"
namespace goose::eir
{
Trie<> Merge( const Trie<>& trie, const Term& term )
{
| | | | 1 2 3 4 5 6 7 8 9 |
#include "eir.h"
namespace goose::eir
{
Trie<> Merge( const Trie<>& trie, const Term& term )
{
return Merge( trie, term, []( auto&& ) { return EmptyPayload(); } );
}
} // namespace goose::eir
|
Changes to bs/eir/merge.inl.
1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_EIR_MERGE_INL
#define GOOSE_EIR_MERGE_INL
namespace goose::eir
{
//--------------------------------------------------------------------------------------------
// Primitive types
//--------------------------------------------------------------------------------------------
template< typename T, typename U, typename F >
| | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#ifndef GOOSE_EIR_MERGE_INL
#define GOOSE_EIR_MERGE_INL
namespace goose::eir
{
//--------------------------------------------------------------------------------------------
// Primitive types
//--------------------------------------------------------------------------------------------
template< typename T, typename U, typename F >
static ptr< TrieNode< T, U > > Merge(
const ptr< TrieNode< T, U > >& lhs, const T& rhs, F&& next )
{
if constexpr( is_same_v< T, void* > )
{
auto pNewNode = make_shared< TrieNode< void*, U > >();
pNewNode->m_next = lhs ? next( lhs->m_next ) : next( U() );
return pNewNode;
}
|
| ︙ | ︙ | |||
34 35 36 37 38 39 40 |
}
}
//--------------------------------------------------------------------------------------------
// Holes
//--------------------------------------------------------------------------------------------
template< typename U, typename F >
| | > | | > | 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 |
}
}
//--------------------------------------------------------------------------------------------
// Holes
//--------------------------------------------------------------------------------------------
template< typename U, typename F >
static ptr< HoleBehaviorNode< U > > Merge(
const ptr< HoleBehaviorNode< U > >& lhs, const Hole& rhs, F&& next )
{
ptr< HoleBehaviorNode< U > > pNewNode;
if( lhs )
pNewNode = make_shared< HoleBehaviorNode< U > >( *lhs );
else
pNewNode = make_shared< HoleBehaviorNode< U > >();
switch( rhs.behavior() )
{
case Hole::Behavior::Standard:
pNewNode->m_standardBhvBranch = make_shared< U >( pNewNode->m_standardBhvBranch ?
next( *pNewNode->m_standardBhvBranch ) :
next( U() ) );
break;
case Hole::Behavior::Pack:
pNewNode->m_packBhvBranch = make_shared< U >(
pNewNode->m_packBhvBranch ? next( *pNewNode->m_packBhvBranch ) : next( U() ) );
break;
|
| ︙ | ︙ | |||
72 73 74 75 76 77 78 |
static ptr< HoleKindNode< U > > Merge( const HoleKindNode< U >& lhs, const Hole& rhs, F&& next )
{
ptr< HoleKindNode< U > > pNewNode;
pNewNode = make_shared< HoleKindNode< U > >( lhs );
if( rhs.kind() == ""_sid )
| > | | > | > > | > | | > | > > | < < | | < | < < < < | > | | | | | | > | > | < | | | < | | | > | | | | | | | | | < | > | > | 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 282 283 284 285 286 287 |
static ptr< HoleKindNode< U > > Merge( const HoleKindNode< U >& lhs, const Hole& rhs, F&& next )
{
ptr< HoleKindNode< U > > pNewNode;
pNewNode = make_shared< HoleKindNode< U > >( lhs );
if( rhs.kind() == ""_sid )
pNewNode->m_anyKindBranch =
Merge( pNewNode->m_anyKindBranch, rhs, forward< F >( next ) );
else
{
auto it = pNewNode->m_branches.find( rhs.kind() );
if( it == pNewNode->m_branches.end() )
pNewNode->m_branches.emplace( rhs.kind(),
Merge( ptr< HoleBehaviorNode< U > >(), rhs, forward< F >( next ) ) );
else
it->second = Merge( it->second, rhs, forward< F >( next ) );
}
return pNewNode;
}
template< typename U, typename F >
static ptr< TrieNode< Hole, U > > Merge(
const ptr< TrieNode< Hole, U > >& lhs, const Hole& rhs, F&& next )
{
ptr< TrieNode< Hole, U > > pNewNode;
if( lhs )
pNewNode = make_shared< TrieNode< Hole, U > >( *lhs );
else
pNewNode = make_shared< TrieNode< Hole, U > >();
if( rhs.name() == ""_sid )
{
if( !pNewNode->m_anyNameBranch )
pNewNode->m_anyNameBranch = Merge( HoleKindNode< U >(), rhs, forward< F >( next ) );
else
pNewNode->m_anyNameBranch =
Merge( *pNewNode->m_anyNameBranch, rhs, forward< F >( next ) );
}
else
{
auto it = pNewNode->m_branches.find( rhs.name() );
if( it == pNewNode->m_branches.end() )
pNewNode->m_branches.emplace(
rhs.name(), Merge( HoleKindNode< U >(), rhs, forward< F >( next ) ) );
else
it->second = Merge( *it->second, rhs, forward< F >( next ) );
}
return pNewNode;
}
//--------------------------------------------------------------------------------------------
// LocationId
//--------------------------------------------------------------------------------------------
template< typename U, typename F >
static ptr< TrieNode< LocationId, U > > Merge(
const ptr< TrieNode< LocationId, U > >& lhs, const LocationId& rhs, F&& next )
{
auto pNewNode = make_shared< TrieNode< LocationId, U > >();
pNewNode->m_next = lhs ? next( lhs->m_next ) : next( U() );
return pNewNode;
}
//--------------------------------------------------------------------------------------------
// ptr< void >
//--------------------------------------------------------------------------------------------
template< typename U, typename F >
static ptr< TrieNode< ptr< void >, U > > Merge(
const ptr< TrieNode< ptr< void >, U > >& lhs, const ptr< void >& rhs, F&& next )
{
auto pNewNode = make_shared< TrieNode< ptr< void >, U > >();
pNewNode->m_next = lhs ? next( lhs->m_next ) : next( U() );
return pNewNode;
}
//--------------------------------------------------------------------------------------------
// Containers
//--------------------------------------------------------------------------------------------
template< typename U, typename IT, typename F >
static TrieContainerBranch_t Merge(
const TrieContainerBranch_t& lhs, const IT& it, const pvec& vec, F&& next )
{
if( it == vec->terms().end() )
{
if( lhs.index() == 1 )
{
const auto& contEnd = get< 1 >( lhs );
return TrieContainerEnd{ any( next( *any_cast< U >( &contEnd.m_payload ) ) ),
vec->weight() };
}
return TrieContainerEnd{ any( next( U() ) ), vec->weight() };
}
assert( lhs.index() == 0 );
ptr< TrieContainerNode > pNewNode;
const auto& pNode = get< 0 >( lhs );
if( pNode )
pNewNode = make_shared< TrieContainerNode >( *pNode );
else
pNewNode = make_shared< TrieContainerNode >();
function< TrieContainerBranch_t( const TrieContainerBranch_t& ) > f(
[&]( auto& payload )
{
auto nextIt = it;
++nextIt;
return Merge< U >( payload, nextIt, vec, forward< F >( next ) );
} );
pNewNode->m_trie = Merge( pNewNode->m_trie, *it, move( f ) );
return pNewNode;
}
template< typename U, typename F >
static ptr< TrieContainerRepetitionNode< U > > Merge(
const ptr< TrieContainerRepetitionNode< U > >& lhs, const Term& rhs, F&& next )
{
auto pNewNode = make_shared< TrieContainerRepetitionNode< U > >();
pNewNode->m_next = next( lhs ? lhs->m_next : U() );
if( lhs )
pNewNode->m_repetition = lhs->m_repetition;
pNewNode->m_repetition = Merge( pNewNode->m_repetition, rhs, forward< F >( next ) );
return pNewNode;
}
template< typename U, typename F >
static ptr< TrieNode< pvec, U > > Merge(
const ptr< TrieNode< pvec, U > >& lhs, const pvec& rhs, F&& next )
{
ptr< TrieNode< pvec, U > > pNewNode;
if( lhs )
pNewNode = make_shared< TrieNode< pvec, U > >( *lhs );
else
pNewNode = make_shared< TrieNode< pvec, U > >();
auto length = rhs->length();
if( length.isVariable() )
{
using rpt_node = ptr< TrieContainerRepetitionNode< U > >;
auto f = [&]( auto&& payload )
{
// Merge the repetition
return Merge( payload, *rhs->repetitionTerm(), forward< F >( next ) );
};
auto it = pNewNode->m_variableLengthBranches.find( length.minLength() );
if( it == pNewNode->m_variableLengthBranches.end() )
{
pNewNode->m_variableLengthBranches.emplace( length.minLength(),
Merge< rpt_node >(
TrieContainerBranch_t(), rhs->terms().begin(), rhs, move( f ) ) );
}
else
{
it->second = Merge< rpt_node >( it->second, rhs->terms().begin(), rhs, move( f ) );
}
}
else
{
auto it = pNewNode->m_fixedLengthBranches.find( length.minLength() );
if( it == pNewNode->m_fixedLengthBranches.end() )
{
pNewNode->m_fixedLengthBranches.emplace( length.minLength(),
Merge< U >( TrieContainerBranch_t(), rhs->terms().begin(), rhs,
forward< F >( next ) ) );
}
else
{
it->second =
Merge< U >( it->second, rhs->terms().begin(), rhs, forward< F >( next ) );
}
}
return pNewNode;
}
//--------------------------------------------------------------------------------------------
// Terms
//--------------------------------------------------------------------------------------------
template< typename U, typename F >
static Trie< U > Merge( const Trie< U >& trie, const Term& term, F&& next )
{
return visit(
[&]< typename T >( const T& t )
{
auto newTrie = trie;
auto& pNode = get< ptr< TrieNode< T, U > > >( newTrie.branches() );
if( pNode )
pNode = Merge( pNode, t, forward< F >( next ) );
else
pNode = Merge( ptr< TrieNode< T, U > >(), t, forward< F >( next ) );
return newTrie;
},
term );
}
} // namespace goose::eir
#endif
|
Changes to bs/eir/pretty.cpp.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 |
{
static PrettyPrinter instance;
return instance;
}
void PrettyPrinter::addRule( const Term& pat, PrintFunc f )
{
| | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
{
static PrettyPrinter instance;
return instance;
}
void PrettyPrinter::addRule( const Term& pat, PrintFunc f )
{
m_printingRules = Merge( m_printingRules, pat, [&]( auto&& ) { return f; } );
}
bool PrettyPrinter::print( ostream& out, const Term& t )
{
MatchScore bestScore;
PrintFunc bestFunc;
bool ambiguous = false;
|
| ︙ | ︙ | |||
53 54 55 56 57 58 59 |
{
PrettyPrinter::GetInstance().print( out, t );
return out;
}
PrettyPrinter::PrettyPrinter()
{
| | > | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | 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 |
{
PrettyPrinter::GetInstance().print( out, t );
return out;
}
PrettyPrinter::PrettyPrinter()
{
addRule( ANYTERM( _ ),
[]( auto&& out, auto&& t )
{
// Don't print locationIds
if( holds_alternative< LocationId >( t ) )
return false;
ToString( out, t );
return true;
} );
addRule( VECOFLENGTH( _ ),
[&]( auto&& out, auto&& t )
{
bool isEmpty = true;
const auto& v = get< pvec >( t );
out << '<';
for( auto&& x : v->terms() )
{
if( isEmpty )
out << ' ';
if( print( out, x ) )
out << ' ';
isEmpty = false;
}
if( v->repetitionTerm() )
{
if( isEmpty )
out << ' ';
out << '*';
if( print( out, *v->repetitionTerm() ) )
out << ' ';
isEmpty = false;
}
out << '>';
return true;
} );
}
|
Changes to bs/eir/pretty.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_EIR_PRETTYPRINTER_H
#define GOOSE_EIR_PRETTYPRINTER_H
namespace goose::eir
{
class PrettyPrinter
{
| | | | | | | | | | | | > | | | 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 |
#ifndef GOOSE_EIR_PRETTYPRINTER_H
#define GOOSE_EIR_PRETTYPRINTER_H
namespace goose::eir
{
class PrettyPrinter
{
public:
static PrettyPrinter& GetInstance();
using PrintFunc = function< bool( ostream& out, const Term& t ) >;
void addRule( const Term& pat, PrintFunc f );
bool print( ostream& out, const Term& t );
private:
PrettyPrinter();
Trie< PrintFunc > m_printingRules;
};
} // namespace goose::eir
namespace std
{
template< class CharT >
struct std::formatter< goose::eir::Term, CharT >
: goose::util::SStreamFormatter< goose::eir::Term, CharT >
{
};
} // namespace std
#endif
|
Changes to bs/eir/term.h.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 |
CloseBracket
};
struct STerm;
class Hole
{
| | | | | | | | | | | | | < | > > | > | > | | | | | | | | < < < < < < < < | < | < < | | | | | | | | | | 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 |
CloseBracket
};
struct STerm;
class Hole
{
public:
enum class Behavior
{
Standard,
Pack,
Any
};
Hole() = default;
Hole( StringId name, StringId kind = ""_sid, Behavior bhv = Behavior::Standard ) :
m_name( name ),
m_kind( kind ),
m_bhv( bhv )
{
}
const auto& name() const { return m_name; }
const auto& kind() const { return m_kind; }
auto behavior() const { return m_bhv; }
bool operator<( const Hole& rhs ) const;
bool operator==( const Hole& rhs ) const;
private:
StringId m_name;
StringId m_kind;
Behavior m_bhv = Behavior::Standard;
};
using Term = variant< uint64_t, LocationId, string, StringId, Delimiter, Hole, AnyTerm,
VecOfLength, pvec,
// Representation for ct_int, the compile time only integers
// with "unlimited" precision
BigInt,
// Compile time representation for normal, fixed size integers
APSInt,
ptr< void >, void* >;
extern bool operator==( const Term& lhs, const Term& rhs );
extern bool operator!=( const Term& lhs, const Term& rhs );
extern ostream& operator<<( ostream& out, const Term& t );
// A term associated with a location id.
// Used to represent tokens and tokens/values coming out of the resolver.
using TermLoc = pair< eir::Term, LocationId >;
} // namespace goose::eir
#define TERM( x ) eir::Term( x )
#define TSTR( x ) TERM( string( x ) )
#define TSID( x ) TERM( #x##_sid )
#define HOLE( ... ) TERM( ( eir::Hole{ __VA_ARGS__ } ) )
#define ANYTERM( x ) TERM( eir::AnyTerm( #x##_sid ) )
#define VECOFLENGTH( x ) TERM( eir::VecOfLength( #x##_sid ) )
#define VEC( ... ) TERM( eir::Vector::Make( __VA_ARGS__ ) )
#define REPEAT( x ) eir::Repetition( x )
#endif
|
Changes to bs/eir/tests/match-terms-trie.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <catch2/catch_all.hpp>
#include "eir/eir.h"
using namespace std;
using namespace goose;
using namespace goose::eir;
SCENARIO( "Match works", "[match]" )
{
WHEN( "Matching various expressions against various patterns stored in a trie" )
{
auto pat0 = ANYTERM( a );
| < | < | < < < | < | < < < | < | < < | < | < < < < < | < < < | | < < < < < | < < < < | | | | | | | | | | > > | > > | | > | > > | | > | > > | | > | > > | | > | > > | > > | | > | > > | > > | > > | > > | | > | > > | > > | > > | > > | | > | > > | > > | > > | 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 |
#include <catch2/catch_all.hpp>
#include "eir/eir.h"
using namespace std;
using namespace goose;
using namespace goose::eir;
SCENARIO( "Match works", "[match]" )
{
WHEN( "Matching various expressions against various patterns stored in a trie" )
{
auto pat0 = ANYTERM( a );
auto pat1 = VEC( VEC( TSTR( "foo" ), TSTR( "bar" ) ), VEC(),
VEC( TSTR( "foo" ), TSTR( "bar" ) ), VEC( TSTR( "foo" ), TSTR( "bar" ) ) );
auto expr1 = pat1;
auto pat2 = VEC( VEC( ANYTERM( a ), TSTR( "bar" ) ), VEC(),
VEC( ANYTERM( a ), TSTR( "bar" ) ), VEC( ANYTERM( a ), TSTR( "bar" ) ) );
auto expr2 = VEC( VEC( TSTR( "meh" ), TSTR( "bar" ) ), VEC(),
VEC( TSTR( "meh" ), TSTR( "bar" ) ), VEC( TSTR( "meh" ), TSTR( "bar" ) ) );
auto pat3 =
VEC( ANYTERM( x ), VEC( ANYTERM( a ), TSTR( "bar" ) ), ANYTERM( z ), ANYTERM( z ) );
auto expr3 = VEC( TSID( a ), VEC( TSTR( "meh" ), TSTR( "bar" ) ), TSID( b ), TSID( b ) );
auto pat4 =
VEC( VEC( ANYTERM( a ), TSTR( "bar" ) ), ANYTERM( y ), ANYTERM( y ), ANYTERM( y ) );
auto expr4 = VEC( VEC( TSTR( "huh" ), TSTR( "bar" ) ), VEC(), VEC(), VEC() );
auto pat5 = HOLE( ""_sid, ""_sid );
auto expr5 = HOLE( "gg"_sid );
auto pat6 = HOLE( "blah"_sid, ""_sid );
auto expr6 = HOLE( "blah"_sid );
auto pat7 = HOLE( "blah"_sid, "gg"_sid );
auto expr7 = HOLE( "blah"_sid, "gg"_sid );
auto pat8 = HOLE( ""_sid, "gg"_sid );
auto expr8 = HOLE( "blah"_sid, "huh"_sid );
Trie< string > testTrie1;
testTrie1 = Merge( testTrie1, pat0, []( auto&& ) { return "pat0"s; } );
testTrie1 = Merge( testTrie1, pat1, []( auto&& ) { return "pat1"s; } );
testTrie1 = Merge( testTrie1, pat2, []( auto&& ) { return "pat2"s; } );
testTrie1 = Merge( testTrie1, pat3, []( auto&& ) { return "pat3"s; } );
testTrie1 = Merge( testTrie1, pat4, []( auto&& ) { return "pat4"s; } );
testTrie1 = Merge( testTrie1, pat5, []( auto&& ) { return "pat5"s; } );
testTrie1 = Merge( testTrie1, pat6, []( auto&& ) { return "pat6"s; } );
testTrie1 = Merge( testTrie1, pat7, []( auto&& ) { return "pat7"s; } );
testTrie1 = Merge( testTrie1, pat8, []( auto&& ) { return "pat8"s; } );
THEN( "Matching the terms and the trie work as expected" )
{
vector< pair< MatchSolution, string > > solutions;
for( auto&& [sol, str] : Match( expr1, testTrie1 ) )
solutions.emplace_back( sol, str );
REQUIRE( solutions.size() == 3 );
REQUIRE( solutions[0].second == "pat1"s );
REQUIRE( solutions[0].first.complexity() == 17 );
REQUIRE( solutions[0].first.numVars() == 0 );
REQUIRE( solutions[1].second == "pat2"s );
REQUIRE( solutions[1].first.complexity() == 11 );
REQUIRE( solutions[1].first.numVars() == 1 );
REQUIRE( solutions[2].second == "pat0"s );
REQUIRE( solutions[2].first.complexity() == 0 );
REQUIRE( solutions[2].first.numVars() == 1 );
solutions.clear();
for( auto&& [sol, str] : Match( expr2, testTrie1 ) )
solutions.emplace_back( sol, str );
REQUIRE( solutions.size() == 2 );
REQUIRE( solutions[0].second == "pat2"s );
REQUIRE( solutions[0].first.complexity() == 11 );
REQUIRE( solutions[0].first.numVars() == 1 );
REQUIRE( solutions[1].second == "pat0"s );
REQUIRE( solutions[1].first.complexity() == 0 );
REQUIRE( solutions[1].first.numVars() == 1 );
solutions.clear();
for( auto&& [sol, str] : Match( expr3, testTrie1 ) )
solutions.emplace_back( sol, str );
REQUIRE( solutions.size() == 2 );
REQUIRE( solutions[0].second == "pat3"s );
REQUIRE( solutions[0].first.complexity() == 4 );
REQUIRE( solutions[0].first.numVars() == 3 );
REQUIRE( solutions[1].second == "pat0"s );
REQUIRE( solutions[1].first.complexity() == 0 );
REQUIRE( solutions[1].first.numVars() == 1 );
solutions.clear();
for( auto&& [sol, str] : Match( expr4, testTrie1 ) )
solutions.emplace_back( sol, str );
REQUIRE( solutions.size() == 2 );
REQUIRE( solutions[0].second == "pat4"s );
REQUIRE( solutions[0].first.complexity() == 4 );
REQUIRE( solutions[0].first.numVars() == 2 );
REQUIRE( solutions[1].second == "pat0"s );
REQUIRE( solutions[1].first.complexity() == 0 );
REQUIRE( solutions[1].first.numVars() == 1 );
solutions.clear();
for( auto&& [sol, str] : Match( expr5, testTrie1 ) )
solutions.emplace_back( sol, str );
REQUIRE( solutions.size() == 3 );
REQUIRE( solutions[0].second == "pat8"s );
REQUIRE( solutions[0].first.complexity() == 4 );
REQUIRE( solutions[0].first.numVars() == 0 );
REQUIRE( solutions[1].second == "pat5"s );
REQUIRE( solutions[1].first.complexity() == 4 );
REQUIRE( solutions[1].first.numVars() == 0 );
REQUIRE( solutions[2].second == "pat0"s );
REQUIRE( solutions[2].first.complexity() == 0 );
REQUIRE( solutions[2].first.numVars() == 1 );
solutions.clear();
for( auto&& [sol, str] : Match( expr6, testTrie1 ) )
solutions.emplace_back( sol, str );
REQUIRE( solutions.size() == 5 );
REQUIRE( solutions[0].second == "pat7"s );
REQUIRE( solutions[0].first.complexity() == 5 );
REQUIRE( solutions[0].first.numVars() == 0 );
REQUIRE( solutions[1].second == "pat6"s );
REQUIRE( solutions[1].first.complexity() == 5 );
REQUIRE( solutions[1].first.numVars() == 0 );
REQUIRE( solutions[2].second == "pat8"s );
REQUIRE( solutions[2].first.complexity() == 4 );
REQUIRE( solutions[2].first.numVars() == 0 );
REQUIRE( solutions[3].second == "pat5"s );
REQUIRE( solutions[3].first.complexity() == 4 );
REQUIRE( solutions[3].first.numVars() == 0 );
REQUIRE( solutions[4].second == "pat0"s );
REQUIRE( solutions[4].first.complexity() == 0 );
REQUIRE( solutions[4].first.numVars() == 1 );
solutions.clear();
for( auto&& [sol, str] : Match( expr7, testTrie1 ) )
solutions.emplace_back( sol, str );
REQUIRE( solutions.size() == 5 );
REQUIRE( solutions[0].second == "pat7"s );
REQUIRE( solutions[0].first.complexity() == 6 );
REQUIRE( solutions[0].first.numVars() == 0 );
REQUIRE( solutions[1].second == "pat6"s );
REQUIRE( solutions[1].first.complexity() == 5 );
REQUIRE( solutions[1].first.numVars() == 0 );
REQUIRE( solutions[2].second == "pat8"s );
REQUIRE( solutions[2].first.complexity() == 5 );
REQUIRE( solutions[2].first.numVars() == 0 );
REQUIRE( solutions[3].second == "pat5"s );
REQUIRE( solutions[3].first.complexity() == 4 );
REQUIRE( solutions[3].first.numVars() == 0 );
REQUIRE( solutions[4].second == "pat0"s );
REQUIRE( solutions[4].first.complexity() == 0 );
REQUIRE( solutions[4].first.numVars() == 1 );
solutions.clear();
for( auto&& [sol, str] : Match( expr8, testTrie1 ) )
solutions.emplace_back( sol, str );
REQUIRE( solutions.size() == 3 );
REQUIRE( solutions[0].second == "pat6"s );
REQUIRE( solutions[0].first.complexity() == 5 );
REQUIRE( solutions[0].first.numVars() == 0 );
REQUIRE( solutions[1].second == "pat5"s );
REQUIRE( solutions[1].first.complexity() == 4 );
REQUIRE( solutions[1].first.numVars() == 0 );
REQUIRE( solutions[2].second == "pat0"s );
REQUIRE( solutions[2].first.complexity() == 0 );
REQUIRE( solutions[2].first.numVars() == 1 );
}
}
}
|
Changes to bs/eir/tests/merge.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <catch2/catch_all.hpp>
#include "eir/eir.h"
using namespace std;
using namespace goose;
using namespace goose::eir;
SCENARIO( "Merge works", "[merge]" )
{
WHEN( "Merging multiple terms into a trie" )
{
auto pat0 = ANYTERM( a );
| < | < | < < < | < | < < | < | < < < | | < < < < < | < < | < < < | | < < | < < < | < < < | < | < < < | < | < < < | < | < < < | < | < < < | < | < | 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 |
#include <catch2/catch_all.hpp>
#include "eir/eir.h"
using namespace std;
using namespace goose;
using namespace goose::eir;
SCENARIO( "Merge works", "[merge]" )
{
WHEN( "Merging multiple terms into a trie" )
{
auto pat0 = ANYTERM( a );
auto pat1 = VEC( VEC( TSTR( "foo" ), TSTR( "bar" ) ), VEC(),
VEC( TSTR( "foo" ), TSTR( "bar" ) ), VEC( TSTR( "foo" ), TSTR( "bar" ) ) );
auto pat2 = VEC( VEC( ANYTERM( a ), TSTR( "bar" ) ), VEC(),
VEC( ANYTERM( a ), TSTR( "bar" ) ), VEC( ANYTERM( a ), TSTR( "bar" ) ) );
auto pat3 =
VEC( ANYTERM( x ), VEC( ANYTERM( a ), TSTR( "bar" ) ), ANYTERM( z ), ANYTERM( z ) );
auto pat4 =
VEC( VEC( ANYTERM( a ), TSTR( "bar" ) ), ANYTERM( y ), ANYTERM( y ), ANYTERM( y ) );
auto pat5 = VEC( VEC( TSTR( "foo" ), ANYTERM( b ) ), ANYTERM( y ), ANYTERM( y ),
VEC( TSTR( "foo" ), ANYTERM( b ) ) );
auto pat6 = VEC( ANYTERM( x ), VEC( TSTR( "foo" ), TSTR( "bar" ) ),
VEC( TSTR( "foo" ), TSTR( "bar" ) ), VEC( TSTR( "foo" ), TSTR( "baz" ) ) );
auto pat7 =
VEC( ANYTERM( v ), ANYTERM( w ), ANYTERM( v ), VEC( TSTR( "foo" ), TSTR( "bar" ) ) );
auto pat8 = VEC( ANYTERM( x ), VEC( ANYTERM( a ), TSTR( "bar" ) ), ANYTERM( x ),
VEC( TSTR( "foo" ), ANYTERM( b ) ) );
auto pat9 = VEC( ANYTERM( x ), VEC( HOLE( ""_sid, ""_sid ), TSTR( "bar" ) ), ANYTERM( x ),
VEC( TSTR( "foo" ), ANYTERM( b ) ) );
auto pat10 = VEC( ANYTERM( x ), VEC( HOLE( "gg"_sid, ""_sid ), TSTR( "bar" ) ),
ANYTERM( x ), VEC( TSTR( "foo" ), ANYTERM( b ) ) );
auto pat11 = VEC( ANYTERM( x ), VEC( HOLE( ""_sid, "meh"_sid ), TSTR( "bar" ) ),
ANYTERM( x ), VEC( TSTR( "foo" ), ANYTERM( b ) ) );
auto pat12 = VEC( ANYTERM( x ), VEC( HOLE( "gg"_sid, "meh"_sid ), TSTR( "bar" ) ),
ANYTERM( x ), VEC( TSTR( "foo" ), ANYTERM( b ) ) );
Trie<> testTrie1;
testTrie1 = Merge( testTrie1, pat0 );
testTrie1 = Merge( testTrie1, pat1 );
testTrie1 = Merge( testTrie1, pat2 );
testTrie1 = Merge( testTrie1, pat3 );
testTrie1 = Merge( testTrie1, pat4 );
|
| ︙ | ︙ |
Changes to bs/eir/tostring.cpp.
| ︙ | ︙ | |||
153 154 155 156 157 158 159 |
llvm::SmallString< 16 > s;
i.toString( s, 10 );
return out << "APSint(" << s.c_str() << ')';
}
ostream& ToString( ostream& out, const Term& t )
{
| | < < < | | 153 154 155 156 157 158 159 160 161 162 163 164 |
llvm::SmallString< 16 > s;
i.toString( s, 10 );
return out << "APSint(" << s.c_str() << ')';
}
ostream& ToString( ostream& out, const Term& t )
{
visit( [&]( auto&& t ) { ToString( out, t ); }, t );
return out;
}
} // namespace goose::eir
|
Changes to bs/eir/tostring.h.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 |
extern ostream& ToString( ostream& out, const void* x );
extern ostream& ToString( ostream& out, const AnyTerm& at );
extern ostream& ToString( ostream& out, const VecOfLength& v );
extern ostream& ToString( ostream& out, const ptr< Vector >& v );
extern ostream& ToString( ostream& out, const BigInt& t );
extern ostream& ToString( ostream& out, const APSInt& t );
| | | 18 19 20 21 22 23 24 25 26 27 |
extern ostream& ToString( ostream& out, const void* x );
extern ostream& ToString( ostream& out, const AnyTerm& at );
extern ostream& ToString( ostream& out, const VecOfLength& v );
extern ostream& ToString( ostream& out, const ptr< Vector >& v );
extern ostream& ToString( ostream& out, const BigInt& t );
extern ostream& ToString( ostream& out, const APSInt& t );
} // namespace goose::eir
#endif
|
Changes to bs/eir/trie.h.
1 2 3 4 5 |
#ifndef GOOSE_EIR_TRIE_H
#define GOOSE_EIR_TRIE_H
namespace goose::eir
{
| | < > | | < < | > | | < < | > | < | > | < | > | | < | < < | > | > | | < | < < | > | < | > | | 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 |
#ifndef GOOSE_EIR_TRIE_H
#define GOOSE_EIR_TRIE_H
namespace goose::eir
{
template< typename T, typename U > struct TrieNode
{
};
template< typename T, typename U > struct ValueTrieNode
{
unordered_map< T, U > m_branches;
};
template< typename U > struct TrieNode< uint64_t, U > : public ValueTrieNode< uint64_t, U >
{
};
template< typename U > struct TrieNode< LocationId, U >
{
U m_next;
};
template< typename U > struct TrieNode< string, U > : public ValueTrieNode< string, U >
{
};
template< typename U > struct TrieNode< StringId, U > : public ValueTrieNode< StringId, U >
{
};
template< typename U > struct TrieNode< Delimiter, U > : public ValueTrieNode< Delimiter, U >
{
};
template< typename U > struct HoleBehaviorNode
{
ptr< U > m_standardBhvBranch;
ptr< U > m_packBhvBranch;
ptr< U > m_anyBhvBranch;
};
template< typename U >
struct HoleKindNode : public ValueTrieNode< StringId, ptr< HoleBehaviorNode< U > > >
{
ptr< HoleBehaviorNode< U > > m_anyKindBranch;
};
template< typename U >
struct TrieNode< Hole, U > : public ValueTrieNode< StringId, ptr< HoleKindNode< U > > >
{
ptr< HoleKindNode< U > > m_anyNameBranch;
};
template< typename U > struct TrieNode< AnyTerm, U > : public ValueTrieNode< AnyTerm, U >
{
};
template< typename U >
struct TrieNode< VecOfLength, U > : public ValueTrieNode< VecOfLength, U >
{
};
template< typename U > struct TrieNode< ptr< void >, U >
{
U m_next;
};
template< typename U > struct TrieNode< void*, U >
{
U m_next;
};
template< typename U > struct TrieNode< BigInt, U > : public ValueTrieNode< BigInt, U >
{
};
template< typename U > struct TrieNode< APSInt, U > : public ValueTrieNode< FixedInt, U >
{
};
struct TrieContainerNode;
// Since Vector terms now carry around their weight (...),
// and since it can be overriden with an arbitrary value, we need
// to preserve it in the trie to be able to restore it (for instance
// when enumerating all the elements). So the final node of a container branch
|
| ︙ | ︙ | |||
103 104 105 106 107 108 109 |
{
any m_payload;
int32_t vecWeight = 0;
};
using TrieContainerBranch_t = variant< ptr< TrieContainerNode >, TrieContainerEnd >;
| | < | < | | < < | < | < | < | < | < < | < | > | | | | < | | 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 |
{
any m_payload;
int32_t vecWeight = 0;
};
using TrieContainerBranch_t = variant< ptr< TrieContainerNode >, TrieContainerEnd >;
template< typename U > struct TrieNode< pvec, U >
{
// All fixed length branches, by length.
unordered_map< size_t, TrieContainerBranch_t > m_fixedLengthBranches;
// All variable length branches, by min length.
map< size_t, TrieContainerBranch_t > m_variableLengthBranches;
};
template< typename U > class Trie
{
public:
using BranchesTuple = tuple< ptr< TrieNode< uint64_t, U > >,
ptr< TrieNode< LocationId, U > >, ptr< TrieNode< string, U > >,
ptr< TrieNode< StringId, U > >, ptr< TrieNode< Delimiter, U > >,
ptr< TrieNode< Hole, U > >, ptr< TrieNode< AnyTerm, U > >,
ptr< TrieNode< VecOfLength, U > >, ptr< TrieNode< ptr< void >, U > >,
ptr< TrieNode< void*, U > >, ptr< TrieNode< pvec, U > >, ptr< TrieNode< BigInt, U > >,
ptr< TrieNode< APSInt, U > > >;
const auto& branches() const { return m_branches; }
auto& branches() { return m_branches; }
private:
BranchesTuple m_branches;
};
struct TrieContainerNode
{
Trie< TrieContainerBranch_t > m_trie;
};
template< typename U > struct TrieContainerRepetitionNode
{
Trie< U > m_repetition;
U m_next;
};
} // namespace goose::eir
#endif
|
Changes to bs/eir/value.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "eir.h"
namespace goose::eir
{
bool Value::isType() const
{
return type() == TypeType() || isMetaType();
}
bool Value::isMetaType() const
{
| | < < < | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include "eir.h"
namespace goose::eir
{
bool Value::isType() const
{
return type() == TypeType() || isMetaType();
}
bool Value::isMetaType() const
{
auto result =
Decompose( type(), Vec( Lit( "type"_sid ), Val< uint64_t >(), Val< LocationId >() ) );
return !!result;
}
const ptr< cir::InstrSeq >& Value::cir() const
{
static ptr< cir::InstrSeq > null;
|
| ︙ | ︙ | |||
49 50 51 52 53 54 55 |
return ~0;
return *pIndex;
}
const Term& TypeType()
{
| | | | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
return ~0;
return *pIndex;
}
const Term& TypeType()
{
static auto typeType = ValueToEIR(
Value( VEC( TSID( type ), TERM( 1U ), static_cast< LocationId >( 0 ) ), TERM( 0U ) ) );
return typeType;
}
// A generic poisoned value of "type" type.
Value PoisonType( LocationId loc )
{
static auto poisonType = Value( TypeType(), TSID( poison ) ).setPoison();
|
| ︙ | ︙ | |||
77 78 79 80 81 82 83 |
Term ValueToEIR( const Value& v )
{
if( v.isConstant() )
{
// Special case for type's type
if( v.isMetaType() )
{
| | < < | < < < | < < | | < | < < < | < < > | | < | < < < < < < | | 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 |
Term ValueToEIR( const Value& v )
{
if( v.isConstant() )
{
// Special case for type's type
if( v.isMetaType() )
{
auto result = Decompose( v.val(), Val< uint64_t >() );
if( result && result->get() > 0 )
{
return VEC( TSID( type ), TERM( result->get() ),
static_cast< LocationId >( v.locationId() ) );
}
}
return VEC( TSID( value ), TSID( constant ), v.type(), v.val(),
static_cast< LocationId >( v.locationId() ) );
}
return VEC( TSID( value ), TSID( computed ), v.type(),
TERM( static_pointer_cast< void >( v.cir() ) ),
static_cast< LocationId >( v.locationId() ) );
}
optional< Value > EIRToValue( const Term& t )
{
// Special case for type's type
auto typedecomp =
Decompose( t, Vec( Lit( "type"_sid ), Val< uint64_t >(), Val< LocationId >() ) );
if( typedecomp )
{
auto&& [universeIndex, loc] = *typedecomp;
return Value( VEC( TSID( type ), TERM( universeIndex + 1 ), LocationId() ),
TERM( universeIndex ), loc );
}
auto result = Decompose( t,
Vec( Lit( "value"_sid ), Val< StringId >(), SubTerm(), SubTerm(),
Val< LocationId >() ) );
if( !result )
return nullopt;
auto&& [sort, type, val, locationId] = *result;
if( sort == "constant"_sid )
return Value( type, val, locationId );
auto cir = Decompose( val, Val< ptr< void > >() );
if( !cir )
return nullopt;
return Value(
type, static_pointer_cast< cir::InstrSeq >( ptr< void >( *cir ) ), locationId );
}
Term ValueToEIR( const ValuePattern& v )
{
return VEC( TSID( value ), v.sort(), v.type(), v.val(), v.locationId() );
}
optional< ValuePattern > EIRToValuePattern( const Term& t )
{
auto result = Decompose(
t, Vec( Lit( "value"_sid ), SubTerm(), SubTerm(), SubTerm(), Val< LocationId >() ) );
if( !result )
return nullopt;
auto&& [sort, type, val, locationId] = *result;
return ValuePattern( sort, type, val, locationId );
}
} // namespace goose::eir
|
Changes to bs/eir/value.h.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 |
extern optional< Value > EIRToValue( const Term& t );
extern Term ValueToEIR( const ValuePattern& v );
extern optional< ValuePattern > EIRToValuePattern( const Term& t );
class Value
{
| | | | | | | | < | > > | > | > | | > | | | | | < | | | | > | | | | | | | | > | | > > > > | > | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | > > | > | | | | | | | > | | > | > | | > | > | | | | | | | | 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 |
extern optional< Value > EIRToValue( const Term& t );
extern Term ValueToEIR( const ValuePattern& v );
extern optional< ValuePattern > EIRToValuePattern( const Term& t );
class Value
{
public:
Value() = default;
template< typename T, typename VL >
Value( T&& type, VL&& valOrCIR, LocationId loc = LocationId() ) :
m_type( forward< T >( type ) ),
m_payload( forward< VL >( valOrCIR ) ),
m_locationId( loc )
{
}
const auto& type() const { return m_type; }
const auto& payload() const { return m_payload; }
const auto& val() const { return get< Term >( m_payload ); }
auto& type() { return m_type; }
auto& val() { return get< Term >( m_payload ); }
const ptr< cir::InstrSeq >& cir() const;
ptr< cir::InstrSeq >& cir();
uint32_t cirIndex() const;
template< typename T > void setCIR( T&& pInstrSeq )
{
m_payload = forward< T >( pInstrSeq );
}
auto locationId() const { return m_locationId; }
auto&& setLocationId( LocationId id )
{
if( m_locationId.isPoison() )
id.setPoison();
m_locationId = id;
return *this;
}
bool isPoison() const { return m_locationId.isPoison(); }
auto&& setPoison()
{
m_locationId.setPoison();
return *this;
}
bool isConstant() const { return holds_alternative< Term >( m_payload ); }
bool isIndex() const { return holds_alternative< uint32_t >( m_payload ); }
bool isType() const;
bool isMetaType() const;
friend ostream& operator<<( ostream& out, const Value& val )
{
return out << ValueToEIR( val );
}
bool operator==( const Value& rhs ) const
{
return m_type == rhs.m_type && m_payload == rhs.m_payload;
}
bool operator!=( const Value& rhs ) const
{
return m_type != rhs.m_type || m_payload != rhs.m_payload;
}
bool operator<( const Value& rhs ) const
{
if( m_type != rhs.m_type )
return m_type < rhs.m_type;
return m_payload < rhs.m_payload;
}
private:
Term m_type;
variant< Term, ptr< cir::InstrSeq >, uint32_t > m_payload;
LocationId m_locationId = LocationId::Poison();
};
class ValuePattern
{
public:
template< typename S, typename T, typename V >
ValuePattern( S&& sort, T&& type, V&& v, LocationId locationId = LocationId() ) :
m_sort( forward< T >( sort ) ),
m_type( forward< T >( type ) ),
m_val( forward< V >( v ) ),
m_locationId( locationId )
{
}
auto locationId() const { return m_locationId; }
auto&& setLocationId( LocationId id )
{
if( !m_locationId.isPoison() )
m_locationId = id;
return *this;
}
bool isPoison() const { return m_locationId.isPoison(); }
void setPoison() { m_locationId.setPoison(); }
const auto& sort() const { return m_sort; }
const auto& type() const { return m_type; }
const auto& val() const { return m_val; }
auto& sort() { return m_sort; }
auto& type() { return m_type; }
auto& val() { return m_val; }
private:
Term m_sort;
Term m_type;
Term m_val;
LocationId m_locationId = 0;
};
// A generic poisoned value of "type" type.
extern Value PoisonType( LocationId loc = Location::Create( source_location::current() ) );
// A generic poisoned value of "poisontype" type.
extern Value PoisonValue( LocationId loc = Location::Create( source_location::current() ) );
using ValueVec = llvm::SmallVector< Value, 4 >;
} // namespace goose::eir
#endif
|
Changes to bs/eir/vecgenerator.cpp.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
{
return m_index >= m_cont.terms().size();
}
size_t VecGenerator::repetitionIndex() const
{
return ( fixedPartFinished() && m_cont.repetitionTerm() ) ?
| | > | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
{
return m_index >= m_cont.terms().size();
}
size_t VecGenerator::repetitionIndex() const
{
return ( fixedPartFinished() && m_cont.repetitionTerm() ) ?
m_index - m_cont.terms().size() :
0;
}
const Term& VecGenerator::operator()()
{
if( m_cont.repetitionTerm() && m_index >= m_cont.terms().size() )
{
++m_index;
return *m_cont.repetitionTerm();
}
assert( !finished() );
return m_cont.terms()[m_index++];
}
} // namespace goose::eir
|
Changes to bs/eir/vecgenerator.h.
1 2 3 4 5 |
#ifndef GOOSE_EIR_VECGENERATOR_H
#define GOOSE_EIR_VECGENERATOR_H
namespace goose::eir
{
| | > | | > | > > | | | | | | | < > | 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 |
#ifndef GOOSE_EIR_VECGENERATOR_H
#define GOOSE_EIR_VECGENERATOR_H
namespace goose::eir
{
// Generator that outputs the elements of a vector term, and then the repetition term (if any)
// forever.
class VecGenerator
{
public:
VecGenerator( const Vector& cont ) :
m_cont( cont )
{
}
bool finished() const;
bool fixedPartFinished() const;
size_t repetitionIndex() const;
const Term& operator()();
private:
const Vector& m_cont;
size_t m_index = 0;
};
} // namespace goose::eir
#endif
|
Changes to bs/eir/vecoflength.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_EIR_VECOFLENGTH_H
#define GOOSE_EIR_VECOFLENGTH_H
namespace goose::eir
{
class VecOfLength
{
| | | | < | > > | > | | | | | | | | < > | | 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 |
#ifndef GOOSE_EIR_VECOFLENGTH_H
#define GOOSE_EIR_VECOFLENGTH_H
namespace goose::eir
{
class VecOfLength
{
public:
VecOfLength( StringId varName ) :
m_varName( varName )
{
}
const auto& varName() const { return m_varName; }
friend ostream& ToString( ostream& out, const VecOfLength& v );
friend ostream& operator<<( ostream& out, const VecOfLength& x )
{
return out << x.m_varName;
}
auto operator<=>( const VecOfLength& ) const = default;
private:
StringId m_varName;
};
} // namespace goose::eir
namespace std
{
template<> struct hash< goose::eir::VecOfLength >
{
size_t operator()( const goose::eir::VecOfLength& x ) const
{
return hash< goose::util::StringId >()( x.varName() );
}
};
} // namespace std
#endif
|
Changes to bs/eir/vector.h.
1 2 3 4 5 6 7 8 9 10 11 |
#ifndef GOOSE_EIR_VECTOR_H
#define GOOSE_EIR_VECTOR_H
namespace goose::eir
{
static inline int32_t GetWeight( const Term& term );
struct Repetition
{
Repetition( const Term& t ) :
m_term( t )
| < > > < > > | | | | < | > > | > | | | | | | | < | | > | > | < | | | < | > | | | < < < | | < < < | < | | | | < | < | | < | | | | | | | | | < | | | | | | < | | | | | | < | | | | | | | < | | | | | | | | | < | | | | | | | | | | | < | > > | | | | < | > > | > | | | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | | | | | | | | | | < < < | | < < < | | | | | < | > | | | | | | > | > | | | | | | < | > | > | 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 |
#ifndef GOOSE_EIR_VECTOR_H
#define GOOSE_EIR_VECTOR_H
namespace goose::eir
{
static inline int32_t GetWeight( const Term& term );
struct Repetition
{
Repetition( const Term& t ) :
m_term( t )
{
}
Repetition( Term&& t ) :
m_term( move( t ) )
{
}
Term m_term;
};
class VecLength
{
public:
VecLength( size_t length, bool isVariable ) :
m_length( length ),
m_isVariable( isVariable )
{
}
auto minLength() const { return m_length; }
auto isVariable() const { return m_isVariable; }
auto operator<=>( const VecLength& ) const = default;
private:
size_t m_length = 0;
bool m_isVariable = false;
};
class Vector
{
public:
Vector() {}
template< typename T > using container_type = llvm::SmallVector< T, 4 >;
const auto& terms() const { return m_terms; }
auto& terms() { return m_terms; }
int32_t weight() const { return m_weight + m_weightOverride; }
void setWeight( int32_t w ) { m_weightOverride = w - m_weight; }
int32_t weightOverride() const { return m_weightOverride; }
void setWeightOverride( int32_t wo ) { m_weightOverride = wo; }
void reserve( size_t n ) { m_terms.reserve( n ); }
void resize( size_t n ) { m_terms.resize( n ); }
template< typename T > void append( T&& term )
{
if constexpr( !is_same_v< Repetition, remove_cvref_t< T > > )
{
// We want the outer structures to have more weight during typechecking. We
// achieve this by using the sum of the weights of the children, multiplied by
// two. This should ensure that typechecking solutions keeping the outer
// structure intact will be used over solutions that don't.
m_weight += GetWeight( term ) * 2;
m_terms.emplace_back( forward< T >( term ) );
}
else
{
setRepetitionTerm( term.m_term );
}
}
template< typename T > void set( size_t index, T&& term )
{
m_weight -= GetWeight( m_terms[index] );
m_weight += GetWeight( term ) * 2;
m_terms[index] = term;
}
template< typename... T > static auto Make( T&&... terms )
{
auto v = make_shared< Vector >();
v->reserve( sizeof...( T ) );
( v->append( terms ), ... );
return v;
}
template< typename... T > static Vector MakeAppend( const Vector& vec, T&&... terms )
{
Vector v( vec );
v.reserve( v.m_terms.size() + sizeof...( T ) );
( v.append( terms ), ... );
return v;
}
template< typename... T > static Vector MakePrepend( const Vector& vec, T&&... terms )
{
Vector v;
v.reserve( v.m_terms.size() + sizeof...( T ) );
( v.append( terms ), ... );
for( auto&& t : vec.terms() )
v.append( t );
return v;
}
template< typename... T > static Vector MakeConcat( const Vector& vec1, const Vector& vec2 )
{
Vector v( vec1 );
v.reserve( vec1.m_terms.size() + vec2.m_terms.size() );
for( auto&& t : vec2.terms() )
v.append( t );
return v;
}
friend ostream& ToString( ostream& out, const pvec& v );
Vector( container_type< Term >&& terms ) :
m_terms( move( terms ) )
{
}
template< typename V >
Vector( V&& terms, Term&& repetition ) :
m_terms( forward< V >( terms ) ),
m_repetitionTerm( move( repetition ) )
{
}
auto length() const { return VecLength( m_terms.size(), !!m_repetitionTerm ); }
const auto& repetitionTerm() const { return m_repetitionTerm; }
bool empty() const { return m_terms.empty(); }
Generator< reference_wrapper< const Term > > forEachTerm() const
{
for( auto&& t : terms() )
co_yield t;
if( m_repetitionTerm )
co_yield *m_repetitionTerm;
}
template< typename F > pvec transform( F&& func ) const
{
auto v = make_shared< Vector >();
v->reserve( m_terms.size() );
for( auto&& x : m_terms )
{
auto newX = func( x );
if( !newX )
return nullptr;
v->append( move( *newX ) );
}
return v;
}
Vector take( size_t n ) const
{
Vector v;
v.reserve( max( n, m_terms.size() ) );
auto it = m_terms.begin();
while( n-- )
v.append( *it++ );
return v;
}
Vector drop( size_t n ) const
{
Vector v;
v.reserve( m_terms.size() > n ? m_terms.size() - n : 0 );
auto it = m_terms.begin();
advance( it, n );
while( it != m_terms.end() )
v.append( *it++ );
return v;
}
template< typename T > void setRepetitionTerm( T&& term )
{
uint32_t c = weight();
if( m_repetitionTerm )
c -= GetWeight( *m_repetitionTerm );
m_repetitionTerm = forward< T >( term );
c += GetWeight( *m_repetitionTerm );
setWeight( c );
}
bool operator==( const Vector& rhs ) const;
const auto& operator[]( size_t i ) const { return m_terms[i]; }
auto& operator[]( size_t i ) { return m_terms[i]; }
private:
container_type< Term > m_terms;
optional< Term > m_repetitionTerm;
int32_t m_weight = 1;
int32_t m_weightOverride = 0;
};
template< typename T > static inline auto&& SetWeight( T&& term, int32_t weight )
{
visit(
[&]< typename TT >( const TT& t )
{
if constexpr( is_same_v< TT, pvec > )
return t->setWeight( weight );
else
assert( weight == 0 );
},
term );
return forward< T >( term );
}
static inline int32_t GetWeight( const Term& term )
{
return visit(
[]< typename T >( const T& t ) -> uint32_t
{
if constexpr( is_same_v< T, pvec > )
return t->weight();
else
return 0;
},
term );
}
} // namespace goose::eir
#endif
|
Changes to bs/execute/binaryops.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "execute.h"
#include "builtins/builtins.h"
#include "binaryops.inl"
using namespace goose;
using namespace goose::execute;
using namespace goose::builtins;
bool VM::execute( const cir::And& bo )
{
| | > | | < | < | < | < | | > | | < | < | < | < | | > | | | | | | | | | | | | | < < < | < < < | < < < | < < < | < < < | < < < | < < < | < < < | < < < | < < < | < < < | < < < | < < < | < < < | < < < | < < < | < < < | < < < | < < < | < < < | 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 |
#include "execute.h"
#include "builtins/builtins.h"
#include "binaryops.inl"
using namespace goose;
using namespace goose::execute;
using namespace goose::builtins;
bool VM::execute( const cir::And& bo )
{
return executeLogicBinOp( bo,
[]< typename T >( const T& lhs, const T& rhs )
{
if constexpr( is_same_v< T, bool > )
return lhs && rhs;
else
return lhs & rhs;
} );
}
bool VM::execute( const cir::Or& bo )
{
return executeLogicBinOp( bo,
[]< typename T >( const T& lhs, const T& rhs )
{
if constexpr( is_same_v< T, bool > )
return lhs || rhs;
else
return lhs | rhs;
} );
}
bool VM::execute( const cir::Xor& bo )
{
return executeLogicBinOp( bo,
[]< typename T >( const T& lhs, const T& rhs )
{
if constexpr( is_same_v< T, bool > )
{
auto l = static_cast< uint8_t >( lhs );
auto r = static_cast< uint8_t >( rhs );
return !!( l ^ r );
}
else
{
return lhs ^ rhs;
}
} );
}
bool VM::execute( const cir::Shl& bo )
{
return executeShiftBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs.shl( rhs ); } );
}
bool VM::execute( const cir::LShr& bo )
{
return executeShiftBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs.lshr( rhs ); } );
}
bool VM::execute( const cir::AShr& bo )
{
return executeShiftBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs.ashr( rhs ); } );
}
bool VM::execute( const cir::Add& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs + rhs; } );
}
bool VM::execute( const cir::Sub& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs - rhs; } );
}
bool VM::execute( const cir::Mul& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs * rhs; } );
}
bool VM::execute( const cir::UDiv& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs / rhs; } );
}
bool VM::execute( const cir::SDiv& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs / rhs; } );
}
bool VM::execute( const cir::URem& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs % rhs; } );
}
bool VM::execute( const cir::SRem& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs % rhs; } );
}
bool VM::execute( const cir::Eq& bo )
{
return executeEqualityBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs == rhs; } );
}
bool VM::execute( const cir::Neq& bo )
{
return executeEqualityBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs != rhs; } );
}
bool VM::execute( const cir::UGT& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs > rhs; } );
}
bool VM::execute( const cir::UGE& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs >= rhs; } );
}
bool VM::execute( const cir::ULT& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs < rhs; } );
}
bool VM::execute( const cir::ULE& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs <= rhs; } );
}
bool VM::execute( const cir::SGT& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs > rhs; } );
}
bool VM::execute( const cir::SGE& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs >= rhs; } );
}
bool VM::execute( const cir::SLT& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs < rhs; } );
}
bool VM::execute( const cir::SLE& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs ) { return lhs <= rhs; } );
}
|
Changes to bs/execute/binaryops.inl.
1 2 3 4 5 |
#ifndef GOOSE_EXECUTE_BINARYOPS_INL
#define GOOSE_EXECUTE_BINARYOPS_INL
namespace goose::execute
{
| < | | | 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 |
#ifndef GOOSE_EXECUTE_BINARYOPS_INL
#define GOOSE_EXECUTE_BINARYOPS_INL
namespace goose::execute
{
template< typename F > bool VM::executeEqualityBinOp( const cir::BinaryOp& bo, F&& func )
{
auto rhs = pop();
auto lhs = pop();
if( !lhs || !rhs )
return false;
if( lhs->isPoison() || rhs->isPoison() )
return false;
assert( lhs->type() == rhs->type() );
if( lhs->type() != rhs->type() )
return false;
auto lval = Evaluate( *lhs, *this );
if( lval.isPoison() )
return false;
if( !lval.isConstant() )
return false;
auto rval = Evaluate( *rhs, *this );
if( rval.isPoison() )
return false;
if( !rval.isConstant() )
return false;
if( auto lbool = FromValue< bool >( lval ) )
{
push( ToValue( func( *lbool, *FromValue< bool >( rval ) ) ) );
|
| ︙ | ︙ | |||
61 62 63 64 65 66 67 |
const auto& rstr = *FromValue< string >( rval );
push( ToValue( func( *lstr, rstr ) ) );
return true;
}
G_TRACE_VAL( lval )
G_TRACE_VAL( rval )
| | < | | | | 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 |
const auto& rstr = *FromValue< string >( rval );
push( ToValue( func( *lstr, rstr ) ) );
return true;
}
G_TRACE_VAL( lval )
G_TRACE_VAL( rval )
G_ERROR( "binop execution failure" )
return false;
}
template< typename F > bool VM::executeLogicBinOp( const cir::BinaryOp& bo, F&& func )
{
auto rhs = pop();
auto lhs = pop();
if( !lhs || !rhs )
return false;
if( lhs->isPoison() || rhs->isPoison() )
return false;
assert( lhs->type() == rhs->type() );
if( lhs->type() != rhs->type() )
return false;
auto lval = Evaluate( *lhs, *this );
if( lval.isPoison() )
return false;
if( !lval.isConstant() )
return false;
auto rval = Evaluate( *rhs, *this );
if( rval.isPoison() )
return false;
if( !rval.isConstant() )
return false;
if( auto lbool = FromValue< bool >( lval ) )
{
push( ToValue( func( *lbool, *FromValue< bool >( rval ) ) ) );
|
| ︙ | ︙ | |||
113 114 115 116 117 118 119 |
auto rint = *FromValue< BigInt >( rval );
push( ToValue( func( *lint, move( rint ) ) ) );
return true;
}
G_TRACE_VAL( lval )
G_TRACE_VAL( rval )
| | < | | | | < | | | | | | 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 |
auto rint = *FromValue< BigInt >( rval );
push( ToValue( func( *lint, move( rint ) ) ) );
return true;
}
G_TRACE_VAL( lval )
G_TRACE_VAL( rval )
G_ERROR( "binop execution failure" )
return false;
}
template< typename F > bool VM::executeBinOp( const cir::BinaryOp& bo, F&& func )
{
auto rhs = pop();
auto lhs = pop();
if( !lhs || !rhs )
return false;
if( lhs->isPoison() || rhs->isPoison() )
return false;
assert( lhs->type() == rhs->type() );
if( lhs->type() != rhs->type() )
return false;
auto lval = Evaluate( *lhs, *this );
if( lval.isPoison() )
return false;
if( !lval.isConstant() )
return false;
auto rval = Evaluate( *rhs, *this );
if( rval.isPoison() )
return false;
if( !rval.isConstant() )
return false;
if( auto lint = FromValue< APSInt >( lval ) )
{
auto rint = *FromValue< APSInt >( rval );
push( ToValue( func( *lint, move( rint ) ) ) );
return true;
}
if( auto lint = FromValue< BigInt >( lval ) )
{
const auto& rint = *FromValue< BigInt >( rval );
push( ToValue( func( *lint, rint ) ) );
return true;
}
G_TRACE_VAL( lval )
G_TRACE_VAL( rval )
G_ERROR( "binop execution failure" )
return false;
}
template< typename F > bool VM::executeShiftBinOp( const cir::BinaryOp& bo, F&& func )
{
auto rhs = pop();
auto lhs = pop();
if( !lhs || !rhs )
return false;
if( lhs->isPoison() || rhs->isPoison() )
return false;
auto lval = Evaluate( *lhs, *this );
if( lval.isPoison() )
return false;
if( !lval.isConstant() )
return false;
auto rval = Evaluate( *rhs, *this );
if( rval.isPoison() )
return false;
if( !rval.isConstant() )
return false;
if( auto lint = FromValue< APSInt >( lval ) )
{
auto rint = *FromValue< APSInt >( rval );
push( ToValue( APSInt( func( *lint, move( rint ) ), lint->isUnsigned() ) ) );
return true;
}
if( auto lint = FromValue< BigInt >( lval ) )
{
auto rint = *FromValue< APSInt >( rval );
push( ToValue( BigInt( func( *lint, move( rint ) ) ) ) );
return true;
}
G_TRACE_VAL( lval )
G_TRACE_VAL( rval )
G_ERROR( "binop execution failure" )
return false;
}
} // namespace goose::execute
#endif
|
Changes to bs/execute/eval.cpp.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 |
if( !v.isConstant() )
{
const auto& cir = val.cir();
if( !cir )
return v.setPoison();
| | | | < | | < | | | | | > | | | | | | | | | 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 |
if( !v.isConstant() )
{
const auto& cir = val.cir();
if( !cir )
return v.setPoison();
// Execution may fail: there are some cases when we can't really be sure that eager
// evaluation is possible until we actually try. In this case we forget about the eager
// evaluation and return the value as is.
if( !vm.execute( *cir ) )
return v;
// Not returning a value isn't a failure, for instance if the value we just evaluated
// was a call to a function returning void. So we return a void typed value in this
// case, otherwise the original value might get evaluated again. And since void function
// are usually called for their side effects, we don't want to evaluate them twice.
// HOW ABOUT I ACTUALLY DO THE THING I SAID IN THE COMMENT TO ACTUALLY AVOID THE PROBLEM
// I DESCRIBED FFS
auto result = vm.pop();
if( !result )
return Value( GetValueType< void >(), 0U ); // FFS
if( result->isPoison() )
return *result;
v = move( *result );
v.setLocationId( val.locationId() );
}
if( IsTuple( v ) )
{
Vector vals;
vals.reserve( TupleSize( v ) );
bool poison = false;
ForEachInTuple( v,
[&]( auto&& v )
{
auto newV = Evaluate( v, vm );
if( newV.isPoison() )
poison = true;
vals.append( ValueToEIR( move( newV ) ) );
return true;
} );
Value newTup( v.type(), make_shared< Vector >( move( vals ) ) );
if( poison )
newTup.setPoison();
return newTup;
}
return v;
}
} // namespace goose::execute
|
Changes to bs/execute/execute.h.
1 2 3 4 5 6 7 8 9 10 11 12 |
#ifndef GOOSE_EXECUTE_H
#define GOOSE_EXECUTE_H
#include "cir/cir.h"
namespace goose::execute
{
using namespace eir;
using namespace cir;
class VM;
| | < < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#ifndef GOOSE_EXECUTE_H
#define GOOSE_EXECUTE_H
#include "cir/cir.h"
namespace goose::execute
{
using namespace eir;
using namespace cir;
class VM;
// If the provided value isn't constant, execute its cir to turn it into a constant.
extern Value Evaluate( const Value& val, VM& vm );
} // namespace goose::execute
#include "vm.h"
#include "termaddr.h"
#endif
|
Changes to bs/execute/termaddr.cpp.
1 2 3 4 5 6 7 8 9 |
#include "execute.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::execute;
using namespace goose::builtins;
const Term& Bridge< eir::Term* >::Type()
{
| > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include "execute.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::execute;
using namespace goose::builtins;
const Term& Bridge< eir::Term* >::Type()
{
static auto type =
ValueToEIR( Value( TypeType(), MkStdType( TSID( ct_type ), TSID( comptime_addr ) ) ) );
return type;
}
Value Bridge< eir::Term* >::ToValue( eir::Term* x )
{
return Value( Type(), TERM( static_cast< void* >( x ) ) );
}
|
| ︙ | ︙ |
Changes to bs/execute/termaddr.h.
1 2 3 4 5 |
#ifndef GOOSE_EXECUTE_TERMADDR_H
#define GOOSE_EXECUTE_TERMADDR_H
namespace goose::eir
{
| < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#ifndef GOOSE_EXECUTE_TERMADDR_H
#define GOOSE_EXECUTE_TERMADDR_H
namespace goose::eir
{
template<> struct Bridge< eir::Term* >
{
static const Term& Type();
static Value ToValue( eir::Term* x );
static eir::Term* FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/execute/vm.cpp.
| ︙ | ︙ | |||
44 45 46 47 48 49 50 |
while( bb )
{
const auto* pInstrs = bb->runnableInstructions();
if( !pInstrs )
return false;
for( auto&& instr : *pInstrs )
| < < < < | < < < | | | | | 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 |
while( bb )
{
const auto* pInstrs = bb->runnableInstructions();
if( !pInstrs )
return false;
for( auto&& instr : *pInstrs )
if( !execute( instr ) )
return false;
if( !bb->terminator() )
break;
m_pPreviousBB = bb;
bb = executeTerminator( *bb->terminator() );
}
m_pPreviousBB = pbbBackup;
if( m_retVal )
push( *m_retVal );
return true;
}
bool VM::execute( const cir::InstrSeq& is )
{
for( const auto& instr : is )
if( !execute( instr ) )
return false;
return true;
}
bool VM::execute( const cir::Instruction& instr )
{
return visit( [&]( auto&& e ) { return execute( e ); }, instr );
}
bool VM::execute( const cir::Call& call )
{
if( !( ms_remainingBranchInstExecutions ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
0, "execute: compilation time execution budget exceeded." );
return false;
}
--ms_remainingBranchInstExecutions;
auto callFunc = pop();
if( !callFunc )
return false;
if( callFunc->isPoison() )
DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );
auto func = Evaluate( *callFunc, *this );
if( func.isPoison() )
return false;
if( !func.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
0, "execute: function evaluation failed." );
return false;
}
if( IsExternalFunc( func ) )
return false;
auto argCount = call.numArgs();
|
| ︙ | ︙ | |||
190 191 192 193 194 195 196 |
if( !pvoid )
return false;
auto val = EIRToValue( *static_cast< Term* >( pvoid ) );
if( !val )
return false;
| | < | | | | | | 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 |
if( !pvoid )
return false;
auto val = EIRToValue( *static_cast< Term* >( pvoid ) );
if( !val )
return false;
// We only support tuples now. In the future, Select will also be used to add an offset to
// another pointer. Everything else (structs, classes, containers, etc.) should build on top of
// those two fundamental types.
if( !IsTuple( *val ) )
{
G_VAL_ERROR( *val, "execute: select: value is not a tuple" );
return false;
}
assert( val->isConstant() );
push( ToValue( &GetConstantTupleElement( *val, s.memberIndex() ) ) );
return true;
}
bool VM::execute( const cir::AllocVar& av )
{
auto stackIndex = m_currentFrameStart + ( av.index() & 0x7fff'ffff );
if( m_stack.size() <= stackIndex )
m_stack.resize( stackIndex + 1 );
m_stack[stackIndex] = BuildUninitializedValue( *EIRToValue( av.type().get() ) );
return true;
}
bool VM::execute( const cir::Load& l )
{
auto baseAddr = pop();
if( !baseAddr )
return false;
auto addr = FromValue< Term* >( *baseAddr );
if( !addr )
return false;
if( *addr == TSID( UNINITIALIZED ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
l.locationId(), "execute: attempting to load from an uninitialized address" );
return false;
}
auto val = EIRToValue( *addr );
if( !val )
return false;
|
| ︙ | ︙ | |||
268 269 270 271 272 273 274 |
return true;
}
bool VM::execute( const cir::Phi& p )
{
bool success = false;
| | > | | | | | | | | | | 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 |
return true;
}
bool VM::execute( const cir::Phi& p )
{
bool success = false;
p.forAllIncomings(
[&]( auto&& bb, auto&& val )
{
if( bb == m_pPreviousBB )
{
m_temporaries.set( p.destIndex(), Evaluate( val, *this ) );
success = true;
return false;
}
return true;
} );
return success;
}
bool VM::execute( const cir::Not& uo )
{
auto operand = pop();
|
| ︙ | ︙ | |||
315 316 317 318 319 320 321 |
{
G_ERROR( "InlineCall encountered during execution" );
return false;
}
ptr< BasicBlock > VM::executeTerminator( const cir::Terminator& terminator )
{
| | < < < | 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 |
{
G_ERROR( "InlineCall encountered during execution" );
return false;
}
ptr< BasicBlock > VM::executeTerminator( const cir::Terminator& terminator )
{
return visit( [&]( auto&& e ) { return executeTerminator( e ); }, terminator.content() );
}
ptr< BasicBlock > VM::executeTerminator( const cir::RetVoid& r )
{
return nullptr;
}
|
| ︙ | ︙ | |||
340 341 342 343 344 345 346 |
return nullptr;
}
ptr< BasicBlock > VM::executeTerminator( const cir::Branch& b )
{
if( !( ms_remainingBranchInstExecutions ) )
{
| | | | | | | | | | > | | | | > | | 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 |
return nullptr;
}
ptr< BasicBlock > VM::executeTerminator( const cir::Branch& b )
{
if( !( ms_remainingBranchInstExecutions ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
0, "execute: compilation time execution budget exceeded." );
m_retVal = PoisonValue();
return nullptr;
}
--ms_remainingBranchInstExecutions;
return b.dest().lock();
}
ptr< BasicBlock > VM::executeTerminator( const cir::CondBranch& cb )
{
if( !( ms_remainingBranchInstExecutions ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
0, "execute: compilation time execution budget exceeded." );
m_retVal = PoisonValue();
return nullptr;
}
--ms_remainingBranchInstExecutions;
auto condVal = pop();
if( !condVal )
return nullptr;
auto cond = Evaluate( *condVal, *this );
if( cond.isPoison() )
return nullptr;
if( !cond.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
condVal->locationId(), "execute: branch condition evaluation failed." );
m_retVal = PoisonValue();
return nullptr;
}
if( *FromValue< bool >( cond ) )
return cb.trueDest().lock();
return cb.falseDest().lock();
}
ptr< BasicBlock > VM::executeTerminator( const cir::GhostBranch& gb )
{
if( !( ms_remainingBranchInstExecutions ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
0, "execute: compilation time execution budget exceeded." );
m_retVal = PoisonValue();
return nullptr;
}
--ms_remainingBranchInstExecutions;
return gb.continuation().lock();
}
ptr< Term > VM::BuildUninitializedValue( const Value& type )
{
if( !IsTupleType( type ) )
return make_shared< Term >( TSID( UNINITIALIZED ) );
auto tupContent = make_shared< Vector >();
tupContent->reserve( TupleTypeSize( type ) );
ForEachInTupleType( type,
[&]( auto&& t )
{
tupContent->append( *BuildUninitializedValue( *EIRToValue( t ) ) );
return true;
} );
return make_shared< Term >(
ValueToEIR( Value( ValueToEIR( type ), TERM( move( tupContent ) ) ) ) );
}
|
Changes to bs/execute/vm.h.
1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_EXECUTE_VM_H
#define GOOSE_EXECUTE_VM_H
namespace goose::execute
{
class VM
{
friend class ExecutionBudgetGuard;
| | | < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | < < < | | | | | | < | < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | | | | | | | | < | < | < | < | | | | | | | | | < | | > | | | < | < > | | | < | < < | | < | | < > | 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 |
#ifndef GOOSE_EXECUTE_VM_H
#define GOOSE_EXECUTE_VM_H
namespace goose::execute
{
class VM
{
friend class ExecutionBudgetGuard;
public:
static void SetExecutionBudget( uint32_t b ) { ms_remainingBranchInstExecutions = b; }
optional< Value > execute( CFG& cfg );
bool execute( ptr< BasicBlock > bb );
bool execute( const cir::InstrSeq& is );
bool execute( const cir::Instruction& instr );
bool execute( const cir::Call& call );
bool execute( const cir::Constant& cst );
bool execute( const cir::Select& s );
bool execute( const cir::AllocVar& av );
bool execute( const cir::Load& l );
bool execute( const cir::Store& s );
bool execute( const cir::Phi& p );
bool execute( const cir::Not& uo );
bool execute( const cir::And& bo );
bool execute( const cir::Or& bo );
bool execute( const cir::Xor& bo );
bool execute( const cir::Shl& bo );
bool execute( const cir::LShr& bo );
bool execute( const cir::AShr& bo );
bool execute( const cir::Add& bo );
bool execute( const cir::Sub& bo );
bool execute( const cir::Mul& bo );
bool execute( const cir::UDiv& bo );
bool execute( const cir::SDiv& bo );
bool execute( const cir::URem& bo );
bool execute( const cir::SRem& bo );
bool execute( const cir::Eq& bo );
bool execute( const cir::Neq& bo );
bool execute( const cir::UGT& bo );
bool execute( const cir::UGE& bo );
bool execute( const cir::ULT& bo );
bool execute( const cir::ULE& bo );
bool execute( const cir::SGT& bo );
bool execute( const cir::SGE& bo );
bool execute( const cir::SLT& bo );
bool execute( const cir::SLE& bo );
bool execute( const cir::InlineCall& ic );
template< typename T > bool execute( const T& ) { return false; }
ptr< BasicBlock > executeTerminator( const cir::Terminator& terminator );
ptr< BasicBlock > executeTerminator( const cir::RetVoid& r );
ptr< BasicBlock > executeTerminator( const cir::Ret& r );
ptr< BasicBlock > executeTerminator( const cir::Branch& b );
ptr< BasicBlock > executeTerminator( const cir::CondBranch& cb );
ptr< BasicBlock > executeTerminator( const cir::GhostBranch& gb );
template< typename T > ptr< BasicBlock > executeTerminator( const T& ) { return nullptr; }
void push( const Value& v )
{
m_stack.emplace_back( make_shared< Term >( ValueToEIR( v ) ) );
}
optional< Value > pop()
{
if( m_stack.empty() || m_stack.size() <= m_currentFrameStart )
return nullopt;
assert( m_stack.back() );
auto result = move( *m_stack.back() );
m_stack.resize( m_stack.size() - 1 );
return EIRToValue( result );
}
optional< Term > popTerm()
{
if( m_stack.empty() || m_stack.size() <= m_currentFrameStart )
return nullopt;
assert( m_stack.back() );
auto result = move( *m_stack.back() );
m_stack.resize( m_stack.size() - 1 );
return result;
}
private:
struct StackFrameHelper
{
StackFrameHelper( VM& vm, CFG& cfg, size_t numArgs = 0 );
~StackFrameHelper();
template< typename T > void set( size_t index, T&& val )
{
vm.m_stack[index + savedStackSize] = forward< T >( val );
}
size_t savedStackSize = 0;
size_t savedFrameStart = 0;
VM& vm;
};
static optional< Value > ExecuteBuiltinFuncCall( const Value& func, const Term& args );
static ptr< Term > BuildUninitializedValue( const Value& type );
template< typename F > bool executeEqualityBinOp( const cir::BinaryOp& bo, F&& func );
template< typename F > bool executeLogicBinOp( const cir::BinaryOp& bo, F&& func );
template< typename F > bool executeBinOp( const cir::BinaryOp& bo, F&& func );
template< typename F > bool executeShiftBinOp( const cir::BinaryOp& bo, F&& func );
llvm::SmallVector< ptr< Term >, 8 > m_stack;
cir::TempStorage< Value > m_temporaries;
optional< Value > m_retVal;
size_t m_currentFrameStart = 0;
// Used to interpret Phi instructions.
ptr< BasicBlock > m_pPreviousBB;
// To avoid compile time code to get stuck, we have a limitation on the total number of
// call and loop instructions that we are willing to execute before giving up.
static uint32_t ms_remainingBranchInstExecutions;
};
// To avoid using up the execution budget on failed non mandatory compile time execution
// attempts
class ExecutionBudgetGuard
{
public:
ExecutionBudgetGuard() :
m_savedBudget( VM::ms_remainingBranchInstExecutions )
{
}
~ExecutionBudgetGuard() { VM::ms_remainingBranchInstExecutions = m_savedBudget; }
// If the budget use should be kept, call this before destruction.
void commit() { m_savedBudget = VM::ms_remainingBranchInstExecutions; }
private:
uint32_t m_savedBudget = 0;
};
} // namespace goose::execute
#endif
|
Changes to bs/g0api/cgapi/cgapi.h.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
static inline void SetupApiCodeGen( Env& e )
{
SetupApiCGModule( e );
SetupApiCGMangle( e );
SetupApiCGFunc( e );
SetupApiCGLinker( e );
}
| | | 12 13 14 15 16 17 18 19 20 21 |
static inline void SetupApiCodeGen( Env& e )
{
SetupApiCGModule( e );
SetupApiCGMangle( e );
SetupApiCGFunc( e );
SetupApiCGLinker( e );
}
} // namespace goose::g0api
#endif
|
Changes to bs/g0api/cgapi/func.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "g0api/g0api.h"
#include "eir/eir.h"
using namespace goose;
namespace goose::g0api
{
void SetupApiCGFunc( Env& e )
{
weak_ptr< Env > pEnv = e.shared_from_this();
| > | > | | > | > | | 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 |
#include "g0api/g0api.h"
#include "eir/eir.h"
using namespace goose;
namespace goose::g0api
{
void SetupApiCGFunc( Env& e )
{
weak_ptr< Env > pEnv = e.shared_from_this();
RegisterBuiltinFunc< bool( TypeWrapper< ptr< codegen::Module > >,
CustomPattern< Value, FuncPattern > ) >( e, "CGGenerateFunction"_sid,
[pEnv]( const TypeWrapper< ptr< codegen::Module > >& module, const Value& f )
{
ProfileZoneScopedN( "CGGenerateFunction" );
if( !IsFuncType( *EIRToValue( f.type() ) ) )
return false;
sema::Context c( pEnv.lock(), RootG0Identity() );
DiagnosticsContext dc( 0, true );
VerbosityContext vc( Verbosity::Normal, true );
auto func = CompileFunc( c, f );
if( func.isPoison() || DiagnosticsManager::GetInstance().errorsWereEmitted() )
return false;
auto llvmFunc = module->getOrCreateFunc( c, *FromValue< builtins::Func >( func ) );
return !!llvmFunc;
} );
RegisterBuiltinFunc< bool( TypeWrapper< ptr< codegen::Module > >,
CustomPattern< Value, FuncPattern >, string ) >( e, "CGGenerateFunction"_sid,
[pEnv]( const TypeWrapper< ptr< codegen::Module > >& module, const Value& f,
const string& name )
{
ProfileZoneScopedN( "CGGenerateFunction" );
if( !IsFuncType( *EIRToValue( f.type() ) ) )
return false;
sema::Context c( pEnv.lock(), RootG0Identity() );
DiagnosticsContext dc( 0, true );
VerbosityContext vc( Verbosity::Normal, true );
auto func = CompileFunc( c, f );
if( func.isPoison() || DiagnosticsManager::GetInstance().errorsWereEmitted() )
return false;
auto llvmFunc = module->getOrCreateFunc( c, *FromValue< builtins::Func >( func ),
name, llvm::Function::ExternalLinkage );
return !!llvmFunc;
} );
}
} // namespace goose::g0api
|
Changes to bs/g0api/cgapi/linker.cpp.
1 2 3 4 5 6 | #include "g0api/g0api.h" #include "eir/eir.h" #include "lld/Common/Driver.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Support/raw_os_ostream.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 |
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "lld/Common/Driver.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/Support/raw_os_ostream.h"
LLD_HAS_DRIVER( coff )
LLD_HAS_DRIVER( elf )
LLD_HAS_DRIVER( mingw )
LLD_HAS_DRIVER( macho )
LLD_HAS_DRIVER( wasm )
using namespace goose;
using namespace goose::eir;
using namespace goose::g0api;
namespace
{
bool BuildArgArrayFromTuple( const Value& tup, vector< string >& array )
{
if( !IsTuple( tup ) )
return false;
array.reserve( TupleSize( tup ) );
bool success = true;
ForEachInTuple( tup,
[&]( auto&& t )
{
auto arg = FromValue< string >( t );
if( !arg )
{
success = false;
return false;
}
array.emplace_back( move( *arg ) );
return true;
} );
return success;
}
void BuildArgPtrArray( vector< string >& args, vector< const char* >& argPtrs )
{
argPtrs.reserve( args.size() + 1 );
argPtrs.emplace_back( "lld" );
for( auto&& a : args )
argPtrs.emplace_back( a.c_str() );
}
} // namespace
namespace goose::g0api
{
void SetupApiCGLinker( Env& e )
{
RegisterBuiltinFunc< int32_t( Value ) >( e, "CGLinkerLLD"_sid,
[]( const Value& argTup )
{
ProfileZoneScopedN( "CGLinkerLLD" );
vector< string > args;
if( !BuildArgArrayFromTuple( argTup, args ) )
return 0;
|
| ︙ | ︙ | |||
71 72 73 74 75 76 77 |
if( !r.canRunAgain )
lld::exitLld( r.retCode );
return r.retCode;
} );
}
| | | 72 73 74 75 76 77 78 79 |
if( !r.canRunAgain )
lld::exitLld( r.retCode );
return r.retCode;
} );
}
} // namespace goose::g0api
|
Changes to bs/g0api/cgapi/mangle.cpp.
1 2 3 4 5 6 7 8 9 |
#include "g0api/g0api.h"
#include "eir/eir.h"
using namespace goose;
namespace goose::g0api
{
void SetupApiCGMangle( Env& 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 |
#include "g0api/g0api.h"
#include "eir/eir.h"
using namespace goose;
namespace goose::g0api
{
void SetupApiCGMangle( Env& e )
{
RegisterBuiltinFunc< string( Value ) >( e, "CGMangle"_sid,
[]( const Value& v ) -> string
{
auto func = FromValue< builtins::Func >( v );
if( !func )
return "";
if( !func->cir() )
return "";
auto result = codegen::Mangle( func->cir()->identity() );
if( !result )
return "";
return *result;
} );
}
} // namespace goose::g0api
|
Changes to bs/g0api/cgapi/module.cpp.
1 2 3 4 5 6 7 |
#include "g0api/g0api.h"
#include "eir/eir.h"
namespace goose::g0api
{
void SetupApiCGModule( Env& 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 38 39 40 41 42 43 44 45 46 47 48 49 50 |
#include "g0api/g0api.h"
#include "eir/eir.h"
namespace goose::g0api
{
void SetupApiCGModule( Env& e )
{
RegisterBuiltinFunc< TypeWrapper< ptr< codegen::Module > >( string ) >( e,
"CGModuleCreate"_sid, []( const string& name ) -> TypeWrapper< ptr< codegen::Module > >
{ return make_shared< codegen::Module >( name ); } );
// TODO add parameters to choose the cpu type, the target for cross compilation etc
RegisterBuiltinFunc< bool( TypeWrapper< ptr< codegen::Module > > ) >( e,
"CGModuleSetupTarget"_sid, []( const TypeWrapper< ptr< codegen::Module > >& module )
{ return module->setupTarget(); } );
// TODO add parameters to control the optimization level etc
RegisterBuiltinFunc< void( TypeWrapper< ptr< codegen::Module > > ) >( e,
"CGModuleOptimize"_sid,
[]( const TypeWrapper< ptr< codegen::Module > >& module )
{
ProfileZoneScopedN( "CGModuleOptimize" );
module->runOptimizationPasses();
} );
RegisterBuiltinFunc< void( TypeWrapper< ptr< codegen::Module > >, string ) >( e,
"CGModuleEmitLLVMIr"_sid,
[]( const TypeWrapper< ptr< codegen::Module > >& module, const string& filename )
{
ProfileZoneScopedN( "CGModuleEmitLLVMIr" );
module->dumpAsLlvmIr( filename );
} );
RegisterBuiltinFunc< bool( TypeWrapper< ptr< codegen::Module > >, string ) >( e,
"CGModuleEmitAsm"_sid,
[]( const TypeWrapper< ptr< codegen::Module > >& module, const string& filename )
{
ProfileZoneScopedN( "CGModuleEmitAsm" );
return module->emitToFile( filename, llvm::CGFT_AssemblyFile );
} );
RegisterBuiltinFunc< bool( TypeWrapper< ptr< codegen::Module > >, string ) >( e,
"CGModuleEmitObj"_sid,
[]( const TypeWrapper< ptr< codegen::Module > >& module, const string& filename )
{
ProfileZoneScopedN( "CGModuleEmitObj" );
return module->emitToFile( filename, llvm::CGFT_ObjectFile );
} );
}
} // namespace goose::g0api
|
Changes to bs/g0api/compiler.cpp.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 |
{
void SetupApiCompiler( Env& e )
{
DefineConstant( e, "TargetOSName"_sid, ValueToEIR( ToValue( string( TARGET_OS_NAME ) ) ) );
wptr< Env > pEnv = e.shared_from_this();
| | < < | < | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
{
void SetupApiCompiler( Env& e )
{
DefineConstant( e, "TargetOSName"_sid, ValueToEIR( ToValue( string( TARGET_OS_NAME ) ) ) );
wptr< Env > pEnv = e.shared_from_this();
RegisterBuiltinFunc< void( bool ) >( e, "DiagnosticsForceColors"_sid,
[]( bool enable ) { DiagnosticsManager::GetInstance().setForceColors( enable ); } );
// TODO_SSA_VERIFICATION reenable
/*RegisterBuiltinFunc< Intrinsic< void ( bool ) > >( e, "#VerificationTraceMode"_sid,
[]( auto&& c, const Value& enable )
{
if( !enable.isConstant() )
{
|
| ︙ | ︙ | |||
44 45 46 47 48 49 50 |
"this doesn't evaluate to a constant." );
return;
}
verify::Func::SetEnableVerification( *FromValue< bool >( enable ) );
} );
| | > | > | > | | | | | | | < < < | | | | | | | | | | | | | | | > | | | < < < | | | > | > | > | | | | | | | | | | < < | < < > | 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 282 283 284 285 286 287 288 289 290 291 |
"this doesn't evaluate to a constant." );
return;
}
verify::Func::SetEnableVerification( *FromValue< bool >( enable ) );
} );
RegisterBuiltinFunc< Intrinsic< void ( bool ) > >( e,
"#VerificationDumpSolverOnFailure"_sid,
[]( auto&& c, const Value& enable )
{
if( !enable.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage( enable.locationId(),
"this doesn't evaluate to a constant." );
return;
}
verify::Func::SetDumpSolverOnFailure( *FromValue< bool >( enable ) );
} );
RegisterBuiltinFunc< Intrinsic< void ( bool ) > >( e,
"#VerificationDumpSolverOnSuccess"_sid,
[]( auto&& c, const Value& enable )
{
if( !enable.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage( enable.locationId(),
"this doesn't evaluate to a constant." );
return;
}
verify::Func::SetDumpSolverOnSuccess( *FromValue< bool >( enable ) );
} );*/
RegisterBuiltinFunc< Intrinsic< void( CustomPattern< Value, FuncPattern >, string ) > >( e,
"#DumpFunctionCFG"_sid,
[]( auto&& c, const Value& funcVal, const Value& filename )
{
if( !funcVal.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
funcVal.locationId(), "this doesn't evaluate to a constant." );
return;
}
if( !filename.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
filename.locationId(), "this doesn't evaluate to a constant." );
return;
}
auto name = *FromValue< string >( filename );
auto func = CompileFunc( c, funcVal );
if( func.isPoison() || DiagnosticsManager::GetInstance().errorsWereEmitted() )
return;
auto f = *FromValue< builtins::Func >( func );
if( !f.cir() )
return;
ofstream out( name.c_str() );
out << *f.cir();
} );
RegisterBuiltinFunc< Eager< void >( CustomPattern< Value, ValuePatternT > ) >(
e, "#DumpValue"_sid, []( const auto& v ) { G_TRACE_VAL( v ); } );
RegisterBuiltinFunc< Intrinsic< void( uint32_t ) > >( e, "#SetExecutionBudget"_sid,
[]( auto&& c, const Value& budget )
{
static bool used = false;
if( !budget.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
budget.locationId(), "this doesn't evaluate to a constant." );
return;
}
if( used )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
0, "#SetExecutionBudget can only be used once." );
return;
}
used = true;
execute::VM::SetExecutionBudget( *FromValue< uint32_t >( budget ) );
} );
RegisterBuiltinFunc< Eager< Value >( Value, string ) >( e, "ExternalFunction"_sid,
[]( const Value& f, const string& symbol )
{
auto ft = FromValue< FuncType >( f );
if( !ft )
return PoisonValue();
return ToValue( BuildExternalFunc( *ft, symbol ) );
} );
RegisterBuiltinFunc< Eager< Value >( Value, string ) >( e, "ExternalVarArgFunction"_sid,
[]( const Value& f, const string& symbol )
{
auto ft = FromValue< FuncType >( f );
if( !ft )
return PoisonValue();
return ToValue( BuildExternalFunc( *ft, symbol, true ) );
} );
// TODO: Clean: this is no longer needed now that we have an api to access the Env
RegisterBuiltinFunc< void( string, Value ) >( e, "CreateConstant"_sid,
[pEnv]( const string& name, const Value& v )
{
if( !v.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
v.locationId(), "this doesn't evaluate to a constant." );
return;
}
pEnv.lock()->storeValue(
AppendToVectorTerm( RootG0Identity(), StringId( name.c_str() ) ), ANYTERM( _ ),
ValueToEIR( v ) );
} );
RegisterBuiltinFunc< Intrinsic< uint32_t( string ) > >( e, "#Include"_sid,
[pEnv]( auto&& c, const Value& fnameval ) -> Value
{
execute::VM vm;
auto fname = Evaluate( fnameval, vm );
auto filename = FromValue< string >( fname );
if( !filename )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
fnameval.locationId(), "can't evaluate this to a string." );
return PoisonValue();
}
auto identity = TakeVectorTerm( c.identity(), VecSize( c.identity() ) - 1 );
get< pvec >( identity )->terms().back() = TSID( 0_locvars );
auto result = Compiler::LoadAndExecuteFile( pEnv.lock(), *filename, identity,
GetValueType< uint32_t >(), ToValue< uint32_t >( 0 ) );
if( !result )
return ToValue< uint32_t >( 0 );
return *result;
} );
RegisterBuiltinFunc< Eager< void >( ptr< OverloadSet >, bool ) >( e,
"#SetVerboseResolution"_sid,
[pEnv]( const ptr< OverloadSet >& pOvl, bool b ) { pOvl->setVerboseResolution( b ); } );
RegisterBuiltinFunc< uint32_t( string ) >( e, "ExecuteFile"_sid,
[pEnv]( const string& filename ) -> uint32_t
{
auto identity =
AppendToVectorTerm( RootG0Identity(), TERM( StringId( Env::NewUniqueId() ) ) );
auto locVarsIdentity = AppendToVectorTerm( identity, TSID( 0_locvars ) );
auto env = pEnv.lock();
env->addVisibilityRule( builtins::RootG0Identity(), identity );
env->addVisibilityRule( identity, locVarsIdentity );
auto result = Compiler::LoadAndExecuteFile( env, filename, locVarsIdentity,
GetValueType< uint32_t >(), ToValue< uint32_t >( 0 ) );
if( !result || result->isPoison() )
return 0;
return *FromValue< uint32_t >( *result );
} );
RegisterBuiltinFunc<
Intrinsic< Value( string, TypeWrapper< Term >, Value, Value, Value ) > >( e,
"#CompileFileToFunction"_sid,
[pEnv]( auto&& c, const Value& filenameVal, const Value& identityVal, const Value& rt,
const Value& defReturnValue, const Value& params ) -> Value
{
ProfileZoneScopedN( "#CompileFileToFunction" );
if( !filenameVal.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
filenameVal.locationId(), "this doesn't evaluate to a constant." );
return PoisonValue();
}
auto filename = *FromValue< string >( filenameVal );
ProfileZoneName( filename.c_str(), filename.size() );
if( !identityVal.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
identityVal.locationId(), "this doesn't evaluate to a constant." );
return PoisonValue();
}
auto identity = *FromValue< TypeWrapper< Term > >( identityVal );
// Validate those generic value inputs
if( !IsType( c, rt ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
rt.locationId(), "type expected.", 0 );
return PoisonValue();
}
if( CheckParamListKind( params ) != ParamListKind::Regular )
{
DiagnosticsManager::GetInstance().emitErrorMessage( params.locationId(),
"template parameter lists are not supported here.", 0 );
return PoisonValue();
}
sema::Context localC( pEnv.lock(), identity, ValueToEIR( rt ) );
auto ftype = BuildFuncType( localC, rt, params );
if( !ftype )
return PoisonValue();
auto funcIdentity =
AppendToVectorTerm( identity, TERM( StringId( Env::NewUniqueId() ) ) );
c.env()->addVisibilityRule( identity, funcIdentity );
auto locVarsIdentity = AppendToVectorTerm( funcIdentity, TSID( 0_locvars ) );
c.env()->addVisibilityRule( funcIdentity, locVarsIdentity );
auto func = BuildFunc( localC, *ftype, identity, funcIdentity, params, nullptr, c );
const auto& pFuncCIR = func.cir();
auto cfg = Compiler::LoadAndParseFile(
pEnv.lock(), filename, locVarsIdentity, ftype->returnType(), defReturnValue );
if( !cfg )
return PoisonValue();
if( cfg->isPoisoned() )
return PoisonValue();
pFuncCIR->body() = cfg;
return ToValue( func );
} );
RegisterBuiltinFunc< void( bool ) >( e, "Exit"_sid,
[pEnv]( bool success ) { exit( success ? EXIT_SUCCESS : EXIT_FAILURE ); } );
}
} // namespace goose::g0api
|
Changes to bs/g0api/extensibility/cir.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"
using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;
namespace
{
template< typename C, typename T, typename O, O opCode, typename... Params >
void RegisterMkOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
{
| | | < | < | | 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 "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"
using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;
namespace
{
template< typename C, typename T, typename O, O opCode, typename... Params >
void RegisterMkOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
{
RegisterBuiltinFunc< TypeWrapper< ptr< C > >(
ConstantParam< uint8_t, static_cast< uint8_t >( opCode ) >, Params... ) >( e, pOvlSet,
[]( uint8_t, const auto&... params ) -> TypeWrapper< ptr< C > >
{ return make_shared< C >( T( WrappedValueAccessor< Params >::Get( params )... ) ); } );
}
template< typename C, typename T, typename O, O opCode, typename... Getters >
void RegisterUnpackOverload( Env& e, const ptr< OverloadSet >& pOvlSet, Getters... getters )
{
using tup_type = tuple< WrapType<
remove_cvref_t< decltype( ( static_cast< T* >( nullptr )->*getters )() ) > >... >;
RegisterBuiltinFunc< bool( ConstantParam< uint8_t, static_cast< uint8_t >( opCode ) >,
TypeWrapper< ptr< C > >, TermRef< tup_type > ) >( e, pOvlSet,
[&]( uint8_t, const auto& instr, auto& out )
{
const T* pt = get_if< T >( &instr->content() );
if( !pt )
return false;
|
| ︙ | ︙ | |||
103 104 105 106 107 108 109 |
template< typename T, InstrOpCode opCode, typename... Params >
void RegisterMkInstrOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
{
RegisterMkOverload< Instruction, T, InstrOpCode, opCode, Params... >( e, pOvlSet );
}
template< typename T, InstrOpCode opCode, typename... Getters >
| > | | | | > | > > | | 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 |
template< typename T, InstrOpCode opCode, typename... Params >
void RegisterMkInstrOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
{
RegisterMkOverload< Instruction, T, InstrOpCode, opCode, Params... >( e, pOvlSet );
}
template< typename T, InstrOpCode opCode, typename... Getters >
void RegisterUnpackInstrOverload(
Env& e, const ptr< OverloadSet >& pOvlSet, Getters... getters )
{
RegisterUnpackOverload< Instruction, T, InstrOpCode, opCode >( e, pOvlSet, getters... );
}
template< typename T, InstrOpCode opCode, typename... Getters >
void RegisterInstrOverloads( Env& e, const ptr< OverloadSet >& MkInstr,
const ptr< OverloadSet >& UnpackInstr, Getters... getters )
{
RegisterMkInstrOverload< T, opCode,
WrapType<
remove_cvref_t< decltype( ( static_cast< T* >( nullptr )->*getters )() ) > >... >(
e, MkInstr );
RegisterUnpackInstrOverload< T, opCode >( e, UnpackInstr );
}
template< typename T, InstrOpCode opCode >
void RegisterBinOpInstrOverloads(
Env& e, const ptr< OverloadSet >& MkInstr, const ptr< OverloadSet >& UnpackInstr )
{
RegisterInstrOverloads< T, opCode >( e, MkInstr, UnpackInstr, &T::locationId );
}
// This enum must match the order of the types in the Terminator variant.
enum class TermiOpCode
{
|
| ︙ | ︙ | |||
141 142 143 144 145 146 147 |
template< typename T, TermiOpCode opCode, typename... Params >
void RegisterMkTermiOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
{
RegisterMkOverload< Terminator, T, TermiOpCode, opCode, Params... >( e, pOvlSet );
}
template< typename T, TermiOpCode opCode, typename... Getters >
| > | | | | > | > | | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > > | | > | > | > | > | > | > | > | > | > | > | > > | | > | > | > | > | > | > | > | > | > | > | < < | < | > | > | > | > | > | > > | > | > | > | > | > | > | 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 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
template< typename T, TermiOpCode opCode, typename... Params >
void RegisterMkTermiOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
{
RegisterMkOverload< Terminator, T, TermiOpCode, opCode, Params... >( e, pOvlSet );
}
template< typename T, TermiOpCode opCode, typename... Getters >
void RegisterUnpackTermiOverload(
Env& e, const ptr< OverloadSet >& pOvlSet, Getters... getters )
{
RegisterUnpackOverload< Terminator, T, TermiOpCode, opCode >( e, pOvlSet, getters... );
}
template< typename T, TermiOpCode opCode, typename... Getters >
void RegisterTermiOverloads( Env& e, const ptr< OverloadSet >& MkTermi,
const ptr< OverloadSet >& UnpackTermi, Getters... getters )
{
RegisterMkTermiOverload< T, opCode,
WrapType<
remove_cvref_t< decltype( ( static_cast< T* >( nullptr )->*getters )() ) > >... >(
e, MkTermi );
RegisterUnpackTermiOverload< T, opCode >( e, UnpackTermi );
}
} // namespace
namespace goose::g0api
{
void SetupCIRExtensibilityFuncs( Env& e )
{
////////////////////////////
// Instruction
////////////////////////////
DefineConstant( e, "InstrOpCodeNone"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::None ) ) ) );
DefineConstant( e, "InstrOpCodeCall"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Call ) ) ) );
DefineConstant( e, "InstrOpCodeCallCheck"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::CallCheck ) ) ) );
DefineConstant( e, "InstrOpCodeVarAddr"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::VarAddr ) ) ) );
DefineConstant( e, "InstrOpCodeTempAddr"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::TempAddr ) ) ) );
DefineConstant( e, "InstrOpCodeSelect"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Select ) ) ) );
DefineConstant( e, "InstrOpCodePushConstant"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Constant ) ) ) );
DefineConstant( e, "InstrOpCodeCreateTemporary"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::CreateTemporary ) ) ) );
DefineConstant( e, "InstrOpCodeGetTemporary"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::GetTemporary ) ) ) );
DefineConstant( e, "InstrOpCodeAllocVar"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::AllocVar ) ) ) );
DefineConstant( e, "InstrOpCodeLoad"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Load ) ) ) );
DefineConstant( e, "InstrOpCodeStore"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Store ) ) ) );
DefineConstant( e, "InstrOpCodePhi"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Phi ) ) ) );
DefineConstant( e, "InstrOpCodeNot"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Not ) ) ) );
DefineConstant( e, "InstrOpCodeAnd"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::And ) ) ) );
DefineConstant( e, "InstrOpCodeOr"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Or ) ) ) );
DefineConstant( e, "InstrOpCodeXor"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Xor ) ) ) );
DefineConstant( e, "InstrOpCodeShl"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Shl ) ) ) );
DefineConstant( e, "InstrOpCodeLShr"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::LShr ) ) ) );
DefineConstant( e, "InstrOpCodeAShr"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::AShr ) ) ) );
DefineConstant( e, "InstrOpCodeAdd"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Add ) ) ) );
DefineConstant( e, "InstrOpCodeSub"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Sub ) ) ) );
DefineConstant( e, "InstrOpCodeMul"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Mul ) ) ) );
DefineConstant( e, "InstrOpCodeUDiv"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::UDiv ) ) ) );
DefineConstant( e, "InstrOpCodeSDiv"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SDiv ) ) ) );
DefineConstant( e, "InstrOpCodeURem"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::URem ) ) ) );
DefineConstant( e, "InstrOpCodeSRem"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SRem ) ) ) );
DefineConstant( e, "InstrOpCodeEq"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Eq ) ) ) );
DefineConstant( e, "InstrOpCodeNeq"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Neq ) ) ) );
DefineConstant( e, "InstrOpCodeUGT"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::UGT ) ) ) );
DefineConstant( e, "InstrOpCodeUGE"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::UGE ) ) ) );
DefineConstant( e, "InstrOpCodeULT"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::ULT ) ) ) );
DefineConstant( e, "InstrOpCodeULE"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::ULE ) ) ) );
DefineConstant( e, "InstrOpCodeSGT"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SGT ) ) ) );
DefineConstant( e, "InstrOpCodeSGE"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SGE ) ) ) );
DefineConstant( e, "InstrOpCodeSLT"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SLT ) ) ) );
DefineConstant( e, "InstrOpCodeSLE"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SLE ) ) ) );
DefineConstant( e, "InstrOpCodeAssert"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Assert ) ) ) );
DefineConstant( e, "InstrOpCodeGhostCall"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::GhostCall ) ) ) );
DefineConstant( e, "InstrOpCodePHOverrideSet"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::PHOverrideSet ) ) ) );
DefineConstant( e, "InstrOpCodePHOverrideClear"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::PHOverrideClear ) ) ) );
DefineConstant( e, "InstrOpCodePlaceholder"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Placeholder ) ) ) );
DefineConstant( e, "InstrOpCodeForAllSetup"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::ForAllSetup ) ) ) );
DefineConstant( e, "InstrOpCodeForAll"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::ForAll ) ) ) );
DefineConstant( e, "InstrOpCodeImplies"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Implies ) ) ) );
DefineConstant( e, "InstrOpCodeIsPrefixOf"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::IsPrefixOf ) ) ) );
RegisterBuiltinFunc< uint8_t( TypeWrapper< ptr< Instruction > > ) >(
e, "GetInstrOpCode"_sid, []( const auto& t ) { return t->content().index(); } );
// MkInstr and UnpackInstr overloads
auto MkInstr = CreateOverloadSet( e, "MkInstr"_sid );
auto UnpackInstr = CreateOverloadSet( e, "UnpackInstr"_sid );
RegisterInstrOverloads< Call, InstrOpCode::Call >(
e, MkInstr, UnpackInstr, &Call::numArgs, &Call::locationId );
RegisterInstrOverloads< CallCheck, InstrOpCode::CallCheck >(
e, MkInstr, UnpackInstr, &CallCheck::numArgs, &CallCheck::locationId );
RegisterInstrOverloads< VarAddr, InstrOpCode::VarAddr >(
e, MkInstr, UnpackInstr, &VarAddr::varIndex, &VarAddr::locationId );
RegisterInstrOverloads< TempAddr, InstrOpCode::TempAddr >(
e, MkInstr, UnpackInstr, &TempAddr::tempIndex, &TempAddr::locationId );
RegisterInstrOverloads< Select, InstrOpCode::Select >(
e, MkInstr, UnpackInstr, &Select::memberIndex, &Select::locationId );
RegisterInstrOverloads< CreateTemporary, InstrOpCode::CreateTemporary >( e, MkInstr,
UnpackInstr, &CreateTemporary::index, &CreateTemporary::verif,
&CreateTemporary::locationId );
RegisterInstrOverloads< GetTemporary, InstrOpCode::GetTemporary >( e, MkInstr, UnpackInstr,
&GetTemporary::type, &GetTemporary::index, &GetTemporary::locationId );
RegisterInstrOverloads< AllocVar, InstrOpCode::AllocVar >(
e, MkInstr, UnpackInstr, &AllocVar::type, &AllocVar::index, &AllocVar::locationId );
RegisterInstrOverloads< Load, InstrOpCode::Load >(
e, MkInstr, UnpackInstr, &Load::type, &Load::locationId );
RegisterInstrOverloads< Store, InstrOpCode::Store >(
e, MkInstr, UnpackInstr, &Store::type, &Store::srcLocId, &Store::destLocId );
RegisterInstrOverloads< Phi, InstrOpCode::Phi >( e, MkInstr, UnpackInstr, &Phi::type,
&Phi::numIncomings, &Phi::destIndex, &Phi::locationId );
RegisterInstrOverloads< Not, InstrOpCode::Not >(
e, MkInstr, UnpackInstr, &Not::locationId );
RegisterBinOpInstrOverloads< And, InstrOpCode::And >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< Or, InstrOpCode::Or >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< Xor, InstrOpCode::Xor >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< Shl, InstrOpCode::Shl >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< LShr, InstrOpCode::LShr >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< AShr, InstrOpCode::AShr >( e, MkInstr, UnpackInstr );
|
| ︙ | ︙ | |||
257 258 259 260 261 262 263 |
RegisterBinOpInstrOverloads< ULT, InstrOpCode::ULT >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< ULE, InstrOpCode::ULE >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< SGT, InstrOpCode::SGT >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< SGE, InstrOpCode::SGE >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< SLT, InstrOpCode::SLT >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< SLE, InstrOpCode::SLE >( e, MkInstr, UnpackInstr );
| | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > | > | < < | < | | | | | | | > | | < < | | | | < < < | | | < < < | < < | < | | > | | | < < < | > | | | | | | | < < | < | | | < < | | < < | < | | | | | | | | | | | | | < < < | < < > | 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 |
RegisterBinOpInstrOverloads< ULT, InstrOpCode::ULT >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< ULE, InstrOpCode::ULE >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< SGT, InstrOpCode::SGT >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< SGE, InstrOpCode::SGE >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< SLT, InstrOpCode::SLT >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< SLE, InstrOpCode::SLE >( e, MkInstr, UnpackInstr );
RegisterInstrOverloads< Assert, InstrOpCode::Assert >(
e, MkInstr, UnpackInstr, &Assert::locationId );
RegisterInstrOverloads< GhostCall, InstrOpCode::GhostCall >(
e, MkInstr, UnpackInstr, &GhostCall::numArgs, &GhostCall::locationId );
RegisterInstrOverloads< PHOverrideSet, InstrOpCode::PHOverrideSet >(
e, MkInstr, UnpackInstr, &PHOverrideSet::name, &PHOverrideSet::locationId );
RegisterInstrOverloads< PHOverrideClear, InstrOpCode::PHOverrideClear >(
e, MkInstr, UnpackInstr, &PHOverrideClear::name, &PHOverrideClear::locationId );
RegisterInstrOverloads< Placeholder, InstrOpCode::Placeholder >( e, MkInstr, UnpackInstr,
&Placeholder::type, &Placeholder::name, &Placeholder::locationId );
RegisterInstrOverloads< ForAllSetup, InstrOpCode::ForAllSetup >(
e, MkInstr, UnpackInstr, &ForAllSetup::numArgs, &ForAllSetup::locationId );
RegisterInstrOverloads< ForAll, InstrOpCode::ForAll >(
e, MkInstr, UnpackInstr, &ForAll::locationId );
RegisterBinOpInstrOverloads< Implies, InstrOpCode::Implies >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< IsPrefixOf, InstrOpCode::IsPrefixOf >(
e, MkInstr, UnpackInstr );
// TODO functions to manipulate Phi's incomings array
////////////////////////////
// Terminator
////////////////////////////
DefineConstant( e, "TermiOpCodeRetVoid"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::RetVoid ) ) ) );
DefineConstant( e, "TermiOpCodeRet"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Ret ) ) ) );
DefineConstant( e, "TermiOpCodeBranch"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Branch ) ) ) );
DefineConstant( e, "TermiOpCodeCondBranch"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::CondBranch ) ) ) );
DefineConstant( e, "TermiOpCodeBreak"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Break ) ) ) );
DefineConstant( e, "TermiOpCodeContinue"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Continue ) ) ) );
// MkTermi and UnpackTermi overloads
auto MkTermi = CreateOverloadSet( e, "MkTermi"_sid );
auto UnpackTermi = CreateOverloadSet( e, "UnpackTermi"_sid );
RegisterTermiOverloads< RetVoid, TermiOpCode::RetVoid >(
e, MkTermi, UnpackTermi, &RetVoid::locationId );
RegisterTermiOverloads< Ret, TermiOpCode::Ret >(
e, MkTermi, UnpackTermi, &Ret::locationId );
RegisterTermiOverloads< Branch, TermiOpCode::Branch >(
e, MkTermi, UnpackTermi, &Branch::dest );
RegisterTermiOverloads< CondBranch, TermiOpCode::CondBranch >(
e, MkTermi, UnpackTermi, &CondBranch::trueDest, &CondBranch::falseDest );
RegisterTermiOverloads< Break, TermiOpCode::Break >(
e, MkTermi, UnpackTermi, &Break::level );
RegisterTermiOverloads< Continue, TermiOpCode::Continue >(
e, MkTermi, UnpackTermi, &Continue::level );
auto AppendInstr = CreateOverloadSet( e, "AppendInstr"_sid );
////////////////////////////
// InstrSeq
////////////////////////////
RegisterBuiltinFunc< TypeWrapper< ptr< InstrSeq > >() >(
e, "MkInstrSeq"_sid, []() { return make_shared< InstrSeq >(); } );
RegisterBuiltinFunc< void(
TypeWrapper< ptr< InstrSeq > >, TypeWrapper< ptr< Instruction > > ) >( e, AppendInstr,
[]( const auto& pIS, auto& instr )
{
auto& is = *pIS.get();
AppendToInstrSeq( is, *instr.get() );
} );
RegisterBuiltinFunc< void(
TypeWrapper< ptr< InstrSeq > >, TypeWrapper< ptr< InstrSeq > > ) >( e, AppendInstr,
[]( const auto& pIS, auto& isToAppend )
{
auto& is = *pIS.get();
AppendToInstrSeq( is, *isToAppend.get() );
} );
RegisterBuiltinFunc< void( TypeWrapper< ptr< InstrSeq > >, TypeWrapper< Value > ) >( e,
AppendInstr,
[]( const auto& pIS, auto& val )
{
auto& is = *pIS.get();
AppendToInstrSeq( is, val.get() );
} );
////////////////////////////
// BasicBlock
////////////////////////////
RegisterBuiltinFunc< TypeWrapper< LocationId >( TypeWrapper< ptr< BasicBlock > > ) >( e,
"GetBasicBlockLocation"_sid,
[]( const auto& pBB ) -> TypeWrapper< LocationId > { return pBB->locationId(); } );
RegisterBuiltinFunc< void( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< LocationId > ) >(
e, "SetBasicBlockLocation"_sid,
[]( const auto& pBB, const auto& loc ) { pBB->setLocationId( loc ); } );
RegisterBuiltinFunc< uint32_t( TypeWrapper< ptr< BasicBlock > > ) >(
e, "GetBasicBlockLoopId"_sid, []( const auto& pBB ) { return pBB->loopId(); } );
RegisterBuiltinFunc< bool( TypeWrapper< ptr< BasicBlock > > ) >( e,
"IsBasicBlockLoopHeader"_sid, []( const auto& pBB ) { return pBB->isLoopHeader(); } );
RegisterBuiltinFunc< bool( TypeWrapper< ptr< BasicBlock > >,
TermRef< TypeWrapper< ptr< Terminator > > > ) >( e, "GetBasicBlockTerminator"_sid,
[]( const auto& pBB, auto& out )
{
const auto& bb = *pBB.get();
if( !bb.terminator() )
return false;
out = make_shared< Terminator >( *bb.terminator() );
return true;
} );
RegisterBuiltinFunc< void( TypeWrapper< ptr< BasicBlock > >,
TypeWrapper< ptr< Terminator > > ) >( e, "SetBasicBlockTerminator"_sid,
[]( const auto& pBB, const auto& termi ) { pBB->setTerminator( *termi.get() ); } );
// TODO InstrSeq, which will be annoying to deal with (we can't just iterate by indexing it
// so we'll need to figure something out for the api)
RegisterBuiltinFunc< void(
TypeWrapper< ptr< BasicBlock > >, TypeWrapper< ptr< Instruction > > ) >( e, AppendInstr,
[]( const auto& pBB, auto& instr )
{
auto& bb = *pBB.get();
bb.append( *instr.get() );
} );
RegisterBuiltinFunc< void(
TypeWrapper< ptr< BasicBlock > >, TypeWrapper< ptr< InstrSeq > > ) >( e, AppendInstr,
[]( const auto& pBB, auto& is )
{
auto& bb = *pBB.get();
bb.append( *is.get() );
} );
RegisterBuiltinFunc< void( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< Value > ) >( e,
AppendInstr,
[]( const auto& pBB, auto& val )
{
auto& bb = *pBB.get();
bb.append( val.get() );
} );
////////////////////////////
// CFG
////////////////////////////
RegisterBuiltinFunc< bool( TypeWrapper< ptr< CFG > > ) >(
e, "IsCFGPoisoned"_sid, []( const auto& pCFG ) { return pCFG->isPoisoned(); } );
RegisterBuiltinFunc< void( TypeWrapper< ptr< CFG > > ) >(
e, "PoisonCFG"_sid, []( const auto& pCFG ) { pCFG->poison(); } );
RegisterBuiltinFunc< uint32_t( TypeWrapper< ptr< CFG > > ) >( e,
"GetCFGBasicBlockCount"_sid,
[]( const auto& pCFG ) { return static_cast< uint32_t >( pCFG->count() ); } );
RegisterBuiltinFunc< bool( TypeWrapper< ptr< CFG > >, uint32_t,
TermRef< TypeWrapper< ptr< BasicBlock > > > ) >( e, "GetCFGBasicBlock"_sid,
[]( const auto& pCFG, uint32_t index, auto& out )
{
const auto& cfg = *pCFG.get();
if( cfg.count() <= index )
return false;
out = cfg.getBB( index );
return true;
} );
RegisterBuiltinFunc< bool( TypeWrapper< ptr< CFG > >,
TermRef< TypeWrapper< ptr< BasicBlock > > > ) >( e, "GetCFGEntryBasicBlock"_sid,
[]( const auto& pCFG, auto& out )
{
const auto& cfg = *pCFG.get();
if( cfg.count() == 0 )
return false;
out = cfg.entryBB();
return true;
} );
RegisterBuiltinFunc< bool( TypeWrapper< ptr< CFG > >,
TermRef< TypeWrapper< ptr< BasicBlock > > > ) >( e, "GetCFGLastBasicBlock"_sid,
[]( const auto& pCFG, auto& out )
{
const auto& cfg = *pCFG.get();
if( cfg.count() == 0 )
return false;
out = cfg.lastBB();
return true;
} );
RegisterBuiltinFunc< bool( TypeWrapper< ptr< CFG > >,
TermRef< TypeWrapper< ptr< BasicBlock > > > ) >( e, "GetCFGCurrentBasicBlock"_sid,
[]( const auto& pCFG, auto& out )
{
const auto& cfg = *pCFG.get();
if( !cfg.currentBB() )
return false;
out = cfg.currentBB();
return true;
} );
RegisterBuiltinFunc< void( TypeWrapper< ptr< CFG > >, TypeWrapper< ptr< BasicBlock > > ) >(
e, "SetCFGCurrentBasicBlock"_sid,
[]( const auto& pCFG, auto& pBB )
{
auto& cfg = *pCFG.get();
cfg.setCurrentBB( pBB.get() );
} );
RegisterBuiltinFunc< TypeWrapper< ptr< BasicBlock > >( TypeWrapper< ptr< CFG > > ) >( e,
"CFGCreateBasicBlock"_sid,
[]( const auto& pCFG ) -> TypeWrapper< ptr< BasicBlock > >
{
auto& cfg = *pCFG.get();
return cfg.createBB();
} );
RegisterBuiltinFunc< uint32_t() >(
e, "GenerateNewUID"_sid, []() { return util::GenerateNewUID(); } );
}
} // namespace goose::g0api
|
Changes to bs/g0api/extensibility/context.cpp.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
namespace goose::g0api
{
void SetupContextExtensibilityFuncs( Env& e )
{
wptr< Env > pEnv = e.shared_from_this();
| | < < | < | < < | < | < < | < | > < | < | | | < < < | | < | < | | < < < < > | 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 |
namespace goose::g0api
{
void SetupContextExtensibilityFuncs( Env& e )
{
wptr< Env > pEnv = e.shared_from_this();
RegisterBuiltinFunc< TypeWrapper< Term >( TypeWrapper< ptr< Context > > ) >(
e, "ContextGetIdentity"_sid, []( const auto& c ) { return c->identity(); } );
////////////////////////////
// Scopes
////////////////////////////
RegisterBuiltinFunc< void( TypeWrapper< ptr< sema::Context > > ) >(
e, "PushLifetimeScope"_sid, []( const auto& c ) { BeginLifetimeScope( *c.get() ); } );
RegisterBuiltinFunc< void( TypeWrapper< ptr< sema::Context > > ) >(
e, "PopLifetimeScope"_sid, []( const auto& c ) { EndLifetimeScope( *c.get() ); } );
RegisterBuiltinFunc< TypeWrapper< ptr< VisibilityScope > >(
TypeWrapper< ptr< sema::Context > > ) >( e, "PushVisibilityScope"_sid,
[]( const auto& c ) -> TypeWrapper< ptr< VisibilityScope > >
{ return make_shared< VisibilityScope >( *c.get() ); } );
RegisterBuiltinFunc< void( TermRef< TypeWrapper< ptr< VisibilityScope > > > ) >(
e, "PopVisibilityScope"_sid, []( auto& vs ) { vs = nullptr; } );
RegisterBuiltinFunc< TypeWrapper< ptr< Scope > >( TypeWrapper< ptr< sema::Context > > ) >(
e, "PushScope"_sid, []( const auto& c ) -> TypeWrapper< ptr< Scope > >
{ return make_shared< Scope >( *c.get() ); } );
RegisterBuiltinFunc< void( TermRef< TypeWrapper< ptr< Scope > > > ) >(
e, "PopScope"_sid, []( auto& vs ) { vs = nullptr; } );
}
} // namespace goose::g0api
|
Changes to bs/g0api/extensibility/diagnostics.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"
using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;
namespace goose::g0api
{
void SetupDiagnosticsExtensibilityFuncs( Env& 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 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 |
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"
using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;
namespace goose::g0api
{
void SetupDiagnosticsExtensibilityFuncs( Env& e )
{
RegisterBuiltinFunc< Intrinsic< void( bool ) > >( e, "#DiagnosticsEnableTraces"_sid,
[]( auto&& c, const Value& enable )
{
if( !enable.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
enable.locationId(), "this doesn't evaluate to a constant." );
return;
}
DiagnosticsManager::GetInstance().setTraceMode( *FromValue< bool >( enable ) );
} );
RegisterBuiltinFunc< void( TypeWrapper< LocationId >, string ) >( e,
"PushDiagnosticsContext"_sid,
[]( const TypeWrapper< LocationId >& locationId, const string& desc )
{
DiagnosticsManager::GetInstance().pushDiagnosticsContext(
make_shared< ContextInfos >( desc, locationId, false ) );
} );
RegisterBuiltinFunc< void( TypeWrapper< LocationId >, string, bool ) >( e,
"PushDiagnosticsContext"_sid,
[]( const TypeWrapper< LocationId >& locationId, const string& desc,
bool isRootContext )
{
DiagnosticsManager::GetInstance().pushDiagnosticsContext(
make_shared< ContextInfos >( desc, locationId, isRootContext ) );
} );
RegisterBuiltinFunc< void() >( e, "PopDiagnosticsContext"_sid,
[]() { DiagnosticsManager::GetInstance().popDiagnosticsContext(); } );
RegisterBuiltinFunc< void( string ) >( e, "EmitErrorMessage"_sid,
[]( const string& message )
{ DiagnosticsManager::GetInstance().emitErrorMessage( 0, message ); } );
RegisterBuiltinFunc< void( TypeWrapper< LocationId >, string ) >( e, "EmitErrorMessage"_sid,
[]( const TypeWrapper< LocationId >& locationId, const string& message )
{ DiagnosticsManager::GetInstance().emitErrorMessage( locationId, message ); } );
RegisterBuiltinFunc< void( TypeWrapper< LocationId >, string, uint32_t ) >( e,
"EmitErrorMessage"_sid,
[]( const TypeWrapper< LocationId >& locationId, const string& message,
uint32_t contextLevels ) {
DiagnosticsManager::GetInstance().emitErrorMessage(
locationId, message, contextLevels );
} );
RegisterBuiltinFunc< void( TypeWrapper< LocationId >, TypeWrapper< StringId >, string ) >(
e, "EmitErrorMessage"_sid,
[]( const TypeWrapper< LocationId >& locationId, const TypeWrapper< StringId >& errId,
const string& message )
{ DiagnosticsManager::GetInstance().emitErrorMessage( locationId, errId, message ); } );
RegisterBuiltinFunc< void( TypeWrapper< LocationId >, TypeWrapper< StringId >, string,
uint32_t ) >( e, "EmitErrorMessage"_sid,
[]( const TypeWrapper< LocationId >& locationId, const TypeWrapper< StringId >& errId,
const string& message, uint32_t contextLevels )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
locationId, errId, message, contextLevels );
} );
RegisterBuiltinFunc< void( TypeWrapper< LocationId >, string ) >( e,
"EmitSyntaxErrorMessage"_sid,
[]( const TypeWrapper< LocationId >& locationId, const string& message )
{ DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId, message ); } );
RegisterBuiltinFunc< void( TypeWrapper< LocationId >, string, uint32_t ) >( e,
"EmitSyntaxErrorMessage"_sid,
[]( const TypeWrapper< LocationId >& locationId, const string& message,
uint32_t contextLevels )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, message, contextLevels );
} );
RegisterBuiltinFunc< void( TypeWrapper< LocationId >, string ) >( e,
"EmitLexerErrorMessage"_sid,
[]( const TypeWrapper< LocationId >& locationId, const string& message )
{ DiagnosticsManager::GetInstance().emitLexerErrorMessage( locationId, message ); } );
RegisterBuiltinFunc< void( TypeWrapper< LocationId >, string ) >( e, "EmitTraceMessage"_sid,
[]( const TypeWrapper< LocationId >& locationId, const string& message )
{ DiagnosticsManager::GetInstance().emitTraceMessage( locationId, message ); } );
RegisterBuiltinFunc< void( TypeWrapper< LocationId >, TypeWrapper< StringId >, string ) >(
e, "DefineCustomDiagnostic"_sid,
[]( const TypeWrapper< LocationId >& locationId, const TypeWrapper< StringId >& errId,
const string& message ) {
DiagnosticsManager::GetInstance().defineCustomDiagnostic(
locationId, errId, message );
} );
RegisterBuiltinFunc< void( TypeWrapper< LocationId >, TypeWrapper< StringId > ) >( e,
"DefineCustomDiagnostic"_sid,
[]( const TypeWrapper< LocationId >& locationId, const TypeWrapper< StringId >& errId )
{ DiagnosticsManager::GetInstance().defineCustomDiagnostic( locationId, errId ); } );
RegisterBuiltinFunc< bool() >( e, "WereErrorsEmitted"_sid,
[]() { return DiagnosticsManager::GetInstance().errorsWereEmitted(); } );
}
} // namespace goose::g0api
|
Changes to bs/g0api/extensibility/eir.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"
using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;
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 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 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 |
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"
using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;
namespace
{
template< typename T > void RegisterMkTermOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
{
if constexpr( IsTypeWrapper< T >::value )
{
RegisterBuiltinFunc< TypeWrapper< Term >( T ) >(
e, pOvlSet, []( const T& v ) -> TypeWrapper< Term > { return TERM( v.get() ); } );
}
else
{
RegisterBuiltinFunc< TypeWrapper< Term >( T ) >(
e, pOvlSet, []( const T& v ) -> TypeWrapper< Term > { return TERM( v ); } );
}
}
template< typename T >
void RegisterGetTermValueOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
{
if constexpr( IsTypeWrapper< T >::value )
{
RegisterBuiltinFunc< bool( TypeWrapper< Term >, TermRef< T > ) >( e, pOvlSet,
[]( const TypeWrapper< Term >& t, TermRef< T >& tref )
{
const auto* pVal = get_if< typename T::type >( &t.get() );
if( !pVal )
return false;
tref = *pVal;
return true;
} );
}
else
{
RegisterBuiltinFunc< bool( TypeWrapper< Term >, TermRef< T > ) >( e, pOvlSet,
[]( const TypeWrapper< Term >& t, TermRef< T >& tref )
{
const auto* pVal = get_if< T >( &t.get() );
if( !pVal )
return false;
tref = *pVal;
return true;
} );
}
}
} // namespace
namespace goose::g0api
{
void SetupEIRExtensibilityFuncs( Env& e )
{
// Constants.
DefineConstant( e, "DelimiterOpenParen"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( Delimiter::OpenParen ) ) ) );
DefineConstant( e, "DelimiterOpenBrace"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( Delimiter::OpenBrace ) ) ) );
DefineConstant( e, "DelimiterOpenBracket"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( Delimiter::OpenBracket ) ) ) );
DefineConstant( e, "DelimiterCloseParen"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( Delimiter::CloseParen ) ) ) );
DefineConstant( e, "DelimiterCloseBrace"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( Delimiter::CloseBrace ) ) ) );
DefineConstant( e, "DelimiterCloseBracket"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( Delimiter::CloseBracket ) ) ) );
// This enum must match the order of the types in the Term variant.
enum class TermType
{
UInt64,
LocationId,
String,
StringId,
Delimiter,
Hole,
AnyTerm,
VecOfLength,
Vec,
BigInt,
FixedInt,
Internal
};
DefineConstant( e, "TermTypeUInt64"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( TermType::UInt64 ) ) ) );
DefineConstant( e, "TermTypeLocationId"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( TermType::LocationId ) ) ) );
DefineConstant( e, "TermTypeString"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( TermType::String ) ) ) );
DefineConstant( e, "TermTypeStringId"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( TermType::StringId ) ) ) );
DefineConstant( e, "TermTypeDelimiter"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Delimiter ) ) ) );
DefineConstant( e, "TermTypeHole"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Hole ) ) ) );
DefineConstant( e, "TermTypeAnyTerm"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( TermType::AnyTerm ) ) ) );
DefineConstant( e, "TermTypeVecOfLength"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( TermType::VecOfLength ) ) ) );
DefineConstant( e, "TermTypeVec"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Vec ) ) ) );
DefineConstant( e, "TermTypeBigInt"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( TermType::BigInt ) ) ) );
DefineConstant( e, "TermTypeFixedInt"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( TermType::FixedInt ) ) ) );
DefineConstant( e, "TermTypeInternal"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Internal ) ) ) );
DefineConstant( e, "HoleBhvStandard"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( Hole::Behavior::Standard ) ) ) );
DefineConstant( e, "HoleBhvPack"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( Hole::Behavior::Pack ) ) ) );
DefineConstant( e, "HoleBhvAny"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( Hole::Behavior::Any ) ) ) );
// Functions
RegisterBuiltinFunc< BigInt( TypeWrapper< Term > ) >( e, "GetTermType"_sid,
[]( const auto& t )
{
return BigInt::FromU32( min< uint8_t >(
t.get().index(), static_cast< uint8_t >( TermType::Internal ) ) );
} );
////////////////////////////
// MkTerm overloads
////////////////////////////
auto MkTerm = CreateOverloadSet( e, "MkTerm"_sid );
RegisterMkTermOverload< uint64_t >( e, MkTerm );
RegisterMkTermOverload< TypeWrapper< LocationId > >( e, MkTerm );
RegisterMkTermOverload< string >( e, MkTerm );
RegisterMkTermOverload< TypeWrapper< StringId > >( e, MkTerm );
RegisterBuiltinFunc< TypeWrapper< Term >( uint8_t ) >( e, "MkDelimiterTerm"_sid,
[]( uint8_t d ) -> TypeWrapper< Term >
{ return TERM( static_cast< Delimiter >( d ) ); } );
RegisterMkTermOverload< TypeWrapper< Hole > >( e, MkTerm );
RegisterMkTermOverload< TypeWrapper< AnyTerm > >( e, MkTerm );
RegisterMkTermOverload< TypeWrapper< VecOfLength > >( e, MkTerm );
RegisterMkTermOverload< TypeWrapper< pvec > >( e, MkTerm );
RegisterMkTermOverload< BigInt >( e, MkTerm );
RegisterMkTermOverload< TypeWrapper< APSInt > >( e, MkTerm );
////////////////////////////
// GetTermValue overloads
////////////////////////////
auto GetTermValue = CreateOverloadSet( e, "GetTermValue"_sid );
RegisterGetTermValueOverload< uint64_t >( e, GetTermValue );
RegisterGetTermValueOverload< TypeWrapper< LocationId > >( e, GetTermValue );
RegisterGetTermValueOverload< string >( e, GetTermValue );
RegisterGetTermValueOverload< TypeWrapper< StringId > >( e, GetTermValue );
RegisterBuiltinFunc< bool( TypeWrapper< Term >, TermRef< uint8_t > ) >( e,
"GetDelimiterTermValue"_sid,
[]( const auto& t, auto& tref )
{
const auto* pVal = get_if< Delimiter >( &t.get() );
if( !pVal )
return false;
tref = static_cast< uint8_t >( *pVal );
return true;
} );
RegisterGetTermValueOverload< TypeWrapper< Hole > >( e, GetTermValue );
RegisterGetTermValueOverload< TypeWrapper< AnyTerm > >( e, GetTermValue );
RegisterGetTermValueOverload< TypeWrapper< VecOfLength > >( e, GetTermValue );
RegisterGetTermValueOverload< TypeWrapper< pvec > >( e, GetTermValue );
RegisterGetTermValueOverload< BigInt >( e, GetTermValue );
RegisterGetTermValueOverload< TypeWrapper< APSInt > >( e, GetTermValue );
////////////////////////////
// LocationId
////////////////////////////
RegisterBuiltinFunc< TypeWrapper< LocationId >(
TypeWrapper< LocationId >, TypeWrapper< LocationId > ) >( e, "MkSpanningLocation"_sid,
[]( const auto& loc1, const auto& loc2 ) -> TypeWrapper< LocationId >
{
return static_cast< LocationId >(
Location::CreateSpanningLocation( loc1.get(), loc2.get() ) );
} );
////////////////////////////
// StringId
////////////////////////////
RegisterBuiltinFunc< Eager< TypeWrapper< StringId > >( string ) >(
e, "MkStringId"_sid, []( string s ) -> TypeWrapper< StringId > { return s; } );
RegisterBuiltinFunc< Eager< TypeWrapper< StringId > >( uint32_t ) >(
e, "MkStringId"_sid, []( uint32_t id ) -> TypeWrapper< StringId > { return id; } );
////////////////////////////
// Hole
////////////////////////////
RegisterBuiltinFunc< Intrinsic< TypeWrapper< Hole >(
TypeWrapper< StringId >, TypeWrapper< StringId >, uint8_t ) > >( e, "MkHole"_sid,
[]( const auto& c, const Value& nameVal, const Value& kindVal,
const Value& bhvVal ) -> Value
{
auto name = *FromValue< TypeWrapper< StringId > >( nameVal );
auto kind = *FromValue< TypeWrapper< StringId > >( kindVal );
auto bhv = *FromValue< uint8_t >( bhvVal );
if( bhv > static_cast< uint8_t >( Hole::Behavior::Any ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
bhvVal.locationId(), "invalid hole behavior." );
return PoisonValue();
}
return ToValue( TypeWrapper< Hole >(
Hole( name, kind, static_cast< Hole::Behavior >( bhv ) ) ) );
} );
RegisterBuiltinFunc< TypeWrapper< Hole >(
TypeWrapper< StringId >, TypeWrapper< StringId > ) >( e, "MkHole"_sid,
[]( const auto& name, const auto& kind ) -> TypeWrapper< Hole >
{ return Hole( name, kind ); } );
RegisterBuiltinFunc< TypeWrapper< StringId >( TypeWrapper< Hole > ) >( e, "GetHoleName"_sid,
[]( const auto& h ) -> TypeWrapper< StringId > { return h.get().name(); } );
RegisterBuiltinFunc< TypeWrapper< Term >( TypeWrapper< Hole > ) >( e, "GetHoleKind"_sid,
[]( const auto& h ) -> TypeWrapper< Term > { return h.get().kind(); } );
RegisterBuiltinFunc< uint8_t( TypeWrapper< Hole > ) >( e, "GetHoleBehavior"_sid,
[]( const auto& h ) { return static_cast< uint8_t >( h.get().behavior() ); } );
////////////////////////////
// AnyTerm
////////////////////////////
RegisterBuiltinFunc< TypeWrapper< StringId >( TypeWrapper< AnyTerm > ) >( e,
"GetAnyTermVarName"_sid,
[]( const auto& at ) -> TypeWrapper< StringId > { return at.get().varName(); } );
RegisterBuiltinFunc< TypeWrapper< AnyTerm >( TypeWrapper< StringId > ) >( e,
"MkAnyTerm"_sid,
[]( const auto& name ) -> TypeWrapper< AnyTerm > { return AnyTerm( name ); } );
////////////////////////////
// VecOfLength
////////////////////////////
RegisterBuiltinFunc< TypeWrapper< StringId >( TypeWrapper< VecOfLength > ) >( e,
"GetVecOfLengthVarName"_sid,
[]( const auto& at ) -> TypeWrapper< StringId > { return at.get().varName(); } );
RegisterBuiltinFunc< TypeWrapper< VecOfLength >( TypeWrapper< StringId > ) >( e,
"MkVecOfLength"_sid,
[]( const auto& name ) -> TypeWrapper< VecOfLength > { return VecOfLength( name ); } );
////////////////////////////
// Vector
////////////////////////////
RegisterBuiltinFunc< TypeWrapper< pvec >() >(
e, "MkVec"_sid, []() -> TypeWrapper< pvec > { return make_shared< Vector >(); } );
RegisterBuiltinFunc< void( TypeWrapper< pvec >, TypeWrapper< pvec > ) >( e,
"MkVecConcat"_sid, []( const auto& vec1, const auto& vec2 )
{ return make_shared< Vector >( Vector::MakeConcat( *vec1.get(), *vec2.get() ) ); } );
RegisterBuiltinFunc< void( TypeWrapper< pvec >, uint32_t len ) >(
e, "VecReserve"_sid, []( const auto& vec, uint32_t len ) { vec->reserve( len ); } );
RegisterBuiltinFunc< void( TypeWrapper< pvec >, uint32_t, TypeWrapper< Term > ) >( e,
"SetTerm"_sid, []( const auto& vec, uint32_t index, const auto& t )
{ vec->terms()[index] = t.get(); } );
RegisterBuiltinFunc< void( TypeWrapper< pvec >, int32_t ) >(
e, "SetVecWeight"_sid, []( const auto& vec, int32_t w ) { vec->setWeight( w ); } );
RegisterBuiltinFunc< void( TypeWrapper< pvec >, int32_t ) >( e, "SetVecWeightOverride"_sid,
[]( const auto& vec, int32_t w ) { vec->setWeightOverride( w ); } );
RegisterBuiltinFunc< void( TypeWrapper< pvec >, TypeWrapper< Term > ) >(
e, "VecAppend"_sid, []( const auto& vec, const auto& t ) { vec->append( t.get() ); } );
RegisterBuiltinFunc< void( TypeWrapper< pvec >, TypeWrapper< Term > ) >( e,
"VecSetRepetition"_sid,
[]( const auto& vec, const auto& t ) { vec->setRepetitionTerm( t.get() ); } );
RegisterBuiltinFunc< tuple< uint32_t, bool >( TypeWrapper< pvec > ) >( e,
"GetVecLength"_sid,
[]( const auto& vec )
{
auto vl = vec->length();
return make_tuple( static_cast< uint32_t >( vl.minLength() ), vl.isVariable() );
} );
RegisterBuiltinFunc< bool(
TypeWrapper< pvec >, uint32_t, TermRef< TypeWrapper< Term > > ) >( e, "GetVecTerm"_sid,
[]( const auto& vec, uint32_t index, auto& out )
{
const auto& v = *vec.get();
if( v.length().minLength() <= index )
return false;
out = v.terms()[index];
return true;
} );
RegisterBuiltinFunc< int32_t( TypeWrapper< pvec > ) >(
e, "GetVecWeight"_sid, []( const auto& vec ) { return vec->weight(); } );
RegisterBuiltinFunc< int32_t( TypeWrapper< pvec > ) >( e, "GetVecWeightOverride"_sid,
[]( const auto& vec ) { return vec->weightOverride(); } );
RegisterBuiltinFunc< int32_t( TypeWrapper< pvec >, TermRef< TypeWrapper< Term > > ) >( e,
"GetVecRepetitionTerm"_sid,
[]( const auto& vec, auto& out )
{
const auto& rt = vec->repetitionTerm();
if( !rt )
return false;
out = *rt;
return true;
} );
RegisterBuiltinFunc< bool( TypeWrapper< pvec > ) >(
e, "IsVecEmpty"_sid, []( const auto& vec ) { return vec->empty(); } );
////////////////////////////
// Helpers
////////////////////////////
RegisterBuiltinFunc< TypeWrapper< Term >( TypeWrapper< Term >, TypeWrapper< Term > ) >( e,
"AppendToVectorTerm"_sid, []( const auto& vec, const auto& t ) -> TypeWrapper< Term >
{ return AppendToVectorTerm( vec, t ); } );
////////////////////////////
// Integers
////////////////////////////
RegisterBuiltinFunc< Intrinsic< TypeWrapper< APSInt >( BigInt, bool ) > >( e,
"ToFixedInt"_sid,
[]( auto&& c, const Value& biVal, const Value& sVal )
{
auto bi = *FromValue< BigInt >( biVal );
auto s = *FromValue< bool >( sVal );
if( !s && bi.isNegative() )
{
DiagnosticsManager::GetInstance().emitErrorMessage( biVal.locationId(),
"this is negative and can't be converted to an unsigned int." );
return PoisonValue();
}
auto ai = bi.getAPSInt();
if( !s )
ai.setIsSigned( false );
return ToValue( TypeWrapper< APSInt >( s ) );
} );
auto ToBigInt = CreateOverloadSet( e, "ToBigInt"_sid );
RegisterBuiltinFunc< Eager< BigInt >( TypeWrapper< APSInt > ) >(
e, ToBigInt, []( const auto& fInt ) { return fInt; } );
RegisterBuiltinFunc< Eager< BigInt >( char32_t ) >(
e, ToBigInt, []( char32_t c ) { return BigInt::FromU32( c ); } );
////////////////////////////
// Propositions
////////////////////////////
RegisterBuiltinFunc< bool( TypeWrapper< ptr< Context > >, TypeWrapper< Value >,
TermRef< TypeWrapper< ptr< Propositions > > > ) >( e, "GetTypePredicates"_sid,
[]( const auto& c, const auto& type, auto& out ) -> bool
{
if( !ParseTypePredicates( *c.get(), type.get() ) )
return false;
auto ppPreds = GetTypePredicates( type );
if( !ppPreds || !( *ppPreds ) )
return false;
out = *ppPreds;
return true;
} );
RegisterBuiltinFunc< uint32_t( TypeWrapper< ptr< Propositions > > ) >( e,
"GetPropositionsCount"_sid, []( const auto& preds ) { return preds->props().size(); } );
RegisterBuiltinFunc< bool( TypeWrapper< ptr< Propositions > >, uint32_t,
TermRef< TypeWrapper< Value > > ) >( e, "GetProposition"_sid,
[]( const auto& preds, uint32_t index, auto& out )
{
if( preds->props().size() <= index )
return false;
out = preds->props()[index];
return true;
} );
}
} // namespace goose::g0api
|
Changes to bs/g0api/extensibility/env.cpp.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
namespace goose::g0api
{
void SetupEnvExtensibilityFuncs( Env& e )
{
wptr< Env > pEnv = e.shared_from_this();
| | > | > | > | > | > > | | > | > > | | > | > | > | < < | < < > | 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 |
namespace goose::g0api
{
void SetupEnvExtensibilityFuncs( Env& e )
{
wptr< Env > pEnv = e.shared_from_this();
DefineConstant( e, "RetrieveValueStatusSuccess"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( Env::Status::Success ) ) ) );
DefineConstant( e, "RetrieveValueStatusNoMatch"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( Env::Status::NoMatch ) ) ) );
DefineConstant( e, "RetrieveValueStatusAmbiguousMatch"_sid,
ValueToEIR( ToValue( static_cast< uint8_t >( Env::Status::AmbiguousMatch ) ) ) );
// We could reconstruct this easily on the goose side but I prefer it to be defined in a
// single place
DefineConstant( e, "RootG0Identity"_sid,
ValueToEIR( ToValue( TypeWrapper< Term >( RootG0Identity() ) ) ) );
RegisterBuiltinFunc< void( TypeWrapper< Term >, TypeWrapper< Term >,
TypeWrapper< Term > ) >( e, "EnvStoreValue"_sid,
[pEnv]( const TypeWrapper< Term >& idPat, const TypeWrapper< Term >& contextIdPat,
const TypeWrapper< Term >& val )
{
if( auto e = pEnv.lock() )
e->storeValue( idPat, contextIdPat, val );
} );
// TODO ValueProvider overload for EnvStoreValue (once we have wrapping of goose functions
// as C++ functions)
RegisterBuiltinFunc< uint8_t( TypeWrapper< Term >, TypeWrapper< Term >,
TermRef< TypeWrapper< Term > > ) >( e, "EnvRetrieveValue"_sid,
[pEnv]( const TypeWrapper< Term >& id, const TypeWrapper< Term >& contextId,
TermRef< TypeWrapper< Term > >& out )
{
auto e = pEnv.lock();
if( !e )
return static_cast< uint8_t >( Env::Status::NoMatch );
Term t;
auto result = e->retrieveValue( id, contextId, t );
out = t;
return static_cast< uint8_t >( result );
} );
RegisterBuiltinFunc< void( TypeWrapper< Term >, TypeWrapper< Term >, bool ) >( e,
"EnvAddVisibilityRule"_sid,
[pEnv]( const TypeWrapper< Term >& toImport, const TypeWrapper< Term >& destination,
bool transitive )
{
if( auto e = pEnv.lock() )
e->addVisibilityRule( toImport, destination, transitive );
} );
RegisterBuiltinFunc< uint32_t() >(
e, "EnvNewUniqueId"_sid, []() { return Env::NewUniqueId(); } );
}
} // namespace goose::g0api
|
Changes to bs/g0api/extensibility/extensibility.h.
| ︙ | ︙ | |||
22 23 24 25 26 27 28 |
SetupValueExtensibilityFuncs( e );
SetupContextExtensibilityFuncs( e );
SetupEnvExtensibilityFuncs( e );
SetupDiagnosticsExtensibilityFuncs( e );
SetupMiscExtensibilityFuncs( e );
SetupParserExtensibilityFuncs( e );
}
| | | 22 23 24 25 26 27 28 29 30 31 |
SetupValueExtensibilityFuncs( e );
SetupContextExtensibilityFuncs( e );
SetupEnvExtensibilityFuncs( e );
SetupDiagnosticsExtensibilityFuncs( e );
SetupMiscExtensibilityFuncs( e );
SetupParserExtensibilityFuncs( e );
}
} // namespace goose::g0api
#endif
|
Changes to bs/g0api/extensibility/misc.cpp.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 |
void SetupMiscExtensibilityFuncs( Env& e )
{
////////////////////////////
// Local variables
////////////////////////////
auto declLocVar = CreateOverloadSet( e, "DeclareLocalVar"_sid );
| | | > | < > | < | | > | < > | < | > | | > | > | < < | < | | | > | | > | | | | | | | < < < | | | < < | | | | < < | | | | < | | | | > | < | < > | | > | > | > | | 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 |
void SetupMiscExtensibilityFuncs( Env& e )
{
////////////////////////////
// Local variables
////////////////////////////
auto declLocVar = CreateOverloadSet( e, "DeclareLocalVar"_sid );
RegisterBuiltinFunc< TypeWrapper< Value >( TypeWrapper< ptr< sema::Context > >,
TypeWrapper< Term >, TypeWrapper< StringId >, TypeWrapper< LocationId > ) >( e,
declLocVar,
[]( const auto& c, const auto& type, const auto& name,
const auto& locId ) -> TypeWrapper< Value >
{ return builtins::DeclareLocalVar( *c.get(), type, name, nullopt, locId ); } );
RegisterBuiltinFunc< TypeWrapper< Value >( TypeWrapper< ptr< sema::Context > >,
TypeWrapper< Term >, TypeWrapper< StringId >, TypeWrapper< Value >,
TypeWrapper< LocationId > ) >( e, declLocVar,
[]( const auto& c, const auto& type, const auto& name, const auto& initializer,
const auto& locId ) -> TypeWrapper< Value >
{ return builtins::DeclareLocalVar( *c.get(), type, name, initializer, locId ); } );
RegisterBuiltinFunc< TypeWrapper< Value >( TypeWrapper< ptr< sema::Context > >,
TypeWrapper< Term >, TypeWrapper< StringId >, TypeWrapper< Value >,
TypeWrapper< LocationId > ) >( e, "DeclareLocalVarWithTypeInference"_sid,
[]( const auto& c, const auto& typeTExpr, const auto& name, const auto& initializer,
const auto& locId ) -> TypeWrapper< Value >
{
return builtins::DeclareLocalVarWithTypeInference(
*c.get(), typeTExpr, name, initializer, locId );
} );
////////////////////////////
// Tuples
////////////////////////////
RegisterBuiltinFunc< uint32_t( CustomPattern< Value, TuplePattern > ) >(
e, "GetTupleSize"_sid, []( const auto& tup ) { return builtins::TupleSize( tup ); } );
RegisterBuiltinFunc< uint32_t( TypeWrapper< Value > ) >( e, "GetTupleSize"_sid,
[]( const auto& tup ) -> uint32_t
{
if( !IsTuple( tup.get() ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
tup.get().locationId(), "this wrapped value is not a tuple." );
return 0;
}
return builtins::TupleSize( tup.get() );
} );
RegisterBuiltinFunc< TypeWrapper< Value >( CustomPattern< Value, TuplePattern >, uint32_t,
AccessSpecifier ) >( e, "GetComputedTupleElement"_sid,
[]( const auto& tup, uint32_t index, AccessSpecifier as )
{
if( index >= TupleSize( tup ) )
return PoisonValue();
return builtins::GetComputedTupleElement( tup, index, ValueToEIR( ToValue( as ) ) );
} );
RegisterBuiltinFunc< TypeWrapper< Value >(
TypeWrapper< Value >, uint32_t, AccessSpecifier ) >( e, "GetComputedTupleElement"_sid,
[]( const auto& tup, uint32_t index, AccessSpecifier as )
{
if( !IsTuple( tup.get() ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
tup.get().locationId(), "this wrapped value is not a tuple." );
return PoisonValue();
}
if( index >= TupleSize( tup ) )
return PoisonValue();
return builtins::GetComputedTupleElement( tup, index, ValueToEIR( ToValue( as ) ) );
} );
////////////////////////////
// Decls
////////////////////////////
using decl_tup_type = tuple< TypeWrapper< StringId >, TypeWrapper< Term > >;
RegisterBuiltinFunc< decl_tup_type( TypeWrapper< ptr< Decl > > ) >( e, "UnpackDecl"_sid,
[]( const auto& decl ) -> decl_tup_type { return { decl->name(), decl->type() }; } );
RegisterBuiltinFunc< decl_tup_type( TypeWrapper< ptr< TDecl > > ) >( e, "UnpackDecl"_sid,
[]( const auto& decl ) -> decl_tup_type { return { decl->name(), decl->type() }; } );
RegisterBuiltinFunc< decl_tup_type( TypeWrapper< ptr< TNamedDecl > > ) >( e,
"UnpackDecl"_sid,
[]( const auto& decl ) -> decl_tup_type { return { decl->name(), decl->type() }; } );
RegisterBuiltinFunc< TypeWrapper< Value >( TypeWrapper< ptr< Context > >,
TypeWrapper< Value >, TypeWrapper< pvec > ) >( e, "Invoke"_sid,
[]( const auto& c, const auto& callee, const auto& args )
{
auto pInvRule = GetInvocationRule( *c->env(), callee );
if( !pInvRule )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
callee.get().locationId(), "this value can't be invoked." );
return PoisonValue();
}
pvec argVec = make_shared< Vector >();
argVec->reserve( args->terms().size() );
// Unwrap the args if needed
for( auto&& t : args->terms() )
if( auto unwrapped = FromValue< TypeWrapper< Value > >( *EIRToValue( t ) ) )
argVec->append( ValueToEIR( *unwrapped ) );
else
argVec->append( t );
return ResolveInvocation(
*c.get(), pInvRule, callee, VectorToTuple( TSID( open ), argVec ) );
} );
////////////////////////////
// Propositions
////////////////////////////
RegisterBuiltinFunc< void( TypeWrapper< ptr< Propositions > >, TypeWrapper< Term > ) >( e,
"PropositionsSetIdentity"_sid,
[]( const TypeWrapper< ptr< Propositions > >& props,
const TypeWrapper< Term >& identity ) { props->setIdentity( identity ); } );
RegisterBuiltinFunc< bool( TypeWrapper< ptr< Propositions > >,
TypeWrapper< ptr< Context > > ) >( e, "PropositionsParse"_sid,
[]( const TypeWrapper< ptr< Propositions > >& props,
const TypeWrapper< ptr< Context > >& c )
{
if( !props.get() || !c.get() )
return false;
return props->parse( *c.get() );
} );
RegisterBuiltinFunc< uint32_t( TypeWrapper< ptr< Propositions > > ) >( e,
"PropositionsGetCount"_sid,
[]( const TypeWrapper< ptr< Propositions > >& props ) -> uint32_t
{
if( !props.get() )
return 0;
return props->props().size();
} );
RegisterBuiltinFunc< Value( TypeWrapper< ptr< Propositions > >, uint32_t ) >( e,
"PropositionsGet"_sid,
[]( const TypeWrapper< ptr< Propositions > >& props, uint32_t i )
{
if( !props.get() || i >= props->props().size() )
return PoisonValue();
return props->props()[i];
} );
}
} // namespace goose::g0api
|
Changes to bs/g0api/extensibility/parser.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"
using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;
namespace goose::g0api
{
void SetupParserExtensibilityFuncs( Env& 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 38 39 40 41 |
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"
using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;
namespace goose::g0api
{
void SetupParserExtensibilityFuncs( Env& e )
{
RegisterBuiltinFunc< TypeWrapper< ptr< parse::Resolver > >(
TypeWrapper< ptr< sema::Context > >, TypeWrapper< ptr< vector< TermLoc > > > ) >( e,
"ResolverCreate"_sid,
[]( const auto& c, const auto& toks ) -> TypeWrapper< ptr< parse::Resolver > >
{
auto tokProvider = lex::MakeVectorAdapter( *toks.get() );
return make_shared< parse::Resolver >( tokProvider, *c.get() );
} );
RegisterBuiltinFunc< TypeWrapper< ptr< parse::Parser > >(
TypeWrapper< ptr< parse::Resolver > > ) >( e, "ParserCreate"_sid,
[]( const auto& r ) -> TypeWrapper< ptr< parse::Parser > >
{ return make_shared< parse::Parser >( r.get() ); } );
RegisterBuiltinFunc< void( TypeWrapper< ptr< parse::Parser > > ) >(
e, "ParserParseSequence"_sid, []( const auto& p ) { p.get()->parseSequence(); } );
RegisterBuiltinFunc< TypeWrapper< Value >( TypeWrapper< ptr< parse::Parser > > ) >( e,
"ParserParseParenBlock"_sid,
[]( const auto& p ) { return p.get()->parseParenBlock(); } );
RegisterBuiltinFunc< bool( TypeWrapper< ptr< parse::Parser > > ) >( e,
"ParserParseBraceBlock"_sid,
[]( const auto& p ) { return p.get()->parseBraceBlock(); } );
RegisterBuiltinFunc< void( TypeWrapper< ptr< parse::Parser > > ) >(
e, "ParserFlushValue"_sid, []( const auto& p ) { p.get()->flushValue(); } );
}
} // namespace goose::g0api
|
Changes to bs/g0api/extensibility/termref.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_G0API_EXTENSIBILITY_TERMREF_H
#define GOOSE_G0API_EXTENSIBILITY_TERMREF_H
#include "execute/termaddr.h"
namespace goose::g0api
{
| | < | | | < | < > | | | < | < | | | | | | | > | < | < | | 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 |
#ifndef GOOSE_G0API_EXTENSIBILITY_TERMREF_H
#define GOOSE_G0API_EXTENSIBILITY_TERMREF_H
#include "execute/termaddr.h"
namespace goose::g0api
{
template< typename T > class TermRef
{
public:
TermRef( Term* pTerm ) :
m_pTerm( pTerm )
{
}
auto* get() const { return m_pTerm; }
template< typename TT > TermRef& operator=( TT&& rhs )
{
if( m_pTerm )
*m_pTerm = ValueToEIR( ToValue< T >( forward< TT >( rhs ) ) );
return *this;
}
private:
Term* m_pTerm = nullptr;
};
} // namespace goose::g0api
namespace goose::eir
{
template< typename T > struct Bridge< g0api::TermRef< T > >
{
static const Term& Type()
{
static auto type = ValueToEIR( ToValue(
builtins::ReferenceType( GetValueType< T >(), builtins::MutAccessSpecifier() ) ) );
return type;
}
static optional< g0api::TermRef< T > > FromValue( const Value& v )
{
auto pTerm = FromValue< Term* >( v );
return g0api::TermRef< T >( pTerm );
}
};
} // namespace goose::eir
#endif
|
Changes to bs/g0api/extensibility/value.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"
using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;
namespace goose::g0api
{
void SetupValueExtensibilityFuncs( Env& 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 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 |
#include "g0api/g0api.h"
#include "eir/eir.h"
#include "parse/parse.h"
using namespace goose;
using namespace goose::parse;
using namespace goose::g0api;
namespace goose::g0api
{
void SetupValueExtensibilityFuncs( Env& e )
{
DefineConstant(
e, "PoisonValue"_sid, ValueToEIR( ToValue( TypeWrapper< Value >( PoisonValue() ) ) ) );
////////////////////////////
// MkValue overloads
////////////////////////////
auto MkValue = CreateOverloadSet( e, "MkValue"_sid );
RegisterBuiltinFunc< TypeWrapper< Value >( TypeWrapper< Term >, TypeWrapper< Term > ) >( e,
MkValue, []( const auto& type, const auto& val ) -> TypeWrapper< Value >
{ return Value( type.get(), val.get() ); } );
RegisterBuiltinFunc< TypeWrapper< Value >(
TypeWrapper< Term >, TypeWrapper< ptr< InstrSeq > > ) >( e, MkValue,
[]( const auto& type, const auto& is ) -> TypeWrapper< Value >
{ return Value( type.get(), is.get() ); } );
RegisterBuiltinFunc< TypeWrapper< Value >( TypeWrapper< Term > ) >( e, MkValue,
[]( const auto& type ) -> TypeWrapper< Value >
{ return Value( type.get(), make_shared< InstrSeq >() ); } );
RegisterBuiltinFunc< TypeWrapper< Term >( TypeWrapper< Value > ) >( e, "GetValueType"_sid,
[]( const auto& val ) -> TypeWrapper< Term > { return val.get().type(); } );
RegisterBuiltinFunc< bool( TypeWrapper< Value >, TermRef< TypeWrapper< Term > > ) >( e,
"GetValueVal"_sid,
[]( const auto& val, auto& out )
{
if( !val.get().isConstant() )
return false;
out = val.get().val();
return true;
} );
RegisterBuiltinFunc< bool( TypeWrapper< Value >,
TermRef< TypeWrapper< ptr< InstrSeq > > > ) >( e, "GetValueCIR"_sid,
[]( const auto& val, auto& out )
{
if( val.get().isConstant() )
return false;
out = val.get().cir();
return true;
} );
RegisterBuiltinFunc< TypeWrapper< LocationId >( TypeWrapper< Value > ) >( e,
"GetValueLocation"_sid,
[]( const auto& val ) -> TypeWrapper< LocationId > { return val.get().locationId(); } );
RegisterBuiltinFunc< bool( TypeWrapper< Value > ) >(
e, "IsPoisonValue"_sid, []( const auto& val ) { return val.get().isPoison(); } );
RegisterBuiltinFunc< bool( TypeWrapper< Value > ) >(
e, "IsConstantValue"_sid, []( const auto& val ) { return val.get().isConstant(); } );
RegisterBuiltinFunc< bool( TypeWrapper< Value > ) >(
e, "IsTypeValue"_sid, []( const auto& val ) { return val.get().isType(); } );
RegisterBuiltinFunc< TypeWrapper< Value >(
TypeWrapper< Value >, TypeWrapper< LocationId > ) >( e, "SetValueLocation"_sid,
[]( const auto& v, const auto& loc ) -> TypeWrapper< Value >
{
Value val( v );
return val.setLocationId( loc );
} );
RegisterBuiltinFunc< TypeWrapper< Term >( TypeWrapper< Value > ) >( e, "ValueToEIR"_sid,
[]( const auto& val ) -> TypeWrapper< Term > { return ValueToEIR( val.get() ); } );
RegisterBuiltinFunc< bool( TypeWrapper< Term >, TermRef< TypeWrapper< Value > > ) >( e,
"EIRToValue"_sid,
[]( const auto& t, auto& out )
{
auto val = EIRToValue( t.get() );
if( !val )
return false;
out = move( *val );
return true;
} );
RegisterBuiltinFunc< Eager< TypeWrapper< Value > >(
CustomPattern< Value, ValuePatternT > ) >(
e, "WrapValue"_sid, []( const auto& v ) -> TypeWrapper< Value > { return v; } );
}
} // namespace goose::g0api
|
Changes to bs/g0api/g0api.h.
1 2 3 4 5 6 7 8 9 10 11 |
#ifndef GOOSE_G0_API_H
#define GOOSE_G0_API_H
#include "sema/sema.h"
#include "builtins/builtins.h"
namespace goose::g0api
{
using namespace sema;
using namespace cir;
using namespace 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 |
#ifndef GOOSE_G0_API_H
#define GOOSE_G0_API_H
#include "sema/sema.h"
#include "builtins/builtins.h"
namespace goose::g0api
{
using namespace sema;
using namespace cir;
using namespace builtins;
} // namespace goose::g0api
#include "types.h"
// TODO_SSA reenable
// #include "cgapi/cgapi.h"
// #include "extensibility/extensibility.h"
namespace goose::g0api
{
extern void SetupApiString( Env& e );
extern void SetupApiCompiler( Env& e );
static inline void SetupG0Api( Env& e )
{
SetupApiString( e );
SetupApiCompiler( e );
// TODO_SSA reenable
// SetupApiCodeGen( e );
// SetupApiExtensibility( e );
}
} // namespace goose::g0api
#endif
|
Changes to bs/g0api/string.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "g0api/g0api.h"
using namespace goose;
namespace goose::g0api
{
void SetupApiString( Env& e )
{
// TODO: these doesn't really belongs here, to be moved the day I deal with
// the builtin io api
| | | | < < < | < | < < < | | | < < < | | | < | | | | | < | | | < | | | 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 |
#include "g0api/g0api.h"
using namespace goose;
namespace goose::g0api
{
void SetupApiString( Env& e )
{
// TODO: these doesn't really belongs here, to be moved the day I deal with
// the builtin io api
RegisterBuiltinFunc< void( uint8_t ) >(
e, "PrintAsciiChar"_sid, []( const uint8_t c ) { putchar( c ); } );
RegisterBuiltinFunc< void() >( e, "FlushOutput"_sid, []() { fflush( nullptr ); } );
RegisterBuiltinFunc< Eager< string >( string, string ) >(
e, "strcat"_sid, []( const string& str1, const string& str2 ) { return str1 + str2; } );
// Strip space characters from the beginning of the first string, if any, then checks
// whether the first provided string begins with the second one. This is needed for the
// proto-front end, as a way to check the commandline for the direct execution escape marker
// without using the standard library (which isn't setup at that early stage).
RegisterBuiltinFunc< Eager< bool >( string, string ) >( e, "StrStartsWith"_sid,
[]( const string& str1, const string& str2 )
{
string_view sv( str1 );
sv.remove_prefix( min( sv.find_first_not_of( " " ), sv.size() ) );
return sv.starts_with( str2 );
} );
// Checks whether the first provided string ends with the second one.
// Needed for early front end code.
RegisterBuiltinFunc< Eager< bool >( string, string ) >( e, "StrEndsWith"_sid,
[]( const string& str1, const string& str2 )
{
string_view sv( str1 );
return sv.ends_with( str2 );
} );
// Strip space characters from the beginning of the string, then remove everything from the
// provided string until the first space, then trim all the spaces until the next non-space
// character. This is needed for the proto-front end, as a way to strip the direct execution
// escape marker without using the standard library (which isn't setup at that early stage).
RegisterBuiltinFunc< Eager< string >( string ) >( e, "StrStripFirstSubStr"_sid,
[]( const string& str )
{
string_view sv( str );
sv.remove_prefix( min( sv.find_first_not_of( " " ), sv.size() ) );
sv.remove_prefix( min( sv.find_first_of( " " ), sv.size() ) );
sv.remove_prefix( min( sv.find_first_not_of( " " ), sv.size() ) );
return string( sv.begin(), sv.end() );
} );
}
} // namespace goose::g0api
|
Changes to bs/g0api/types.cpp.
1 2 3 4 5 6 7 8 9 |
#include "g0api/g0api.h"
#include "eir/eir.h"
using namespace goose;
using namespace goose::g0api;
namespace
{
template< typename T >
| | > > | > | | | | | < | < | < | < | | | 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 |
#include "g0api/g0api.h"
#include "eir/eir.h"
using namespace goose;
using namespace goose::g0api;
namespace
{
template< typename T >
void SetupWrapperForType( Env& e, StringId name, const ptr< OverloadSet >& pEquals,
const ptr< OverloadSet >& pNotEquals )
{
DefineConstant( e, name, GetValueType< TypeWrapper< T > >() );
using reftype =
CustomPattern< Value, ReferenceType::PatternMutableOfType< TypeWrapper< T > > >;
auto type = ValueToEIR( ToValue(
builtins::ReferenceType( GetValueType< TypeWrapper< T > >(), MutAccessSpecifier() ) ) );
RegisterBuiltinFunc< Intrinsic< Value( reftype ) > >( e, e.extInitialize(),
[]( auto&& c, const Value& r )
{
// Do nothing: the types we're wrapping are default initialized anyway.
// The only purpose of this Initialize overload is to let goose know that
// the type has a default initialization.
return Value( GetValueType< void >(), 0U );
} );
using constreftype = CustomPattern< TypeWrapper< T >,
ReferenceType::PatternConstOfType< TypeWrapper< T > > >;
RegisterBuiltinFunc< bool( constreftype, constreftype ) >( e, pEquals,
[]( const TypeWrapper< T >& lhs, const TypeWrapper< T >& rhs )
{ return lhs.get() == rhs.get(); } );
RegisterBuiltinFunc< bool( constreftype, constreftype ) >( e, pNotEquals,
[]( const TypeWrapper< T >& lhs, const TypeWrapper< T >& rhs )
{ return lhs.get() != rhs.get(); } );
RegisterBuiltinFunc< string( TypeWrapper< T > ) >( e, "ToString"_sid,
[]( const TypeWrapper< T >& t )
{
stringstream sstr;
sstr << t.get();
return sstr.str();
} );
}
} // namespace
namespace goose::g0api
{
void SetupTypeWrappers( Env& e )
{
auto pEquals = GetOverloadSet( e, "operator_equals"_sid );
auto pNotEquals = GetOverloadSet( e, "operator_not_equals"_sid );
|
| ︙ | ︙ | |||
85 86 87 88 89 90 91 |
SetupWrapperForType< ptr< Terminator > >( e, "Terminator"_sid, pEquals, pNotEquals );
////////////////////////////
// Context
////////////////////////////
SetupWrapperForType< ptr< Context > >( e, "Context"_sid, pEquals, pNotEquals );
| | > | | < < | | | > > | | > < | < | > > | < | < | > | < > | < > | > | > | | < | < | > < | < | > < | < | | | < < < | | < < < | > | | < < | | | | 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 |
SetupWrapperForType< ptr< Terminator > >( e, "Terminator"_sid, pEquals, pNotEquals );
////////////////////////////
// Context
////////////////////////////
SetupWrapperForType< ptr< Context > >( e, "Context"_sid, pEquals, pNotEquals );
RegisterBuiltinFunc< TypeWrapper< Value >( TypeWrapper< ptr< Context > > ) >( e,
"ContextGetBuilder"_sid,
[]( const TypeWrapper< ptr< Context > >& c ) { return c->builder(); } );
RegisterBuiltinFunc< bool( TypeWrapper< ptr< Context > >,
TermRef< TypeWrapper< ptr< CFG > > > ) >( e, "ContextGetCFG"_sid,
[]( const TypeWrapper< ptr< Context > >& c, TermRef< TypeWrapper< ptr< CFG > > >& out )
{
auto cfg = GetCFG( *c.get() );
if( !cfg )
return false;
out = cfg;
return true;
} );
SetupWrapperForType< ptr< VisibilityScope > >(
e, "VisibilityScope"_sid, pEquals, pNotEquals );
SetupWrapperForType< ptr< Scope > >( e, "Scope"_sid, pEquals, pNotEquals );
////////////////////////////
// ReferenceType
////////////////////////////
SetupWrapperForType< ptr< ReferenceType > >( e, "RefTypeDesc"_sid, pEquals, pNotEquals );
RegisterBuiltinFunc< bool( TypeWrapper< Term >,
TermRef< TypeWrapper< ptr< ReferenceType > > > ) >( e, "EIRToRefTypeDesc"_sid,
[]( const TypeWrapper< Term >& t, TermRef< TypeWrapper< ptr< ReferenceType > > >& out )
{
auto val = EIRToValue( t.get() );
if( !val )
return false;
auto refType = FromValue< ReferenceType >( *val );
if( !refType )
return false;
out = make_shared< ReferenceType >( move( *refType ) );
return true;
} );
RegisterBuiltinFunc< TypeWrapper< Term >( TypeWrapper< ptr< ReferenceType > > ) >( e,
"RefTypeDescToEIR"_sid,
[]( const TypeWrapper< ptr< ReferenceType > >& rt ) -> TypeWrapper< Term >
{ return ValueToEIR( ToValue( *rt.get() ) ); } );
RegisterBuiltinFunc< TypeWrapper< ptr< ReferenceType > >(
TypeWrapper< Term >, TypeWrapper< Term > ) >( e, "MkRefTypeDesc"_sid,
[]( const TypeWrapper< Term >& type,
const TypeWrapper< Term >& bhv ) -> TypeWrapper< ptr< ReferenceType > >
{ return make_shared< ReferenceType >( type, bhv ); } );
RegisterBuiltinFunc< tuple< TypeWrapper< Term >, TypeWrapper< Term > >(
TypeWrapper< ptr< ReferenceType > > ) >( e, "UnpackRefTypeDesc"_sid,
[]( const TypeWrapper< ptr< ReferenceType > >& refType )
-> tuple< TypeWrapper< Term >, TypeWrapper< Term > >
{ return { refType->type(), refType->accessSpec() }; } );
RegisterBuiltinFunc<
Intrinsic< Address( CustomPattern< Value, ReferenceType::PatternAny > ) > >( e,
"RefToAddr"_sid,
[]( auto&& c, Value ref )
{
ref.type() = GetValueType< Address >();
return ref;
} );
////////////////////////////
// AccessSpecifier
////////////////////////////
RegisterBuiltinFunc< bool( TypeWrapper< Term >, TermRef< AccessSpecifier > ) >( e,
"EIRToAccessSpecifier"_sid,
[]( const TypeWrapper< Term >& t, TermRef< AccessSpecifier >& out )
{
auto val = EIRToValue( t.get() );
if( !val )
return false;
auto as = FromValue< AccessSpecifier >( *val );
if( !as )
return false;
out = move( *as );
return true;
} );
RegisterBuiltinFunc< TypeWrapper< Term >( AccessSpecifier ) >( e,
"AccessSpecifierToEIR"_sid, []( const AccessSpecifier& as ) -> TypeWrapper< Term >
{ return ValueToEIR( ToValue( as ) ); } );
RegisterBuiltinFunc< AccessSpecifier( TypeWrapper< Term >, TypeWrapper< Term > ) >( e,
"MkAccessSpecifier"_sid,
[]( const TypeWrapper< Term >& mode, const TypeWrapper< Term >& lifetime )
{ return AccessSpecifier( mode, lifetime ); } );
RegisterBuiltinFunc< tuple< TypeWrapper< Term >, TypeWrapper< Term > >( AccessSpecifier ) >(
e, "UnpackAccessSpecifier"_sid,
[]( const AccessSpecifier& as ) -> tuple< TypeWrapper< Term >, TypeWrapper< Term > >
{ return { as.mode(), as.lifetime() }; } );
////////////////////////////
// Lifetime
////////////////////////////
RegisterBuiltinFunc< TypeWrapper< StringId >( Lifetime ) >( e, "LifetimeToStringId"_sid,
[]( const Lifetime& lt ) -> TypeWrapper< StringId > { return lt; } );
RegisterBuiltinFunc< Lifetime( TypeWrapper< StringId > ) >( e, "StringIdToLifetime"_sid,
[]( const TypeWrapper< StringId >& sid ) -> TypeWrapper< StringId > { return sid; } );
////////////////////////////
// Tuple
////////////////////////////
RegisterBuiltinFunc< BigInt( CustomPattern< Value, TuplePattern > ) >( e,
"GetTupleSize"_sid,
[]( const Value& tup ) { return BigInt::FromU32( TupleSize( tup ) ); } );
RegisterBuiltinFunc< TypeWrapper< Value >( CustomPattern< Value, TuplePattern >, BigInt ) >(
e, "GetConstantTupleElement"_sid,
[]( const auto& tup, BigInt index )
{
auto result = EIRToValue( builtins::GetConstantTupleElement( tup, index.toU64() ) );
return result ? *result : PoisonValue();
} );
////////////////////////////
// Decl stuff
////////////////////////////
SetupWrapperForType< ptr< Decl > >( e, "WrappedDecl"_sid, pEquals, pNotEquals );
SetupWrapperForType< ptr< TDecl > >( e, "WrappedTDecl"_sid, pEquals, pNotEquals );
SetupWrapperForType< ptr< TNamedDecl > >( e, "WrappedTNamedDecl"_sid, pEquals, pNotEquals );
////////////////////////////
// Parsing stuff
////////////////////////////
SetupWrapperForType< ptr< vector< TermLoc > > >( e, "Tokens"_sid, pEquals, pNotEquals );
SetupWrapperForType< ptr< parse::Resolver > >( e, "Resolver"_sid, pEquals, pNotEquals );
SetupWrapperForType< ptr< parse::Parser > >( e, "Parser"_sid, pEquals, pNotEquals );
}
} // namespace goose::g0api
|
Changes to bs/g0api/types.h.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 |
// TODO_SSA reenable
/*template<>
struct TypeWrapperTraits< ptr< codegen::Module > >
{
static auto typeId() { return "CGModule"_sid; }
};*/
| < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < | < > | 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 |
// TODO_SSA reenable
/*template<>
struct TypeWrapperTraits< ptr< codegen::Module > >
{
static auto typeId() { return "CGModule"_sid; }
};*/
template<> struct TypeWrapperTraits< LocationId >
{
static auto typeId() { return "LocationId"_sid; }
};
template<> struct TypeWrapperTraits< StringId >
{
static auto typeId() { return "StringId"_sid; }
};
template<> struct TypeWrapperTraits< Hole >
{
static auto typeId() { return "Hole"_sid; }
};
template<> struct TypeWrapperTraits< AnyTerm >
{
static auto typeId() { return "AnyTerm"_sid; }
};
template<> struct TypeWrapperTraits< VecOfLength >
{
static auto typeId() { return "VecOfLength"_sid; }
};
template<> struct TypeWrapperTraits< APSInt >
{
static auto typeId() { return "FixedInt"_sid; }
};
template<> struct TypeWrapperTraits< ptr< cir::BasicBlock > >
{
static auto typeId() { return "BasicBlock"_sid; }
};
template<> struct TypeWrapperTraits< wptr< cir::BasicBlock > >
{
static auto typeId() { return "BasicBlock"_sid; }
};
template<> struct TypeWrapperTraits< ptr< cir::Instruction > >
{
static auto typeId() { return "Instruction"_sid; }
};
template<> struct TypeWrapperTraits< ptr< cir::InstrSeq > >
{
static auto typeId() { return "InstrSeq"_sid; }
};
template<> struct TypeWrapperTraits< ptr< cir::Terminator > >
{
static auto typeId() { return "Terminator"_sid; }
};
template<> struct TypeWrapperTraits< ptr< ReferenceType > >
{
static auto typeId() { return "ReferenceType"_sid; }
};
template<> struct TypeWrapperTraits< ptr< VisibilityScope > >
{
static auto typeId() { return "VisibilityScope"_sid; }
};
template<> struct TypeWrapperTraits< ptr< Scope > >
{
static auto typeId() { return "Scope"_sid; }
};
template<> struct TypeWrapperTraits< ptr< parse::Resolver > >
{
static auto typeId() { return "Resolver"_sid; }
};
template<> struct TypeWrapperTraits< ptr< parse::Parser > >
{
static auto typeId() { return "Parser"_sid; }
};
} // namespace goose::builtins
#endif
|
Changes to bs/lex/comment.cpp.
1 2 3 4 5 | #include "lex.h" using namespace goose; using namespace goose::lex; | | < | | | < | < | < < < | < | 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 |
#include "lex.h"
using namespace goose;
using namespace goose::lex;
// TODO handle strings nested inside of comments, so that we ignore any comment marker appearing as
// part of a commented out string literal.
// Skip the current comment. We expect that the first / of the introducer was already consumed. This
// skips any combination of nested/overlapping block and line comments. In particular, comment
// blocks that start or end in the middle of a line comment are handled properly. Line comments
// aren't interrupted by the presence of an end of comment block later on the same line either.
void Lexer::skipComment()
{
uint32_t blockCommentDepth = 0;
uint32_t lastCommentLine = 0;
auto c = m_input.get();
if( c == '*' )
++blockCommentDepth;
else
lastCommentLine = m_lineNum;
auto isInComment = [&]() { return blockCommentDepth || lastCommentLine == m_lineNum; };
while( m_input.good() && isInComment() )
{
auto c = m_input.get();
if( c == '\n' )
{
|
| ︙ | ︙ |
Changes to bs/lex/intlit.cpp.
| ︙ | ︙ | |||
32 33 34 35 36 37 38 |
TermLoc Lexer::readIntegerLiteralBin( const LocationPoint& ls )
{
// skip leading zeros
while( m_input.good() )
{
auto c = m_input.peek();
| | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
TermLoc Lexer::readIntegerLiteralBin( const LocationPoint& ls )
{
// skip leading zeros
while( m_input.good() )
{
auto c = m_input.peek();
if( c != '0' )
break;
m_input.get();
}
string str;
while( m_input.good() )
|
| ︙ | ︙ | |||
89 90 91 92 93 94 95 |
{
uint32_t consumedLen = 2;
// skip leading zeros
while( m_input.good() )
{
auto c = m_input.peek();
| | | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
{
uint32_t consumedLen = 2;
// skip leading zeros
while( m_input.good() )
{
auto c = m_input.peek();
if( c != '0' )
break;
m_input.get();
++consumedLen;
}
string str;
|
| ︙ | ︙ |
Changes to bs/lex/lex.h.
1 2 3 4 5 6 7 8 9 10 11 12 |
#ifndef GOOSE_LEX_H
#define GOOSE_LEX_H
#include "util/util.h"
#include "eir/eir.h"
#include "diagnostics/diagnostics.h"
namespace goose::lex
{
using namespace util;
using namespace eir;
using namespace diagnostics;
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#ifndef GOOSE_LEX_H
#define GOOSE_LEX_H
#include "util/util.h"
#include "eir/eir.h"
#include "diagnostics/diagnostics.h"
namespace goose::lex
{
using namespace util;
using namespace eir;
using namespace diagnostics;
} // namespace goose::lex
#include "tokenprovider.h"
#include "lexer.h"
#include "vectoradapter.h"
#include "vectoradapter.inl"
|
| ︙ | ︙ |
Changes to bs/lex/lexer.cpp.
1 2 3 4 5 6 7 8 |
#include "lex.h"
using namespace goose;
using namespace goose::lex;
Lexer::Lexer( istream& input, const string& filename ) :
m_filename( Location::GetCachedFilename( filename ) ),
m_input( input )
| < > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include "lex.h"
using namespace goose;
using namespace goose::lex;
Lexer::Lexer( istream& input, const string& filename ) :
m_filename( Location::GetCachedFilename( filename ) ),
m_input( input )
{
}
void Lexer::newLine()
{
++m_lineNum;
m_lastLineBreakOffset = getCurrentPos();
}
|
| ︙ | ︙ | |||
69 70 71 72 73 74 75 |
str += c;
}
return TermLoc( StringId( str ), ls.toLoc( str.size() ) );
}
// TODO this is inefficient, put that stuff into a lookup table.
| | < | | | | | | | < | 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 |
str += c;
}
return TermLoc( StringId( str ), ls.toLoc( str.size() ) );
}
// TODO this is inefficient, put that stuff into a lookup table.
// Also maybe instead of being hardcoded in the lexer it could be configurable, so that when a new
// operator is defined it can reserve its characters here (either as a first char only, or not)
// Currently, - $ ~ and ! can only be used as the first character of an operator identifier, so that
// they can be used as the first character of a prefix operator (we don't want to make it mandatory
// to insert a space between an infix and a prefix operator).
bool Lexer::isOpFirstChar( char c )
{
static string charTable = "+*/%&|^<>=?:.,-$!~";
return charTable.find_first_of( c ) != string::npos;
}
bool Lexer::isOpChar( char c, char firstChar )
{
// $ is accepted if the identifier started with a $, because we have a $$ operator. Bit hackish,
// bleh
if( firstChar == '$' && c == '$' )
return true;
static string charTable = "+*/%&|^<>=?:.,";
return charTable.find_first_of( c ) != string::npos;
}
TermLoc Lexer::readOperatorIdentifier( const LocationPoint& ls )
{
// TODO this will need to be reworked some day to handle unicode identifiers.
string str;
auto firstChar = m_input.get();
str += firstChar;
while( m_input.good() )
|
| ︙ | ︙ | |||
120 121 122 123 124 125 126 |
return TermLoc( StringId( str ), ls.toLoc( str.size() ) );
}
TermLoc Lexer::readSemicolon( const LocationPoint& ls )
{
auto offset = getCurrentPos();
| | | | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
return TermLoc( StringId( str ), ls.toLoc( str.size() ) );
}
TermLoc Lexer::readSemicolon( const LocationPoint& ls )
{
auto offset = getCurrentPos();
// The semicolon is just an identifier, but with a special lexing rule: consecutive semicolons
// are collapsed into one.
while( m_input.good() )
{
skipSpacingIfAny();
auto c = m_input.peek();
if( c != ';' )
break;
|
| ︙ | ︙ | |||
194 195 196 197 198 199 200 |
case ';':
return readSemicolon( ls );
}
if( isOpFirstChar( c ) )
return readOperatorIdentifier( ls );
| | < | 193 194 195 196 197 198 199 200 201 202 |
case ';':
return readSemicolon( ls );
}
if( isOpFirstChar( c ) )
return readOperatorIdentifier( ls );
DiagnosticsManager::GetInstance().emitLexerErrorMessage( ls.toLoc(), "unrecognized token." );
return nullopt;
}
|
Changes to bs/lex/lexer.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_LEX_LEXER_H
#define GOOSE_LEX_LEXER_H
namespace goose::lex
{
class Lexer : public TokenProvider
{
| | | | | | | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 |
#ifndef GOOSE_LEX_LEXER_H
#define GOOSE_LEX_LEXER_H
namespace goose::lex
{
class Lexer : public TokenProvider
{
public:
Lexer( istream& input, const string& filename );
virtual bool eos() const override final;
virtual optional< TermLoc > consume() override final;
virtual optional< TermLoc > lookAhead( size_t distance = 0 ) override final;
virtual LocationId currentLocation() override final;
virtual pos_type position() const override final;
virtual void setPosition( pos_type position ) override final;
private:
LocationPoint currentLocationPoint() const;
auto getCurrentPos() const
{
// Use this instead of tellg because tellg refuses to give a correct position once
// we have reached eof (or merely gazed upon it through peek()) which is some
// fucking bullshit imo
return m_input.rdbuf()->pubseekoff( 0, ios::cur );
}
void newLine();
void skipComment();
void skipSpacingIfAny();
optional< char32_t > readCodePoint();
TermLoc readCharacterLiteral( const LocationPoint& ls );
TermLoc readStringLiteral( const LocationPoint& ls );
TermLoc readIntegerLiteral( const LocationPoint& ls );
TermLoc readIntegerLiteralBin( const LocationPoint& ls );
TermLoc readIntegerLiteralDec( const LocationPoint& ls, uint32_t consumedLen );
TermLoc readIntegerLiteralHex( const LocationPoint& ls );
TermLoc readAlphanumericIdentifier( const LocationPoint& ls );
static bool isOpFirstChar( char c );
static bool isOpChar( char c, char firstChar );
TermLoc readOperatorIdentifier( const LocationPoint& ls );
TermLoc readSemicolon( const LocationPoint& ls );
optional< TermLoc > readToken();
const string& m_filename;
istream& m_input;
using lookahead_cache_entry = tuple< TermLoc, istream::pos_type, uint32_t >;
deque< lookahead_cache_entry > m_lookAheadCache;
istream::pos_type m_lastLineBreakOffset = 0;
istream::pos_type m_commitedLastLineBreakOffset = 0;
uint32_t m_lineNum = 1;
uint32_t m_commitedLineNum = 1;
};
} // namespace goose::lex
#endif
|
Changes to bs/lex/lookahead.cpp.
| ︙ | ︙ | |||
35 36 37 38 39 40 41 |
return get< 0 >( m_lookAheadCache[distance] );
}
LocationPoint Lexer::currentLocationPoint() const
{
auto offset = getCurrentPos();
| | < < < < < | < < < < < | > | 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 |
return get< 0 >( m_lookAheadCache[distance] );
}
LocationPoint Lexer::currentLocationPoint() const
{
auto offset = getCurrentPos();
return LocationPoint( m_filename, offset, m_lineNum, offset - m_lastLineBreakOffset + 1 );
}
LocationId Lexer::currentLocation()
{
if( auto la = lookAhead() )
return la->second;
auto offset = getCurrentPos();
return Location::Create( m_filename, offset, 1, m_lineNum, offset - m_lastLineBreakOffset + 1 );
}
TokenProvider::pos_type Lexer::position() const
{
return { getCurrentPos(), static_cast< size_t >( m_commitedLastLineBreakOffset ),
m_commitedLineNum };
}
void Lexer::setPosition( pos_type position )
{
m_lookAheadCache.clear();
auto&& [offst, lbOffst, lnum] = position;
|
| ︙ | ︙ |
Changes to bs/lex/strlit.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "lex.h"
using namespace goose;
using namespace goose::lex;
namespace
{
optional< char32_t > ReadOctalCodePoint( istream& in )
{
char32_t codePoint = 0;
| | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
#include "lex.h"
using namespace goose;
using namespace goose::lex;
namespace
{
optional< char32_t > ReadOctalCodePoint( istream& in )
{
char32_t codePoint = 0;
for( int i = 0; i < 3; ++i )
{
if( in.eof() )
return nullopt;
auto c = in.get();
if( c < '0' || c > '7' )
return nullopt;
codePoint <<= 3;
codePoint |= c - '0';
}
return codePoint;
}
optional< char32_t > ReadHexCodePoint( istream& in, int len )
{
char32_t codePoint = 0;
for( int i = 0; i < len; ++i )
{
if( in.eof() )
return nullopt;
auto c = in.get();
if( isdigit( c ) )
|
| ︙ | ︙ | |||
70 71 72 73 74 75 76 |
}
else if( codePoint <= 0xffff )
{
str += static_cast< uint8_t >( ( ( codePoint >> 12 ) & 0xf ) | 0xe0 );
codePoint <<= ( 32 - 12 );
trailingCharsCount = 2;
}
| | | | 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 |
}
else if( codePoint <= 0xffff )
{
str += static_cast< uint8_t >( ( ( codePoint >> 12 ) & 0xf ) | 0xe0 );
codePoint <<= ( 32 - 12 );
trailingCharsCount = 2;
}
else if( codePoint <= 0x10'ffff )
{
str += static_cast< uint8_t >( ( ( codePoint >> 18 ) & 7 ) | 0xf0 );
codePoint <<= ( 32 - 18 );
trailingCharsCount = 3;
}
else
return false;
while( trailingCharsCount-- )
{
str += static_cast< uint8_t >( ( ( codePoint >> ( 32 - 6 ) ) & 0x3f ) | 0x80 );
codePoint <<= 6;
}
return true;
}
} // namespace
TermLoc Lexer::readCharacterLiteral( const LocationPoint& ls )
{
m_input.get();
u32string str;
bool complete = false;
|
| ︙ | ︙ | |||
132 133 134 135 136 137 138 |
m_input.get();
// Read an utf-8 character and decode it.
int len = 0;
if( c >= 0xf8 )
{
DiagnosticsManager::GetInstance().emitLexerErrorMessage(
| | < | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
m_input.get();
// Read an utf-8 character and decode it.
int len = 0;
if( c >= 0xf8 )
{
DiagnosticsManager::GetInstance().emitLexerErrorMessage(
currentLocationPoint().toLoc( 1 ), "invalid utf-8 code unit." );
error = true;
}
else if( c >= 0xf0 )
{
len = 3;
codePoint = c & 7;
}
|
| ︙ | ︙ | |||
167 168 169 170 171 172 173 |
newLine();
break;
}
if( c >= 0xc0 && !error )
{
DiagnosticsManager::GetInstance().emitLexerErrorMessage(
| | < < < | | | | | | | 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 |
newLine();
break;
}
if( c >= 0xc0 && !error )
{
DiagnosticsManager::GetInstance().emitLexerErrorMessage(
currentLocationPoint().toLoc( 1 ), "invalid utf-8 code unit." );
error = true;
}
codePoint <<= 6;
codePoint |= c & 0x3f;
}
if( m_input.fail() )
break;
str += codePoint;
}
}
if( !complete )
{
auto loc = ls.toLoc( str.size() + 1 );
DiagnosticsManager::GetInstance().emitLexerErrorMessage(
loc, "unterminated char literal." );
return TermLoc( U' ', loc );
}
auto loc = ls.toLoc( str.size() + 2 );
// TODO currently we define ct_char as being a u32 containing one unicode codepoint.
// Maybe it would be more intuitive to instead have it be a grapheme literal that contains one
// unicode grapheme cluster, the idea being that a single thing displayed in the source should
// be able to be represented by this literal. But that would involve reading up on unicode and
// grapheme clusters and shit and implement it so let's say fuck it for the bs compiler
if( str.size() > 1 && !error )
{
auto loc = ls.toLoc( str.size() + 1 );
DiagnosticsManager::GetInstance().emitLexerErrorMessage(
loc, "a char literal can contain only one unicode codepoint." );
}
return TermLoc( str.front(), loc );
}
TermLoc Lexer::readStringLiteral( const LocationPoint& ls )
{
|
| ︙ | ︙ | |||
246 247 248 249 250 251 252 |
if( m_input.fail() )
break;
}
if( !complete )
{
auto loc = ls.toLoc( str.size() + 1 );
| | | | < | | < | | 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 282 283 |
if( m_input.fail() )
break;
}
if( !complete )
{
auto loc = ls.toLoc( str.size() + 1 );
DiagnosticsManager::GetInstance().emitLexerErrorMessage(
loc, "unterminated string literal." );
return TermLoc( str, loc );
}
return TermLoc( str, ls.toLoc( str.size() + 2 ) );
}
optional< char32_t > Lexer::readCodePoint()
{
auto escLS = currentLocationPoint();
auto c = m_input.get();
// Just handle the same set of escape sequences as C++ for now, I cba having a profound and
// detailed reflection about fucking escape characters right now.
if( c != '\\' )
return c;
c = m_input.peek();
if( isdigit( c ) )
{
auto codePoint = ReadOctalCodePoint( m_input );
if( !codePoint )
{
DiagnosticsManager::GetInstance().emitLexerErrorMessage(
escLS.toLoc( 4 ), "invalid octal character code." );
}
return codePoint;
}
else
{
m_input.get();
|
| ︙ | ︙ | |||
319 320 321 322 323 324 325 |
case 'x':
{
auto codePoint = ReadHexCodePoint( m_input, 2 );
if( !codePoint )
{
DiagnosticsManager::GetInstance().emitLexerErrorMessage(
| < | < | | < | | < | < | 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 |
case 'x':
{
auto codePoint = ReadHexCodePoint( m_input, 2 );
if( !codePoint )
{
DiagnosticsManager::GetInstance().emitLexerErrorMessage(
escLS.toLoc( 4 ), "invalid hex character code." );
}
return codePoint;
}
break;
case 'u':
{
auto codePoint = ReadHexCodePoint( m_input, 4 );
if( !codePoint )
{
DiagnosticsManager::GetInstance().emitLexerErrorMessage(
escLS.toLoc( 6 ), "invalid hex character code." );
}
return codePoint;
}
break;
case 'U':
{
auto codePoint = ReadHexCodePoint( m_input, 8 );
if( !codePoint )
{
DiagnosticsManager::GetInstance().emitLexerErrorMessage(
escLS.toLoc( 10 ), "invalid hex character code." );
}
if( codePoint > 0x10'ffff )
{
DiagnosticsManager::GetInstance().emitLexerErrorMessage(
escLS.toLoc( 10 ), "invalid code point value." );
}
return codePoint;
}
break;
default:
DiagnosticsManager::GetInstance().emitLexerErrorMessage(
escLS.toLoc( 2 ), "invalid escape sequence." );
break;
}
}
return nullopt;
}
|
Changes to bs/lex/tokenprovider.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_LEX_TOKENPROVIDER_H
#define GOOSE_LEX_TOKENPROVIDER_H
namespace goose::lex
{
class TokenProvider
{
| | | | | | | | | | | | | 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 |
#ifndef GOOSE_LEX_TOKENPROVIDER_H
#define GOOSE_LEX_TOKENPROVIDER_H
namespace goose::lex
{
class TokenProvider
{
public:
virtual ~TokenProvider() {}
virtual bool eos() const = 0;
virtual optional< TermLoc > consume() = 0;
virtual optional< TermLoc > lookAhead( size_t distance = 0 ) = 0;
virtual LocationId currentLocation() = 0;
// For backtracking
using pos_type = tuple< size_t, size_t, uint32_t >;
virtual pos_type position() const = 0;
virtual void setPosition( pos_type position ) = 0;
};
} // namespace goose::lex
#endif
|
Changes to bs/lex/vectoradapter.h.
1 2 3 4 5 |
#ifndef GOOSE_LEX_VECTORADAPTER_H
#define GOOSE_LEX_VECTORADAPTER_H
namespace goose::lex
{
| < | | | | < | > > | | | | | | | | | | < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#ifndef GOOSE_LEX_VECTORADAPTER_H
#define GOOSE_LEX_VECTORADAPTER_H
namespace goose::lex
{
template< typename V > class VectorAdapter : public TokenProvider
{
public:
VectorAdapter( const V& v ) :
m_vector( v )
{
}
virtual bool eos() const override final;
virtual optional< TermLoc > consume() override final;
virtual optional< TermLoc > lookAhead( size_t distance = 0 ) override final;
virtual LocationId currentLocation() override final;
virtual pos_type position() const override final;
virtual void setPosition( pos_type position ) override final;
private:
const V& m_vector;
size_t m_index = 0;
};
template< typename V > auto MakeVectorAdapter( V&& vec )
{
return make_shared< VectorAdapter< V > >( forward< V >( vec ) );
}
} // namespace goose::lex
#endif
|
Changes to bs/lex/vectoradapter.inl.
1 2 3 4 5 |
#ifndef GOOSE_LEX_VECTORADAPTER_INL
#define GOOSE_LEX_VECTORADAPTER_INL
namespace goose::lex
{
| | < < | < | < | < | < | | | 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 |
#ifndef GOOSE_LEX_VECTORADAPTER_INL
#define GOOSE_LEX_VECTORADAPTER_INL
namespace goose::lex
{
template< typename V > bool VectorAdapter< V >::eos() const
{
return m_index >= m_vector.size();
}
template< typename V > optional< TermLoc > VectorAdapter< V >::consume()
{
if( eos() )
return nullopt;
return m_vector[m_index++];
}
template< typename V > optional< TermLoc > VectorAdapter< V >::lookAhead( size_t distance )
{
if( ( m_index + distance ) >= m_vector.size() )
return nullopt;
return m_vector[m_index + distance];
}
// This is a bit approximative, might have to figure out a way to improve this.
// Especially since almost all non top-level code is parsed through a vector adapter.
template< typename V > LocationId VectorAdapter< V >::currentLocation()
{
if( m_vector.empty() )
return {};
if( eos() )
return m_vector.back().second;
return lookAhead()->second;
}
template< typename V > TokenProvider::pos_type VectorAdapter< V >::position() const
{
return { m_index, 0, 0 };
}
template< typename V > void VectorAdapter< V >::setPosition( pos_type position )
{
m_index = get< 0 >( position );
}
} // namespace goose::lex
#endif
|
Changes to bs/parse/bracketblock.cpp.
| ︙ | ︙ | |||
36 37 38 39 40 41 42 |
{
if( m_lastValue )
{
// Maybe a bit hackish: if there's currently a left value,
// this bracket block is part of an expression (ie "requires" and such)
// and should be parsed as a proposition list.
auto [props, loc] = tokenizeBracketBlock();
| | < | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
{
if( m_lastValue )
{
// Maybe a bit hackish: if there's currently a left value,
// this bracket block is part of an expression (ie "requires" and such)
// and should be parsed as a proposition list.
auto [props, loc] = tokenizeBracketBlock();
return ToValue( TypeWrapper< ptr< Propositions > >( props ) ).setLocationId( loc );
}
auto cfg = GetCFG( context() );
if( !cfg )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
resolver()->currentLocation(), "ghost code blocks aren't allowed here.", 0 );
|
| ︙ | ︙ | |||
123 124 125 126 127 128 129 |
if( preds )
preds = make_shared< Propositions >( *preds );
else
{
preds = make_shared< Propositions >();
auto& c = context();
| | | > | | > | | 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 |
if( preds )
preds = make_shared< Propositions >( *preds );
else
{
preds = make_shared< Propositions >();
auto& c = context();
// Create a new identity for the predicates, which imports everything from the parent
// identity and also makes @val visible as a placeholder for the type's value.
preds->setIdentity( VEC( Env::NewUniqueId() ) );
c.env()->addVisibilityRule( c.identity(), preds->identity() );
auto name = "@val"_sid;
auto typeTerm = ValueToEIR( type );
auto phVal =
ValueToEIR( BuildComputedValue( typeTerm, cir::Placeholder( typeTerm, name, {} ) ) );
auto valuePlaceholderIdentity = AppendToVectorTerm( preds->identity(), TERM( name ) );
c.env()->storeValue( valuePlaceholderIdentity, ANYTERM( _ ), phVal );
// The placeholder is also available as the default lhs value, so "@val" can be omitted in
// many cases
auto defLhsIdentity = AppendToVectorTerm( preds->identity(), TSID( 0_def_lhs ) );
c.env()->storeValue( defLhsIdentity, ANYTERM( _ ), move( phVal ) );
}
auto [props, loc] = tokenizeBracketBlock();
preds->append( move( *props ) );
loc = Location::CreateSpanningLocation( type.locationId(), loc );
pushValue( Value( type.type(), InjectPredicatesIntoStdType( type.val(), preds ) )
.setLocationId( loc ) );
return true;
}
|
Changes to bs/parse/func.cpp.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 |
{
if( !parseFuncType( returnType, paramsDecl ) )
return false;
if( IsTExpr( *peekLastValue() ) )
return parseTemplateFunctionExpression( paramsDecl );
| | > > | | | | | | > | 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 |
{
if( !parseFuncType( returnType, paramsDecl ) )
return false;
if( IsTExpr( *peekLastValue() ) )
return parseTemplateFunctionExpression( paramsDecl );
// Check if a brace block follows, in which case this is a function declaration. Otherwise, it's
// just a function type.
auto next = m_resolver->lookAheadUnresolved();
if( !next )
{
// Just leave the type as is, nothing to do.
return true;
}
auto decomp = Decompose( next->first, Val< Delimiter >() );
if( !decomp || *decomp != Delimiter::OpenBrace )
{
// Just leave the type as is, nothing to do.
return true;
}
return parseFunctionDeclExpr( paramsDecl );
}
bool Parser::parseFunctionDeclExpr( const Value& paramsDecl )
{
auto loc =
Location::CreateSpanningLocation( peekLastValue()->locationId(), paramsDecl.locationId() );
auto funcType = *FromValue< FuncType >( *popValue() );
auto pBody = getFuncBody();
if( !pBody )
return false;
const auto& c = context();
// TODO: see if we can replace this unique id with something that is just unique within the
// current function. This would avoid wild changes in the mangled names based on unrelated
// definitions being added elsewhere.
auto identity = DuplicateVectorTerm( c.identity() );
get< pvec >( identity )->terms().back() = TERM( StringId( Env::NewUniqueId() ) );
auto bodyContext = context();
auto func = ToValue( BuildFunc( context(), funcType, c.identity(), identity, paramsDecl,
move( pBody ), bodyContext ) )
.setLocationId( loc );
DiagnosticsContext dc( 0, true );
VerbosityContext( Verbosity::Normal, true );
auto newFunc = CompileFunc( c, func );
pushValue( move( newFunc ) );
return true;
|
| ︙ | ︙ | |||
76 77 78 79 80 81 82 |
get< pvec >( funcIdentity )->terms().back() = TERM( d->name() );
// If we're here, this is not an overload, but the first time we come accross this function.
// So create a new overload set. The case of actually overloading an existing function is parsed
// elsewhere.
auto pOvlSet = make_shared< OverloadSet >( funcIdentity );
| | < | > > | | 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 |
get< pvec >( funcIdentity )->terms().back() = TERM( d->name() );
// If we're here, this is not an overload, but the first time we come accross this function.
// So create a new overload set. The case of actually overloading an existing function is parsed
// elsewhere.
auto pOvlSet = make_shared< OverloadSet >( funcIdentity );
c.env()->storeValue( funcIdentity, ANYTERM( _ ), ValueToEIR( ToValue( pOvlSet ) ) );
auto func = parseFunctionDeclaration( funcIdentity, paramsDecl, pOvlSet );
if( !func )
return false;
pushValue( move( *func ) );
return true;
}
optional< Value > Parser::parseFunctionDeclaration(
const Term& identity, const Value& paramsDecl, const ptr< sema::OverloadSet >& pOvlSet )
{
if( !peekLastValue() )
return nullopt;
auto& c = context();
if( IsTExpr( *peekLastValue() ) )
return parseTemplateFunctionDeclaration( c.identity(), identity, paramsDecl, pOvlSet );
auto loc =
Location::CreateSpanningLocation( peekLastValue()->locationId(), paramsDecl.locationId() );
auto funcType = *FromValue< FuncType >( *popValue() );
auto bodyContext = c;
auto func = BuildFunc( c, funcType, c.identity(), identity, paramsDecl, nullptr, bodyContext );
return ToValue( FuncDecl( func, pOvlSet ) ).setLocationId( loc );
}
|
| ︙ | ︙ |
Changes to bs/parse/funcdecl.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "parse.h"
#include "builtins/builtins.h"
#include "precedence.h"
using namespace goose;
using namespace goose::parse;
using namespace goose::builtins;
void parse::SetupFuncDeclDropValue( Env& 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 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 |
#include "parse.h"
#include "builtins/builtins.h"
#include "precedence.h"
using namespace goose;
using namespace goose::parse;
using namespace goose::builtins;
void parse::SetupFuncDeclDropValue( Env& e )
{
RegisterBuiltinFunc< Intrinsic< void( Value, FuncDecl ) > >( e, e.extDropValue(),
[]( const Context& c, const Value& b, const Value& v )
{
auto fd = *FromValue< FuncDecl >( v );
if( fd.func().type().kind() == FuncType::Kind::Ghost )
{
// Complain if the overload set isn't empty and isn't ghost
if( fd.ovlSet()->empty() )
fd.ovlSet()->setGhost( true );
else
{
DiagnosticsManager::GetInstance().emitErrorMessage( v.locationId(),
"a function can't have both ghost and non-ghost overloads." );
return;
}
}
else if( !fd.func().tokens() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
v.locationId(), "expected body after function declaration." );
return;
}
if( !fd.ovlSet()->add(
*c.env(), ToValue( fd.func() ).setLocationId( v.locationId() ) ) )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage(
v.locationId(), "Duplicate function overload." );
}
} );
}
namespace goose::eir
{
const Term& Bridge< parse::FuncDecl >::Type()
{
static auto type =
ValueToEIR( Value( TypeType(), VEC( TSID( ct_type ), TSID( func_decl ) ) ) );
return type;
}
Value Bridge< parse::FuncDecl >::ToValue( const parse::FuncDecl& fd )
{
return Value( Type(),
VEC( TERM( static_pointer_cast< void >( fd.ovlSet() ) ),
ValueToEIR( ::ToValue( fd.func() ) ) ) );
}
optional< parse::FuncDecl > Bridge< parse::FuncDecl >::FromValue( const Value& v )
{
if( v.type() != Type() )
return nullopt;
auto decomp = Decompose( v.val(), Vec( Val< ptr< void > >(), SubTerm() ) );
assert( decomp );
auto&& [pOvlSet, func] = *decomp;
return FuncDecl{ *FromValue< Func >( *EIRToValue( func ) ),
static_pointer_cast< sema::OverloadSet >( pOvlSet ) };
}
} // namespace goose::eir
|
Changes to bs/parse/funcdecl.h.
1 2 3 4 5 6 7 8 9 10 11 |
#ifndef GOOSE_PARSE_FUNCDECL_H
#define GOOSE_PARSE_FUNCDECL_H
// TODO: make a TFuncDecl version of this
#include "parse.h"
namespace goose::parse
{
extern void SetupFuncDeclDropValue( sema::Env& 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 38 39 40 41 42 43 44 45 46 47 48 |
#ifndef GOOSE_PARSE_FUNCDECL_H
#define GOOSE_PARSE_FUNCDECL_H
// TODO: make a TFuncDecl version of this
#include "parse.h"
namespace goose::parse
{
extern void SetupFuncDeclDropValue( sema::Env& e );
// Describes a function declaration. It can be augmented with a function body if it expects one
// (ie its not a ghost function) and we encounter an infix brace block, with a func decl as the
// current left value. This allows function qualifier operators (such as "ghost") to modify the
// decl before we deal with affixing a body (which can be influenced by said operator)
class FuncDecl
{
public:
template< typename F, typename O >
FuncDecl( F&& func, O&& pOvlSet ) :
m_func( forward< F >( func ) ),
m_pOvlSet( forward< O >( pOvlSet ) )
{
}
const auto& func() const { return m_func; }
auto& func() { return m_func; }
const auto& ovlSet() const { return m_pOvlSet; }
private:
builtins::Func m_func;
ptr< sema::OverloadSet > m_pOvlSet;
};
} // namespace goose::parse
namespace goose::eir
{
template<> struct Bridge< parse::FuncDecl >
{
static const Term& Type();
static Value ToValue( const parse::FuncDecl& fd );
static optional< parse::FuncDecl > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/parse/functype.cpp.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
optional< Term > verificationIdentity;
// Check if the params are valid, and whether they might actually be template params.
switch( CheckParamListKind( paramsDecl ) )
{
case ParamListKind::Invalid:
| | | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
optional< Term > verificationIdentity;
// Check if the params are valid, and whether they might actually be template params.
switch( CheckParamListKind( paramsDecl ) )
{
case ParamListKind::Invalid:
DiagnosticsManager::GetInstance().emitErrorMessage(
paramsDecl.locationId(), "invalid parameter list." );
return false;
case ParamListKind::Template:
pushValue( ToValue( BuildTFuncType( returnType, paramsDecl ) ).setLocationId( loc ) );
break;
case ParamListKind::Regular:
|
| ︙ | ︙ |
Changes to bs/parse/overload.cpp.
| ︙ | ︙ | |||
32 33 34 35 36 37 38 |
return false;
auto& c = context();
optional< Value > func;
if( IsTExpr( *peekLastValue() ) )
| | > | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
return false;
auto& c = context();
optional< Value > func;
if( IsTExpr( *peekLastValue() ) )
func = parseTemplateFunctionDeclaration(
c.identity(), pOvlSet->identity(), paramsDecl, pOvlSet );
else
func = parseFunctionDeclaration( pOvlSet->identity(), paramsDecl, pOvlSet );
if( !func )
return false;
pushValue( *func );
|
| ︙ | ︙ |
Changes to bs/parse/parenblock.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "parse.h"
#include "builtins/builtins.h"
#include "precedence.h"
using namespace goose;
using namespace goose::parse;
using namespace goose::builtins;
Value Parser::parseParenBlock()
{
| | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include "parse.h"
#include "builtins/builtins.h"
#include "precedence.h"
using namespace goose;
using namespace goose::parse;
using namespace goose::builtins;
Value Parser::parseParenBlock()
{
// Create a nested verbosity context to restrict the scope of error silencing caused by syntax
// errors to the block.
VerbosityContext vc( Verbosity::Normal );
auto tok = m_resolver->consume();
auto p = makeNestedParser( Delimiter::OpenParen );
p.parseExpression();
auto content = p.popValue();
|
| ︙ | ︙ | |||
40 41 42 43 44 45 46 |
if( !content )
return Value( EmptyClosedTuple() ).setLocationId( loc );
// If the content is an open tuple, turn it into a closed (parenthesized) tuple.
if( IsOpenTuple( *content ) )
return CloseTuple( *content ).setLocationId( loc );
| | | | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
if( !content )
return Value( EmptyClosedTuple() ).setLocationId( loc );
// If the content is an open tuple, turn it into a closed (parenthesized) tuple.
if( IsOpenTuple( *content ) )
return CloseTuple( *content ).setLocationId( loc );
// Otherwise wrap the content into a tuple: we prefer to treat single elements enclosed in
// parentheses as tuples for consistency. A type checking rule takes care of peeling off single
// element tuples where applicable.
auto tup = EmptyClosedTuple();
return AppendToTuple( tup, *content ).setLocationId( loc );
}
// If the left value is invocable or is a type, the postfix paren block is a call or
// a function type expression and have application precedence.
// Otherwise, it is an implicit separator and have separator precedence.
|
| ︙ | ︙ |
Changes to bs/parse/parse.h.
1 2 3 4 5 6 7 | #ifndef GOOSE_PARSE_H #define GOOSE_PARSE_H #include "cir/cir.h" #include "lex/lex.h" #include "sema/sema.h" // TODO_SSA reenable | | | | | 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 |
#ifndef GOOSE_PARSE_H
#define GOOSE_PARSE_H
#include "cir/cir.h"
#include "lex/lex.h"
#include "sema/sema.h"
// TODO_SSA reenable
// #include "verify/verify.h"
#include "execute/execute.h"
#include "diagnostics/diagnostics.h"
#include "builtins/builtins.h"
#include "precedence.h"
namespace goose::parse
{
using namespace util;
using namespace eir;
using namespace diagnostics;
} // namespace goose::parse
#include "resolver.h"
#include "parser.h"
#include "rule.h"
#include "rule-helpers.h"
#include "funcdecl.h"
#include "tfuncdecl.h"
#include "parser.inl"
#include "rule-helpers.inl"
namespace goose::parse
{
static inline void SetupParsingBuiltins( sema::Env& e )
{
SetupFuncDeclDropValue( e );
SetupTFuncDeclDropValue( e );
}
} // namespace goose::parse
#endif
|
Changes to bs/parse/parser.cpp.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 |
return p;
}
// Parse a sequence of expression. Each expression is
// a statement.
void Parser::parseSequence()
{
| | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
return p;
}
// Parse a sequence of expression. Each expression is
// a statement.
void Parser::parseSequence()
{
for( ;; )
{
LifetimeScopeGuard lsg( context() );
{
// Each statement gets its own visibility scope,
// so that vars defined inside of the statement are only
// visible from within it.
|
| ︙ | ︙ | |||
75 76 77 78 79 80 81 |
break;
}
}
void Parser::flushValue()
{
auto cfg = GetCFG( context() );
| | | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
break;
}
}
void Parser::flushValue()
{
auto cfg = GetCFG( context() );
if( m_lastValue && cfg && ( !cfg->currentBB() || cfg->currentBB()->terminator() ) )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
m_lastValue->locationId(), "unreachable code.", 0 );
}
// Flush the pending value, by invoking the DropValue
// extension point, where an overload will decide
|
| ︙ | ︙ | |||
101 102 103 104 105 106 107 |
InvokeOverloadSet( context(), context().env()->extDropValue(),
MakeClosedTuple( context().builder(), val ), val.locationId() );
}
}
optional< uint32_t > Parser::getPrecedence( const Term& t )
{
| | < < < | < < < | < < < | 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 |
InvokeOverloadSet( context(), context().env()->extDropValue(),
MakeClosedTuple( context().builder(), val ), val.locationId() );
}
}
optional< uint32_t > Parser::getPrecedence( const Term& t )
{
return visit( [&]( auto&& content ) { return getPrecedence( t, content ); }, t );
}
bool Parser::parsePrefix( const Term& t, uint32_t precedence )
{
return visit( [&]( auto&& content ) { return parsePrefix( content, precedence ); }, t );
}
bool Parser::parseInfix( const Term& t, uint32_t precedence )
{
return visit( [&]( auto&& content ) { return parseInfix( content, precedence ); }, t );
}
bool Parser::parsePrefix( const BigInt& intlit, uint32_t )
{
auto tok = m_resolver->consume();
pushValue( ToValue( intlit ).setLocationId( tok->second ) );
return true;
|
| ︙ | ︙ | |||
144 145 146 147 148 149 150 |
bool Parser::parsePrefix( const string& strlit, uint32_t )
{
auto tok = m_resolver->consume();
pushValue( ToValue( strlit ).setLocationId( tok->second ) );
return true;
}
| | | | | < | | | | | | 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 |
bool Parser::parsePrefix( const string& strlit, uint32_t )
{
auto tok = m_resolver->consume();
pushValue( ToValue( strlit ).setLocationId( tok->second ) );
return true;
}
// Standalone (ie not part of a grammar construct that expects them such as a decl), unresolved
// identifiers end up here.
bool Parser::parsePrefix( StringId strid, uint32_t )
{
auto tok = m_resolver->consume();
pushValue( ToValue( strid ).setLocationId( tok->second ) );
return true;
}
// Infix identifiers: if the left value is a type, then we have a decl.
optional< uint32_t > Parser::getPrecedence( const Term&, StringId strid )
{
const auto& leftVal = peekLastValue();
if( !leftVal )
return nullopt;
// If leftVal is a type, this is a decl, so set the precedence to Application.
if( IsTExpr( *leftVal ) || IsType( context(), *leftVal ) )
{
// If it is a regular decl (such as function parameters and variable declarations),
// it have the "Decl" precedence.
// However, if the identifier is followed by a parent block, this is a function declaration,
// and it gets the "FuncDecl" precedence, which is set to be lower than the comma operator.
//
// The reason for this is that while we want to construct tuples of about anything just
// by separating things with commas (not only to build function parameters and function
// arguments but also for multiple return values and multiple variable declarations), we
// also want to be able to use a tuple of those things as the return type for a function, so
// the construction of the tuple (the comma operator) needs a higher precedence than the
// function declaration. In other words, we don't want the last type of a tuple of type to
// become the return type of a function subsequently added to the tuple.
auto next = m_resolver->lookAheadUnresolved( 1 );
if( !next )
return precedence::Decl;
auto decomp = Decompose( next->first, Val< Delimiter >() );
|
| ︙ | ︙ | |||
266 267 268 269 270 271 272 |
return nullopt;
if( val->type() == GetValueType< ptr< OverloadSet > >() )
{
if( !peekLastValue() )
return nullopt;
| | > | 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
return nullopt;
if( val->type() == GetValueType< ptr< OverloadSet > >() )
{
if( !peekLastValue() )
return nullopt;
if( ( !IsTExpr( *peekLastValue() ) || IsTFunc( *peekLastValue() ) )
&& !IsType( context(), *peekLastValue() ) )
return nullopt;
return getInfixOverloadSetPrecedence();
}
// If the term is an infix rule value, invoke its getPrecedence() function.
auto rule = FromValue< Rule >( *val );
|
| ︙ | ︙ | |||
309 310 311 312 313 314 315 |
auto rule = FromValue< Rule >( *val );
if( !rule )
return false;
if( !( *rule )->isInfix() )
return false;
| | < | | | 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 |
auto rule = FromValue< Rule >( *val );
if( !rule )
return false;
if( !( *rule )->isInfix() )
return false;
// If an infix parsing rule fails to parse, attempt to backtrack, so we can try to parse it
// again as a prefix rule if it supports it (case of infix operators that may act as prefix
// operators using a default lhs value in some contexts)
auto savedPosition = m_resolver->position();
auto savedLastVal = m_lastValue;
m_resolver->consume();
if( ( *rule )->parseInfix( *this, t.second, prec ) )
return true;
|
| ︙ | ︙ | |||
335 336 337 338 339 340 341 |
if( !m_lastValue )
return nullopt;
auto typeVal = *popValue();
if( !typeVal.isConstant() )
{
| | | | 325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
if( !m_lastValue )
return nullopt;
auto typeVal = *popValue();
if( !typeVal.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
typeVal.locationId(), "this type expression can't be evaluated here.", 0 );
return PoisonType();
}
return typeVal;
}
|
Changes to bs/parse/parser.h.
1 2 3 4 5 |
#ifndef GOOSE_PARSE_PARSER_H
#define GOOSE_PARSE_PARSER_H
namespace goose::parse
{
| | | | < | | | | | | < | > > | | | < | < > | | | < | | | | | > | | > | | | | | | | < | < < < | | | | | | | | | | | | | | | | | | | < | | < | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | > | | | | | | < | < | | | | | | 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 |
#ifndef GOOSE_PARSE_PARSER_H
#define GOOSE_PARSE_PARSER_H
namespace goose::parse
{
// This is a modified Pratt parser. Instead of directly operating on the input token stream, we
// first attempt to resolve identifiers according to the current semantic context. Keywords and
// operator names are resolved to pratt parsing rules, which are then applied directly. This
// provide a powerful way to externally extend the syntax (for instance to implement custom
// statements) and to decide which keywords and operators should be in scope depending on which
// feature sets of the language we want to use.
class Parser
{
public:
Parser( const ptr< Resolver >& r ) :
m_resolver( r )
{
}
Parser( const ptr< Resolver >& r, Delimiter introDelimiter ) :
m_resolver( r ),
m_introDelimiter( introDelimiter )
{
}
~Parser() { flushValue(); }
// Build a parser for a nested parsing context that needs to share everything
// except the current sequence and value.
Parser makeNestedParser();
Parser makeNestedParser( Delimiter introDelimiter );
const auto& resolver() const { return m_resolver; }
auto& resolver() { return m_resolver; }
const auto& context() const { return resolver()->context(); }
auto& context() { return resolver()->context(); }
void parseSequence();
bool parseExpression( uint32_t precedence = 0 );
void parsePostfixExpression( uint32_t precedence );
Value parseParenBlock();
bool parseBraceBlock();
template< typename V > void pushValue( V&& val );
optional< Value > peekLastValue() const { return m_lastValue; }
optional< Value > popValue()
{
optional< Value > result;
swap( result, m_lastValue );
return result;
}
optional< Value > popType();
void flushValue();
bool isInParenExpr() const
{
return m_introDelimiter && *m_introDelimiter == Delimiter::OpenParen;
}
ptr< void > getFuncBody();
tuple< ptr< builtins::Propositions >, LocationId > tokenizeBracketBlock();
private:
optional< uint32_t > getPrecedence( const Term& t );
bool parsePrefix( const Term&, uint32_t precedence );
bool parseInfix( const Term&, uint32_t precedence );
// Default getPrecedence overload
template< typename T > optional< uint32_t > getPrecedence( const Term&, const T& );
// Default parsePrefix overload
template< typename T > bool parsePrefix( const T& t, uint32_t );
// Default parseInfix overload
template< typename T > bool parseInfix( const T& t, uint32_t );
// Literals
bool parsePrefix( const BigInt& intlit, uint32_t );
bool parsePrefix( uint64_t charlit, uint32_t );
bool parsePrefix( const string& strlit, uint32_t );
// Unresolved identifiers
bool parsePrefix( StringId strid, uint32_t );
optional< uint32_t > getPrecedence( const Term&, StringId strid );
bool parseInfix( StringId strid, uint32_t );
// Vector terms (values)
optional< uint32_t > getPrecedence( const Term&, const pvec& vec );
bool parsePrefix( const pvec& vec, uint32_t prec );
bool parseInfix( const pvec& vec, uint32_t prec );
// Blocks
optional< uint32_t > getPrecedence( const Term&, const Delimiter& d );
bool parsePrefix( const Delimiter& d, uint32_t prec );
bool parseInfix( const Delimiter& d, uint32_t prec );
optional< uint32_t > getPostfixParenBlockPrecedence();
bool parsePostfixParenBlock( uint32_t prec );
Value parseParamList();
bool parseNestedBraceBlock();
optional< uint32_t > getPostfixBraceBlockPrecedence();
bool parsePostfixBraceBlock( uint32_t prec );
Value parseBracketBlock();
optional< uint32_t > getPostfixBracketBlockPrecedence();
bool parsePostfixBracketBlock( uint32_t prec );
// Function types
bool parseFuncType( const Value& returnType, const Value& paramsDecl );
bool parseTFuncType( const Value& returnType, const Value& paramsDecl );
void parseFuncTypeVerificationStatements();
// Functions
bool parseFunctionExpression( const Value& returnType, const Value& paramsDecl );
bool parseFunctionDeclExpr( const Value& paramsDecl );
bool parseFunctionDeclaration( const Value& decl, const Value& paramsDecl );
optional< Value > parseFunctionDeclaration( const Term& identity, const Value& paramsDecl,
const ptr< sema::OverloadSet >& pOvlSet );
// Template functions
bool parseTemplateFunctionExpression( const Value& returnType, const Value& paramsDecl );
bool parseTemplateFunctionExpression( const Value& paramsDecl );
bool parseTemplateFunctionTNamedDecl( const Value& tnamedDecl, const Value& paramsDecl );
Value parseTemplateFunctionDeclaration( const Term& parentIdentity, const Term& identity,
const Value& paramsDecl, const ptr< sema::OverloadSet >& pOvlSet );
// Overloading
optional< uint32_t > getInfixOverloadSetPrecedence();
bool parseInfixOverloadSet( const ptr< sema::OverloadSet >& pOvlSet, uint32_t prec );
// Resolver to fetch input terms from.
ptr< Resolver > m_resolver;
// The last value that we have built, not yet pushed onto the output sequence since it
// can be transformed by the next infix or postfix rule.
optional< Value > m_lastValue;
// The delimiter that caused the creation of this nested parser, if any.
// Used to diagnose misplaced delimiters.
optional< Delimiter > m_introDelimiter;
};
} // namespace goose::parse
#endif
|
Changes to bs/parse/parser.inl.
1 2 3 4 5 |
#ifndef GOOSE_PARSE_PARSER_INL
#define GOOSE_PARSE_PARSER_INL
namespace goose::parse
{
| | < | | | | < | < | < | | | 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 |
#ifndef GOOSE_PARSE_PARSER_INL
#define GOOSE_PARSE_PARSER_INL
namespace goose::parse
{
template< typename V > void Parser::pushValue( V&& val )
{
if( val.isPoison() )
{
if( !DiagnosticsManager::GetInstance().errorsWereEmitted() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
val.locationId(), "internal: pushing an undiagnosed poison value" );
}
DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );
builtins::PoisonBuilder( context() );
}
flushValue();
m_lastValue = forward< V >( val );
if( !context().locationId().invalid() )
m_lastValue->setLocationId( context().locationId() );
// If the value we just pushed is of void type, it was pushed only for its side effects (for
// instance a SetVar instruction), so it should be flushed immediately.
if( m_lastValue->type() == GetValueType< void >() )
flushValue();
}
template< typename T > optional< uint32_t > Parser::getPrecedence( const Term&, const T& )
{
return nullopt;
}
template< typename T > bool Parser::parsePrefix( const T&, uint32_t )
{
return false;
}
template< typename T > bool Parser::parseInfix( const T&, uint32_t prec )
{
return false;
}
} // namespace goose::parse
#endif
|
Changes to bs/parse/resolver.cpp.
1 2 3 4 5 6 7 |
#include "parse.h"
using namespace goose;
using namespace goose::parse;
void Resolver::init()
{
| | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include "parse.h"
using namespace goose;
using namespace goose::parse;
void Resolver::init()
{
m_context.onContextRestoredSignal().subscribe( [&] { clearLookAheadCache(); } );
}
bool Resolver::eos() const
{
clearCacheIfNeeded();
return m_tokProvider->eos() && m_lookAheadCache.empty();
}
|
| ︙ | ︙ | |||
193 194 195 196 197 198 199 |
co_yield consumeUnit();
}
switch( end )
{
case Delimiter::CloseParen:
| | > | > | > | 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 |
co_yield consumeUnit();
}
switch( end )
{
case Delimiter::CloseParen:
DiagnosticsManager::GetInstance().emitLexerErrorMessage(
currentLocation(), "missing ')'." );
break;
case Delimiter::CloseBrace:
DiagnosticsManager::GetInstance().emitLexerErrorMessage(
currentLocation(), "missing '}'." );
break;
case Delimiter::CloseBracket:
DiagnosticsManager::GetInstance().emitLexerErrorMessage(
currentLocation(), "missing ']'." );
break;
default:
break;
}
}
|
| ︙ | ︙ |
Changes to bs/parse/resolver.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_PARSE_RESOLVER_H
#define GOOSE_PARSE_RESOLVER_H
namespace goose::parse
{
class Resolver
{
| | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 |
#ifndef GOOSE_PARSE_RESOLVER_H
#define GOOSE_PARSE_RESOLVER_H
namespace goose::parse
{
class Resolver
{
public:
Resolver( const ptr< lex::TokenProvider >& tokProv, const sema::Context& c ) :
m_tokProvider( tokProv ),
m_context( c ),
m_valueStoreVersion( c.env()->valueStoreVersion() )
{
init();
}
Resolver( const Resolver& r, const sema::Context& c ) :
m_tokProvider( r.m_tokProvider ),
m_context( c ),
m_valueStoreVersion( c.env()->valueStoreVersion() )
{
init();
}
const auto& context() const { return m_context; }
auto& context() { return m_context; }
bool eos() const;
auto currentLocation() { return m_tokProvider->currentLocation(); }
// Consume the next token. Bound identifiers are resolved.
optional< TermLoc > consume();
// Consume the next token. No resolution is performed on identifiers.
optional< TermLoc > consumeUnresolved();
optional< TermLoc > lookAhead( size_t distance = 0 );
optional< TermLoc > lookAheadUnresolved( size_t distance = 0 );
// Consume the next unit from the provider and yield each token that it contains.
// An unit is:
// - any brace, paren or bracket block and every unit they contain
// - any other token
Generator< TermLoc > consumeUnit();
void clearLookAheadCache() const;
lex::TokenProvider::pos_type position() const;
void setPosition( lex::TokenProvider::pos_type position );
private:
void init();
TermLoc resolve( const TermLoc& t ) const;
void clearCacheIfNeeded() const
{
if( m_context.env()->valueStoreVersion() == m_valueStoreVersion )
return;
clearLookAheadCache();
}
Generator< TermLoc > consumeBlock( Delimiter end );
ptr< lex::TokenProvider > m_tokProvider;
sema::Context m_context;
mutable deque< TermLoc > m_lookAheadCache;
mutable uint64_t m_valueStoreVersion = 0;
};
} // namespace goose::parse
#endif
|
Changes to bs/parse/rule-helpers.cpp.
| ︙ | ︙ | |||
21 22 23 24 25 26 27 |
{
if( v.type() != Type() )
return nullopt;
const auto& p = get< ptr< void > >( v.val() );
return static_pointer_cast< parse::Rule >( p );
}
| | | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
{
if( v.type() != Type() )
return nullopt;
const auto& p = get< ptr< void > >( v.val() );
return static_pointer_cast< parse::Rule >( p );
}
} // namespace goose::eir
namespace goose::parse
{
void RegisterRule( sema::Env& env, const Term& identity, Rule&& rule )
{
auto ruleVal = ToValue( move( rule ) );
auto ruleTerm = ValueToEIR( ruleVal );
env.storeValue( identity, ANYTERM( _ ), ruleTerm );
}
} // namespace goose::parse
|
Changes to bs/parse/rule-helpers.h.
1 2 3 4 5 |
#ifndef GOOSE_PARSE_RULE_HELPERS_H
#define GOOSE_PARSE_RULE_HELPERS_H
namespace goose::eir
{
| < | | > | > | > | < | 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 |
#ifndef GOOSE_PARSE_RULE_HELPERS_H
#define GOOSE_PARSE_RULE_HELPERS_H
namespace goose::eir
{
template<> struct Bridge< parse::Rule >
{
static const Term& Type();
static Value ToValue( parse::Rule&& r );
static optional< ptr< parse::Rule > > FromValue( const Value& v );
};
} // namespace goose::eir
namespace goose::parse
{
void RegisterRule( sema::Env& env, const Term& identity, Rule&& rule );
template< typename... R >
void BuildParseRule( sema::Env& env, StringId name, const Term& identity, R&&... ruleBuilders )
{
Rule r;
( ( ruleBuilders( env, r, name ) ), ... );
RegisterRule( env, identity, move( r ) );
}
template< typename F >
void MakePrefixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func );
template< typename F >
void MakePostfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func );
template< typename F >
void MakeLeftAssInfixOp(
sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func );
template< typename F >
void MakeRightAssInfixOp(
sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func );
} // namespace goose::parse
#endif
|
Changes to bs/parse/rule-helpers.inl.
1 2 3 4 5 6 7 8 9 10 |
#ifndef GOOSE_PARSE_RULE_HELPERS_INL
#define GOOSE_PARSE_RULE_HELPERS_INL
// TODO do something about the horrific code duplication in there someday
namespace goose::parse
{
template< typename F >
void MakePrefixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
{
| > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | | | 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 |
#ifndef GOOSE_PARSE_RULE_HELPERS_INL
#define GOOSE_PARSE_RULE_HELPERS_INL
// TODO do something about the horrific code duplication in there someday
namespace goose::parse
{
template< typename F >
void MakePrefixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
{
r.setPrefixFunc(
[=]( Parser& p, LocationId locationId, uint32_t prec )
{
optional< Value > rightVal;
{
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence + 1 ) )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "expected an expression.", 0 );
return false;
}
rightVal = np.popValue();
if( !rightVal )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "expected an expression.", 0 );
return false;
}
}
if( rightVal->isPoison() )
{
p.pushValue( PoisonValue() );
return true;
}
DiagnosticsContext dc( locationId, false );
auto loc = Location::CreateSpanningLocation( locationId, rightVal->locationId() );
sema::Context::CurrentLocationGuard clg( p.context(), loc );
auto result = func( p, move( *rightVal ) );
p.pushValue( move( result ) );
return true;
} );
}
template< typename F >
void MakePostfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
{
r.setInfixFunc( [=]( const Parser& p ) { return precedence; },
[=]( Parser& p, LocationId locationId, uint32_t prec )
{
auto leftVal = p.popValue();
if( !leftVal )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "expected a value operand.", 0 );
return false;
}
if( leftVal->isPoison() )
{
p.pushValue( PoisonValue() );
return true;
|
| ︙ | ︙ | |||
78 79 80 81 82 83 84 |
return true;
} );
}
template< typename F >
void MakeLeftAssInfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
{
| | | | | > > | | > | | | | | | | | | | | | | | | | | | | | > | | | | | | < | | | | | | | | > | > | > | > | | | > | | > | | | | | | | | | | | | | | | | | | | | > | | | | | | < | | | | | | | | > > | | | 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 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
return true;
} );
}
template< typename F >
void MakeLeftAssInfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
{
// If there's a contextually defined default lhs value, we can parse infix operators as if
// they were prefix operators.
//
// This is so @val can be omitted in type predicates to write super terse conditions like
// int(32)[>5 <10] which is a delightful combination of neat and fucked up that's never
// going to bite me in the ass
if( !r.isPrefix() )
{
r.setPrefixFunc(
[=]( Parser& p, LocationId locationId, uint32_t prec )
{
auto defLhsVal =
p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
if( !defLhsVal )
return false;
optional< Value > rightVal;
{
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence + 1 ) )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "expected an expression.", 0 );
return false;
}
rightVal = np.popValue();
if( !rightVal )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "expected an expression.", 0 );
return false;
}
}
auto loc =
Location::CreateSpanningLocation( locationId, rightVal->locationId() );
sema::Context::CurrentLocationGuard clg( p.context(), loc );
auto result = func( p, *EIRToValue( *defLhsVal ), move( *rightVal ) );
p.pushValue( move( result ) );
return true;
} );
}
r.setInfixFunc( [=]( const Parser& p ) { return precedence; },
[=]( Parser& p, LocationId locationId, uint32_t prec )
{
auto leftVal = p.popValue();
if( !leftVal )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "expected a value operand.", 0 );
return false;
}
optional< Value > rightVal;
{
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence + 1 ) )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "expected an expression.", 0 );
return false;
}
rightVal = np.popValue();
if( !rightVal )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "expected an expression.", 0 );
return false;
}
}
if( leftVal->isPoison() || rightVal->isPoison() )
{
p.pushValue( PoisonValue() );
return true;
}
DiagnosticsContext dcl( leftVal->locationId(), false );
DiagnosticsContext dcr( rightVal->locationId(), false );
DiagnosticsContext dc( locationId, false );
auto loc = Location::CreateSpanningLocation(
leftVal->locationId(), rightVal->locationId() );
sema::Context::CurrentLocationGuard clg( p.context(), loc );
// If there is a default lhs value available in our current context, fail silently
// and put back the leftVal. The parsing will then end and drop the current
// expression (leftVal), and try parsing this again as a prefix operator with the
// default lhs value. That's not hackish at all, honest
auto defLhsVal =
p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
if( defLhsVal )
{
VerbosityContext vc( Verbosity::Silent );
auto result = func( p, *leftVal, move( *rightVal ) );
if( result.isPoison() )
return false;
p.pushValue( move( result ) );
return true;
}
auto result = func( p, move( *leftVal ), move( *rightVal ) );
p.pushValue( move( result ) );
return true;
} );
}
template< typename F >
void MakeRightAssInfixOp(
sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
{
// See the comment in MakeLeftAssInfixOp for an explanation of this fantastic "hey lets
// pretend that this infix operator is a prefix operator with a default lhs value" trick
if( !r.isPrefix() )
{
r.setPrefixFunc(
[=]( Parser& p, LocationId locationId, uint32_t prec )
{
auto defLhsVal =
p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
if( !defLhsVal )
return false;
optional< Value > rightVal;
{
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence ) )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "expected an expression.", 0 );
return false;
}
rightVal = np.popValue();
if( !rightVal )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "expected an expression.", 0 );
return false;
}
}
auto loc =
Location::CreateSpanningLocation( locationId, rightVal->locationId() );
sema::Context::CurrentLocationGuard clg( p.context(), loc );
auto result = func( p, *EIRToValue( *defLhsVal ), move( *rightVal ) );
p.pushValue( move( result ) );
return true;
} );
}
r.setInfixFunc( [=]( const Parser& p ) { return precedence; },
[=]( Parser& p, LocationId locationId, uint32_t prec )
{
auto leftVal = p.popValue();
if( !leftVal )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "expected a value operand.", 0 );
return false;
}
optional< Value > rightVal;
{
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence ) )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "expected an expression.", 0 );
return false;
}
rightVal = np.popValue();
if( !rightVal )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage(
locationId, "expected an expression.", 0 );
return false;
}
}
if( leftVal->isPoison() || rightVal->isPoison() )
{
p.pushValue( PoisonValue() );
return true;
}
DiagnosticsContext dcl( leftVal->locationId(), false );
DiagnosticsContext dcr( rightVal->locationId(), false );
DiagnosticsContext dc( locationId, false );
auto loc = Location::CreateSpanningLocation(
leftVal->locationId(), rightVal->locationId() );
sema::Context::CurrentLocationGuard clg( p.context(), loc );
auto defLhsVal =
p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
if( defLhsVal )
{
VerbosityContext vc( Verbosity::Silent );
auto result = func( p, move( *leftVal ), move( *rightVal ) );
if( result.isPoison() )
return false;
p.pushValue( move( result ) );
return true;
}
auto result = func( p, move( *leftVal ), move( *rightVal ) );
p.pushValue( move( result ) );
return true;
} );
}
} // namespace goose::parse
#endif
|
Changes to bs/parse/rule.h.
1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_PARSE_RULE_H
#define GOOSE_PARSE_RULE_H
#include "parse.h"
namespace goose::parse
{
class 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 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 |
#ifndef GOOSE_PARSE_RULE_H
#define GOOSE_PARSE_RULE_H
#include "parse.h"
namespace goose::parse
{
class Rule
{
public:
using parse_func = function< bool( Parser& p, LocationId location, uint32_t precedence ) >;
using precedence_func = function< optional< uint32_t >( const Parser& p ) >;
Rule() {}
Rule( parse_func&& prefixParseFunc ) :
m_prefixParseFunc( move( prefixParseFunc ) )
{
}
Rule( precedence_func&& precFunc, parse_func&& infixParseFunc ) :
m_infixParseFunc( move( infixParseFunc ) ),
m_precFunc( move( precFunc ) )
{
}
Rule( parse_func&& prefixParseFunc, precedence_func&& precFunc,
parse_func&& infixParseFunc ) :
m_prefixParseFunc( move( prefixParseFunc ) ),
m_infixParseFunc( move( infixParseFunc ) ),
m_precFunc( move( precFunc ) )
{
}
bool isPrefix() const { return !!m_prefixParseFunc; }
bool isInfix() const { return !!m_infixParseFunc && !!m_precFunc; }
void setPrefixFunc( parse_func&& prefixParseFunc )
{
m_prefixParseFunc = move( prefixParseFunc );
}
void setInfixFunc( precedence_func&& precFunc, parse_func&& infixParseFunc )
{
m_precFunc = move( precFunc );
m_infixParseFunc = move( infixParseFunc );
}
optional< uint32_t > getPrecedence( const Parser& p ) { return m_precFunc( p ); }
bool parsePrefix( Parser& p, LocationId locationId, uint32_t precedence )
{
return m_prefixParseFunc( p, locationId, precedence );
}
bool parseInfix( Parser& p, LocationId locationId, uint32_t precedence )
{
return m_infixParseFunc( p, locationId, precedence );
}
private:
parse_func m_prefixParseFunc;
parse_func m_infixParseFunc;
precedence_func m_precFunc;
};
} // namespace goose::parse
#endif
|
Changes to bs/parse/tfunc.cpp.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
return false;
return parseTemplateFunctionExpression( paramsDecl );
}
bool Parser::parseTemplateFunctionExpression( const Value& paramsDecl )
{
| | > | | | | < > | | 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 |
return false;
return parseTemplateFunctionExpression( paramsDecl );
}
bool Parser::parseTemplateFunctionExpression( const Value& paramsDecl )
{
// Check if a brace block follows, in which case this is a function declaration. Otherwise, it's
// just a function type.
auto next = m_resolver->lookAheadUnresolved();
if( !next )
{
// Just leave the type as is, nothing to do.
return true;
}
auto decomp = Decompose( next->first, Val< Delimiter >() );
if( !decomp || *decomp != Delimiter::OpenBrace )
{
// Just leave the type as is, nothing to do.
return true;
}
// TODO: see if we can replace this unique id with something that is just unique within the
// current function. This would avoid wild changes in the mangled names based on unrelated
// definitions being added elsewhere.
const auto& c = context();
auto tfuncIdentity = AppendToVectorTerm( c.identity(), TERM( StringId( Env::NewUniqueId() ) ) );
auto loc =
Location::CreateSpanningLocation( peekLastValue()->locationId(), paramsDecl.locationId() );
auto tFuncType = *FromValue< TFuncType >( *popValue() );
ptr< void > pBody;
if( tFuncType.kind() != FuncType::Kind::Ghost )
{
pBody = getFuncBody();
|
| ︙ | ︙ | |||
71 72 73 74 75 76 77 |
get< pvec >( tfuncIdentity )->terms().back() = TERM( d->name() );
// If we're here, this is not an overload, but the first time we come accross this function.
// So create a new overload set. The case of actually overloading an existing function is parsed
// elsewhere.
auto pOvlSet = make_shared< OverloadSet >( tfuncIdentity );
| | < > | | > > | | 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 |
get< pvec >( tfuncIdentity )->terms().back() = TERM( d->name() );
// If we're here, this is not an overload, but the first time we come accross this function.
// So create a new overload set. The case of actually overloading an existing function is parsed
// elsewhere.
auto pOvlSet = make_shared< OverloadSet >( tfuncIdentity );
c.env()->storeValue( tfuncIdentity, ANYTERM( _ ), ValueToEIR( ToValue( pOvlSet ) ) );
auto tfunc =
parseTemplateFunctionDeclaration( c.identity(), tfuncIdentity, paramsDecl, pOvlSet );
if( tfunc.isPoison() )
return true;
pushValue( move( tfunc ) );
return true;
}
Value Parser::parseTemplateFunctionDeclaration( const Term& parentIdentity, const Term& identity,
const Value& paramsDecl, const ptr< sema::OverloadSet >& pOvlSet )
{
auto loc =
Location::CreateSpanningLocation( peekLastValue()->locationId(), paramsDecl.locationId() );
auto& c = context();
auto tFuncType = *FromValue< TFuncType >( *popValue() );
auto tfunc = BuildTFunc( c, tFuncType, parentIdentity, identity, paramsDecl, nullptr );
if( !tfunc )
return PoisonValue();
return ToValue( TFuncDecl( *tfunc, pOvlSet ) ).setLocationId( loc );
}
|
Changes to bs/parse/tfuncdecl.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "parse.h"
#include "builtins/builtins.h"
#include "precedence.h"
using namespace goose;
using namespace goose::parse;
using namespace goose::builtins;
void parse::SetupTFuncDeclDropValue( Env& 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 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 |
#include "parse.h"
#include "builtins/builtins.h"
#include "precedence.h"
using namespace goose;
using namespace goose::parse;
using namespace goose::builtins;
void parse::SetupTFuncDeclDropValue( Env& e )
{
RegisterBuiltinFunc< Intrinsic< void( Value, TFuncDecl ) > >( e, e.extDropValue(),
[]( const Context& c, const Value& b, const Value& v )
{
auto tfd = *FromValue< TFuncDecl >( v );
if( tfd.tFunc().type().kind() == FuncType::Kind::Ghost )
{
// Complain if the overload set isn't empty and isn't ghost
if( tfd.ovlSet()->empty() )
tfd.ovlSet()->setGhost( true );
else
{
DiagnosticsManager::GetInstance().emitErrorMessage( v.locationId(),
"a function can't have both ghost and non-ghost overloads." );
return;
}
}
else if( !tfd.tFunc().tokens() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
v.locationId(), "expected body after function declaration." );
return;
}
if( !tfd.ovlSet()->add(
*c.env(), ToValue( tfd.tFunc() ).setLocationId( v.locationId() ) ) )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage(
v.locationId(), "Duplicate function overload." );
}
} );
}
namespace goose::eir
{
const Term& Bridge< parse::TFuncDecl >::Type()
{
static auto type =
ValueToEIR( Value( TypeType(), VEC( TSID( ct_type ), TSID( tfunc_decl ) ) ) );
return type;
}
Value Bridge< parse::TFuncDecl >::ToValue( const parse::TFuncDecl& tfd )
{
return Value( Type(),
VEC( TERM( static_pointer_cast< void >( tfd.ovlSet() ) ),
ValueToEIR( ::ToValue( tfd.tFunc() ) ) ) );
}
optional< parse::TFuncDecl > Bridge< parse::TFuncDecl >::FromValue( const Value& v )
{
if( v.type() != Type() )
return nullopt;
auto decomp = Decompose( v.val(), Vec( Val< ptr< void > >(), SubTerm() ) );
assert( decomp );
auto&& [pOvlSet, tfunc] = *decomp;
return TFuncDecl{ *FromValue< TFunc >( *EIRToValue( tfunc ) ),
static_pointer_cast< sema::OverloadSet >( pOvlSet ) };
}
} // namespace goose::eir
|
Changes to bs/parse/tfuncdecl.h.
1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_PARSE_TFUNCDECL_H
#define GOOSE_PARSE_TFUNCDECL_H
#include "parse.h"
namespace goose::parse
{
extern void SetupTFuncDeclDropValue( sema::Env& 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 38 39 40 41 42 43 44 45 46 47 |
#ifndef GOOSE_PARSE_TFUNCDECL_H
#define GOOSE_PARSE_TFUNCDECL_H
#include "parse.h"
namespace goose::parse
{
extern void SetupTFuncDeclDropValue( sema::Env& e );
// Describes a template function declaration. It can be augmented with a function body if it
// expects one (ie its not a ghost function) and we encounter an infix brace block, with a func
// decl as the current left value. This allows function qualifier operators (such as "ghost") to
// modify the decl before we deal with affixing a body (which can be influenced by said
// operator)
class TFuncDecl
{
public:
template< typename F, typename O >
TFuncDecl( F&& tFunc, O&& pOvlSet ) :
m_tFunc( forward< F >( tFunc ) ),
m_pOvlSet( forward< O >( pOvlSet ) )
{
}
const auto& tFunc() const { return m_tFunc; }
auto& tFunc() { return m_tFunc; }
const auto& ovlSet() const { return m_pOvlSet; }
private:
builtins::TFunc m_tFunc;
ptr< sema::OverloadSet > m_pOvlSet;
};
} // namespace goose::parse
namespace goose::eir
{
template<> struct Bridge< parse::TFuncDecl >
{
static const Term& Type();
static Value ToValue( const parse::TFuncDecl& tfd );
static optional< parse::TFuncDecl > FromValue( const Value& v );
};
} // namespace goose::eir
#endif
|
Changes to bs/precedence.h.
| ︙ | ︙ | |||
21 22 23 24 25 26 27 |
constexpr uint32_t FuncQualifier = 600;
// A funcdecl is a decl followed by a paren block.
// This needs a lower precedence than the comma operator so that
// we can use a tuple of types as the return type of a function.
constexpr uint32_t FuncDecl = 700;
| | | | | | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
constexpr uint32_t FuncQualifier = 600;
// A funcdecl is a decl followed by a paren block.
// This needs a lower precedence than the comma operator so that
// we can use a tuple of types as the return type of a function.
constexpr uint32_t FuncDecl = 700;
// Comma should have a lower precedence than most other operators: we want to be able to
// construct tuples of types simply by separating type expressions with commas, and we want to
// be able to use any operator in a type expression. It needs to be higher than assignment
// however, as putting assignment into a tuple makes no sense, but we want to be able to assign
// something to a tuple.
constexpr uint32_t CommaOp = 800;
constexpr uint32_t EllipsisOp = 850;
constexpr uint32_t Decl = 900;
constexpr uint32_t AccessQualifier = 950;
|
| ︙ | ︙ | |||
47 48 49 50 51 52 53 |
constexpr uint32_t EqualityOp = 1500;
constexpr uint32_t GreaterLesserOp = 1600;
constexpr uint32_t BitShiftOp = 1700;
constexpr uint32_t AddSubOp = 1800;
constexpr uint32_t MulDivOp = 1900;
constexpr uint32_t UnaryOps = 2000;
| | | | | | | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
constexpr uint32_t EqualityOp = 1500;
constexpr uint32_t GreaterLesserOp = 1600;
constexpr uint32_t BitShiftOp = 1700;
constexpr uint32_t AddSubOp = 1800;
constexpr uint32_t MulDivOp = 1900;
constexpr uint32_t UnaryOps = 2000;
// Applications are all cases where a tuple is applied to something that immediately precedes
// it: ie function calls and function type expressions.
constexpr uint32_t Application = 3000;
constexpr uint32_t DotOp = 4000;
// ', used to attach a lifetime to an access specifier, may have a tvar on the rhs
// (for overload resolution depending on whether some references are aliasing),
// so it has a priority a smidge below that of the dollar operator.
constexpr uint32_t ApostropheOp = 5000;
// The dollar operator, used to build a template named capture texpr, is never expected to have
// a sub-expression to its right so it have the max priority.
constexpr uint32_t Dollar = 6000;
} // namespace goose::precedence
#endif
|
Changes to bs/sema/context.cpp.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 |
{
auto parentBaseIdentity = identity();
parentBaseIdentity = TakeVectorTerm( parentBaseIdentity, VecSize( parentBaseIdentity ) - 1 );
// We construct an identity to hold all the compilation time constants for this scope,
// that will be visible in every nested scopes and functions.
| | | | | | | | | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
{
auto parentBaseIdentity = identity();
parentBaseIdentity = TakeVectorTerm( parentBaseIdentity, VecSize( parentBaseIdentity ) - 1 );
// We construct an identity to hold all the compilation time constants for this scope,
// that will be visible in every nested scopes and functions.
// We create a separate identity for the local variables, from which both all inherited
// constants and local variables from the parent scope will be visible. That one will be made
// current, and can be truncated to find the "compmilation constant" identity where we need to
// see only the compilation constants (for instance, in parameter lists).
// TODO: see if we can replace this unique id with something that is just unique within the
// current function. This would avoid wild changes in the mangled names based on unrelated
// definitions being added elsewhere.
auto localIdentity =
AppendToVectorTerm( parentBaseIdentity, TERM( StringId( Env::NewUniqueId() ) ) );
auto localVarsIdentity = AppendToVectorTerm( localIdentity, TSID( 0_locvars ) );
env()->addVisibilityRule( parentBaseIdentity, localIdentity );
env()->addVisibilityRule( identity(), localVarsIdentity );
setIdentity( localVarsIdentity );
}
|
Changes to bs/sema/context.h.
1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_SEMA_CONTEXT_H
#define GOOSE_SEMA_CONTEXT_H
namespace goose::sema
{
class Env;
class Context
{
| | | | | | < > > | | | | | < | < < > | | | < | > | > | > | | < | | < | < < < | < < < | | | | | | | | | | | | | < < < | | | | | | | | | | | < > > | | | | | | | | | | | | | | < < < | | | | | | | < < | | < | | | | | | | | | | < > | 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 |
#ifndef GOOSE_SEMA_CONTEXT_H
#define GOOSE_SEMA_CONTEXT_H
namespace goose::sema
{
class Env;
class Context
{
public:
template< typename E, typename I >
Context( E&& pEnv, I&& identity ) :
m_pEnv( forward< E >( pEnv ) ),
m_identity( forward< I >( identity ) )
{
}
template< typename E, typename I, typename R >
Context( E&& pEnv, I&& identity, R&& returnType ) :
m_pEnv( forward< E >( pEnv ) ),
m_identity( forward< I >( identity ) ),
m_returnType( forward< R >( returnType ) )
{
}
template< typename B > void setBuilder( B&& b ) { m_builder = forward< B >( b ); }
const auto& env() const { return m_pEnv; }
const auto& identity() const { return m_identity; }
const auto& returnType() const { return m_returnType; }
const auto& builder() const { return m_builder; }
auto& onContextRestoredSignal() { return m_onContextRestored; }
template< typename T > T builder() const { return FromValue< T >( m_builder ); }
template< typename T > void setEnv( T&& pEnv ) { m_pEnv = forward< T >( pEnv ); }
void setIdentity( const Term& identity ) { m_identity = identity; }
auto locationId() const { return m_currentLoc; }
void beginVisibilityScope();
class CurrentLocationGuard
{
public:
CurrentLocationGuard( Context& c, LocationId loc ) :
m_context( c ),
m_previousLoc( c.m_currentLoc )
{
c.m_currentLoc = loc;
}
~CurrentLocationGuard() { m_context.m_currentLoc = m_previousLoc; }
private:
Context& m_context;
LocationId m_previousLoc;
};
class CurrentContextGuard
{
public:
CurrentContextGuard( Context& c ) :
m_context( c ),
m_savedBuilder( c.builder() ),
m_savedIdentity( c.identity() )
{
}
~CurrentContextGuard()
{
m_context.setIdentity( m_savedIdentity );
m_context.setBuilder( m_savedBuilder );
m_context.m_onContextRestored();
}
private:
Context& m_context;
Value m_savedBuilder;
Term m_savedIdentity;
};
// Crappy solution for LowerableType/LowerableValue for the BS compiler
static inline const auto* DefaultContext() { return m_defaultContext; }
struct DefaultContextGuard
{
DefaultContextGuard( const Context& newContext )
{
m_previousContext = Context::DefaultContext();
Context::m_defaultContext = &newContext;
}
~DefaultContextGuard() { Context::m_defaultContext = m_previousContext; }
const Context* m_previousContext = nullptr;
};
private:
Signal<> m_onContextRestored;
ptr< Env > m_pEnv;
Term m_identity;
optional< Term > m_returnType;
Value m_builder;
LocationId m_currentLoc;
static const Context* m_defaultContext;
};
} // namespace goose::sema
#endif
|
Changes to bs/sema/ctmm.h.
1 2 3 4 5 6 7 8 |
#ifndef GOOSE_SEMA_CTMM_H
#define GOOSE_SEMA_CTMM_H
namespace goose::sema
{
// This implements a virtual memory address space for compilation time execution.
class CTMemoryManager
{
| | | | | | | | | | | | | | < < < < | | | | | | | | | < | | | | | | | | | | | | < < < | | < < < | | | | > > > | | | | | | | | | | | | | | | | | | | 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 |
#ifndef GOOSE_SEMA_CTMM_H
#define GOOSE_SEMA_CTMM_H
namespace goose::sema
{
// This implements a virtual memory address space for compilation time execution.
class CTMemoryManager
{
public:
static constexpr uint64_t PageSizeBits = 12;
static constexpr uint64_t PageSize = 1 << PageSizeBits;
static constexpr uint64_t PageSizeMask = PageSize - 1;
using VirtualPointer = uint64_t;
using PageIndex = uint64_t;
bool createPage( PageIndex pi, bool readOnly = false )
{
auto&& [it, success] = m_pages.emplace( pi, readOnly );
return success;
}
void destroyPage( PageIndex pi, bool readOnly = false ) { m_pages.erase( pi ); }
template< typename T > optional< T > load( VirtualPointer vp ) const
{
if( !IsPointerAlignmentValid( vp, sizeof( T ) ) )
return nullopt;
auto page = getPage( GetPageIndex( vp ) );
if( !page )
return nullopt;
return *reinterpret_cast< const T* >( page->m_pData->data() + GetOffsetInPage( vp ) );
}
template< typename T > bool store( VirtualPointer vp, const T& val )
{
if( !IsPointerAlignmentValid( vp, sizeof( T ) ) )
return false;
auto page = getPage( GetPageIndex( vp ) );
if( !page || page->readOnly )
return false;
*reinterpret_cast< const T* >( page->m_pData->data() + GetOffsetInPage( vp ) ) = val;
return true;
}
static PageIndex GetPageIndex( VirtualPointer vp ) { return vp >> PageSizeBits; }
static PageIndex GetOffsetInPage( VirtualPointer vp ) { return vp & PageSizeMask; }
private:
struct Page
{
Page( bool ro ) :
readOnly( ro )
{
}
unique_ptr< array< uint8_t, PageSize > > m_pData;
bool readOnly = false;
};
const Page* getPage( PageIndex pi ) const
{
auto it = m_pages.find( pi );
if( it == m_pages.end() )
return nullptr;
return &it->second;
}
// Checks that the given virtual pointer has correct
// alignment for the size of a type (which must be a power of 2)
static bool IsPointerAlignmentValid( VirtualPointer vp, size_t typeSize )
{
return ( vp & ( typeSize - 1 ) ) == 0;
}
unordered_map< PageIndex, Page > m_pages;
};
} // namespace goose::sema
#endif
|
Changes to bs/sema/env.cpp.
1 2 3 4 5 6 | #include "sema.h" using namespace goose; using namespace goose::cir; 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 |
#include "sema.h"
using namespace goose;
using namespace goose::cir;
using namespace goose::sema;
vector< ptr< cir::Func > > Env::ms_cirFuncs;
DependencyGraph Env::ms_depsGraph;
uint32_t Env::ms_nextUniqueId = 0;
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 )
{
storeValue( idPat, contextIdPat,
make_shared< ValueProvider >(
[val]( const Env& e, const Term& identity, const Term& contextId, Term& result )
{
result = val;
return Status::Success;
} ) );
}
void Env::storeValue(
const Term& idPat, const Term& contextIdPat, const ptr< ValueProvider >& valp )
{
m_valueStore = Merge( m_valueStore, VEC( idPat, contextIdPat ),
[valp]( auto&& oldValp )
{
if( !oldValp )
return valp;
// If another value provider was already registered with the same pattern,
// combine them through a lambda that will call them both and check for ambiguousness.
return make_shared< ValueProvider >(
[oldValp, valp]( Env& e, const Term& identity, const Term& contextId, Term& result )
{
auto s1 = ( *oldValp )( e, identity, contextId, result );
if( s1 == Status::AmbiguousMatch )
return s1;
auto s2 = ( *valp )( e, identity, contextId, result );
if( s2 == Status::AmbiguousMatch )
return s2;
if( s1 == s2 )
{
if( s1 == Status::NoMatch )
return Status::NoMatch;
else
return Status::AmbiguousMatch;
}
return Status::Success;
} );
} );
++m_valueStoreVersion;
}
Env::Status Env::retrieveValue( const Term& id, const Term& contextId, Term& result )
{
|
| ︙ | ︙ | |||
119 120 121 122 123 124 125 |
// Create the pattern: it is the destination identity,
// followed by one or more elements.
auto vecPat = Vector::MakeAppend( vec, ANYTERM( _ ) );
vecPat.setRepetitionTerm( ANYTERM( _ ) );
auto pat = TERM( make_shared< Vector >( move( vecPat ) ) );
| | > > | | > | | | | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
// Create the pattern: it is the destination identity,
// followed by one or more elements.
auto vecPat = Vector::MakeAppend( vec, ANYTERM( _ ) );
vecPat.setRepetitionTerm( ANYTERM( _ ) );
auto pat = TERM( make_shared< Vector >( move( vecPat ) ) );
storeValue( pat, transitive ? ANYTERM( _ ) : destination,
make_shared< ValueProvider >(
[toImport, prefixLength](
Env& e, const Term& identity, const Term& contextId, Term& result )
{
auto newId =
ConcatenateVectorTerms( toImport, DropVectorTerm( identity, prefixLength ) );
return e.retrieveValue( newId, contextId, result );
} ) );
}
|
Changes to bs/sema/env.h.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 |
class TemplateRuleSet;
class InvocationRuleSet;
class TypeCheckingRuleSet;
class OverloadSet;
class Env : public enable_shared_from_this< Env >
{
| | | | | | | | | | > | | > | | | | > | | > | > | | > | > | | | > | | > | | > | | > | | > | | > | | > | | > | | > | > > < | | > | | > | | > | | > | | > | | > | | > | > > > > > > < < < < | | > | | > | | > | | > | | > | | > | > > > > > > > > > > < | | > | | | | | | > > | | | > > | | > > > | > | | > | | > | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 |
class TemplateRuleSet;
class InvocationRuleSet;
class TypeCheckingRuleSet;
class OverloadSet;
class Env : public enable_shared_from_this< Env >
{
public:
Env();
~Env();
enum class Status
{
Success,
NoMatch,
AmbiguousMatch
};
using ValueProvider =
function< Status( Env& e, const Term& identity, const Term& contextId, Term& result ) >;
void storeValue( const Term& idPat, const Term& contextIdPat, const Term& val );
void storeValue(
const Term& idPat, const Term& contextIdPat, const ptr< ValueProvider >& valp );
Status retrieveValue( const Term& id, const Term& contextId, Term& result );
optional< Term > retrieveValue( const Term& id, const Term& contextId );
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& extIsType() { return m_extIsType; }
const auto& extIsType() const { return m_extIsType; }
auto& extDropValue() { return m_extDropValue; }
const auto& extDropValue() const { return m_extDropValue; }
auto& extDestroyValue() { return m_extDestroyValue; }
const auto& extDestroyValue() const { return m_extDestroyValue; }
auto& extInitialize() { return m_extInitialize; }
const auto& extInitialize() const { return m_extInitialize; }
auto& extIsTrivialInitialization() { return m_extIsTrivialInitialization; }
const auto& extIsTrivialInitialization() const { return m_extIsTrivialInitialization; }
auto& extIsTrivialAssignment() { return m_extIsTrivialAssignment; }
const auto& extIsTrivialAssignment() const { return m_extIsTrivialAssignment; }
auto& extGetUnderlyingType() { return m_extGetUnderlyingType; }
const auto& extGetUnderlyingType() const { return m_extGetUnderlyingType; }
auto& extLowerType() { return m_extLowerType; }
const auto& extLowerType() const { return m_extLowerType; }
auto& extLowerValue() { return m_extLowerValue; }
const auto& extLowerValue() const { return m_extLowerValue; }
auto& extConvertFuncParam() { return m_extConvertFuncParam; }
const auto& extConvertFuncParam() const { return m_extConvertFuncParam; }
auto& extConvertFuncArgs() { return m_extConvertFuncArgs; }
const auto& extConvertFuncArgs() const { return m_extConvertFuncArgs; }
auto& extPoisonBuilder() { return m_extPoisonBuilder; }
const auto& extPoisonBuilder() const { return m_extPoisonBuilder; }
auto& extBuilderAllowsOverloading() { return m_extBuilderAllowsOverloading; }
const auto& extBuilderAllowsOverloading() const { return m_extBuilderAllowsOverloading; }
auto& extGetCFG() { return m_extGetCFG; }
const auto& extGetCFG() const { return m_extGetCFG; }
auto& extFinalize() { return m_extFinalize; }
const auto& extFinalize() const { return m_extFinalize; }
auto& extGetBreakableScopeLevels() { return m_extGetBreakableScopeLevels; }
const auto& extGetBreakableScopeLevels() const { return m_extGetBreakableScopeLevels; }
auto& extGetContinuableScopeLevels() { return m_extGetContinuableScopeLevels; }
const auto& extGetContinuableScopeLevels() const { return m_extGetContinuableScopeLevels; }
auto& extBeginVisibilityScope() { return m_extBeginVisibilityScope; }
const auto& extBeginVisibilityScope() const { return m_extBeginVisibilityScope; }
auto& extBeginLifetimeScope() { return m_extBeginLifetimeScope; }
const auto& extBeginLifetimeScope() const { return m_extBeginLifetimeScope; }
auto& extEndLifetimeScope() { return m_extEndLifetimeScope; }
const auto& extEndLifetimeScope() const { return m_extEndLifetimeScope; }
auto& extBeginBreakableScope() { return m_extBeginBreakableScope; }
const auto& extBeginBreakableScope() const { return m_extBeginBreakableScope; }
auto& extEndBreakableScope() { return m_extEndBreakableScope; }
const auto& extEndBreakableScope() const { return m_extEndBreakableScope; }
auto& extBeginContinuableScope() { return m_extBeginContinuableScope; }
const auto& extBeginContinuableScope() const { return m_extBeginContinuableScope; }
auto& extEndContinuableScope() { return m_extEndContinuableScope; }
const auto& extEndContinuableScope() const { return m_extEndContinuableScope; }
auto& extDeclareValue() { return m_extDeclareValue; }
const auto& extDeclareValue() const { return m_extDeclareValue; }
auto& extOnValueDeclared() { return m_extOnValueDeclared; }
const auto& extOnValueDeclared() const { return m_extOnValueDeclared; }
auto& extDestroyLiveValue() { return m_extDestroyLiveValue; }
const auto& extDestroyLiveValue() const { return m_extDestroyLiveValue; }
auto& extDestroyAllLiveValues() { return m_extDestroyAllLiveValues; }
const auto& extDestroyAllLiveValues() const { return m_extDestroyAllLiveValues; }
auto& extDestroyAllLiveValuesFromBreakScope()
{
return m_extDestroyAllLiveValuesFromBreakScope;
}
const auto& extDestroyAllLiveValuesFromBreakScope() const
{
return m_extDestroyAllLiveValuesFromBreakScope;
}
auto& extDestroyAllLiveValuesFromContinueScope()
{
return m_extDestroyAllLiveValuesFromContinueScope;
}
const auto& extDestroyAllLiveValuesFromContinueScope() const
{
return m_extDestroyAllLiveValuesFromContinueScope;
}
auto& extInVerificationCode() { return m_extInVerificationCode; }
const auto& extInVerificationCode() const { return m_extInVerificationCode; }
auto& extIsBranchingAllowed() { return m_extIsBranchingAllowed; }
const auto& extIsBranchingAllowed() const { return m_extIsBranchingAllowed; }
auto& extCTForEach() { return m_extCTForEach; }
const auto& extCTForEach() const { return m_extCTForEach; }
template< typename... T > static auto CreateCIRFunc( T&&... args )
{
ms_cirFuncs.emplace_back( make_shared< cir::Func >( forward< T >( args )... ) );
return ms_cirFuncs.back();
}
static inline auto& DepsGraph() { return ms_depsGraph; }
private:
Trie< ptr< ValueProvider > > m_valueStore;
ptr< TemplateRuleSet > m_templateRuleSet;
ptr< InvocationRuleSet > m_invocationRuleSet;
ptr< TypeCheckingRuleSet > m_unificationRuleSet;
ptr< OverloadSet > m_extIsType;
ptr< OverloadSet > m_extDropValue;
ptr< OverloadSet > m_extDestroyValue;
ptr< OverloadSet > m_extInitialize;
ptr< OverloadSet > m_extIsTrivialInitialization;
ptr< OverloadSet > m_extIsTrivialAssignment;
ptr< OverloadSet > m_extGetUnderlyingType;
ptr< OverloadSet > m_extLowerType;
ptr< OverloadSet > m_extLowerValue;
ptr< OverloadSet > m_extConvertFuncParam;
ptr< OverloadSet > m_extConvertFuncArgs;
ptr< OverloadSet > m_extPoisonBuilder;
ptr< OverloadSet > m_extBuilderAllowsOverloading;
ptr< OverloadSet > m_extGetCFG;
ptr< OverloadSet > m_extFinalize;
ptr< OverloadSet > m_extGetBreakableScopeLevels;
ptr< OverloadSet > m_extGetContinuableScopeLevels;
ptr< OverloadSet > m_extBeginVisibilityScope;
ptr< OverloadSet > m_extBeginLifetimeScope;
ptr< OverloadSet > m_extEndLifetimeScope;
ptr< OverloadSet > m_extBeginBreakableScope;
ptr< OverloadSet > m_extEndBreakableScope;
ptr< OverloadSet > m_extBeginContinuableScope;
ptr< OverloadSet > m_extEndContinuableScope;
ptr< OverloadSet > m_extDeclareValue;
ptr< OverloadSet > m_extOnValueDeclared;
ptr< OverloadSet > m_extDestroyLiveValue;
ptr< OverloadSet > m_extDestroyAllLiveValues;
ptr< OverloadSet > m_extDestroyAllLiveValuesFromBreakScope;
ptr< OverloadSet > m_extDestroyAllLiveValuesFromContinueScope;
ptr< OverloadSet > m_extInVerificationCode;
ptr< OverloadSet > m_extIsBranchingAllowed;
ptr< OverloadSet > m_extCTForEach;
uint64_t m_valueStoreVersion = 0;
ptr< CTMemoryManager > m_memManager;
// CIR funcs form a cyclic graph, since functions can
// be recursive or mutually recursive.
// Since they end up being stored in cir::Call in the form of values,
// it means that func values have to keep a weak_ptr to their cir func.
// Therefore we have Env take care of keeping the cir func ownership.
static vector< ptr< cir::Func > > ms_cirFuncs;
static DependencyGraph ms_depsGraph;
static uint32_t ms_nextUniqueId;
};
} // namespace goose::sema
#endif
|
Changes to bs/sema/hole.cpp.
| ︙ | ︙ | |||
32 33 34 35 36 37 38 |
{
static auto hp = HOLE( ""_sid, ""_sid, Hole::Behavior::Any );
return hp;
}
optional< Hole > HoleFromIRExpr( const Term& t )
{
| | < < | < < | | 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 |
{
static auto hp = HOLE( ""_sid, ""_sid, Hole::Behavior::Any );
return hp;
}
optional< Hole > HoleFromIRExpr( const Term& t )
{
auto result = Decompose( t, Val< Hole >() );
return result;
}
const Term& ForwardingHolePattern()
{
static auto hp = HOLE( ""_sid, "fwd"_sid, Hole::Behavior::Any );
return hp;
}
optional< Hole > ForwardingHoleFromIRExpr( const Term& t )
{
auto result = Decompose( t, Val< Hole >() );
return result;
}
} // namespace goose::sema
|
Changes to bs/sema/hole.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#ifndef GOOSE_SEMA_HOLE_H
#define GOOSE_SEMA_HOLE_H
namespace goose::sema
{
extern StringId HoleBhvToSid( Hole::Behavior bhv );
extern Hole::Behavior SidToHoleBhv( StringId sid );
extern const Term& HolePattern();
extern optional< Hole > HoleFromIRExpr( const Term& t );
extern const Term& ForwardingHolePattern();
extern optional< Hole > ForwardingHoleFromIRExpr( const Term& t );
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#ifndef GOOSE_SEMA_HOLE_H
#define GOOSE_SEMA_HOLE_H
namespace goose::sema
{
extern StringId HoleBhvToSid( Hole::Behavior bhv );
extern Hole::Behavior SidToHoleBhv( StringId sid );
extern const Term& HolePattern();
extern optional< Hole > HoleFromIRExpr( const Term& t );
extern const Term& ForwardingHolePattern();
extern optional< Hole > ForwardingHoleFromIRExpr( const Term& t );
} // namespace goose::sema
#endif
|
Changes to bs/sema/inv-ruleset.cpp.
1 2 3 4 5 6 7 |
#include "sema.h"
using namespace goose;
using namespace goose::sema;
void InvocationRuleSet::addRule( const Term& calleePat, const ptr< InvocationRule >& r )
{
| | | 1 2 3 4 5 6 7 8 9 |
#include "sema.h"
using namespace goose;
using namespace goose::sema;
void InvocationRuleSet::addRule( const Term& calleePat, const ptr< InvocationRule >& r )
{
m_rules = Merge( m_rules, calleePat, [r]( auto&& ) { return r; } );
}
|
Changes to bs/sema/inv-ruleset.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_SEMA_INV_RULESET_H
#define GOOSE_SEMA_INV_RULESET_H
namespace goose::sema
{
class InvocationRule
{
| | | | | < < | | | > | | | | | < < | | | | | | | | > | | | < > | 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 |
#ifndef GOOSE_SEMA_INV_RULESET_H
#define GOOSE_SEMA_INV_RULESET_H
namespace goose::sema
{
class InvocationRule
{
public:
virtual ~InvocationRule() {}
virtual bool canBeInvoked( const Context& c, const Value& callee ) const { return true; }
virtual Value resolveInvocation(
Context& c, LocationId locationId, const Value& callee, const Term& args ) const = 0;
virtual Value invoke( Context& c, LocationId locationId, const Value& callee,
const Term& args, const Term& checkedCallPat, TypeCheckingContext& tcc ) const
{
return PoisonValue();
}
virtual optional< Term > getSignature( const Value& callee ) const { return nullopt; }
virtual Value prepareFunc( const Context& c, LocationId funcValLocation,
const Value& callee, const Term& checkedCallPat, TypeCheckingContext& tcc ) const
{
return PoisonValue();
}
};
class InvocationRuleSet
{
public:
InvocationRuleSet() {}
void addRule( const Term& calleePat, const ptr< InvocationRule >& r );
const auto& rules() const { return m_rules; }
private:
Trie< ptr< InvocationRule > > m_rules;
};
} // namespace goose::sema
#endif
|
Changes to bs/sema/invocation.cpp.
| ︙ | ︙ | |||
39 40 41 42 43 44 45 |
bool CanBeInvoked( const Context& c, const Value& callee )
{
auto pIR = GetInvocationRule( *c.env(), callee );
return pIR && pIR->canBeInvoked( c, callee );
}
| > | | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
bool CanBeInvoked( const Context& c, const Value& callee )
{
auto pIR = GetInvocationRule( *c.env(), callee );
return pIR && pIR->canBeInvoked( c, callee );
}
Value ResolveInvocation(
Context& c, const ptr< InvocationRule >& pInvRule, const Value& callee, const Value& args )
{
if( callee.isPoison() || args.isPoison() )
return PoisonValue();
auto loc = Location::CreateSpanningLocation( callee.locationId(), args.locationId() );
return pInvRule->resolveInvocation( c, loc, callee, args.val() ).setLocationId( loc );
}
} // namespace goose::sema
|
Changes to bs/sema/invocation.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_SEMA_INVOCATION_H
#define GOOSE_SEMA_INVOCATION_H
namespace goose::sema
{
extern ptr< InvocationRule > GetInvocationRule( const Env& e, const Value& callee );
extern bool CanBeInvoked( const Context& c, const Value& callee );
| > | < > | 1 2 3 4 5 6 7 8 9 10 11 12 |
#ifndef GOOSE_SEMA_INVOCATION_H
#define GOOSE_SEMA_INVOCATION_H
namespace goose::sema
{
extern ptr< InvocationRule > GetInvocationRule( const Env& e, const Value& callee );
extern bool CanBeInvoked( const Context& c, const Value& callee );
extern Value ResolveInvocation(
Context& c, const ptr< InvocationRule >& pInvRule, const Value& callee, const Value& args );
} // namespace goose::sema
#endif
|
Changes to bs/sema/lower.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include "sema.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::builtins;
namespace goose::sema
{
optional< Value > GetUnderlyingType( const Context& c, const Value& type )
{
DiagnosticsContext dc( type.locationId(), "When invoking _GetUnderlyingType." );
| | | | | | | | | 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 |
#include "sema.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::eir;
using namespace goose::builtins;
namespace goose::sema
{
optional< Value > GetUnderlyingType( const Context& c, const Value& type )
{
DiagnosticsContext dc( type.locationId(), "When invoking _GetUnderlyingType." );
auto result = InvokeOverloadSet(
c, c.env()->extGetUnderlyingType(), MakeOpenTuple( type ), type.locationId() );
if( result.isPoison() )
return nullopt;
return result;
}
optional< Value > LowerType( const Context& c, const Value& type )
{
DiagnosticsContext dc( type.locationId(), "When invoking _LowerType." );
auto result = InvokeOverloadSet(
c, c.env()->extLowerType(), MakeOpenTuple( type ), type.locationId() );
if( result.isPoison() )
return nullopt;
return result;
}
optional< Value > LowerValue( const Context& c, const Value& val )
{
DiagnosticsContext dc( val.locationId(), "When invoking _LowerValue." );
auto result = InvokeOverloadSet(
c, c.env()->extLowerValue(), MakeOpenTuple( val ), val.locationId() );
if( result.isPoison() )
return nullopt;
return result;
}
} // namespace goose::sema
|
Changes to bs/sema/overloadset.cpp.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 |
}
bool OverloadSet::add( const Env& e, const Value& callee, const ptr< InvocationRule >& pInvRule )
{
auto signature = pInvRule->getSignature( callee );
assert( signature );
| | < < | > | | | | | | | > | < < | | 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 |
}
bool OverloadSet::add( const Env& e, const Value& callee, const ptr< InvocationRule >& pInvRule )
{
auto signature = pInvRule->getSignature( callee );
assert( signature );
auto sigDecomp = Decompose( *signature, Val< pvec >() );
assert( sigDecomp );
bool success = false;
m_trie = m_trie->merge( *sigDecomp->get(),
[&]( auto&& previous ) -> Overload
{
if( previous.callee )
return move( previous );
success = true;
return { pInvRule, callee };
} );
m_resolutionCache.clear();
return success;
}
OverloadSet::TCGen OverloadSet::typeCheck(
const Term& callPat, const TypeCheckingContext& tcc ) const
{
auto callDecomp = Decompose( callPat, Val< pvec >() );
if( !callDecomp )
co_return;
for( auto&& [callVec, uniCallVec, ovl, tcc] : m_trie->typeCheck( *callDecomp->get(), tcc ) )
{
auto uniCall = TERM( make_shared< Vector >( uniCallVec ) );
co_yield { move( uniCall ), ovl, tcc };
}
}
optional< OverloadSet::Overload > OverloadSet::getResolutionFromCache( const Term& args ) const
|
| ︙ | ︙ |
Changes to bs/sema/overloadset.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_SEMA_OVERLOADSET_H
#define GOOSE_SEMA_OVERLOADSET_H
namespace goose::sema
{
class OverloadSet
{
| | | | | | | | | < | > > | > | | > | | | | | | < < < < | | | | | | | | | | | | | 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 |
#ifndef GOOSE_SEMA_OVERLOADSET_H
#define GOOSE_SEMA_OVERLOADSET_H
namespace goose::sema
{
class OverloadSet
{
public:
struct Overload
{
ptr< InvocationRule > pInvRule;
optional< Value > callee;
};
OverloadSet( const Term& id ) :
m_identity( id )
{
}
bool ghost() const { return m_ghost; }
void setGhost( bool g ) { m_ghost = g; }
bool verboseResolution() const { return m_verboseResolution; }
void setVerboseResolution( bool vr ) { m_verboseResolution = vr; }
bool empty() const { return m_trie->empty(); }
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& callPat, const TypeCheckingContext& tcc ) const;
optional< Overload > getResolutionFromCache( const Term& args ) const;
void addResolutionToCache( const Term& args, const Overload& ovl );
Generator< const Overload* > enumerate() const;
private:
Term m_identity;
using trie_type = TCTrie< Overload >;
ptr< trie_type > m_trie = make_shared< trie_type >();
unordered_map< Term, Overload > m_resolutionCache;
bool m_ghost = false;
bool m_verboseResolution = false;
};
} // namespace goose::sema
#endif
|
Changes to bs/sema/postprocess.cpp.
1 2 3 4 5 6 |
#include "sema.h"
namespace goose::sema
{
Term WrapWithPostprocFunc( const Term& t, const ptr< PostProcFunc >& pp )
{
| | | < | > | > | | < | < < < < | | 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 "sema.h"
namespace goose::sema
{
Term WrapWithPostprocFunc( const Term& t, const ptr< PostProcFunc >& pp )
{
return SetWeight(
VEC( TSID( postproc ), TERM( static_pointer_cast< void >( pp ) ), t ), GetWeight( t ) );
}
Term WrapWithPostprocFunc( const Term& t, PostProcFunc&& pp )
{
return SetWeight(
VEC( TSID( postproc ),
TERM( static_pointer_cast< void >( make_shared< PostProcFunc >( move( pp ) ) ) ),
t ),
GetWeight( t ) );
}
optional< pair< Term, ptr< PostProcFunc > > > UnwrapPostprocFunc( const Term& ppt )
{
auto result =
Decompose( ppt, Vec( Lit( "postproc"_sid ), Val< ptr< void > >(), SubTerm() ) );
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() )
return src;
else
{
const auto& optVal = tcc.getValue( hole.name().id() );
if( !optVal )
return HOLE( "_"_sid );
return Postprocess( *optVal, tcc );
|
| ︙ | ︙ | |||
84 85 86 87 88 89 90 |
return nullopt;
outputTerms->setRepetitionTerm( move( *newRpt ) );
}
return outputTerms;
}
| | | 80 81 82 83 84 85 86 87 |
return nullopt;
outputTerms->setRepetitionTerm( move( *newRpt ) );
}
return outputTerms;
}
} // namespace goose::sema
|
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 );
} // namespace goose::sema
#endif
|
Changes to bs/sema/scope.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_SEMA_SCOPE_H
#define GOOSE_SEMA_SCOPE_H
namespace goose::sema
{
class LifetimeScopeGuard
{
| | | | | | | | | | | | | | | | | | | | | | | | < | > > | | < > | 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 |
#ifndef GOOSE_SEMA_SCOPE_H
#define GOOSE_SEMA_SCOPE_H
namespace goose::sema
{
class LifetimeScopeGuard
{
public:
LifetimeScopeGuard( const Context& c );
~LifetimeScopeGuard();
private:
const sema::Context& m_context;
};
class BreakableScopeGuard
{
public:
BreakableScopeGuard( const Context& c );
~BreakableScopeGuard();
private:
const sema::Context& m_context;
};
class ContinuableScopeGuard
{
public:
ContinuableScopeGuard( const Context& c );
~ContinuableScopeGuard();
private:
const sema::Context& m_context;
};
// Helper object that creates a nested visibility scope.
class VisibilityScope
{
public:
VisibilityScope( Context& c );
private:
Context::CurrentContextGuard m_identityGuard;
};
// Helper object that creates a nested visibility scope
// along with a lifetime scope.
class Scope : public VisibilityScope
{
public:
Scope( Context& c ) :
VisibilityScope( c ),
m_lifetimeScopeGuard( c )
{
}
private:
LifetimeScopeGuard m_lifetimeScopeGuard;
};
} // namespace goose::sema
#endif
|
Changes to bs/sema/sema.h.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
using namespace eir;
using namespace diagnostics;
class Context;
extern optional< Value > LowerType( const Context& c, const Value& type );
extern optional< Value > LowerValue( const Context& c, const Value& type );
| | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
using namespace eir;
using namespace diagnostics;
class Context;
extern optional< Value > LowerType( const Context& c, const Value& type );
extern optional< Value > LowerValue( const Context& c, const Value& type );
} // namespace goose::sema
#include "hole.h"
#include "context.h"
#include "ctmm.h"
#include "env.h"
#include "tc-score.h"
|
| ︙ | ︙ |
Changes to bs/sema/substitute.cpp.
| ︙ | ︙ | |||
33 34 35 36 37 38 39 |
if( auto rpt = vec.repetitionTerm() )
outputTerms->setRepetitionTerm( Substitute( *rpt, context ) );
outputTerms->setWeightOverride( vec.weightOverride() );
return outputTerms;
}
| | | 33 34 35 36 37 38 39 40 |
if( auto rpt = vec.repetitionTerm() )
outputTerms->setRepetitionTerm( Substitute( *rpt, context ) );
outputTerms->setWeightOverride( vec.weightOverride() );
return outputTerms;
}
} // namespace goose::sema
|
Changes to bs/sema/tc-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 |
#include "sema.h"
namespace goose::sema
{
TCGen TypeCheckVectors(
TCVecGenerator lvg, TCVecGenerator rvg, const Vector& solutionVec, TypeCheckingContext tcc )
{
for( auto&& [u, tcc] :
TypeCheck( lvg( tcc.LHSSubContext() ), rvg( tcc.RHSSubContext() ), tcc ) )
{
auto vec = Vector::MakeAppend( solutionVec, move( u ) );
if( lvg.fixedPartFinished() && rvg.fixedPartFinished() )
co_yield { TERM( make_shared< Vector >( move( vec ) ) ), tcc };
else
co_yield TypeCheckVectors( lvg, rvg, vec, tcc );
}
}
void SetupBasicTypeCheckingRules( TypeCheckingRuleSet& ruleSet )
{
// Identity type checking rule
ruleSet.addTypeCheckingRule( ANYTERM( T ), 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 type checking 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 type checking rule
ruleSet.addTypeCheckingRule( TERM( LocationId( 0 ) ), 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 type checking as much as possible. This only
// impacts the quality of error messages.
auto lloc = get< LocationId >( lhs ).index();
auto rloc = get< LocationId >( rhs ).index();
// 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 { LocationId( result ), tcc };
} );
// void* type checking rule
ruleSet.addTypeCheckingRule( TERM( nullptr ), TERM( nullptr ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
// Only preserve void* accross type checking 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 };
} );
// Type checking 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.addTypeCheckingRule( 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 );
TCVecGenerator lvg( *lVector );
TCVecGenerator rvg( *rVector );
if( lvg.fixedPartFinished() && rvg.fixedPartFinished() )
{
co_yield { lhs, tcc };
co_return;
}
Vector sol;
co_yield TypeCheckVectors( lvg, rvg, sol, tcc );
} );
}
} // namespace goose::sema
|
Changes to bs/sema/tc-context.cpp.
1 2 3 4 5 6 7 |
#include "sema.h"
using namespace goose;
using namespace goose::sema;
TypeCheckingContext::TypeCheckingContext( const Context& c ) :
m_context( c )
| < > > < | > > | > | > | 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 |
#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 ) )
{
}
optional< Term > TypeCheckingContext::getLHSHoleValue(
StringId name, uint32_t repetitionIndex ) const
{
auto index = getLHSHoleIndex( name, repetitionIndex );
if( index == InvalidIndex )
return nullopt;
return getValue( index );
}
optional< Term > TypeCheckingContext::getRHSHoleValue(
StringId name, uint32_t repetitionIndex ) const
{
auto index = getRHSHoleIndex( name, repetitionIndex );
if( index == InvalidIndex )
return nullopt;
return getValue( index );
|
| ︙ | ︙ | |||
57 58 59 60 61 62 63 |
return InvalidIndex;
return it->second;
}
uint32_t TypeCheckingContext::getLHSHoleIndex( StringId name, Hole::Behavior bhv ) const
{
| > | > | | 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
return InvalidIndex;
return it->second;
}
uint32_t TypeCheckingContext::getLHSHoleIndex( StringId name, Hole::Behavior bhv ) const
{
return getLHSHoleIndex(
name, bhv == Hole::Behavior::Pack ? LHSSubContext().repetitionIndex : 0 );
}
uint32_t TypeCheckingContext::getRHSHoleIndex( StringId name, Hole::Behavior bhv ) const
{
return getRHSHoleIndex(
name, bhv == Hole::Behavior::Pack ? LHSSubContext().repetitionIndex : 0 );
}
uint32_t TypeCheckingContext::createValue( bool required )
{
CoW( m_pCow )->values.emplace_back( StoredValue{ nullopt, required } );
uint32_t index = m_pCow->values.size() - 1;
if( required )
|
| ︙ | ︙ |
Changes to bs/sema/tc-context.h.
1 2 3 4 5 6 7 8 9 10 11 |
#ifndef GOOSE_SEMA_TC_CONTEXT_H
#define GOOSE_SEMA_TC_CONTEXT_H
namespace goose::sema
{
#ifndef NDEBUG
struct TCRuleInfo;
#endif
class TypeCheckingContext
{
| | | | | | > | > | | | | | | | | | | | | | | | | | | > | > | > | | | | | | | | | < < < | | < < < | | | | | | < | | | | | | | | | | | | > | | > | | > > | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 |
#ifndef GOOSE_SEMA_TC_CONTEXT_H
#define GOOSE_SEMA_TC_CONTEXT_H
namespace goose::sema
{
#ifndef NDEBUG
struct TCRuleInfo;
#endif
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(); }
optional< Term > getLHSHoleValue( StringId name, uint32_t repetitionIndex ) const;
optional< Term > getRHSHoleValue( StringId name, uint32_t repetitionIndex ) const;
uint32_t getLHSHoleIndex( StringId name, uint32_t index ) const;
uint32_t getRHSHoleIndex( StringId name, uint32_t index ) const;
uint32_t getLHSHoleIndex( StringId name, Hole::Behavior bhv ) const;
uint32_t getRHSHoleIndex( StringId name, Hole::Behavior bhv ) const;
uint32_t createValue( bool required = false );
void setLHSHoleIndex( StringId name, Hole::Behavior bhv, uint32_t index );
void setRHSHoleIndex( StringId name, Hole::Behavior bhv, uint32_t index );
void eraseLHSName( StringId name );
void eraseRHSName( StringId name );
struct SubContext
{
uint32_t namespaceIndex = 0;
uint32_t repetitionIndex = 0;
};
const SubContext& LHSSubContext() const { return m_lhsSubContext; }
const SubContext& RHSSubContext() const { return m_rhsSubContext; }
SubContext& LHSSubContext() { return m_lhsSubContext; }
SubContext& RHSSubContext() { return m_rhsSubContext; }
uint32_t newNamespaceIndex() { return m_nextNamespaceIndex++; }
// By default, any encountered hole will be considered as required, ie
// they will count towards numUnknownValues() if we can't solve them.
// This function allows to temporarily disable this, so that any hole
// encountered from that point on will not count towards unresolved holes,
// unless they also appear in a section where holes are required.
void setValueResolutionRequired( bool required ) { m_valuesAreRequired = required; }
bool isValueResolutionRequired() const { return m_valuesAreRequired; }
const optional< Term >& getValue( uint32_t index ) const
{
assert( m_pCow->values.size() > index );
return m_pCow->values[index].m_term;
}
template< typename T > void setValue( uint32_t index, T&& val )
{
assert( m_pCow->values.size() > index );
if( m_pCow->values[index].m_required && !m_pCow->values[index].m_term )
--m_numUnknownValues;
CoW( m_pCow )->values[index] = { forward< T >( val ), true };
}
TypeCheckingContext& flip()
{
swap( m_lhsSubContext, m_rhsSubContext );
return *this;
}
uint32_t numUnknownValues() const { return m_numUnknownValues; }
int32_t cost() const { return m_cost; }
void addCost( int32_t c ) { m_cost += c; }
void setCost( int32_t cost ) { m_cost = cost; }
void addAnonymousHole() { ++m_numAnonymousHoles; }
auto score() const
{
return TypeCheckingScore( m_cost, 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 );
#ifndef NDEBUG
void TCRuleTrace( const TCRuleInfo* pRule ) const;
void PushRuleTraceForParam() const;
void DumpParamsTraces( ostream& out ) const;
#endif
private:
void setValueRequired( uint32_t index );
Context m_context;
struct StoredValue
{
optional< Term > m_term;
bool m_required = false;
};
SubContext m_lhsSubContext{ 1, 0 };
SubContext m_rhsSubContext{ 2, 0 };
uint32_t m_nextNamespaceIndex = 3;
uint32_t m_numUnknownValues = 0;
int32_t m_cost = 0;
uint32_t m_numAnonymousHoles = 0;
using HoleKey = tuple< StringId, uint32_t, uint32_t >;
struct Cow
{
vector< StoredValue > values;
map< HoleKey, uint32_t > holeDict;
unordered_set< uint32_t > lockedHoles;
#ifndef NDEBUG
// In debug, keep track of which "path" was taken through the various
// rules to end up with the result.
using TCTrace = vector< const TCRuleInfo* >;
mutable TCTrace currentTypeCheckingTrace;
mutable vector< TCTrace > paramsTypeCheckingTrace;
#endif
};
mutable ptr< Cow > m_pCow = make_shared< Cow >();
bool m_valuesAreRequired = true;
};
} // namespace goose::sema
#endif
|
Changes to bs/sema/tc-postproc.cpp.
1 2 3 4 5 6 7 8 9 10 |
#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.
// Likewise when encountering them during type checking.
void SetupPostProcUnificationRules( TypeCheckingRuleSet& ruleSet )
{
| | | | < | | < | | | < | < | | | | | 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 |
#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.
// Likewise when encountering them during type checking.
void SetupPostProcUnificationRules( TypeCheckingRuleSet& ruleSet )
{
ruleSet.addUnificationRule( 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, _] = *unwrap;
co_yield Unify( t, rhs, tcc );
} );
ruleSet.addUnificationRule( 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, _] = *unwrap;
co_yield Unify( t, rhs, tcc );
} );
ruleSet.addTypeCheckingRule( 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, _] = *unwrap;
co_yield TypeCheck( t, rhs, tcc );
} );
ruleSet.addTypeCheckingRule( ANYTERM( _ ),
VEC( TSID( postproc ), ANYTERM( _ ), ANYTERM( _ ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto unwrap = UnwrapPostprocFunc( rhs );
if( !unwrap )
co_return;
auto&& [t, _] = *unwrap;
co_yield TypeCheck( lhs, t, tcc );
} );
ruleSet.addTypeCheckingRule( VEC( TSID( postproc ), ANYTERM( _ ), ANYTERM( _ ) ),
VEC( TSID( postproc ), ANYTERM( _ ), ANYTERM( _ ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
auto lunwrap = UnwrapPostprocFunc( lhs );
if( !lunwrap )
co_return;
auto&& [lt, _] = *lunwrap;
auto runwrap = UnwrapPostprocFunc( rhs );
if( !runwrap )
co_return;
auto&& [rt, __] = *runwrap;
co_yield TypeCheck( lt, rt, tcc );
} );
}
} // namespace goose::sema
|
Changes to bs/sema/tc-ruleset.cpp.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
SetupBasicTypeCheckingRules( *this );
SetupBasicUnificationRules( *this );
SetupPostProcUnificationRules( *this );
SetupHoleUnificationRules( *this );
SetupQuoteUnificationRules( *this );
}
| | > | > | > > | > | | > | | > | > | | | | | 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 |
SetupBasicTypeCheckingRules( *this );
SetupBasicUnificationRules( *this );
SetupPostProcUnificationRules( *this );
SetupHoleUnificationRules( *this );
SetupQuoteUnificationRules( *this );
}
void TypeCheckingRuleSet::addTypeCheckingRule(
const Term& pat1, const Term& pat2, BinaryFunc f, bool exclusive, TCRuleInfo infos )
{
m_typeCheckingRules = Merge( m_typeCheckingRules, VEC( pat1, pat2 ),
[&]( auto&& ) { return BinaryRule{ f, infos, exclusive }; } );
}
void TypeCheckingRuleSet::addUnificationRule(
const Term& pat, BinaryFunc f, bool exclusive, TCRuleInfo infos )
{
m_uniRules = Merge(
m_uniRules, VEC( pat, pat ), [&]( auto&& ) { return BinaryRule{ f, infos, exclusive }; } );
}
TCGen FlippedContextAdapter(
const Term& lhs, const Term& rhs, TypeCheckingContext tcc, TypeCheckingRuleSet::BinaryFunc f )
{
for( auto&& [e, tcc] : f( rhs, lhs, tcc.flip() ) )
co_yield { move( e ), tcc.flip() };
}
void TypeCheckingRuleSet::addUnificationRule(
const Term& pat1, const Term& pat2, BinaryFunc f, bool exclusive, TCRuleInfo infos )
{
m_uniRules = Merge( m_uniRules, VEC( pat1, pat2 ),
[&]( auto&& ) { return BinaryRule{ f, infos, exclusive }; } );
m_uniRules = Merge( m_uniRules, VEC( pat2, pat1 ),
[&]( auto&& )
{
return BinaryRule{ BinaryFunc( [&, f]( auto&& lhs, auto&& rhs, auto&& c ) -> TCGen
{ return FlippedContextAdapter( lhs, rhs, c, f ); } ),
infos, exclusive };
} );
}
void TypeCheckingRuleSet::addHalfUnificationRule( const Term& pat, UnaryFunc f, TCRuleInfo infos )
{
m_halfUniRules = Merge( m_halfUniRules, pat, [&]( auto&& ) { return UnaryRule{ f, infos }; } );
}
|
Changes to bs/sema/tc-ruleset.h.
1 2 3 4 | #ifndef GOOSE_SEMA_TC_RULESET_H #define GOOSE_SEMA_TC_RULESET_H #ifndef NDEBUG | | > < > > > | > | | | | | | | | | | | | | | | | | | | | | | | > | > | | | | | < > | 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 |
#ifndef GOOSE_SEMA_TC_RULESET_H
#define GOOSE_SEMA_TC_RULESET_H
#ifndef NDEBUG
#define TCRULES_DEBUG
#endif
namespace goose::sema
{
class TypeCheckingContext;
#ifndef NDEBUG
struct TCRuleInfo
{
TCRuleInfo() {}
TCRuleInfo( const source_location& sl ) :
pFilename( sl.file_name() ),
line( sl.line() )
{
}
const char* pFilename = nullptr;
uint32_t line = 0;
};
#else
struct TCRuleInfo
{
TCRuleInfo() {}
TCRuleInfo( const source_location& sl ) {}
};
#endif
class TypeCheckingRuleSet
{
public:
using BinaryFunc =
function< TCGen( const Term& lhs, const Term& rhs, const TypeCheckingContext& ) >;
using UnaryFunc = function< optional< Term >( const Term& lhs, TypeCheckingContext& ) >;
struct BinaryRule
{
BinaryFunc func;
TCRuleInfo infos;
bool exclusive = false;
};
struct UnaryRule
{
UnaryFunc func;
TCRuleInfo infos;
};
TypeCheckingRuleSet();
void addTypeCheckingRule( const Term& pat1, const Term& pat2, BinaryFunc f,
bool exclusive = false, TCRuleInfo infos = source_location::current() );
void addUnificationRule( const Term& pat, BinaryFunc f, bool exclusive = false,
TCRuleInfo infos = source_location::current() );
void addUnificationRule( const Term& pat1, const Term& pat2, BinaryFunc f,
bool exclusive = false, TCRuleInfo infos = source_location::current() );
void addHalfUnificationRule(
const Term& pat, UnaryFunc f, TCRuleInfo infos = source_location::current() );
const auto& typeCheckingRules() const { return m_typeCheckingRules; }
const auto& uniRules() const { return m_uniRules; }
const auto& halfUniRules() const { return m_halfUniRules; }
private:
Trie< BinaryRule > m_typeCheckingRules;
Trie< BinaryRule > m_uniRules;
Trie< UnaryRule > m_halfUniRules;
};
} // namespace goose::sema
#endif
|
Changes to bs/sema/tc-score.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_SEMA_TC_SCORE_H
#define GOOSE_SEMA_TC_SCORE_H
namespace goose::sema
{
class TypeCheckingScore
{
| | | | | | < > > | | | | | | | | | | | | | < | | | < < < | | | | | | | | | | | | | | < < < | < < < | | | | | | | | | | > | | | 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 |
#ifndef GOOSE_SEMA_TC_SCORE_H
#define GOOSE_SEMA_TC_SCORE_H
namespace goose::sema
{
class TypeCheckingScore
{
public:
TypeCheckingScore() {}
TypeCheckingScore( int32_t cost, uint32_t uniqueHoles ) :
m_cost( cost ),
m_uniqueHoles( uniqueHoles )
{
}
auto& operator+=( const TypeCheckingScore& rhs )
{
m_cost += rhs.m_cost;
m_uniqueHoles += rhs.m_uniqueHoles;
return *this;
}
auto operator+( const TypeCheckingScore& rhs ) const
{
return TypeCheckingScore( m_cost + rhs.m_cost, m_uniqueHoles + rhs.m_uniqueHoles );
}
bool operator==( const TypeCheckingScore& rhs ) const
{
return m_cost == rhs.m_cost && m_uniqueHoles == rhs.m_uniqueHoles;
}
bool operator!=( const TypeCheckingScore& rhs ) const { return !operator==( rhs ); }
bool operator<( const TypeCheckingScore& rhs ) const
{
if( m_cost != rhs.m_cost )
return m_cost > rhs.m_cost;
return m_uniqueHoles > rhs.m_uniqueHoles;
}
bool operator>( const TypeCheckingScore& rhs ) const
{
if( m_cost != rhs.m_cost )
return m_cost < rhs.m_cost;
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_cost << ',' << s.m_uniqueHoles << ')';
}
private:
int32_t m_cost = 0;
uint32_t m_uniqueHoles = 0;
};
} // namespace goose::sema
namespace std
{
template< class CharT >
struct std::formatter< goose::sema::TypeCheckingScore, CharT >
: goose::util::SStreamFormatter< goose::sema::TypeCheckingScore, CharT >
{
};
} // namespace std
#endif
|
Changes to bs/sema/tc-vecgenerator.h.
1 2 3 4 5 |
#ifndef GOOSE_SEMA_TC_VECGENERATOR_H
#define GOOSE_SEMA_TC_VECGENERATOR_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 |
#ifndef GOOSE_SEMA_TC_VECGENERATOR_H
#define GOOSE_SEMA_TC_VECGENERATOR_H
namespace goose::sema
{
// VecGenerator wrapper that updates the current repetition state of the provided typechecking
// sub context
class TCVecGenerator
{
public:
TCVecGenerator( const Vector& cont ) :
m_gen( cont )
{
}
bool finished() const { return m_gen.finished(); }
bool fixedPartFinished() const { return m_gen.fixedPartFinished(); }
size_t repetitionIndex() const { return m_gen.repetitionIndex(); }
const Term& operator()( TypeCheckingContext::SubContext& sc )
{
if( auto ri = repetitionIndex() )
sc.repetitionIndex = ri;
return m_gen();
}
private:
VecGenerator m_gen;
};
} // namespace goose::sema
#endif
|
Changes to bs/sema/tctrie-typecheck.inl.
1 2 3 4 5 |
#ifndef GOOSE_SEMA_TCTRIE_TYPECHECK_INL
#define GOOSE_SEMA_TCTRIE_TYPECHECK_INL
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 |
#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( TCVecGenerator vgen,
const Vector& lhsVec, const Vector& solutionVec, const RepetitionNode& rptNode,
const TypeCheckingContext& tcc )
{
auto newC = tcc;
if( vgen.fixedPartFinished() )
{
newC.LHSSubContext().repetitionIndex = 0;
co_yield { lhsVec, solutionVec, rptNode.m_next, newC };
}
else
{
const auto& vt = vgen( newC.RHSSubContext() );
for( auto&& rt : Enumerate( rptNode.m_repetition ) )
{
auto localRhsVec = lhsVec;
localRhsVec.setRepetitionTerm( rt );
for( auto&& [s, tcc] : sema::TypeCheck( rt, vt, newC ) )
{
#ifndef NDEBUG
tcc.PushRuleTraceForParam();
#endif
++tcc.LHSSubContext().repetitionIndex;
auto v = Vector::MakeAppend( solutionVec, move( s ) );
co_yield TypeCheckRepetition( vgen, localRhsVec, v, rptNode, tcc );
}
}
}
}
template< typename U >
template< typename T >
Generator< tuple< Vector, Vector, const T&, TypeCheckingContext, TCVecGenerator > >
TCTrie< U >::TypeCheck( TCVecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec,
const branch_t< T >& branch, const TypeCheckingContext& tcc )
{
if( branch.index() == 0 )
{
auto newC = tcc;
const auto& vt = vgen( newC.RHSSubContext() );
for( auto&& [t, payload] : Enumerate( get< 0 >( branch )->m_trie ) )
{
auto lvec = Vector::MakeAppend( lhsVec, t );
for( auto&& [s, tcc] : sema::TypeCheck( t, vt, newC ) )
{
#ifndef NDEBUG
tcc.PushRuleTraceForParam();
#endif
auto v = Vector::MakeAppend( solutionVec, move( s ) );
co_yield TypeCheck( vgen, lvec, v, payload, tcc );
}
}
}
else
co_yield { lhsVec, solutionVec, 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();
if( auto it = m_fixedLengthBranches.find( exprLen.minLength() );
it != m_fixedLengthBranches.end() )
{
TCVecGenerator 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 };
|
| ︙ | ︙ | |||
89 90 91 92 93 94 95 |
for( auto&& [lv, s, pRptNode, tcc, g] : TypeCheck( vgen, lhs, sol, it->second, tcc ) )
co_yield TypeCheckRepetition( g, lv, s, *pRptNode, tcc );
++it;
}
}
| | > > | > | | > | > | | 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 |
for( auto&& [lv, s, pRptNode, tcc, g] : TypeCheck( vgen, lhs, sol, it->second, tcc ) )
co_yield TypeCheckRepetition( g, lv, s, *pRptNode, tcc );
++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< T >& branch, const TypeCheckingContext& tcc )
{
if( branch.index() == 0 )
{
for( auto&& [t, payload] : Enumerate( get< 0 >( branch )->m_trie ) )
{
auto newC = tcc;
auto lvec = Vector::MakeAppend( lhsVec, t );
if( auto s = sema::HalfUnify( t, newC ) )
co_yield HalfUnify(
lvec, Vector::MakeAppend( solutionVec, move( *s ) ), payload, newC );
}
}
else
co_yield { lhsVec, solutionVec, get< 1 >( branch ), tcc };
}
template< typename U >
typename TCTrie< U >::template TCGen< U > TCTrie< U >::halfUnify(
TypeCheckingContext tcc ) const
{
for( auto&& [len, branch] : m_fixedLengthBranches )
{
Vector lhs;
Vector sol;
for( auto&& [lv, s, content, c] : HalfUnify( lhs, sol, branch, tcc ) )
co_yield { lv, s, content, c };
}
for( auto&& [minLen, branch] : m_variableLengthBranches )
{
Vector lhs;
Vector sol;
for( auto&& [lv, sol, pRptNode, tcc] :
HalfUnify< p_rpt_node >( lhs, sol, branch, tcc ) )
{
for( auto&& rt : Enumerate( pRptNode->m_repetition ) )
{
if( auto s = sema::HalfUnify( rt, tcc ) )
{
auto localLhsVec = lv;
localLhsVec.setRepetitionTerm( rt );
auto localSol = sol;
localSol.setRepetitionTerm( move( *s ) );
co_yield { localLhsVec, localSol, pRptNode->m_next, tcc };
}
}
}
}
}
} // namespace goose::sema
#endif
|
Changes to bs/sema/tctrie.h.
1 2 3 4 5 |
#ifndef GOOSE_SEMA_TCTRIE_H
#define GOOSE_SEMA_TCTRIE_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 |
#ifndef GOOSE_SEMA_TCTRIE_H
#define GOOSE_SEMA_TCTRIE_H
namespace goose::sema
{
// Similar to an eir 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;
Generator< const U* > enumerate() const;
bool empty() const
{
return m_fixedLengthBranches.empty() && m_variableLengthBranches.empty();
}
private:
template< typename T > struct Node;
struct RepetitionNode;
template< typename T > using branch_t = variant< ptr< Node< T > >, T >;
using p_rpt_node = ptr< RepetitionNode >;
template< typename T, typename IT, typename F >
static branch_t< T > Merge( const branch_t< 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( TCVecGenerator vgen, const Vector& lhsVec,
const Vector& solutionVec, const RepetitionNode& rptNode,
const TypeCheckingContext& c );
template< typename T >
static Generator< tuple< Vector, Vector, const T&, TypeCheckingContext, TCVecGenerator > >
TypeCheck( TCVecGenerator vgen, const Vector& lhsVec, const Vector& solutionVec,
const branch_t< T >& branch, const TypeCheckingContext& c );
template< typename T >
static TCGen< T > HalfUnify( const Vector& lhsVec, const Vector& solutionVec,
const branch_t< T >& branch, const TypeCheckingContext& c );
template< typename T >
static Generator< const T* > EnumerateBranch( const branch_t< T >& branch );
template< typename T > struct Node
{
Trie< branch_t< T > > m_trie;
};
struct RepetitionNode
{
Trie<> m_repetition;
U m_next;
};
// All fixed length branches, by length.
unordered_map< size_t, branch_t< U > > m_fixedLengthBranches;
// All variable length branches, by min length.
map< size_t, branch_t< p_rpt_node > > m_variableLengthBranches;
};
} // namespace goose::sema
#endif
|
Changes to bs/sema/tctrie.inl.
1 2 3 4 5 |
#ifndef GOOSE_SEMA_TCTRIE_INL
#define GOOSE_SEMA_TCTRIE_INL
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 |
#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 >::template branch_t< T > TCTrie< U >::Merge(
const branch_t< T >& b, const IT& it, const IT& end, F&& next )
{
if( it == end )
{
if( b.index() == 1 )
return next( get< 1 >( b ) );
return next( T() );
}
assert( b.index() == 0 );
ptr< Node< T > > pNewNode;
const auto& pNode = get< 0 >( b );
if( pNode )
pNewNode = make_shared< Node< T > >( *pNode );
else
pNewNode = make_shared< Node< T > >();
function< branch_t< T >( const branch_t< T >& ) > f(
[&]( auto& payload )
{
auto nextIt = it;
++nextIt;
return Merge( payload, nextIt, end, forward< F >( next ) );
} );
pNewNode->m_trie = eir::Merge( pNewNode->m_trie, *it, move( f ) );
return pNewNode;
}
template< typename U >
template< typename F >
typename TCTrie< U >::p_rpt_node 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 = eir::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() )
{
auto f = [&]( auto&& payload )
{
// Merge the repetition
return Merge( payload, *v.repetitionTerm(), forward< F >( next ) );
};
auto it = pNewTrie->m_variableLengthBranches.find( length.minLength() );
if( it == pNewTrie->m_variableLengthBranches.end() )
{
pNewTrie->m_variableLengthBranches.emplace( length.minLength(),
Merge(
branch_t< p_rpt_node >(), v.terms().begin(), v.terms().end(), move( f ) ) );
}
else
{
it->second = Merge( it->second, v.terms().begin(), v.terms().end(), move( f ) );
}
}
else
{
auto it = pNewTrie->m_fixedLengthBranches.find( length.minLength() );
if( it == pNewTrie->m_fixedLengthBranches.end() )
{
pNewTrie->m_fixedLengthBranches.emplace( length.minLength(),
Merge( branch_t< U >(), v.terms().begin(), v.terms().end(),
forward< F >( next ) ) );
}
else
{
it->second =
Merge( it->second, v.terms().begin(), v.terms().end(), forward< F >( next ) );
}
}
return pNewTrie;
}
template< typename U >
template< typename T >
Generator< const T* > TCTrie< U >::EnumerateBranch( const branch_t< T >& branch )
{
if( holds_alternative< T >( branch ) )
co_yield &get< T >( branch );
else
{
auto node = get< ptr< Node< T > > >( branch );
if( !node )
co_return;
for( auto&& [_, branch] : Enumerate( node->m_trie ) )
co_yield EnumerateBranch( branch );
}
}
template< typename U > Generator< const U* > TCTrie< U >::enumerate() const
{
for( auto&& [_, branch] : m_fixedLengthBranches )
co_yield EnumerateBranch( branch );
for( auto&& [_, branch] : m_variableLengthBranches )
for( const auto& rptNode : EnumerateBranch( branch ) )
co_yield &( ( *rptNode )->m_next );
}
} // namespace goose::sema
#endif
|
Changes to bs/sema/template.cpp.
| ︙ | ︙ | |||
65 66 67 68 69 70 71 |
const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
if( !pTemplateRuleSet )
return;
pTemplateRuleSet->setup( c, tcc, tpl );
}
| > | | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
if( !pTemplateRuleSet )
return;
pTemplateRuleSet->setup( c, tcc, tpl );
}
uint64_t TemplateHashTypePredicates(
const Context& c, const TypeCheckingContext& tcc, const Term& tpl )
{
const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
if( !pTemplateRuleSet )
return 0;
return pTemplateRuleSet->hashTypePredicates( c, tcc, tpl );
}
|
| ︙ | ︙ | |||
91 92 93 94 95 96 97 |
{
const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
if( !pTemplateRuleSet )
return false;
return pTemplateRuleSet->isPackExpr( c, tpl );
}
| | | 92 93 94 95 96 97 98 99 |
{
const auto pTemplateRuleSet = GetTemplateRuleSet( c, tpl );
if( !pTemplateRuleSet )
return false;
return pTemplateRuleSet->isPackExpr( c, tpl );
}
} // namespace goose::sema
|
Changes to bs/sema/template.h.
1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_SEMA_TEMPLATE_H
#define GOOSE_SEMA_TEMPLATE_H
namespace goose::sema
{
extern ptr< TemplateRule > GetTemplateRuleSet( const Context& c, const Term& tpl );
extern bool PrepareTemplate( TemplateContext& c, const Term& tpl );
extern optional< Term > BuildTemplateSignature( const TemplateContext& c, const Term& tpl );
| | > | > < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#ifndef GOOSE_SEMA_TEMPLATE_H
#define GOOSE_SEMA_TEMPLATE_H
namespace goose::sema
{
extern ptr< TemplateRule > GetTemplateRuleSet( const Context& c, const Term& tpl );
extern bool PrepareTemplate( TemplateContext& c, const Term& tpl );
extern optional< Term > BuildTemplateSignature( const TemplateContext& c, const Term& tpl );
extern Generator< Value > BuildTemplateParamDecls(
const Context& c, const Term& tpl, TermGen& args );
extern void TemplateSetup( const Context& c, TypeCheckingContext& tcc, const Term& tpl );
extern uint64_t TemplateHashTypePredicates(
const Context& c, const TypeCheckingContext& tcc, const Term& tpl );
extern optional< Term > BuildTemplateArgPattern( const Context& c, const Term& tpl );
extern bool IsTemplatePackExpr( const Context& c, const Term& tpl );
} // namespace goose::sema
#endif
|
Changes to bs/sema/tests/tctrie-merge.cpp.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 |
auto vec6 = Vector::Make( HOLE( "a"_sid ), HOLE( "b"_sid ) );
auto vec7 = Vector::Make( TSTR( "foo" ), HOLE( "c"_sid ) );
auto vec8 = Vector::Make( HOLE( "a"_sid ), HOLE( "c"_sid ) );
auto vec9 = Vector::Make( TERM( vec5 ), TERM( vec1 ) );
| > | | | | | | | | | | | | | | | | | | < | < | 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 |
auto vec6 = Vector::Make( HOLE( "a"_sid ), HOLE( "b"_sid ) );
auto vec7 = Vector::Make( TSTR( "foo" ), HOLE( "c"_sid ) );
auto vec8 = Vector::Make( HOLE( "a"_sid ), HOLE( "c"_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( *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( 0, 0 ) ) );
REQUIRE( results[1] == make_pair( "vec10"s, TypeCheckingScore( 0, 3 ) ) );
REQUIRE( results[2] == make_pair( "vec11"s, TypeCheckingScore( 0, 2 ) ) );
REQUIRE( results[3] == make_pair( "vec12"s, TypeCheckingScore( 0, 1 ) ) );
REQUIRE( results[4] == make_pair( "vec13"s, TypeCheckingScore( 0, 1 ) ) );
|
| ︙ | ︙ |
Changes to bs/sema/tests/tctrie-typecheck.cpp.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 |
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;
| | | < | < | | > | > | | | | > | | | | | | | | > > | > > | > > | > > | | < > | < | < | < > | | < < | < > > | > > | | | 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 |
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() );
}
std::sort( solutions.begin(), solutions.end(),
[]( auto&& a, auto&& b ) { return get< 1 >( a ) < get< 1 >( b ); } );
return solutions;
}
} // namespace
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( 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 vec14 = Vector::Make( TSTR( "gg" ) );
vec14->setRepetitionTerm( HOLE( "_"_sid ) );
auto vec15 = Vector::Make( TSTR( "gg" ), TERM( 456U ), TSTR( "blah" ) );
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; } );
trie = trie->merge( *vec14, []( auto&& ) { return "vec14"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 );
auto sols15 = GetSortedSolutions( e, trie, *vec15 );
REQUIRE( sols1.size() == 1 );
REQUIRE( sols1[0]
== make_tuple(
VEC( TSTR( "foo" ), TSTR( "bar" ) ), "vec4"s, TypeCheckingScore( 0, 1 ) ) );
REQUIRE( sols3.size() == 1 );
REQUIRE( sols3[0]
== make_tuple(
VEC( TSTR( "meh" ), TSTR( "meh" ) ), "vec2"s, TypeCheckingScore( 0, 1 ) ) );
REQUIRE( sols5.size() == 2 );
REQUIRE( sols5[0]
== make_tuple(
VEC( TSTR( "foo" ), TSTR( "foo" ) ), "vec2"s, TypeCheckingScore( 0, 2 ) ) );
REQUIRE( sols5[1]
== make_tuple(
VEC( TSTR( "foo" ), TSTR( "bar" ) ), "vec4"s, TypeCheckingScore( 0, 2 ) ) );
REQUIRE( sols9.size() == 1 );
REQUIRE( sols9[0]
== make_tuple(
VEC( VEC( TSTR( "foo" ), TSTR( "bar" ) ), VEC( TSTR( "foo" ), TSTR( "bar" ) ) ),
"vec2"s, TypeCheckingScore( 1, 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( 1, 5 ) ) );
REQUIRE( sols13.size() == 2 );
REQUIRE( sols13[0]
== make_tuple(
VEC( TSTR( "gg" ), TSTR( "gg" ) ), "vec14"s, TypeCheckingScore( 0, 2 ) ) );
REQUIRE( sols13[1]
== make_tuple(
VEC( TSTR( "bar" ), TSTR( "bar" ) ), "vec4"s, TypeCheckingScore( 0, 2 ) ) );
REQUIRE( sols15.size() == 1 );
REQUIRE( sols15[0] == make_tuple( vec15, "vec14"s, TypeCheckingScore( 0, 2 ) ) );
}
}
}
|
Changes to bs/sema/tests/unify-holes.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <catch2/catch_all.hpp>
#include "sema/sema.h"
using namespace std;
using namespace goose;
using namespace goose::eir;
using namespace goose::sema;
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.
| | > | | 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 |
#include <catch2/catch_all.hpp>
#include "sema/sema.h"
using namespace std;
using namespace goose;
using namespace goose::eir;
using namespace goose::sema;
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 );
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 );
|
| ︙ | ︙ | |||
38 39 40 41 42 43 44 |
TypeCheckingContext tcc( ctxt );
auto g = Unify( lhs, rhs, tcc );
auto it = g.begin();
REQUIRE( it == g.end() );
}
| | | | | | | | < | < < | < < | | < < < | < | 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 |
TypeCheckingContext tcc( ctxt );
auto g = Unify( lhs, rhs, tcc );
auto it = g.begin();
REQUIRE( it == g.end() );
}
} // namespace
SCENARIO( "Unify works", "[unify]" )
{
WHEN( "Unifying various expressions" )
{
auto expr0 = HOLE( "a"_sid );
auto expr1 = VEC( TSTR( "foo" ), TSTR( "bar" ) );
auto expr2 = VEC( HOLE( "a"_sid ), HOLE( "a"_sid ) );
auto expr3 = VEC( TSTR( "meh" ), TSTR( "meh" ) );
auto expr4 = VEC( HOLE( "a"_sid ), TSTR( "bar" ) );
auto expr5 = VEC( TSTR( "foo" ), HOLE( "b"_sid ) );
auto expr6 = VEC( HOLE( "a"_sid ), HOLE( "a"_sid ) );
auto expr7 = VEC( TSTR( "foo" ), HOLE( "b"_sid ) );
auto expr8 = VEC( HOLE( "a"_sid ), HOLE( "a"_sid ) );
auto expr9 = VEC( expr5, expr1 );
auto expr10 = VEC( HOLE( "x"_sid ), expr4, HOLE( "z"_sid ), HOLE( "z"_sid ) );
auto expr11 = VEC( expr5, HOLE( "y"_sid ), HOLE( "y"_sid ), expr5 );
auto expr12 = VEC( VEC( HOLE( "a"_sid ), TSTR( "foo" ) ), HOLE( "a"_sid ) );
auto expr13 = VEC( HOLE( "b"_sid ), HOLE( "b"_sid ) );
auto expr14 = VEC( TSTR( "gg" ), REPEAT( HOLE( "_"_sid ) ) );
auto expr15 = VEC( TSTR( "gg" ), TERM( 456U ), TSTR( "blah" ) );
THEN( "Unifications yields the expected solutions" )
{
CheckForUniqueSolution( expr0, expr1, VEC( TSTR( "foo" ), TSTR( "bar" ) ), { 1, 1 } );
CheckForUniqueSolution( expr2, expr3, VEC( TSTR( "meh" ), TSTR( "meh" ) ), { 0, 1 } );
CheckForNoSolution( expr2, expr1 );
CheckForUniqueSolution( expr4, expr5, VEC( TSTR( "foo" ), TSTR( "bar" ) ), { 0, 2 } );
CheckForUniqueSolution( expr6, expr7, VEC( TSTR( "foo" ), TSTR( "foo" ) ), { 0, 2 } );
CheckForUniqueSolution( expr8, expr9,
VEC( VEC( TSTR( "foo" ), TSTR( "bar" ) ), VEC( TSTR( "foo" ), TSTR( "bar" ) ) ),
{ 1, 2 } );
CheckForUniqueSolution( expr10, expr11,
VEC( VEC( TSTR( "foo" ), TSTR( "bar" ) ), VEC( TSTR( "foo" ), TSTR( "bar" ) ),
VEC( TSTR( "foo" ), TSTR( "bar" ) ), VEC( TSTR( "foo" ), TSTR( "bar" ) ) ),
{ 1, 5 } );
CheckForNoSolution( expr12, expr13 );
CheckForUniqueSolution( expr14, expr13, VEC( TSTR( "gg" ), TSTR( "gg" ) ), { 0, 2 } );
CheckForUniqueSolution( expr14, expr15, expr15, { 0, 2 } );
}
}
}
|
Changes to bs/sema/tpl-ruleset.cpp.
1 2 3 4 5 6 7 |
#include "sema.h"
using namespace goose;
using namespace goose::sema;
void TemplateRuleSet::addRule( const Term& paramPat, ptr< TemplateRule >&& r )
{
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include "sema.h"
using namespace goose;
using namespace goose::sema;
void TemplateRuleSet::addRule( const Term& paramPat, ptr< TemplateRule >&& r )
{
m_rules = Merge( m_rules, paramPat, [&]( auto&& ) { return move( r ); } );
}
void TemplateContext::TVarOccurred( StringId name )
{
auto it = m_TVarPackMap.find( name );
if( it == m_TVarPackMap.end() )
{
|
| ︙ | ︙ |
Changes to bs/sema/tpl-ruleset.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_SEMA_TPL_RULESET_H
#define GOOSE_SEMA_TPL_RULESET_H
namespace goose::sema
{
class TemplateContext
{
| | | | < | > > | | > | | | | | | | | | | | | | | | > | > | > | > > | > > > > | | | | | > | | | < > | 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 |
#ifndef GOOSE_SEMA_TPL_RULESET_H
#define GOOSE_SEMA_TPL_RULESET_H
namespace goose::sema
{
class TemplateContext
{
public:
TemplateContext( const Context& c ) :
m_semaContext( c )
{
}
const auto& semaContext() const { return m_semaContext; }
uint32_t currentPackIndex() const { return m_currentPackIndex; }
void setCurrentPackIndex( uint32_t index ) { m_currentPackIndex = index; }
uint32_t newPackIndex() { return m_nextPackIndex++; }
void TVarOccurred( StringId name );
void TTVarOccurred( StringId name );
uint32_t getTVarPackIndex( StringId name ) const;
uint32_t getTTVarPackIndex( StringId name ) const;
private:
const Context& m_semaContext;
unordered_map< StringId, uint32_t > m_TVarPackMap;
unordered_map< StringId, uint32_t > m_TTVarPackMap;
uint32_t m_currentPackIndex = 0;
uint32_t m_nextPackIndex = 1;
};
class TemplateRule
{
public:
virtual ~TemplateRule() {}
virtual bool prepare( TemplateContext& c, const Term& tpl ) const { return true; }
virtual optional< Term > buildSignature(
const TemplateContext& c, const Term& tpl ) const = 0;
virtual Generator< Value > buildParamDecls(
const Context& c, const Term& param, TermGen& args ) const = 0;
virtual void setup( const Context& c, TypeCheckingContext& tcc, const Term& tpl ) const {};
virtual uint64_t hashTypePredicates(
const Context& c, const TypeCheckingContext& tcc, const Term& tpl ) const
{
return 0;
}
virtual optional< Term > buildArgPattern( const Context& c, const Term& tpl ) const = 0;
virtual bool isPackExpr( const Context& c, const Term& tpl ) const = 0;
};
class TemplateRuleSet
{
public:
TemplateRuleSet() {}
void addRule( const Term& paramPat, ptr< TemplateRule >&& r );
const auto& rules() const { return m_rules; }
private:
Trie< ptr< TemplateRule > > m_rules;
};
} // namespace goose::sema
#endif
|
Changes to bs/sema/typecheck.cpp.
1 2 | #include "sema.h" | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include "sema.h"
// #define VERBOSE_TC
#if !defined( NDEBUG ) && defined( VERBOSE_TC )
#define TC_LOG_RULES
#endif
namespace goose::sema
{
TypeCheckingSolution FindBestTyping( const Term& lhs, const Term& rhs, const Context& context )
{
optional< TypeCheckingContext > bestTCC;
|
| ︙ | ︙ | |||
53 54 55 56 57 58 59 |
const auto& rules = tcc.rules()->typeCheckingRules();
struct Candidate
{
MatchScore score;
const TypeCheckingRuleSet::BinaryRule* rule = nullptr;
| | < < < > | | > | 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 |
const auto& rules = tcc.rules()->typeCheckingRules();
struct Candidate
{
MatchScore score;
const TypeCheckingRuleSet::BinaryRule* rule = nullptr;
bool operator<( const Candidate& rhs ) const { return score < rhs.score; }
};
const TypeCheckingRuleSet::BinaryRule* exclusiveRule = nullptr;
priority_queue< Candidate > candidates;
auto expr = VEC( lhs, rhs );
for( auto&& [s, rule] : Match( expr, rules ) )
{
if( rule.exclusive )
{
if( exclusiveRule )
G_VAL_ERROR(
*EIRToValuePattern( rhs ), "ambiguous exclusive type checking rule" );
exclusiveRule = &rule;
}
else if( !exclusiveRule )
candidates.push( { s.score(), &rule } );
}
if( exclusiveRule )
{
#ifdef TC_LOG_RULES
cout << "calling exclusive typechecking rule " << exclusiveRule->infos.pFilename << ':'
<< exclusiveRule->infos.line << endl;
cout << " lhs " << lhs << endl;
cout << " rhs " << rhs << endl;
#endif
#ifndef NDEBUG
tcc.TCRuleTrace( &exclusiveRule->infos );
#endif
|
| ︙ | ︙ | |||
136 137 138 139 140 141 142 |
const Candidate& c = candidates.top();
if( c.score < bestScore )
break;
auto gen = c.rule->func( lhs, rhs, tcc );
if( gen.begin() != gen.end() )
{
| | | > | > | | | > | 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 |
const Candidate& c = candidates.top();
if( c.score < bestScore )
break;
auto gen = c.rule->func( lhs, rhs, tcc );
if( gen.begin() != gen.end() )
{
#ifdef TCRULES_DEBUG
G_VAL_ERROR( *EIRToValuePattern( rhs ),
format( "ambiguous type checking rule: {}:{} and {}:{} both match",
bestInfos->pFilename, bestInfos->line, c.rule->infos.pFilename,
c.rule->infos.line ) );
#else
G_VAL_ERROR( *EIRToValuePattern( rhs ), "ambiguous type checking rule" );
#endif
}
candidates.pop();
}
#ifdef TC_LOG_RULES
cout << "calling typechecking rule " << bestInfos->pFilename << ':' << bestInfos->line
<< endl;
cout << " lhs " << lhs << endl;
cout << " rhs " << rhs << endl;
#endif
co_yield move( *bestGen );
}
|
| ︙ | ︙ | |||
195 196 197 198 199 200 201 |
if( ambiguous )
G_ERROR( "ambiguous unification rule" );
if( !bestRule )
co_return;
#ifdef TC_LOG_RULES
| | > | 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
if( ambiguous )
G_ERROR( "ambiguous unification rule" );
if( !bestRule )
co_return;
#ifdef TC_LOG_RULES
cout << "calling unification rule " << bestRule->infos.pFilename << ':'
<< bestRule->infos.line << endl;
cout << " lhs " << lhs << endl;
cout << " rhs " << rhs << endl;
#endif
co_yield bestRule->func( lhs, rhs, tcc );
}
|
| ︙ | ︙ | |||
242 243 244 245 246 247 248 |
// (ns index 2) is skipped (due to being caught in a hole),
// we increase the cost of the current solution accordingly.
if( tcc.LHSSubContext().namespaceIndex == 2 )
tcc.addCost( GetWeight( lhs ) );
return bestRule->func( lhs, tcc );
}
| | | 245 246 247 248 249 250 251 252 |
// (ns index 2) is skipped (due to being caught in a hole),
// we increase the cost of the current solution accordingly.
if( tcc.LHSSubContext().namespaceIndex == 2 )
tcc.addCost( GetWeight( lhs ) );
return bestRule->func( lhs, tcc );
}
} // namespace goose::sema
|
Changes to bs/sema/typecheck.h.
1 2 3 4 5 |
#ifndef GOOSE_SEMA_TYPECHECK_H
#define GOOSE_SEMA_TYPECHECK_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 |
#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& tcc );
TCGen Unify( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc );
optional< Term > HalfUnify( const Term& lhs, TypeCheckingContext& tcc );
} // namespace goose::sema
#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 |
#include "sema.h"
namespace goose::sema
{
TCGen UnifyVectors(
TCVecGenerator lvg, TCVecGenerator rvg, const Vector& solutionVec, TypeCheckingContext tcc )
{
for( auto&& [u, tcc] :
Unify( lvg( tcc.LHSSubContext() ), rvg( tcc.RHSSubContext() ), tcc ) )
{
auto vec = Vector::MakeAppend( solutionVec, move( u ) );
if( lvg.fixedPartFinished() && rvg.fixedPartFinished() )
co_yield { TERM( make_shared< Vector >( move( vec ) ) ), tcc };
else
co_yield UnifyVectors( lvg, rvg, vec, tcc );
}
}
void SetupBasicUnificationRules( TypeCheckingRuleSet& ruleSet )
{
// Default half-unification rule
ruleSet.addHalfUnificationRule( ANYTERM( _ ),
[]( const Term& lhs, TypeCheckingContext& tcc )
{
// NOTE: the score here is always 0 because:
// Vectors are going to fall into the vector half-unification rule below,
// and holes are going to have their own rules. So we only are here for terminal
// expressions.
return lhs;
} );
// Identity unification rule
ruleSet.addUnificationRule( 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( 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 = get< LocationId >( lhs ).index();
auto rloc = get< LocationId >( rhs ).index();
// 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 { LocationId( result ), tcc };
} );
// void* unification rule
ruleSet.addUnificationRule( 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
ruleSet.addHalfUnificationRule( VECOFLENGTH( _ ),
[]( const Term& lhs, TypeCheckingContext& tcc ) -> optional< Term >
{
assert( holds_alternative< pvec >( lhs ) );
const auto& lVector = get< pvec >( lhs );
const auto& lvg = lVector->terms();
Vector result;
for( auto&& t : lvg )
{
auto s = HalfUnify( t, tcc );
if( !s )
return nullopt;
result.append( *s );
}
return TERM( make_shared< Vector >( move( result ) ) );
} );
// Unification rule for vectors of identical length:
// They are recursed into and each contained members are unified.
// Note: the repetition term, if any, is ignored. The repetition term is only for vector
// patterns for now. We'll have to see how to handle variadic functions later.
ruleSet.addUnificationRule( 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 );
TCVecGenerator lvg( *lVector );
TCVecGenerator rvg( *rVector );
if( lvg.fixedPartFinished() && rvg.fixedPartFinished() )
{
co_yield { lhs, tcc };
co_return;
}
Vector sol;
co_yield UnifyVectors( lvg, rvg, sol, tcc );
} );
}
} // namespace goose::sema
|
Changes to bs/sema/uni-holes.cpp.
| ︙ | ︙ | |||
96 97 98 99 100 101 102 |
tcc.lockHole( index );
auto holeExpr = HOLE( StringId( index ), h.kind(), h.behavior() );
auto& maybeVal = tcc.getValue( index );
if( maybeVal )
{
| | | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
tcc.lockHole( index );
auto holeExpr = HOLE( StringId( index ), h.kind(), h.behavior() );
auto& maybeVal = tcc.getValue( index );
if( maybeVal )
{
for( auto&& [e, tcc] : Unify( *maybeVal, rhs, tcc ) )
{
tcc.unlockHole( index );
tcc.setValue( index, move( e ) );
co_yield { move( holeExpr ), tcc };
}
}
else if( auto s = HalfUnify( rhs, tcc.flip() ) )
|
| ︙ | ︙ | |||
141 142 143 144 145 146 147 |
rname = rh.name();
rindex = tcc.getRHSHoleIndex( rname, rh.behavior() );
}
else
rindex = rh.name().id();
// If neither hole currently have a value, create a new one.
| | > | | | | | | | | 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 |
rname = rh.name();
rindex = tcc.getRHSHoleIndex( rname, rh.behavior() );
}
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, lh.behavior(), index );
tcc.setRHSHoleIndex( rname, rh.behavior(), index );
auto lk = TERM( lh.kind() );
auto rk = TERM( rh.kind() );
for( auto&& [k, tcc] : Unify( lk, rk, tcc ) )
{
assert( holds_alternative< StringId >( k ) );
if( holds_alternative< StringId >( k ) )
co_yield { HOLE( StringId( index ), get< StringId >( k ) ), tcc };
}
co_return;
}
// If both holes actually point to the same value, just yield it as the solution.
if( lindex == rindex )
{
auto lk = TERM( lh.kind() );
auto rk = TERM( rh.kind() );
for( auto&& [k, tcc] : Unify( lk, rk, tcc ) )
{
assert( holds_alternative< StringId >( k ) );
if( holds_alternative< StringId >( k ) )
co_yield { HOLE( StringId( lindex ), get< StringId >( k ) ), 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, lh.behavior(), rindex );
auto lk = TERM( lh.kind() );
auto rk = TERM( rh.kind() );
for( auto&& [k, tcc] : Unify( lk, rk, tcc ) )
{
assert( holds_alternative< StringId >( k ) );
if( holds_alternative< StringId >( k ) )
co_yield { HOLE( StringId( rindex ), get< StringId >( k ) ), tcc };
}
co_return;
}
if( rindex == TypeCheckingContext::InvalidIndex )
{
tcc.setRHSHoleIndex( rname, rh.behavior(), lindex );
auto lk = TERM( lh.kind() );
auto rk = TERM( rh.kind() );
for( auto&& [k, tcc] : Unify( lk, rk, tcc ) )
{
assert( holds_alternative< StringId >( k ) );
if( holds_alternative< StringId >( k ) )
co_yield { HOLE( StringId( lindex ), get< StringId >( k ) ), 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 )
{
if( HalfUnify( *lval, tcc ) )
{
tcc.unlockHole( lindex );
tcc.unlockHole( rindex );
auto lk = TERM( lh.kind() );
auto rk = TERM( rh.kind() );
for( auto&& [k, tcc] : Unify( lk, rk, tcc ) )
{
assert( holds_alternative< StringId >( k ) );
if( !holds_alternative< StringId >( k ) )
continue;
auto kind = get< StringId >( k );
tcc.setValue( rindex, HOLE( StringId( lindex ), kind ) );
|
| ︙ | ︙ | |||
248 249 250 251 252 253 254 |
if( HalfUnify( *rval, tcc.flip() ) )
{
tcc.unlockHole( lindex );
tcc.unlockHole( rindex );
auto lk = TERM( lh.kind() );
auto rk = TERM( rh.kind() );
| | | | | | | < < < | | < < | < < < | | < < | < < | < < < | < < < | < < < < | < < < | < < | < | < | < < | < | < | < < | < | < | < < < | < < < < | < < < | < < < < | | < < > | 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 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 |
if( HalfUnify( *rval, tcc.flip() ) )
{
tcc.unlockHole( lindex );
tcc.unlockHole( rindex );
auto lk = TERM( lh.kind() );
auto rk = TERM( rh.kind() );
for( auto&& [k, tcc] : Unify( lk, rk, tcc ) )
{
assert( holds_alternative< StringId >( k ) );
if( !holds_alternative< StringId >( k ) )
continue;
auto kind = get< StringId >( k );
tcc.setValue( lindex, HOLE( StringId( rindex ), kind ) );
co_yield { HOLE( StringId( rindex ), kind ), 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.
for( auto&& [e, tcc] : Unify( *lval, *rval, tcc ) )
{
tcc.unlockHole( lindex );
tcc.unlockHole( rindex );
tcc.setValue( lindex, move( e ) );
auto lk = TERM( lh.kind() );
auto rk = TERM( rh.kind() );
for( auto&& [k, tcc] : Unify( lk, rk, tcc ) )
{
assert( holds_alternative< StringId >( k ) );
if( !holds_alternative< StringId >( k ) )
continue;
auto kind = get< StringId >( k );
tcc.setValue( rindex, HOLE( StringId( lindex ), kind ) );
co_yield { HOLE( StringId( lindex ), kind ), tcc };
}
}
}
} // namespace
namespace goose::sema
{
void SetupHoleUnificationRules( TypeCheckingRuleSet& ruleSet )
{
ruleSet.addUnificationRule( TSID( anykind ), ANYTERM( _ ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{ co_yield { rhs, tcc }; } );
ruleSet.addUnificationRule( TSID( anykind ), TSID( anykind ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{ co_yield { rhs, tcc }; } );
ruleSet.addHalfUnificationRule(
HOLE( "_"_sid, ""_sid, Hole::Behavior::Any ), HalfUniAnonHole );
ruleSet.addHalfUnificationRule( HolePattern(), HalfUniHole );
ruleSet.addUnificationRule(
HOLE( "_"_sid, ""_sid, Hole::Behavior::Any ), ANYTERM( _ ), UniAnonHoleAny );
ruleSet.addUnificationRule( HolePattern(), ANYTERM( _ ), UniHoleAny );
ruleSet.addUnificationRule( HolePattern(), UniHoles );
// Any typecheck involving a hole on one side
// becomes an unification
ruleSet.addTypeCheckingRule( HolePattern(), ANYTERM( _ ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{ co_yield Unify( lhs, rhs, tcc ); } );
ruleSet.addTypeCheckingRule( ANYTERM( _ ), HolePattern(),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{ co_yield Unify( lhs, rhs, tcc ); } );
ruleSet.addTypeCheckingRule( HolePattern(), HolePattern(),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{ co_yield Unify( lhs, rhs, tcc ); } );
// Forwarding holes unification
ruleSet.addUnificationRule(
HOLE( "_"_sid, "fwd"_sid, Hole::Behavior::Any ), ANYTERM( _ ), UniAnonHoleAny, true );
ruleSet.addUnificationRule( ForwardingHolePattern(), ANYTERM( _ ), UniHoleAny, true );
ruleSet.addUnificationRule( ForwardingHolePattern(), UniHoles, true );
// Typechecking forwarding holes is the same as typechecking holes
// except that we directly call the hole unification rules,
// rather than going through Unify: this way, we prevent any rule
// from overriding the default behavior, such as the reference
// unification rules that forbid unification against anything else
// than a ref
ruleSet.addTypeCheckingRule(
ValueToEIR( ValuePattern( ANYTERM( _ ), ForwardingHolePattern(), ANYTERM( _ ) ) ),
ANYTERM( _ ),
[]( const Term& lhs, const Term& rhs, TypeCheckingContext tcc ) -> TCGen
{
auto lVal = *EIRToValuePattern( lhs );
auto h = *ForwardingHoleFromIRExpr( lVal.type() );
auto rVal = *EIRToValue( rhs );
TCGen g;
if( h.name() == "_"_sid )
g = UniAnonHoleAny( lVal.type(), rVal.type(), tcc );
else
g = UniHoleAny( lVal.type(), rVal.type(), tcc );
for( auto&& [s, tcc] : g )
if( auto result = HalfUnify( rhs, tcc.flip() ) )
co_yield { move( *result ), tcc.flip() };
},
true );
}
} // namespace goose::sema
|
Changes to bs/sema/uni-quote.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "sema.h"
namespace goose::sema
{
Term Quote( const Term& t )
{
return SetWeight( VEC( TSID( quote ), t ), 0 );
}
optional< Term > Unquote( const Term& t )
{
| | < < < < < | < < < | 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 |
#include "sema.h"
namespace goose::sema
{
Term Quote( const Term& t )
{
return SetWeight( VEC( TSID( quote ), t ), 0 );
}
optional< Term > Unquote( const Term& t )
{
auto result = Decompose( t, Vec( Lit( "quote"_sid ), SubTerm() ) );
if( !result )
return nullopt;
auto&& [quoted] = *result;
return quoted;
}
void SetupQuoteUnificationRules( TypeCheckingRuleSet& ruleSet )
{
ruleSet.addHalfUnificationRule( VEC( TSID( quote ), ANYTERM( _ ) ),
[]( const Term& lhs, TypeCheckingContext& tcc ) { return lhs; } );
ruleSet.addUnificationRule( VEC( TSID( quote ), ANYTERM( _ ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
if( lhs == rhs )
co_yield { lhs, tcc };
} );
|
| ︙ | ︙ | |||
48 49 50 51 52 53 54 |
ruleSet.addTypeCheckingRule( ANYTERM( _ ), VEC( TSID( quote ), ANYTERM( _ ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
if( lhs == rhs )
co_yield { lhs, tcc };
} );
| | > | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
ruleSet.addTypeCheckingRule( ANYTERM( _ ), VEC( TSID( quote ), ANYTERM( _ ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
if( lhs == rhs )
co_yield { lhs, tcc };
} );
ruleSet.addTypeCheckingRule( VEC( TSID( quote ), ANYTERM( _ ) ),
VEC( TSID( quote ), ANYTERM( _ ) ),
[]( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc ) -> TCGen
{
if( lhs == rhs )
co_yield { lhs, tcc };
} );
}
} // namespace goose::sema
|
Changes to bs/sema/uni-quote.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#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 );
| | | 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 );
} // namespace goose::sema
#endif
|
Changes to bs/util/bigint.cpp.
| ︙ | ︙ | |||
37 38 39 40 41 42 43 |
return APInt( APInt::getBitsNeeded( str, 10 ) + 1, str, 10 );
}
BigInt BigInt::FromHexString( const string& str )
{
return APInt( str.size() * 4 + 1, str, 16 );
}
| | | 37 38 39 40 41 42 43 44 |
return APInt( APInt::getBitsNeeded( str, 10 ) + 1, str, 10 );
}
BigInt BigInt::FromHexString( const string& str )
{
return APInt( str.size() * 4 + 1, str, 16 );
}
} // namespace goose::util
|
Changes to bs/util/bigint.h.
1 2 3 4 5 6 7 8 |
#ifndef GOOSE_UTIL_BIGINT_H
#define GOOSE_UTIL_BIGINT_H
namespace goose::util
{
using llvm::APInt;
using llvm::APSInt;
| | > | | | | | < < | < | | | | | | | | | | | | | | | | | < < < | | | | < < | | | | | | | | | | | > | | > | > | | | | | < < | | | | | | | | | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < < < | | < < < | | < | < | | | | < | < | | | | < | < | | | | | | | | | | | | | | | | | 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 |
#ifndef GOOSE_UTIL_BIGINT_H
#define GOOSE_UTIL_BIGINT_H
namespace goose::util
{
using llvm::APInt;
using llvm::APSInt;
// This is a wrapper around llvm::APSInt used to represent compile time integer with "infinite"
// width.
//
// It wraps the various operations so that they work on integers of different sizes, where the
// smallest one is automatically sign extended first.
//
// It also wraps arithmetic operations to automatically extend the size to make sure that the
// result won't overflow.
//
// They are always signed.
class BigInt
{
public:
BigInt() { m_value.setIsSigned( true ); }
BigInt( APSInt&& v );
BigInt( const APSInt& v );
BigInt( APInt&& v ) :
m_value( move( v ) )
{
m_value.setIsSigned( true );
}
BigInt( const APInt& v ) :
m_value( v )
{
m_value.setIsSigned( true );
}
static BigInt FromBinString( const string& str );
static BigInt FromDecString( const string& str );
static BigInt FromHexString( const string& str );
static BigInt FromU32( uint32_t val ) { return APSInt::getUnsigned( val ).trunc( 33 ); }
static BigInt FromU64( uint64_t val ) { return APSInt::getUnsigned( val ).zext( 65 ); }
uint64_t toU64() const { return m_value.getLimitedValue(); }
int64_t toS64() const
{
return isNegative() ? -m_value.getLimitedValue() : m_value.getLimitedValue();
}
auto toString() const
{
llvm::SmallString< 16 > s;
m_value.toString( s, 10 );
return s;
}
const auto& getAPSInt() const { return m_value; }
operator const APSInt &() { return m_value; }
auto getActiveBits() const { return m_value.getActiveBits(); }
auto getSignificantBits() const { return m_value.getSignificantBits(); }
bool isNegative() const { return m_value.isNegative(); }
bool fitsAsS64() const { return getSignificantBits() <= 63; }
auto zext( uint32_t size ) const { return m_value.zext( size ); }
auto sext( uint32_t size ) const { return m_value.sext( size ); }
friend ostream& operator<<( ostream& out, const BigInt& bi )
{
llvm::SmallString< 16 > s;
bi.m_value.toString( s, 10 );
return out << s.c_str();
}
void toHex( ostream& out ) const
{
llvm::SmallString< 16 > s;
m_value.toString( s, 16 );
out << s.c_str();
}
private:
template< typename F >
static auto applyOpOnMixedSizes( const BigInt& lhs, const BigInt& rhs, F&& func )
{
if( lhs.m_value.getBitWidth() < rhs.m_value.getBitWidth() )
return func( lhs.m_value.sext( rhs.m_value.getBitWidth() ), rhs.m_value );
if( lhs.m_value.getBitWidth() > rhs.m_value.getBitWidth() )
return func( lhs.m_value, rhs.m_value.sext( lhs.m_value.getBitWidth() ) );
return func( lhs.m_value, rhs.m_value );
}
template< typename F >
static auto extendAndApplyOp(
uint32_t wantedSize, const BigInt& lhs, const BigInt& rhs, F&& func )
{
return func( lhs.m_value.sext( wantedSize ), rhs.m_value.sext( wantedSize ) );
}
public:
auto operator==( const BigInt& rhs ) const
{
return applyOpOnMixedSizes(
*this, rhs, []( const APInt& l, const APInt& r ) { return l == r; } );
}
auto operator!=( const BigInt& rhs ) const
{
return applyOpOnMixedSizes(
*this, rhs, []( const APInt& l, const APInt& r ) { return l != r; } );
}
auto operator>( const BigInt& rhs ) const
{
return applyOpOnMixedSizes(
*this, rhs, []( const APInt& l, const APInt& r ) { return l.sgt( r ); } );
}
auto operator>=( const BigInt& rhs ) const
{
return applyOpOnMixedSizes(
*this, rhs, []( const APInt& l, const APInt& r ) { return l.sge( r ); } );
}
auto operator<( const BigInt& rhs ) const
{
return applyOpOnMixedSizes(
*this, rhs, []( const APInt& l, const APInt& r ) { return l.slt( r ); } );
}
auto operator<=( const BigInt& rhs ) const
{
return applyOpOnMixedSizes(
*this, rhs, []( const APInt& l, const APInt& r ) { return l.sle( r ); } );
}
BigInt operator&( const BigInt& rhs ) const
{
return applyOpOnMixedSizes(
*this, rhs, []( const APInt& l, const APInt& r ) { return l & r; } );
}
BigInt operator|( const BigInt& rhs ) const
{
return applyOpOnMixedSizes(
*this, rhs, []( const APInt& l, const APInt& r ) { return l | r; } );
}
BigInt operator^( const BigInt& rhs ) const
{
return applyOpOnMixedSizes(
*this, rhs, []( const APInt& l, const APInt& r ) { return l ^ r; } );
}
BigInt shl( const APSInt& rhs ) const
{
return sext( getSignificantBits() + rhs.getLimitedValue() ).shl( rhs );
}
BigInt ashr( const APSInt& rhs ) const { return m_value.ashr( rhs ); }
BigInt lshr( const APSInt& rhs ) const { return m_value.lshr( rhs ); }
BigInt operator+( const BigInt& rhs ) const
{
return extendAndApplyOp( max( getSignificantBits(), rhs.getSignificantBits() ) + 1,
*this, rhs, []( const APInt& l, const APInt& r ) { return l + r; } );
}
BigInt operator-( const BigInt& rhs ) const
{
return extendAndApplyOp( max( getSignificantBits(), rhs.getSignificantBits() ) + 1,
*this, rhs, []( const APInt& l, const APInt& r ) { return l - r; } );
}
BigInt operator*( const BigInt& rhs ) const
{
return extendAndApplyOp( getSignificantBits() + rhs.getSignificantBits(), *this, rhs,
[]( const APInt& l, const APInt& r ) { return l * r; } );
}
BigInt operator/( const BigInt& rhs ) const
{
return applyOpOnMixedSizes(
*this, rhs, []( const APInt& l, const APInt& r ) { return l.sdiv( r ); } );
}
BigInt operator%( const BigInt& rhs ) const
{
return applyOpOnMixedSizes(
*this, rhs, []( const APInt& l, const APInt& r ) { return l.srem( r ); } );
}
private:
llvm::APSInt m_value;
};
} // namespace goose::util
namespace std
{
template<> struct hash< goose::util::BigInt >
{
size_t operator()( const goose::util::BigInt& x ) const
{
return llvm::hash_value( x.getAPSInt() );
}
};
} // namespace std
#endif
|
Changes to bs/util/depgraph.cpp.
| ︙ | ︙ | |||
13 14 15 16 17 18 19 |
if( haveUnmetDependencies( dependee ) )
return;
llvm::SmallVector< const void*, 8 > pendingDeps;
auto [first, last] = m_reverseDeps.equal_range( dependee );
| | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
if( haveUnmetDependencies( dependee ) )
return;
llvm::SmallVector< const void*, 8 > pendingDeps;
auto [first, last] = m_reverseDeps.equal_range( dependee );
for( auto&& [_, depender] : ranges::subrange( first, last ) )
{
m_deps.erase( depender );
if( m_deps.count( depender ) == 0 )
pendingDeps.emplace_back( depender );
}
|
| ︙ | ︙ | |||
35 36 37 38 39 40 41 |
satisfy( dep );
}
bool DependencyGraph::haveUnmetDependencies( const void* depender ) const
{
return m_deps.count( depender ) != 0;
}
| | | 35 36 37 38 39 40 41 42 |
satisfy( dep );
}
bool DependencyGraph::haveUnmetDependencies( const void* depender ) const
{
return m_deps.count( depender ) != 0;
}
} // namespace goose::util
|
Changes to bs/util/depgraph.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_UTIL_DEPGRAPH_H
#define GOOSE_UTIL_DEPGRAPH_H
namespace goose::util
{
class DependencyGraph
{
| | < | | | | | | | | | | | | | | < > | 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 |
#ifndef GOOSE_UTIL_DEPGRAPH_H
#define GOOSE_UTIL_DEPGRAPH_H
namespace goose::util
{
class DependencyGraph
{
public:
template< typename F > void setCallBack( const void* depender, F&& callback )
{
m_callbacks.emplace( depender, callback );
}
void add( const void* depender, const void* dependee );
void satisfy( const void* dependee );
bool haveUnmetDependencies( const void* depender ) const;
bool empty() const { return m_deps.empty(); }
// TODO add something to detect and diagnose cyclic dependencies
private:
unordered_multimap< const void*, const void* > m_deps;
unordered_multimap< const void*, const void* > m_reverseDeps;
using callback = function< void() >;
unordered_map< const void*, callback > m_callbacks;
};
} // namespace goose::util
#endif
|
Changes to bs/util/fixedint.h.
1 2 3 4 5 6 7 8 |
#ifndef GOOSE_UTIL_FIXEDINT_H
#define GOOSE_UTIL_FIXEDINT_H
namespace goose::util
{
using llvm::APInt;
using llvm::APSInt;
| | > | | | | | > | > > | | | | | | | < | | > | < | < < < | | 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 |
#ifndef GOOSE_UTIL_FIXEDINT_H
#define GOOSE_UTIL_FIXEDINT_H
namespace goose::util
{
using llvm::APInt;
using llvm::APSInt;
// This is a wrapper around llvm::APSInt used to represent compile time integer with "fixed"
// width.
//
// The only thing that it does is to provide equality comparison even when signedness/bit size
// differ, so that we can put them in hash maps.
class FixedInt : public APSInt
{
public:
template< typename... T >
FixedInt( T&&... args ) :
APSInt( forward< T >( args )... )
{
}
FixedInt( const FixedInt& rhs ) = default;
FixedInt( FixedInt&& rhs ) = default;
FixedInt& operator=( const FixedInt& ) = default;
FixedInt& operator=( FixedInt&& ) = default;
bool operator==( const FixedInt& rhs ) const
{
return isUnsigned() == rhs.isUnsigned() && getBitWidth() == rhs.getBitWidth()
&& eq( rhs );
}
};
} // namespace goose::util
namespace std
{
template<> struct hash< goose::util::FixedInt >
{
size_t operator()( const goose::util::FixedInt& x ) const { return llvm::hash_value( x ); }
};
} // namespace std
#endif
|
Changes to bs/util/generator.h.
1 2 3 4 5 |
#ifndef GOOSE_UTIL_GENERATOR_H
#define GOOSE_UTIL_GENERATOR_H
namespace goose::util
{
| | | | | > | < | | | | > | > | > | | | | | | | | | | | | | | | | | | < < < | < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | > | | | | | | | | | | | > | > | | | | < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | > | > | | | | | | | | | | | | | < | < < < | | | | < | | | | | | | | | | | | | | | | | < < < | | | | | | | | | | | | < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
#ifndef GOOSE_UTIL_GENERATOR_H
#define GOOSE_UTIL_GENERATOR_H
namespace goose::util
{
// A generator have a source: the coroutine that it currently needs to resume to obtain a new
// value. We say that this coroutine currently have control (it is where the whole interruptible
// call tree continues) A generator also have a parent: the parent coroutine, to which control
// should be given (back) upon completion of the current control coroutine. (ie "where to return
// to")
// A promise have a consumer: this is the generator currently consuming our coro's output.
// When yielding a value, this value is passed to our consummer.
// When yielding a generator, we should transfer control to that generator's source coroutine
// by setting its parent to the current coro, and making the generator's source the source of
// our consumer.
template< typename T > class Generator
{
public:
struct suspend
{
bool ready = false;
bool await_ready() const noexcept { return ready; }
constexpr void await_suspend( coroutine_handle<> ) const noexcept {}
constexpr void await_resume() const noexcept {}
};
struct promise_type
{
auto initial_suspend() noexcept
{
// We suspend the coro at the start because if it is a nested generator that
// is yielded by value, we need a chance to setup the necessary control
// transfer links before we start it.
return suspend_always();
}
auto final_suspend() noexcept
{
m_state = State::Returned;
return suspend_never();
}
void unhandled_exception() noexcept { terminate(); }
auto get_return_object() noexcept { return Generator( *this ); }
// Yield a T
auto yield_value( T&& v ) noexcept
{
m_state = State::Ready;
m_pResult = &v;
return suspend_always();
}
auto yield_value( const T& v ) noexcept
{
m_state = State::Ready;
m_pResult = &v;
return suspend_always();
}
// Yield a nested generator (to be used when we want to pass everything
// yielded from one generator to our caller as is)
suspend yield_value( Generator&& g ) noexcept
{
m_state = State::Yielding;
// Our consumer becomes the consumer of the nested coroutine.
auto& other_p = g.m_source.promise();
other_p.m_pConsumer = m_pConsumer;
m_pConsumer->m_source = g.m_source;
g.m_source = nullptr;
auto* otherParentCaller = &other_p;
while( otherParentCaller->m_returnCoro )
otherParentCaller = &otherParentCaller->m_returnCoro.promise();
assert( !otherParentCaller->m_returnCoro );
// Set ourself up as the coroutine to transfer control to once the nested
// one is finished.
otherParentCaller->m_returnCoro =
coroutine_handle< promise_type >::from_promise( *this );
assert( otherParentCaller->m_returnCoro );
// For our consumer the fact that its next values are coming from a nested
// coroutine is transparent, so resume the nested coroutine right away to
// give it the next value that it is already expecting. Unless it has
// already been resumed, for instance to compare iterators to check whether
// the nested coroutine yielded anything.
if( other_p.m_state == State::InitialSuspend )
coroutine_handle< promise_type >::from_promise( other_p ).resume();
m_state = State::Ready;
// If the nested coroutine was an empty generator, it will have set us back
// as the source for the consumer generator, but couldn't resume us directly
// since we are already running. In this case, simply request not to be
// suspended now so that we can proceed.
return { m_pConsumer->m_source
== coroutine_handle< promise_type >::from_promise( *this ) };
}
void return_void() { m_state = State::Returned; }
auto& value() { return *m_pResult; }
~promise_type()
{
m_pConsumer->m_source = m_returnCoro;
if( m_returnCoro )
{
// Set the consumer back to the caller coroutine: it may
// have been moved since the last time our caller was
// active.
m_returnCoro.promise().m_pConsumer = m_pConsumer;
if( m_state == State::Returned
&& m_returnCoro.promise().m_state != State::Yielding )
{
m_returnCoro.resume();
}
}
}
T* m_pResult = nullptr;
// The generator currently consuming our results.
Generator* m_pConsumer = nullptr;
// Coroutine to transfer control back to once we end, if any.
coroutine_handle< promise_type > m_returnCoro;
// Indicate if we have encountered a final return in the coroutine,
// In which case, upon destruction of this promise, we'll resume
// the return coro, if any.
enum class State
{
InitialSuspend, // At initial suspend, waiting to be started once everything
// is set up
Ready,
Returned, // Encountered a return, meaning its calling generator should be
// resumed
Yielding, // Inside of yield_value, which means that it can't be resumed at
// the moment
};
State m_state = State::InitialSuspend;
};
class iterator
{
public:
using iterator_category = input_iterator_tag;
using difference_type = size_t;
using value_type = remove_reference_t< T >;
using reference = value_type&;
using pointer = value_type*;
iterator( Generator* pGen ) :
m_pGenerator( pGen )
{
}
bool operator==( const iterator& rhs ) const { return coro() == rhs.coro(); }
bool operator!=( const iterator& rhs ) const { return coro() != rhs.coro(); }
iterator& operator++()
{
assert( m_pGenerator );
assert( m_pGenerator->m_source );
m_pGenerator->m_source.resume();
if( !m_pGenerator->m_source )
m_pGenerator = nullptr;
return *this;
}
reference operator*() const noexcept
{
assert( m_pGenerator );
assert( m_pGenerator->m_source );
return m_pGenerator->m_source.promise().value();
}
pointer operator->() const noexcept { return addressof( operator*() ); }
private:
coroutine_handle< promise_type > coro() const
{
if( !m_pGenerator )
return {};
if( m_pGenerator->m_source.done() )
return {};
return m_pGenerator->m_source;
}
Generator* m_pGenerator = nullptr;
};
bool finished() const { return !m_source || m_source.done(); }
// Consume the next result from the generator
// (will CRASH if called on a finished() generator)
T consume()
{
assert( !finished() );
// This is fine.
// FOR A CLOWN
// TO CONSUME DATA FROM THE CIRCUS' GENERATOR
auto it = begin();
auto result = *it;
++it;
return result;
}
// should be called "current()" really, but it's easier like this
iterator begin()
{
if( !m_source )
return end();
// Since the coro starts suspended, we have to kick it
// to obtain the first element if it hasn't been started
// yet.
if( m_source.promise().m_state == promise_type::State::InitialSuspend )
{
m_source.resume();
if( !m_source )
return end();
}
return iterator( this );
}
iterator end() noexcept { return iterator( nullptr ); }
Generator() {}
Generator( Generator&& rhs ) :
m_source( rhs.m_source )
{
assert( m_source );
*this = move( rhs );
}
~Generator()
{
// If a generator is destroyed before consuming all of its content,
// make sure that the coroutine of every nested generators currently
// active are destroyed starting from the leaf (ie in the same order
// that c++ function scopes are normally deleted)
while( m_source )
{
auto src = m_source;
m_source = nullptr;
src.destroy();
}
}
// Move operator: if the source is the current consumer
// of the source coroutine, update the consumer pointer
// in that coro's promise.
Generator& operator=( Generator&& rhs )
{
m_source = rhs.m_source;
if( !m_source )
return *this;
rhs.m_source = nullptr;
auto& p = m_source.promise();
if( p.m_pConsumer == &rhs )
p.m_pConsumer = this;
return *this;
}
private:
Generator( const Generator& ) = delete;
Generator( promise_type& p ) :
m_source( coroutine_handle< promise_type >::from_promise( p ) )
{
assert( m_source );
p.m_pConsumer = this;
}
coroutine_handle< promise_type > m_source;
};
} // namespace goose::util
#endif
|
Changes to bs/util/graphviz.cpp.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 |
const char* base09 = "dc9656";
const char* base0A = "f7ca88";
const char* base0B = "a1b56c";
const char* base0C = "86c1b9";
const char* base0D = "7cafc2";
const char* base0E = "ba8baf";
const char* base0F = "a16946";
| | | < < < < < | < < < < < | | | > > > | > | | > | 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 |
const char* base09 = "dc9656";
const char* base0A = "f7ca88";
const char* base0B = "a1b56c";
const char* base0C = "86c1b9";
const char* base0D = "7cafc2";
const char* base0E = "ba8baf";
const char* base0F = "a16946";
} // namespace goose::util::colors
namespace
{
const char* accent_colors[] = { colors::base08, colors::base09, colors::base0A, colors::base0B,
colors::base0C, colors::base0D, colors::base0E, colors::base0F };
}
namespace goose::util
{
const char* GraphVizBuilder::BGColor = colors::base00;
const char* GraphVizBuilder::TextColor = colors::base05;
GraphVizBuilder::GraphVizBuilder( ostream& output, bool vertical ) :
m_output( output )
{
m_output << "digraph \"goose\"\n"
"{\n"
"\tbgcolor=\"#"
<< BGColor
<< "\";\n"
"\trankdir="
<< ( vertical ? "TB" : "LR" )
<< ";\n"
"\tnode [ shape=none ];\n"
"\tedge [ color=\"#"
<< TextColor << "\" ];\n";
}
GraphVizBuilder::~GraphVizBuilder()
{
while( !m_workQueue.empty() )
{
m_workQueue.front()();
|
| ︙ | ︙ | |||
78 79 80 81 82 83 84 |
{
m_output << '\n';
return indent();
}
const char* GraphVizBuilder::GetNodeColor( uint32_t id )
{
| | | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
{
m_output << '\n';
return indent();
}
const char* GraphVizBuilder::GetNodeColor( uint32_t id )
{
return accent_colors[id % ( sizeof( accent_colors ) / sizeof( const char* ) )];
}
uint32_t GraphVizBuilder::getNodeId( const void* pNodeAddress )
{
auto it = m_nodesDict.find( pNodeAddress );
if( it != m_nodesDict.end() )
return it->second;
|
| ︙ | ︙ | |||
101 102 103 104 105 106 107 |
return m_generatedNodes.find( pNodeAddress ) != m_generatedNodes.end();
}
void GraphVizBuilder::addEdge( uint32_t fromId, uint32_t toId )
{
auto portId = m_portCount - 1;
| | > | < | < | | < | | > | 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 |
return m_generatedNodes.find( pNodeAddress ) != m_generatedNodes.end();
}
void GraphVizBuilder::addEdge( uint32_t fromId, uint32_t toId )
{
auto portId = m_portCount - 1;
queueWork(
[=, this]
{
newLine() << "object" << fromId << ':' << portId << " -> object" << toId
<< "[ color=\"#" << GetNodeColor( toId ) << "\" ]";
} );
}
GraphVizBuilder::Indention::Indention( GraphVizBuilder& builder ) :
m_builder( builder )
{
++m_builder.m_indention;
}
GraphVizBuilder::Indention::~Indention()
{
--m_builder.m_indention;
}
GraphVizBuilder::Table::Table( GraphVizBuilder& builder, const char* pColor ) :
m_builder( builder )
{
m_builder.newLine() << "<table border=\"0\" cellborder=\"1\" cellspacing=\"0\""
" color=\"#"
<< pColor << "\">";
++m_builder.m_indention;
}
GraphVizBuilder::Table::~Table()
{
--m_builder.m_indention;
|
| ︙ | ︙ | |||
166 167 168 169 170 171 172 |
--m_builder.m_indention;
m_builder.newLine() << "</td>";
}
GraphVizBuilder::Color::Color( GraphVizBuilder& builder, const char* pColor ) :
m_builder( builder )
{
| < | > > | | < | | > | 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 |
--m_builder.m_indention;
m_builder.newLine() << "</td>";
}
GraphVizBuilder::Color::Color( GraphVizBuilder& builder, const char* pColor ) :
m_builder( builder )
{
m_builder.newLine() << "<font face=\"Noto Sans\" point-size=\"12\" color=\"#" << pColor
<< "\">";
++m_builder.m_indention;
}
GraphVizBuilder::Color::~Color()
{
--m_builder.m_indention;
m_builder.newLine() << "</font>";
}
GraphVizBuilder::Node::Node(
GraphVizBuilder& builder, const void* pAddress, const char* pTitle ) :
m_builder( builder )
{
m_builder.m_generatedNodes.insert( pAddress );
m_builder.m_currentNode = m_builder.getNodeId( pAddress );
m_builder.newLine() << "object" << m_builder.m_currentNode;
m_builder.newLine() << "[ label=<";
++m_builder.m_indention;
auto color = GetNodeColor( m_builder.m_currentNode );
m_builder.newLine() << "<table border=\"0\" cellborder=\"1\" cellspacing=\"0\""
" color=\"#"
<< color << "\">";
++m_builder.m_indention;
if( pTitle )
{
Row row( m_builder );
Cell cell( m_builder );
|
| ︙ | ︙ | |||
215 216 217 218 219 220 221 |
{
--m_builder.m_indention;
m_builder.newLine() << "</table>";
--m_builder.m_indention;
m_builder.newLine() << "> ]";
}
| | | 210 211 212 213 214 215 216 217 |
{
--m_builder.m_indention;
m_builder.newLine() << "</table>";
--m_builder.m_indention;
m_builder.newLine() << "> ]";
}
} // namespace goose::util
|
Changes to bs/util/graphviz.h.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 |
extern const char* base09;
extern const char* base0A;
extern const char* base0B;
extern const char* base0C;
extern const char* base0D;
extern const char* base0E;
extern const char* base0F;
| | | | | | | > | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 |
extern const char* base09;
extern const char* base0A;
extern const char* base0B;
extern const char* base0C;
extern const char* base0D;
extern const char* base0E;
extern const char* base0F;
} // namespace colors
// Helper class to dump data structures into a graphiz dot file.
class GraphVizBuilder
{
public:
GraphVizBuilder( ostream& output, bool vertical = false );
~GraphVizBuilder();
ostream& indent() const;
ostream& newLine() const;
ostream& output() const { return m_output; }
static const char* BGColor;
static const char* TextColor;
static const char* GetNodeColor( uint32_t id );
uint32_t getNodeId( const void* pNodeAddress );
uint32_t currentNodeId() const { return m_currentNode; }
bool wasNodeGenerated( const void* pNodeAddress ) const;
template< typename F > void queueWork( F&& func )
{
m_workQueue.push( forward< F >( func ) );
}
void addEdge( uint32_t fromId, uint32_t toId );
class Indention
{
public:
Indention( GraphVizBuilder& builder );
~Indention();
private:
GraphVizBuilder& m_builder;
};
class Table
{
public:
Table( GraphVizBuilder& builder, const char* pColor = TextColor );
~Table();
private:
GraphVizBuilder& m_builder;
};
class Row
{
public:
Row( GraphVizBuilder& builder );
~Row();
private:
GraphVizBuilder& m_builder;
};
class Cell
{
public:
Cell( GraphVizBuilder& builder );
~Cell();
private:
GraphVizBuilder& m_builder;
};
class Color
{
public:
Color( GraphVizBuilder& builder, const char* pColor = TextColor );
~Color();
private:
GraphVizBuilder& m_builder;
};
class Node
{
public:
Node( GraphVizBuilder& builder, const void* pAddress, const char* pTitle = nullptr );
~Node();
private:
GraphVizBuilder& m_builder;
};
private:
ostream& m_output;
unordered_map< const void*, uint32_t > m_nodesDict;
unordered_set< const void* > m_generatedNodes;
queue< function< void() > > m_workQueue;
uint32_t m_indention = 1;
uint32_t m_nodeCount = 0;
uint32_t m_currentNode = 0;
uint32_t m_portCount = 0;
};
} // namespace goose::util
#endif
|
Changes to bs/util/location.cpp.
1 2 3 4 5 |
#include "util.h"
namespace goose::util
{
unordered_set< string > Location::ms_filenames;
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include "util.h"
namespace goose::util
{
unordered_set< string > Location::ms_filenames;
vector< Location > Location::ms_locations;
uint32_t LocationPoint::toLoc( uint32_t length ) const
{
return Location::Create( *this, length );
}
uint32_t LocationPoint::toLoc( const LocationPoint& end ) const
|
| ︙ | ︙ | |||
21 22 23 24 25 26 27 |
}
optional< Location > Location::Get( LocationId locationId )
{
if( locationId.invalid() )
return nullopt;
| | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
}
optional< Location > Location::Get( LocationId locationId )
{
if( locationId.invalid() )
return nullopt;
uint32_t index = ( locationId.index() & 0x7fff'ffff ) - 1;
if( index >= ms_locations.size() )
return nullopt;
return ms_locations[index];
}
LocationId Location::CreateSpanningLocation( LocationId loc )
|
| ︙ | ︙ | |||
98 99 100 101 102 103 104 |
return false;
if( l2end < l1->column() )
return false;
return true;
}
| | | 98 99 100 101 102 103 104 105 |
return false;
if( l2end < l1->column() )
return false;
return true;
}
} // namespace goose::util
|
Changes to bs/util/location.h.
1 2 3 4 5 6 7 8 9 10 11 |
#ifndef GOOSE_UTIL_LOCATION_H
#define GOOSE_UTIL_LOCATION_H
namespace goose::util
{
class LocationId;
// Partial representation of a location, useful to construct a location
// in two steps.
class LocationPoint
{
| | | | | | | < | > > | > | > | > | | | | | | | | | | > | | | | | < | > > | | | | | | < > > | | | | | | < | > > | > | > | > | > | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | 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 |
#ifndef GOOSE_UTIL_LOCATION_H
#define GOOSE_UTIL_LOCATION_H
namespace goose::util
{
class LocationId;
// Partial representation of a location, useful to construct a location
// in two steps.
class LocationPoint
{
public:
LocationPoint( const string& filename, uint32_t offset, uint32_t line, uint32_t column ) :
m_filename( &filename ),
m_offset( offset ),
m_line( line ),
m_column( column )
{
}
const auto& filename() const { return *m_filename; }
auto offset() const { return m_offset; }
auto line() const { return m_line; }
auto column() const { return m_column; }
uint32_t toLoc( uint32_t length = 1 ) const;
uint32_t toLoc( const LocationPoint& end ) const;
private:
const string* m_filename = nullptr;
uint32_t m_offset = 0;
uint32_t m_line = 0;
uint32_t m_column = 0;
};
class Location
{
public:
Location( const string& filename, uint32_t offset, uint32_t length, uint32_t line,
uint32_t column ) :
m_filename( filename ),
m_offset( offset ),
m_length( length ),
m_line( line ),
m_column( column )
{
}
Location( const LocationPoint& ls, uint32_t length ) :
m_filename( ls.filename() ),
m_offset( ls.offset() ),
m_length( length ),
m_line( ls.line() ),
m_column( ls.column() )
{
}
Location( const source_location& sloc ) :
m_filename( GetCachedFilename( sloc.file_name() ) ),
m_offset( ~0U ),
m_length( 1 ),
m_line( sloc.line() ),
m_column( sloc.column() )
{
}
const auto& filename() const { return m_filename; }
auto offset() const { return m_offset; }
auto length() const { return m_length; }
auto line() const { return m_line; }
auto column() const { return m_column; }
bool isOffsetValid() const { return m_offset != ~0U; }
static const string& GetCachedFilename( const string& filename );
template< typename... T > static uint32_t Create( T&&... args )
{
ms_locations.emplace_back( forward< T >( args )... );
return ms_locations.size();
}
static optional< Location > Get( LocationId locationId );
// Given two input locations, create and return a location that spans both.
// The two input locations must be from the same file.
static LocationId CreateSpanningLocation( LocationId loc1, LocationId loc2 );
static LocationId CreateSpanningLocation( LocationId loc );
static LocationId CreateSpanningLocation();
template< typename H, typename... T >
static LocationId CreateSpanningLocation( H locHead, T... locTail );
// Checks if two locations overlap. Only consider the first line spanned by
// each location.
static bool Overlaps( LocationId loc1, LocationId loc2 );
private:
const string& m_filename;
uint32_t m_offset = 0;
uint32_t m_length = 0;
uint32_t m_line = 0;
uint32_t m_column = 0;
static unordered_set< string > ms_filenames;
static vector< Location > ms_locations;
};
} // namespace goose::util
#endif
|
Changes to bs/util/location.inl.
1 2 3 4 5 6 7 8 9 10 |
#ifndef GOOSE_UTIL_LOCATION_INL
#define GOOSE_UTIL_LOCATION_INL
namespace goose::util
{
template< typename H, typename... T >
LocationId Location::CreateSpanningLocation( H locHead, T... locTail )
{
return CreateSpanningLocation( locHead, CreateSpanningLocation( locTail... ) );
}
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 |
#ifndef GOOSE_UTIL_LOCATION_INL
#define GOOSE_UTIL_LOCATION_INL
namespace goose::util
{
template< typename H, typename... T >
LocationId Location::CreateSpanningLocation( H locHead, T... locTail )
{
return CreateSpanningLocation( locHead, CreateSpanningLocation( locTail... ) );
}
} // namespace goose::util
#endif
|
Changes to bs/util/locationid.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_UTIL_LOCATIONID_H
#define GOOSE_UTIL_LOCATIONID_H
namespace goose::util
{
class LocationId
{
| | | | > | > > | > | | > | | | | | | | | | > | < | < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#ifndef GOOSE_UTIL_LOCATIONID_H
#define GOOSE_UTIL_LOCATIONID_H
namespace goose::util
{
class LocationId
{
public:
LocationId() = default;
LocationId( uint32_t index ) :
m_index( index )
{
}
bool invalid() const { return m_index == 0; }
uint32_t index() const { return m_index; }
bool isPoison() const { return m_index & 0x8000'0000; }
void setPoison() { m_index |= 0x8000'0000; }
auto operator<=>( const LocationId& ) const = default;
static auto Poison( LocationId loc = Location::Create( source_location::current() ) )
{
return LocationId( loc.m_index | 0x8'0000'0000 );
}
friend ostream& operator<<( ostream& o, const LocationId& loc ) { return o << loc.m_index; }
private:
uint32_t m_index = 0;
};
} // namespace goose::util
namespace std
{
template<> struct hash< goose::util::LocationId >
{
size_t operator()( const goose::util::LocationId& x ) const { return x.index(); }
};
} // namespace std
#endif
|
Changes to bs/util/profiling.h.
1 2 3 4 5 6 | #ifndef GOOSE_UTIL_PROFILING_H #define GOOSE_UTIL_PROFILING_H // To avoid requiring the tracy dependency to be downloaded even when not building // a profiling version, we provide dummy versions of the profiling macros here. #ifdef TRACY_ENABLE | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#ifndef GOOSE_UTIL_PROFILING_H
#define GOOSE_UTIL_PROFILING_H
// To avoid requiring the tracy dependency to be downloaded even when not building
// a profiling version, we provide dummy versions of the profiling macros here.
#ifdef TRACY_ENABLE
#include "public/tracy/Tracy.hpp"
#define ProfileZoneScoped ZoneScoped
#define ProfileZoneScopedN ZoneScopedN
#define ProfileZoneName ZoneName
#else
#define ProfileZoneScoped
#define ProfileZoneScopedN( n )
#define ProfileZoneName( n, s )
#endif
#endif
|
Changes to bs/util/stringid.cpp.
| ︙ | ︙ | |||
25 26 27 28 29 30 31 |
{
auto it = ms_strings().find( s );
if( it != ms_strings().end() )
{
m_id = it->second;
#ifndef NDEBUG
| | | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
{
auto it = ms_strings().find( s );
if( it != ms_strings().end() )
{
m_id = it->second;
#ifndef NDEBUG
m_dbgName = str().c_str();
#endif
return;
}
m_id = ms_nextUniqueId();
ms_strings().emplace( s, m_id );
ms_IDs().emplace( m_id, s );
|
| ︙ | ︙ | |||
47 48 49 50 51 52 53 |
{
auto it = ms_strings().find( s );
if( it != ms_strings().end() )
{
m_id = it->second;
#ifndef NDEBUG
| | | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
{
auto it = ms_strings().find( s );
if( it != ms_strings().end() )
{
m_id = it->second;
#ifndef NDEBUG
m_dbgName = str().c_str();
#endif
return;
}
m_id = ms_nextUniqueId();
ms_strings().emplace( s, m_id );
ms_IDs().emplace( m_id, move( s ) );
|
| ︙ | ︙ |
Changes to bs/util/stringid.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_UTIL_STRINGID_H
#define GOOSE_UTIL_STRINGID_H
namespace goose::util
{
class StringId
{
| | | > > > > | | | | < | > > | | < | < | < | < > | > | | < < < | | < < < | | | | | < < | | | | | | | | | | | | | 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 |
#ifndef GOOSE_UTIL_STRINGID_H
#define GOOSE_UTIL_STRINGID_H
namespace goose::util
{
class StringId
{
public:
StringId() :
StringId( "" )
{
}
explicit StringId( const string& str );
explicit StringId( string&& str );
explicit StringId( const char* pString ) :
StringId( string( pString ) )
{
}
explicit StringId( const char* pString, size_t size ) :
StringId( string( pString, size ) )
{
}
explicit StringId( uint32_t uniqueId );
auto operator<=>( StringId rhs ) const { return m_id <=> rhs.m_id; }
auto operator==( StringId rhs ) const { return m_id == rhs.m_id; }
auto operator<( StringId rhs ) const { return m_id < rhs.m_id; }
const string& str() const;
friend ostream& operator<<( ostream& out, StringId sid ) { return out << sid.str(); }
bool isNumerical() const { return !( m_id & stringMask ); }
auto id() const { return m_id; }
private:
static constexpr uint32_t stringMask = 1 << 31;
uint32_t m_id = 0;
#ifndef NDEBUG
// To be able to see the name in the debugger
const char* m_dbgName = nullptr;
#endif
static unordered_map< string, uint32_t >& ms_strings();
static unordered_map< uint32_t, string >& ms_IDs();
static uint32_t ms_nextUniqueId();
};
} // namespace goose::util
namespace std
{
template<> struct hash< goose::util::StringId >
{
size_t operator()( const goose::util::StringId& x ) const
{
if( x.isNumerical() )
return hash< uint32_t >()( x.id() );
return hash< string >()( x.str() );
}
};
} // namespace std
static inline auto operator"" _sid( const char* pString, std::size_t s )
{
return goose::util::StringId( pString, s );
}
#endif
|
Changes to bs/util/tests/generator.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <catch2/catch_all.hpp>
#include "util/util.h"
using namespace std;
using namespace goose::util;
namespace
{
// Sentinel object to verify that everything in every generator scopes
// gets properly deleted
class Sentinel
{
| | | > | | | | | 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 |
#include <catch2/catch_all.hpp>
#include "util/util.h"
using namespace std;
using namespace goose::util;
namespace
{
// Sentinel object to verify that everything in every generator scopes
// gets properly deleted
class Sentinel
{
public:
Sentinel() { ++ms_count; }
~Sentinel() { --ms_count; }
static size_t Count() { return ms_count; }
private:
static size_t ms_count;
};
size_t Sentinel::ms_count = 0;
// Simple generator outputing three strings
Generator< string > TestGen3()
{
|
| ︙ | ︙ | |||
48 49 50 51 52 53 54 |
for( auto&& x : TestGen3() )
co_yield x + "!";
co_yield "bar";
co_yield EmptyGen();
co_yield "blah";
| < | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
for( auto&& x : TestGen3() )
co_yield x + "!";
co_yield "bar";
co_yield EmptyGen();
co_yield "blah";
auto g = TestGen3();
// Peeking at the content of a nested generator
// before yielding it shouldn't affect the result.
if( g.begin() == g.end() )
co_return;
|
| ︙ | ︙ | |||
90 91 92 93 94 95 96 |
auto gg = TestGen4();
if( gg.begin() != gg.end() )
co_yield "ok";
co_yield move( gg );
}
| | | < < < < | < < < < | < < < > | > | < < < < < < < | 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 |
auto gg = TestGen4();
if( gg.begin() != gg.end() )
co_yield "ok";
co_yield move( gg );
}
} // namespace
SCENARIO( "Generators work", "[generator]" )
{
WHEN( "Consuming the test generator's content" )
{
vector< string > result;
for( auto&& x : TestGen() )
result.emplace_back( x );
THEN( "The results are correct" )
{
REQUIRE( result
== vector< string >{ "fuck", "foo", "aa!", "ggggg!", "bbbb!", "bar", "blah", "aa",
"ggggg", "bbbb", "shit" } );
REQUIRE( Sentinel::Count() == 0 );
}
}
WHEN( "Destroying a generator after consuming only part of its results" )
{
{
auto g = TestGen();
auto it = g.begin();
advance( it, 3 );
}
THEN( "There should be no leak" )
{
REQUIRE( Sentinel::Count() == 0 );
}
}
WHEN(
"Yielding a generator whose coroutine already started and which yielded another nested "
"generator" )
{
vector< string > result;
for( auto&& x : TestGen5() )
result.emplace_back( x );
THEN( "The results are correct" )
{
REQUIRE( result == vector< string >{ "ok", "aa", "ggggg", "bbbb", "fuck" } );
REQUIRE( Sentinel::Count() == 0 );
}
}
}
|
Changes to bs/util/util.cpp.
1 2 3 4 5 6 7 8 9 |
#include "util.h"
#include "diagnostics/diagnostics.h"
namespace goose::util
{
using namespace diagnostics;
uint32_t GenerateNewUID()
{
| | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include "util.h"
#include "diagnostics/diagnostics.h"
namespace goose::util
{
using namespace diagnostics;
uint32_t GenerateNewUID()
{
static uint32_t nextUid = 0x8000'0000;
return nextUid++;
}
} // namespace goose::util
|
Changes to bs/util/util.h.
| ︙ | ︙ | |||
46 47 48 49 50 51 52 |
// since it's not in the standard yet... (based on
// boost's hash_combine)
template< typename A, typename B > struct hash< pair< A, B > >
{
template< typename P > size_t operator()( P&& x ) const
{
size_t result = hash< A >()( x.first );
| | < | < < < | < | | < | < | < < | | < | | | | | < | < > | | | < | < | | | | | | < < < | | < < < | | < < < | < < < | | | < | < | < | < < | | < | < > | < | > | < | | < | < < | | > | < | < | < > | | < > | | < | | | | | | | < | | | | | | > > | > | 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 |
// since it's not in the standard yet... (based on
// boost's hash_combine)
template< typename A, typename B > struct hash< pair< A, B > >
{
template< typename P > size_t operator()( P&& x ) const
{
size_t result = hash< A >()( x.first );
result ^= hash< B >()( x.second ) + 0x9e37'79b9 + ( result << 6 ) + ( result >> 2 );
return result;
}
};
template<> struct hash< llvm::APSInt >
{
size_t operator()( const llvm::APSInt& x ) const { return llvm::hash_value( x ); }
};
} // namespace std
namespace goose
{
using namespace std;
}
#include "generator.h"
#include "stringid.h"
#include "fixedint.h"
#include "bigint.h"
#include "depgraph.h"
#include "location.h"
#include "locationid.h"
#include "location.inl"
#include "graphviz.h"
namespace goose::util
{
// C++ representation of a compilation time sequence
template< typename T > using Sequence = llvm::SmallVector< T, 4 >;
template< class T, class CharT > struct SStreamFormatter : std::formatter< string, CharT >
{
template< class TT, class FormatContext > auto format( TT&& t, FormatContext& fc ) const
{
stringstream sstr;
sstr << t;
return std::formatter< string, CharT >::format( sstr.str(), fc );
}
};
template< typename T > auto ComputeHash( const T& x )
{
return std::hash< T >()( x );
}
template< typename C > Generator< uint64_t > ContainerHashGenerator( const C& c )
{
for( const auto& x : c )
co_yield ComputeHash( x );
}
template< typename T > class NullInit
{
public:
NullInit() {}
template< typename TT >
NullInit( TT&& other ) :
m_value( forward< TT >( other ) )
{
}
operator T() { return m_value; }
template< typename TT > NullInit& operator=( TT&& rhs )
{
m_value = forward< TT >( rhs );
return *this;
}
T& operator->() { return m_value; }
const T& operator->() const { return m_value; }
T& operator*() { return m_value; }
const T& operator*() const { return m_value; }
private:
T m_value = nullptr;
};
template< typename T > using ptr = shared_ptr< T >;
template< typename T > using wptr = weak_ptr< T >;
template< typename T > struct remove_sptr
{
using type = T;
};
template< typename T > struct remove_sptr< ptr< T > >
{
using type = T;
};
template< typename T > using remove_sptr_t = typename remove_sptr< T >::type;
// moo
template< typename T > static auto& CoW( ptr< T >& ptr )
{
if( ptr.use_count() > 1 )
ptr = make_shared< T >( *ptr );
return ptr;
}
template< typename T > struct is_optional : public false_type
{
};
template< typename T > struct is_optional< optional< T > > : public true_type
{
};
template< typename T > constexpr bool is_optional_v = is_optional< T >::value;
template< typename T > struct remove_optional
{
using type = T;
};
template< typename T > struct remove_optional< optional< T > >
{
using type = T;
};
template< typename T > using remove_optional_t = typename remove_optional< T >::type;
// FFS why can't is_convertible just tell me that a type isn't convertible to a variant
// Just look at this garbage
template< typename T, typename variant_type, int i = 0,
int size = variant_size_v< variant_type > >
struct SIsTypeInVariant
: public conditional_t< is_same_v< T, variant_alternative_t< i, variant_type > >, true_type,
SIsTypeInVariant< T, variant_type, i + 1 > >
{
};
template< typename T, typename variant_type, int size >
struct SIsTypeInVariant< T, variant_type, size, size > : public false_type
{
};
template< typename T, typename variant_type >
constexpr bool IsTypeInVariant = SIsTypeInVariant< T, variant_type >::value;
// Simple observer pattern implementation. Can't unsubscribe, don't need it
// for now anyway.
template< typename... T > class Signal
{
public:
void operator()( const T&... args )
{
for( auto&& o : m_observers )
o( args... );
}
template< typename F > void subscribe( F&& observer )
{
m_observers.emplace_back( forward< F >( observer ) );
}
private:
vector< function< void( T... ) > > m_observers;
};
// Simple uid generator
extern uint32_t GenerateNewUID();
static inline bool IsUid( uint32_t x )
{
return x & 0x8000'0000;
}
} // namespace goose::util
#endif
|
Changes to bs/verify/basicblock.cpp.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 |
m_builder.convertedBBIndices().emplace( bbid );
m_viz.addBlock( bbid, bb.index() );
m_builder.setCurrentBB( bbid );
optional< z3::expr > pred;
| | > | | | | | | | < < | | 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 |
m_builder.convertedBBIndices().emplace( bbid );
m_viz.addBlock( bbid, bb.index() );
m_builder.setCurrentBB( bbid );
optional< z3::expr > pred;
m_remapper.forEachIncomingEdge( bbid,
[&]( auto&& predIndex, auto&& expr )
{
auto edgeExpr = makeEdgeExpression( predIndex, bbid, expr );
if( pred )
pred = *pred || edgeExpr;
else
pred = edgeExpr;
} );
auto& c = GetZ3Context();
auto predVar = c.bool_const( format( "b{}", bbid ).c_str() );
if( pred )
m_builder.add( predVar == *pred );
m_builder.setCurrentBBPredicate( predVar );
for( auto&& x : bb.instructions() )
if( !BuildZ3Op( m_builder, x ) )
m_builder.setCheckFailed();
// We pass the non-remapped index here because handleTerminator will do its own remapping.
if( bb.terminator() )
handleTerminator( bb.index(), *bb.terminator() );
return true;
}
} // namespace goose::verify
|
Changes to bs/verify/builder.h.
1 2 3 4 5 6 7 8 9 10 |
#ifndef GOOSE_VERIFY_BUILDER_H
#define GOOSE_VERIFY_BUILDER_H
namespace goose::builtins
{
class Func;
}
namespace goose::verify
{
| | | | > | > | > | > | | | < | | | < | | < < < | | < < < | | | | > | | < < < | | | | | | | | | | | | | | | | | | | < < | | | | < | < | < < < | < | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | < > | | | < | | | < > | 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 |
#ifndef GOOSE_VERIFY_BUILDER_H
#define GOOSE_VERIFY_BUILDER_H
namespace goose::builtins
{
class Func;
}
namespace goose::verify
{
using AssHandler = function< bool( const z3::expr&, const z3::expr&, LocationId ) >;
// Helper class that tracks the states of a few things
// to build z3 expressions from a cfg.
class Builder
{
public:
Builder( const sema::Context& c, z3::solver& solver, size_t numVars,
Remapper* remapper = nullptr );
const auto& context() const { return *m_context; }
auto* solver() { return m_solver; }
auto* remapper() { return m_remapper; }
uint32_t currentBBIndex() const { return m_currentBBIndex; }
void setTraceMode( bool b ) { m_traceMode = b; }
const auto& cfg() const { return m_cfg; }
void setCFG( const ptr< cir::CFG >& cfg ) { m_cfg = cfg; }
void setCurrentBB( uint32_t index ) { m_currentBBIndex = index; }
void setCurrentBBPredicate( const z3::expr& predicate ) { m_currentPredicate = predicate; }
void add( const z3::expr& e );
void assume( const z3::expr& e );
bool checkAssertion( const z3::expr& e, LocationId locationId );
void setLoadMustAssume( bool lma ) { m_loadMustAssume = lma; }
bool mustLoadAssume() const { return m_loadMustAssume; }
void resetVarStorage( uint32_t size ) { m_varTracker = VarTracker( size ); }
optional< Z3Val > retrieveVar( uint32_t index, uint32_t bbIndex = ~0 );
optional< Z3Val > setVar( uint32_t index, Z3Val&& v );
optional< Z3Val > retrieveGFC( const GhostFuncApplication& gfa, uint32_t bbIndex = ~0 );
void setGFC( const GhostFuncApplication& gfa, Z3Val&& v );
optional< Z3Val > getTemporary( uint32_t index );
void setTemporary( uint32_t index, Z3Val&& v );
const z3::expr* retrievePlaceholder( StringId sid ) const;
bool setPlaceholder( StringId sid, const z3::expr& expr );
void unsetPlaceholder( StringId sid );
void beginForAll( size_t argCount );
bool declareForAllVar( StringId name, Term&& type );
bool pushForAll( Z3Val&& expr, LocationId loc );
auto& convertedBBIndices() { return m_convertedBBIndices; }
uint32_t newUniqueId()
{
assert( m_nextUniqueId );
return ( *m_nextUniqueId )++;
}
bool hasCheckFailed() const { return m_checkFailed; }
void setCheckFailed() { m_checkFailed = true; }
template< typename T > void push( T&& val ) { m_stack.push( forward< T >( val ) ); }
template< typename T = Z3Val > optional< T > pop() { return m_stack.pop< T >( *this ); }
template< typename F > void setAssertionHandler( F&& handler )
{
m_assertionHandler = forward< F >( handler );
}
auto assertionHandler() const { return m_assertionHandler; }
private:
optional< z3::expr > getCurrentPredicatesExpr();
const sema::Context* m_context;
z3::solver* m_solver = nullptr;
Remapper* m_remapper = nullptr;
ptr< cir::CFG > m_cfg;
uint32_t m_currentBBIndex = 1;
// All emitted assumptions and assertions are wrapped
// with implications from this predicate.
// This is used to model the control flow, where each
// basic block gets a predicate that indicates that
// the execution flow is going through that block.
optional< z3::expr > m_currentPredicate;
AssHandler m_assertionHandler;
unordered_set< uint32_t > m_convertedBBIndices;
TempStorage< optional< Z3Val > > m_temporaries;
unordered_map< StringId, z3::expr > m_placeholders;
VarTracker m_varTracker;
GFCTracker m_gfcTracker;
Stack m_stack;
using NameList = llvm::SmallVector< StringId, 8 >;
using PendingForAlls = pair< NameList, z3::expr_vector >;
stack< PendingForAlls > m_pendingForAlls;
shared_ptr< uint32_t > m_nextUniqueId = make_shared< uint32_t >( 0 );
bool m_traceMode = false;
bool m_checkFailed = false;
bool m_loadMustAssume = false;
};
class AssertionHandlerGuard
{
public:
AssertionHandlerGuard( Builder& b ) :
m_builder( b ),
m_savedAssHandler( b.assertionHandler() )
{
}
~AssertionHandlerGuard() { m_builder.setAssertionHandler( m_savedAssHandler ); }
private:
Builder& m_builder;
AssHandler m_savedAssHandler;
};
} // namespace goose::verify
#endif
|
Changes to bs/verify/call.cpp.
1 2 3 4 5 6 7 8 |
#include "verify.h"
#include "builtins/builtins.h"
#include "helpers.inl"
using namespace goose::diagnostics;
namespace goose::verify
{
| | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include "verify.h"
#include "builtins/builtins.h"
#include "helpers.inl"
using namespace goose::diagnostics;
namespace goose::verify
{
template< bool CheckResult, typename I > bool BuildZ3CallCheck( Builder& b, const I& instr )
{
Value ft;
auto fVal = b.pop< Value >();
if( fVal )
ft = *EIRToValue( fVal->type() );
else
|
| ︙ | ︙ | |||
27 28 29 30 31 32 33 |
return false;
optional< Z3Val > retExpr;
if constexpr( CheckResult )
{
if( fvd->returnType != GetValueType< void >() )
| > | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
return false;
optional< Z3Val > retExpr;
if constexpr( CheckResult )
{
if( fvd->returnType != GetValueType< void >() )
retExpr =
BuildZ3ConstantFromType( b, fvd->returnType, format( "r{}", b.newUniqueId() ) );
}
auto fvi = fvd->verifInfos;
if( !fvi )
{
if( retExpr )
b.push( move( *retExpr ) );
|
| ︙ | ︙ | |||
62 63 64 65 66 67 68 |
cb.setVar( argCount - argIndex - 1, move( *zv ) );
}
// Asserts the parameter types's predicates.
uint32_t varId = 0;
| | > | | < | < < < | | < < | | | > | | | > | | | | | | | > | | | | | | | | < | < | > | | | | | | 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 |
cb.setVar( argCount - argIndex - 1, move( *zv ) );
}
// Asserts the parameter types's predicates.
uint32_t varId = 0;
ForEachInVectorTerm( fvd->params,
[&]( auto&& param )
{
auto result = Decompose( param,
Vec( Lit( "value"_sid ), Lit( "param"_sid ), SubTerm(), SubTerm(),
Val< LocationId >() ) );
if( !result )
return true;
auto&& [type, val, locId] = *result;
auto paramVal =
BuildComputedValue( type, VarAddr( varId++, locId ), Load( type, locId ) );
auto zv = BuildZ3ExprFromValue( cb, paramVal );
ForEachPredicate( cb, type, zv.expr,
[&]( auto&& z3expr, auto locId )
{
DiagnosticsContext dc( fVal->locationId(), "At this call." );
b.checkAssertion( z3expr, locId );
} );
return true;
} );
if constexpr( CheckResult )
{
// Setup the return value placeholder
if( retExpr )
cb.setPlaceholder( "@result"_sid, retExpr->expr );
}
// Check preconditions.
const auto& preConds = fvi->preConds();
preConds->forEach(
[&]( auto&& val )
{
if( auto zv = TryBuildZ3ExprFromValue( cb, val ) )
{
DiagnosticsContext dc( fVal->locationId(), "At this call." );
b.checkAssertion( zv->expr, val.locationId() );
}
} );
if constexpr( CheckResult )
{
// Add the return type's predicates as assumptions.
ForEachPredicate( cb, fvd->returnType, retExpr->expr,
[&]( auto&& z3expr, auto locId ) { b.assume( z3expr ); } );
// Add postconditions as assumptions.
const auto& postConds = fvi->postConds();
postConds->forEach(
[&]( auto&& val )
{
if( auto zv = TryBuildZ3ExprFromValue( cb, val ) )
b.assume( zv->expr );
} );
if( retExpr )
b.push( move( *retExpr ) );
}
return true;
}
bool BuildZ3Op( Builder& b, const Call& instr )
{
return BuildZ3CallCheck< true >( b, instr );
}
bool BuildZ3Op( Builder& b, const CallCheck& instr )
{
return BuildZ3CallCheck< false >( b, instr );
}
} // namespace goose::verify
|
Changes to bs/verify/cfg.cpp.
1 2 3 4 5 6 7 8 |
#include "verify.h"
#include "builtins/builtins.h"
#include "diagnostics/diagnostics.h"
using namespace goose::diagnostics;
namespace goose::verify
{
| > | | | | | | | 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 |
#include "verify.h"
#include "builtins/builtins.h"
#include "diagnostics/diagnostics.h"
using namespace goose::diagnostics;
namespace goose::verify
{
bool Func::buildZ3Expressions(
const cir::BasicBlock& bb, queue< const BasicBlock* >* parentWorkQueue )
{
uint32_t currentLoopId = m_remapper.getCurrentLoopId();
queue< const BasicBlock* > workQueue;
workQueue.push( &bb );
while( !workQueue.empty() )
{
const auto* pBB = workQueue.front();
workQueue.pop();
if( currentLoopId && pBB->loopId() != currentLoopId && pBB->index() != currentLoopId )
{
// If we encounter a successor BB that belongs to a different loop than the
// current one, pass it up to be handled by our parent work queue (which should
// belong to a checkLoop call). The only case where we don't have a parent work
// queue is at the root level of the loop nesting tree, and we should never have an
// edge from a BB there into a loop BB without en countering that loop header first,
// because we only handle reducible CFGs.
assert( parentWorkQueue );
parentWorkQueue->push( pBB );
continue;
}
// If we encounter a loop header for a different
// loop than the current one, check the loop.
|
| ︙ | ︙ | |||
52 53 54 55 56 57 58 |
workQueue.push( pBB );
continue;
}
if( buildZ3ExpressionsForBB( *pBB ) )
{
const cir::GhostBranch* pGB = nullptr;
| > | | > | | | | | | 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 |
workQueue.push( pBB );
continue;
}
if( buildZ3ExpressionsForBB( *pBB ) )
{
const cir::GhostBranch* pGB = nullptr;
if( pBB->terminator()
&& ( pGB = get_if< cir::GhostBranch >( &pBB->terminator()->content() ) ) )
{
const auto& pSuccBB = pGB->ghostCode().lock();
if( pSuccBB->index() != pBB->loopId() )
workQueue.push( pSuccBB.get() );
}
else
{
pBB->forEachSuccessor(
[&]( auto&& pSuccBB )
{
if( pSuccBB->index() != pBB->loopId() )
workQueue.push( pSuccBB.get() );
} );
}
}
}
return !m_builder.hasCheckFailed();
}
z3::expr Func::makeEdgeExpression( uint32_t srcBBId, uint32_t destBBId, const z3::expr& cond )
{
auto& c = GetZ3Context();
auto srcBBExpr = c.bool_const( format( "b{}", srcBBId ).c_str() );
auto edgeExpr = c.bool_const( format( "e{}_{}", srcBBId, destBBId ).c_str() );
m_builder.add( edgeExpr == ( srcBBExpr && cond ) );
return edgeExpr;
}
} // namespace goose::verify
|
Changes to bs/verify/func.cpp.
| ︙ | ︙ | |||
15 16 17 18 19 20 21 |
Func::Func( const sema::Context& c, const builtins::Func& func ) :
m_func( &func ),
m_cfg( func.cir()->body() ),
m_retType( func.type().returnType() ),
m_solver( GetZ3Context(), z3::solver::simple() ),
m_remapper( m_cfg->count() + 1 ),
m_builder( c, m_solver, m_cfg->temporariesCount(), &m_remapper )
| < > > < > > > | < | < | 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 |
Func::Func( const sema::Context& c, const builtins::Func& func ) :
m_func( &func ),
m_cfg( func.cir()->body() ),
m_retType( func.type().returnType() ),
m_solver( GetZ3Context(), z3::solver::simple() ),
m_remapper( m_cfg->count() + 1 ),
m_builder( c, m_solver, m_cfg->temporariesCount(), &m_remapper )
{
}
Func::Func( const sema::Context& c, const ptr< cir::CFG >& cfg, const Term& retType ) :
m_cfg( cfg ),
m_retType( retType ),
m_solver( GetZ3Context(), z3::solver::simple() ),
m_remapper( m_cfg->count() + 1 ),
m_builder( c, m_solver, m_cfg->temporariesCount(), &m_remapper )
{
}
bool Func::verify()
{
if( !ms_EnableVerification )
return true;
ProfileZoneScoped;
if( m_func )
m_viz.setFunctionIdentity( m_func->cir()->identity() );
IdentifyLoops( m_cfg );
IdentifyLoopModifiedAddrs( m_cfg );
try
{
m_builder.setTraceMode( ms_TraceMode );
if( ms_TraceMode )
cout << "=== Begin function verification trace ===\n";
m_builder.setAssertionHandler(
[&]( auto&& expr, auto&& exprToCheck, LocationId locationId )
{ return checkAssertion( expr, exprToCheck, locationId ); } );
m_builder.setCFG( m_cfg );
if( m_func )
{
// Add assumptions of the function's parameter types predicates
uint32_t varId = 0;
|
| ︙ | ︙ | |||
74 75 76 77 78 79 80 |
// Skip wrapped params
if( builtins::IsWrappedType( t ) )
{
++varId;
return true;
}
| > | | | > > | | > | | | | | > | | | | | | > | > | > | | > | > | | > | > | | 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 |
// Skip wrapped params
if( builtins::IsWrappedType( t ) )
{
++varId;
return true;
}
auto paramVal =
BuildComputedValue( t, VarAddr( varId, locId ), Load( t, locId ) )
.setLocationId( locId );
// Initialize every parameter containing variable with a freshly named constant
// of the right type.
auto paramInit =
BuildZ3ConstantFromType( m_builder, t, format( "p{}", varId ) );
m_builder.setVar( varId, move( paramInit ) );
++varId;
auto zv = BuildZ3ExprFromValue( m_builder, paramVal );
ForEachPredicate( m_builder, t, zv.expr,
[&]( auto&& z3expr, auto locId )
{
if( z3expr.is_bool() )
m_builder.assume( z3expr );
} );
}
// Add assumptions of the function's requirements
if( const auto& fvi = m_func->type().verifInfos() )
{
const auto& reqs = fvi->preConds();
reqs->forEach(
[&]( auto&& val )
{
// Don't do any error handling here, it should already have been taken
// care of by the condition verifier.
if( auto zv = TryBuildZ3ExprFromValue( m_builder, val ) )
m_builder.assume( zv->expr );
} );
}
}
bool result = buildZ3Expressions( *m_cfg->entryBB(), nullptr );
if( ms_TraceMode )
cout << "=== End function verification trace ===\n\n";
return result;
}
catch( const z3::exception& e )
{
cerr << "Func: z3 exception: " << e << endl;
// rethrow so we get a stack trace dump to know where the error happened
throw;
}
}
bool Func::checkAssertion(
const z3::expr& expr, const z3::expr& exprToCheck, LocationId locationId )
{
if( ms_TraceMode )
return true;
ProfileZoneScoped;
z3::expr_vector exprVec( GetZ3Context() );
exprVec.push_back( exprToCheck );
m_solver.push();
bool result = false;
switch( m_solver.check( exprVec ) )
{
case z3::check_result::unsat:
// We are using the solver to try and find a counter example,
// so if it's unsat, it's good.
if( ms_DumpSolverOnSuccess )
cout << "Solver dump:\n"
<< m_solver << "check_unsat: " << exprToCheck << endl
<< endl;
result = true;
break;
case z3::check_result::sat:
m_viz.assertionFailed();
DiagnosticsManager::GetInstance().emitErrorMessage(
locationId, "assert"_sid, "this condition may not be met." );
if( ms_DumpSolverOnFailure )
{
cout << "Solver dump:\n"
<< m_solver << "check_unsat: " << exprToCheck << endl
<< endl;
cout << "Model dump:\n" << m_solver.get_model() << endl << endl;
}
result = false;
break;
default:
m_viz.assertionFailed();
DiagnosticsManager::GetInstance().emitErrorMessage(
locationId, "assert"_sid, "couldn't verify this condition." );
if( ms_DumpSolverOnFailure )
cout << "Solver dump:\n"
<< m_solver << "check_unsat: " << exprToCheck << endl
<< endl;
result = false;
break;
}
m_solver.pop();
return result;
}
} // namespace goose::verify
|
Changes to bs/verify/func.h.
1 2 3 4 5 6 7 8 9 10 11 12 |
#ifndef GOOSE_VERIFY_FUNC_H
#define GOOSE_VERIFY_FUNC_H
namespace goose::builtins
{
class Func;
}
namespace goose::verify
{
class Func
{
| | | | | | > | > | > | | > | > | | > | | > | | < | | | | | | | | | | > | | | | | | | | | | | | | | 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 |
#ifndef GOOSE_VERIFY_FUNC_H
#define GOOSE_VERIFY_FUNC_H
namespace goose::builtins
{
class Func;
}
namespace goose::verify
{
class Func
{
public:
Func( const sema::Context& c, const builtins::Func& func );
Func( const sema::Context& c, const ptr< cir::CFG >& cfg, const Term& retType );
bool verify();
static void SetEnableVerification( bool b ) { ms_EnableVerification = b; }
static void SetTraceMode( bool b ) { ms_TraceMode = b; }
static void SetDumpSolverOnFailure( bool b ) { ms_DumpSolverOnFailure = b; }
static void SetDumpSolverOnSuccess( bool b ) { ms_DumpSolverOnSuccess = b; }
static bool TraceMode() { return ms_TraceMode; }
static bool DumpSolverOnFailure() { return ms_DumpSolverOnFailure; }
static bool DumpSolverOnSuccess() { return ms_DumpSolverOnSuccess; }
private:
bool buildZ3Expressions(
const cir::BasicBlock& bb, queue< const BasicBlock* >* parentWorkQueue );
bool buildZ3ExpressionsForBB( const cir::BasicBlock& bb );
bool checkLoop(
const cir::BasicBlock& header, queue< const BasicBlock* >& parentWorkQueue );
bool handleTerminator( uint32_t bbIndex, const cir::Terminator& t );
template< typename T > bool handleTerminator( uint32_t bbIndex, const T& t )
{
return true;
}
bool handleTerminator( uint32_t bbIndex, const cir::RetVoid& t );
bool handleTerminator( uint32_t bbIndex, const cir::Ret& t );
bool handleTerminator( uint32_t bbIndex, const cir::Branch& t );
bool handleTerminator( uint32_t bbIndex, const cir::CondBranch& t );
bool handleTerminator( uint32_t bbIndex, const cir::GhostBranch& t );
z3::expr makeEdgeExpression( uint32_t srcBBId, uint32_t destBBId, const z3::expr& cond );
bool checkAssertion(
const z3::expr& expr, const z3::expr& exprToCheck, LocationId locationId );
const builtins::Func* m_func = nullptr;
ptr< cir::CFG > m_cfg;
Term m_retType;
z3::solver m_solver;
Remapper m_remapper;
Builder m_builder;
VerifyViz m_viz;
static bool ms_EnableVerification;
static bool ms_TraceMode;
static bool ms_DumpSolverOnFailure;
static bool ms_DumpSolverOnSuccess;
};
} // namespace goose::verify
#endif
|
Changes to bs/verify/gfctracker.cpp.
| ︙ | ︙ | |||
15 16 17 18 19 20 21 |
gfIndex = m_ghostFuncIndices.size();
m_ghostFuncIndices.emplace( gfa.func(), gfIndex );
}
return gfIndex;
}
| > | | > | | < | < < < < < < | | > | | | | | > | | | | | | | > | | > > | | | < | > | | | > | | | | | | | | | | | | | | | > > | | | | | | | | | > | | | | | 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 |
gfIndex = m_ghostFuncIndices.size();
m_ghostFuncIndices.emplace( gfa.func(), gfIndex );
}
return gfIndex;
}
z3::expr GFCTracker::buildFunctionApplication(
Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const Value& gf )
{
auto gft = *FromValue< builtins::FuncType >( *EIRToValue( gf.type() ) );
llvm::SmallVector< z3::expr, 8 > z3args;
z3args.reserve( VecSize( gft.params() ) + 1 );
z3args.emplace_back( GetZ3Context().int_val( uid ) );
uint32_t argIndex = 0;
ForEachInVectorTerm( gft.params(),
[&]( auto&& t )
{
auto result = Decompose( t,
Vec( Lit( "value"_sid ), SubTerm(), SubTerm(), SubTerm(), Val< LocationId >() ) );
assert( result );
auto&& [sort, type, val, locId] = *result;
z3args.emplace_back(
BuildZ3ConstantFromType( b, type, format( "gca{}_{}", argIndex, uid ) ).expr );
++argIndex;
return true;
} );
return gfDecl.z3decl( z3args.size(), z3args.data() );
}
z3::expr GFCTracker::buildFunctionApplication( Builder& b, uint32_t uid,
const GhostFuncDeclCache::GFDecl& gfDecl, const GhostFuncApplication& gfa )
{
llvm::SmallVector< z3::expr, 8 > z3args;
z3args.reserve( gfa.args().size() + 1 );
z3args.emplace_back( GetZ3Context().int_val( uid ) );
for( auto&& zv : gfa.args() )
z3args.emplace_back( zv.expr );
return gfDecl.z3decl( z3args.size(), z3args.data() );
}
uint32_t GFCTracker::getCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex ) const
{
const auto* gfcs = m_gfcStorage.get( gfIndex );
if( !gfcs )
return 0;
if( gfcs->size() <= bbIndex )
return 0;
return ( *gfcs )[bbIndex];
}
void GFCTracker::setCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex, uint32_t uid )
{
auto* gfcs = m_gfcStorage.get( gfIndex );
if( !gfcs )
{
GFCState newGFCS;
newGFCS.resize( bbIndex + 1, 0 );
auto& gfcsRef = m_gfcStorage.set( gfIndex, move( newGFCS ) );
gfcs = &gfcsRef;
}
else if( gfcs->size() <= bbIndex )
gfcs->resize( bbIndex + 1, 0 );
( *gfcs )[bbIndex] = uid;
}
void GFCTracker::setForBasicBlock( Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa,
uint32_t gfIndex, const GhostFuncDeclCache::GFDecl& gfDecl, z3::expr&& expr )
{
uint32_t oldUid = getCurrentUidForBasicBlock( bbIndex, gfIndex );
uint32_t newUid = b.newUniqueId();
auto& c = GetZ3Context();
auto predicate = c.bool_const( format( "b{}", bbIndex ).c_str() );
if( oldUid == 0 )
{
auto apply = buildFunctionApplication( b, newUid, gfDecl, gfa );
b.add( z3::implies(
predicate, apply == Coerce( expr, { apply, gfDecl.returnType, gfa.locationId() } ) ) );
}
else
{
// Not the first time we set a value for this ghost func:
// we need to express that its new value at the current assignation point is the new one
// only if the args are equal to the passed args, otherwise it is equal to whatever it was
// at its previous assignment.
auto argCount = gfa.args().size();
llvm::SmallVector< z3::expr, 8 > z3args;
z3args.reserve( argCount + 1 );
z3args.emplace_back( c.int_val( newUid ) );
uint32_t argIndex = 0;
z3::expr_vector z3argComparisons( c );
z3argComparisons.resize( argCount );
z3::expr_vector z3argsVars( c );
z3argsVars.resize( argCount );
for( auto&& arg : gfa.args() )
{
auto argConst =
BuildZ3ConstantFromType( b, arg.type, format( "gca{}_{}", argIndex, newUid ) ).expr;
z3args.emplace_back( argConst );
z3argsVars.set( argIndex, argConst );
auto comp = argConst == arg.expr;
z3argComparisons.set( argIndex, comp );
++argIndex;
}
auto newApply = gfDecl.z3decl( z3args.size(), z3args.data() );
z3args[0] = c.int_val( oldUid );
auto previousApply = gfDecl.z3decl( z3args.size(), z3args.data() );
// AAAAAAAH
b.add( z3::forall( z3argsVars,
z3::implies( predicate,
newApply
== z3::ite( z3::mk_and( z3argComparisons ),
Coerce( expr, { newApply, gfDecl.returnType, gfa.locationId() } ),
previousApply ) ) ) );
}
setCurrentUidForBasicBlock( bbIndex, gfIndex, newUid );
}
optional< Z3Val > GFCTracker::retrieve(
Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa )
{
if( bbIndex == b.currentBBIndex() )
return nullopt;
if( bbIndex == ~0U )
bbIndex = b.currentBBIndex();
auto gfDecl = GhostFuncDeclCache::GetInstance()->getDecl( b.context(), gfa.func() );
if( !gfDecl )
return nullopt;
auto gft = *FromValue< builtins::FuncType >( *EIRToValue( gfa.func().type() ) );
uint32_t gfIndex = getGFIndex( gfa );
auto uid = getCurrentUidForBasicBlock( bbIndex, gfIndex );
if( uid )
return Z3Val{ buildFunctionApplication( b, uid, *gfDecl, gfa ),
*EIRToValue( gft.returnType() ), gfa.locationId() };
if( !b.cfg() )
return nullopt;
uid = b.newUniqueId();
auto& c = GetZ3Context();
optional< z3::expr > lhsExpr;
llvm::SmallVector< z3::expr, 8 > z3args;
z3::expr_vector z3argsVars( c );
optional< z3::expr > newApply;
// Model the ssa phi operation by creating a series of equality assertions, each implied by
// one of the possible incoming edge condition for the current basic block.
if( b.remapper() )
{
b.remapper()->forEachIncomingEdge( bbIndex,
[&]( auto&& predIndex, auto&& expr )
{
if( auto predVal = retrieve( b, predIndex, gfa ) )
{
if( !lhsExpr )
lhsExpr = buildFunctionApplication( b, uid, *gfDecl, gfa );
if( z3args.empty() )
{
auto argCount = gfa.args().size();
z3args.reserve( argCount + 1 );
z3args.emplace_back( c.int_val( uid ) );
z3argsVars.resize( argCount );
uint32_t argIndex = 0;
for( auto&& arg : gfa.args() )
{
auto argConst = BuildZ3ConstantFromType(
b, arg.type, format( "gca{}_{}", argIndex, uid ) )
.expr;
z3args.emplace_back( argConst );
z3argsVars.set( argIndex, argConst );
++argIndex;
}
newApply = gfDecl->z3decl( z3args.size(), z3args.data() );
}
auto predUid = getCurrentUidForBasicBlock( predIndex, gfIndex );
z3args[0] = c.int_val( predUid );
auto previousApply = gfDecl->z3decl( z3args.size(), z3args.data() );
b.add( z3::forall( z3argsVars,
z3::implies( c.bool_const( format( "e{}_{}", predIndex, bbIndex ).c_str() ),
*newApply == previousApply ) ) );
}
} );
}
if( !lhsExpr )
return nullopt;
setCurrentUidForBasicBlock( bbIndex, gfIndex, uid );
setForBasicBlock( b, bbIndex, gfa, gfIndex, *gfDecl, move( *lhsExpr ) );
|
| ︙ | ︙ |
Changes to bs/verify/gfctracker.h.
1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_VERIFY_GFCTRACKER_H
#define GOOSE_VERIFY_GFCTRACKER_H
namespace goose::verify
{
class Builder;
class GFCTracker
{
| | | | < | > > | | | | > | | > | | | | | | | | | | | < > | 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 |
#ifndef GOOSE_VERIFY_GFCTRACKER_H
#define GOOSE_VERIFY_GFCTRACKER_H
namespace goose::verify
{
class Builder;
class GFCTracker
{
public:
GFCTracker( size_t size ) :
m_gfcStorage( size )
{
}
optional< Z3Val > retrieve( Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa );
void set( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val );
private:
uint32_t getGFIndex( const GhostFuncApplication& gfa );
z3::expr buildFunctionApplication(
Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const Value& gf );
z3::expr buildFunctionApplication( Builder& b, uint32_t uid,
const GhostFuncDeclCache::GFDecl& gfDecl, const GhostFuncApplication& gfa );
uint32_t getCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex ) const;
void setCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex, uint32_t uid );
void setForBasicBlock( Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa,
uint32_t gfIndex, const GhostFuncDeclCache::GFDecl& gfDecl, z3::expr&& expr );
// Each ghost func have a unique index within the current function.
// (0: invalid index)
unordered_map< Value, uint32_t > m_ghostFuncIndices;
// For each ghostfunc closure, store its last index.
using GFCState = llvm::SmallVector< uint32_t, 16 >;
using GFCStorage = cir::TempStorage< GFCState >;
GFCStorage m_gfcStorage;
};
} // namespace goose::verify
#endif
|
Changes to bs/verify/ghostfunc.cpp.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
GhostFuncDeclCache* GhostFuncDeclCache::GetInstance()
{
static unique_ptr< GhostFuncDeclCache > gfdcache( new GhostFuncDeclCache() );
return gfdcache.get();
}
| | > | > | < < | > | < | 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 |
GhostFuncDeclCache* GhostFuncDeclCache::GetInstance()
{
static unique_ptr< GhostFuncDeclCache > gfdcache( new GhostFuncDeclCache() );
return gfdcache.get();
}
optional< GhostFuncDeclCache::GFDecl > GhostFuncDeclCache::getDecl(
const sema::Context& c, const Value& gf )
{
auto it = m_decls.find( gf );
if( it != m_decls.end() )
return it->second;
auto fdecl = createDecl( c, gf );
if( !fdecl )
return nullopt;
m_decls.emplace( gf, *fdecl );
return fdecl;
}
optional< GhostFuncDeclCache::GFDecl > GhostFuncDeclCache::createDecl(
const sema::Context& c, const Value& gf )
{
auto f = *FromValue< builtins::Func >( gf );
if( !f.cir() )
return nullopt;
auto mangledName = codegen::Mangle( f.cir()->identity() );
if( !mangledName )
return nullopt;
const auto& ft = f.type();
auto& tc = TypeCache::GetInstance();
// Return type sort (aka range)
auto rtinfo = tc.getTypeInfo( c, ft.returnType() );
if( !rtinfo || !rtinfo->sort )
return nullopt;
llvm::SmallVector< z3::sort, 8 > domain; // param sorts aka domain
domain.reserve( VecSize( ft.params() ) + 1 );
domain.emplace_back( GetZ3Context().int_sort() );
const auto& fli = ft.loweringInfos();
for( const auto& loweredType : fli->loweredParamTypes )
{
auto tinfo = tc.getTypeInfo( c, ValueToEIR( loweredType ) );
if( !tinfo || !tinfo->sort )
return nullopt;
domain.emplace_back( *tinfo->sort );
}
return GFDecl{ *EIRToValue( ft.returnType() ),
GetZ3Context().function(
mangledName->c_str(), domain.size(), domain.data(), *rtinfo->sort ) };
}
|
Changes to bs/verify/ghostfunc.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_VERIFY_GHOSTFUNC_H
#define GOOSE_VERIFY_GHOSTFUNC_H
namespace goose::verify
{
class GhostFuncDeclCache
{
| | | | | | | | | | | | | | | | < | > > | | | < | > > | > | > | | | | | | < > | 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 |
#ifndef GOOSE_VERIFY_GHOSTFUNC_H
#define GOOSE_VERIFY_GHOSTFUNC_H
namespace goose::verify
{
class GhostFuncDeclCache
{
public:
static GhostFuncDeclCache* GetInstance();
struct GFDecl
{
eir::Value returnType;
z3::func_decl z3decl;
};
optional< GFDecl > getDecl( const sema::Context& c, const Value& gf );
private:
optional< GFDecl > createDecl( const sema::Context& c, const Value& gf );
unordered_map< Value, GFDecl > m_decls;
};
class GhostFuncApplication
{
public:
GhostFuncApplication( const cir::Value& func, LocationId loc ) :
m_func( func ),
m_loc( loc )
{
}
GhostFuncApplication( cir::Value&& func, LocationId loc ) :
m_func( move( func ) ),
m_loc( loc )
{
}
const auto& func() const { return m_func; }
const auto& args() const { return m_args; }
auto& args() { return m_args; }
auto locationId() const { return m_loc; }
private:
eir::Value m_func;
llvm::SmallVector< Z3Val, 4 > m_args;
LocationId m_loc;
};
} // namespace goose::verify
#endif
|
Changes to bs/verify/helpers.h.
1 2 3 4 5 6 7 8 9 10 11 |
#ifndef GOOSE_HELPERS_H
#define GOOSE_HELPERS_H
namespace goose::verify
{
template< typename F >
void ForEachPredicate( Builder& b, const Term& t, const z3::expr& valExpr, F&& func );
template< typename F >
void ForEachPredicate( Builder& b, const Value& type, const z3::expr& valExpr, F&& func );
| | > | | | > | > | | | | | > | | | | | 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 |
#ifndef GOOSE_HELPERS_H
#define GOOSE_HELPERS_H
namespace goose::verify
{
template< typename F >
void ForEachPredicate( Builder& b, const Term& t, const z3::expr& valExpr, F&& func );
template< typename F >
void ForEachPredicate( Builder& b, const Value& type, const z3::expr& valExpr, F&& func );
// A modified version of z3::context::tuple_sort that returns both the tuple constructor's
// func_decl and the tuple's sort because I don't understand how I am supposed to get the
// tuple's sort when using the original C++ function, especially since it seems straightforward
// in the C api (other than the normal suckage expected from a C api).
//
// Maybe I'm just missing something but it's hard to tell from the barren doxygen dump offered
// by z3 in lieu of api documentation
template< typename NameVec, typename SortVec, typename ProjVec >
static inline pair< z3::sort, z3::func_decl > mkZ3TupleSort(
const char* name, unsigned n, const NameVec& names, const SortVec& sorts, ProjVec& projs )
{
auto& ctx = GetZ3Context();
z3::array< Z3_symbol > _names( n );
z3::array< Z3_sort > _sorts( n );
for( unsigned i = 0; i < n; ++i )
{
_names[i] = Z3_mk_string_symbol( ctx, names[i].c_str() );
_sorts[i] = sorts[i];
}
z3::array< Z3_func_decl > _projs( n );
Z3_symbol _name = Z3_mk_string_symbol( ctx, name );
Z3_func_decl tuple;
z3::sort _s = to_sort( ctx,
Z3_mk_tuple_sort( ctx, _name, n, _names.ptr(), _sorts.ptr(), &tuple, _projs.ptr() ) );
ctx.check_error();
for( unsigned i = 0; i < n; ++i )
projs.emplace_back( z3::func_decl( ctx, _projs[i] ) );
return make_pair( move( _s ), z3::func_decl( ctx, tuple ) );
}
} // namespace goose::verify
#endif
|
Changes to bs/verify/helpers.inl.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 |
if( !optTypePreds || !*optTypePreds )
return;
auto& tp = **optTypePreds;
const auto* prevValPH = b.retrievePlaceholder( "@val"_sid );
b.setPlaceholder( "@val"_sid, valExpr );
| | > | | | | | | | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
if( !optTypePreds || !*optTypePreds )
return;
auto& tp = **optTypePreds;
const auto* prevValPH = b.retrievePlaceholder( "@val"_sid );
b.setPlaceholder( "@val"_sid, valExpr );
tp.forEach(
[&]( auto&& p )
{
auto zv = BuildZ3ExprFromValue( b, p );
func( zv.expr, p.locationId() );
return true;
} );
if( prevValPH )
b.setPlaceholder( "@val"_sid, *prevValPH );
else
b.unsetPlaceholder( "@val"_sid );
}
} // namespace goose::verify
#endif
|
Changes to bs/verify/instrseq.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "verify.h"
#include "builtins/builtins.h"
#include "diagnostics/diagnostics.h"
#include "helpers.inl"
using namespace goose;
using namespace goose::verify;
using namespace goose::diagnostics;
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 |
#include "verify.h"
#include "builtins/builtins.h"
#include "diagnostics/diagnostics.h"
#include "helpers.inl"
using namespace goose;
using namespace goose::verify;
using namespace goose::diagnostics;
namespace
{
bool CheckAssertion( z3::solver& solver, const z3::expr& expr, const z3::expr& exprToCheck,
LocationId locationId )
{
if( verify::Func::TraceMode() )
return true;
ProfileZoneScoped;
z3::expr_vector exprVec( GetZ3Context() );
exprVec.push_back( exprToCheck );
solver.push();
bool result = false;
switch( solver.check( exprVec ) )
{
case z3::check_result::unsat:
// We are using the solver to try and find a counter example,
// so if it's unsat, it's good.
if( verify::Func::DumpSolverOnSuccess() )
cout << "Solver dump:\n"
<< solver << "check_unsat: " << exprToCheck << endl
<< endl;
result = true;
break;
case z3::check_result::sat:
DiagnosticsManager::GetInstance().emitErrorMessage(
locationId, "assert"_sid, "this condition may not be met." );
if( verify::Func::DumpSolverOnFailure() )
{
cout << "Solver dump:\n"
<< solver << "check_unsat: " << exprToCheck << endl
<< endl;
cout << "Model dump:\n" << solver.get_model() << endl << endl;
}
result = false;
break;
default:
DiagnosticsManager::GetInstance().emitErrorMessage(
locationId, "assert"_sid, "couldn't verify this condition." );
if( verify::Func::DumpSolverOnFailure() )
cout << "Solver dump:\n"
<< solver << "check_unsat: " << exprToCheck << endl
<< endl;
result = false;
break;
}
solver.pop();
return result;
}
} // namespace
namespace goose::verify
{
Result VerifyInstrSeq( const sema::Context& c, const InstrSeq& is )
{
z3::solver solver( GetZ3Context(), z3::solver::simple() );
Builder b( c, solver, 0 );
if( Func::TraceMode() )
{
cout << "=== Checking instr seq ===\n";
b.setTraceMode( true );
}
bool passes = true;
b.setAssertionHandler(
[&]( auto&& expr, auto&& exprToCheck, LocationId locationId )
{
if( !CheckAssertion( solver, expr, exprToCheck, locationId ) )
{
passes = false;
return false;
}
return true;
} );
if( !BuildZ3Op( b, is ) )
return Result::Unverifiable;
return passes ? Result::Pass : Result::Fails;
}
} // namespace goose::verify
|
Changes to bs/verify/loop.cpp.
1 2 3 4 5 6 7 8 9 |
#include "verify.h"
#include "builtins/builtins.h"
using namespace goose::diagnostics;
// This is an implementation of the "Software Verification Using k-Induction" algorithm.
// http://www.cprover.org/kinduction/
namespace goose::verify
{
| > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include "verify.h"
#include "builtins/builtins.h"
using namespace goose::diagnostics;
// This is an implementation of the "Software Verification Using k-Induction" algorithm.
// http://www.cprover.org/kinduction/
namespace goose::verify
{
bool Func::checkLoop(
const cir::BasicBlock& header, queue< const BasicBlock* >& parentWorkQueue )
{
ProfileZoneScoped;
if( ms_TraceMode )
cout << " == Checking loop " << header.index() << endl;
DiagnosticsContext dc( header.locationId(), "In this loop." );
|
| ︙ | ︙ | |||
34 35 36 37 38 39 40 |
{
++k;
if( ms_TraceMode )
cout << " == Unrolling base case " << k << " for loop " << header.index() << endl;
{
| | | | | | | > > | | | | | | > | < > | | | > > | | | | | | | | > > | > | | > | < | < | | 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 |
{
++k;
if( ms_TraceMode )
cout << " == Unrolling base case " << k << " for loop " << header.index() << endl;
{
// Unroll the base case just once for each increasing K: we save the state of the z3
// solver before generating the induction case, so we can incrementally add
// unrolling of the base case one at a time to save some work.
m_viz.nextLoopIteration();
m_remapper.nextLoopIteration();
if( !buildZ3Expressions( header, &parentWorkQueue ) )
{
// If a counter example was found while emitting the base case,
// we know the verification failed and we can stop here.
completed = true;
break;
}
}
// Push the solver state, builder state and remapper before unrolling the induction
// case, so that we can rollback all of this for the next attempt, if any.
m_solver.push();
auto savedBuilder = m_builder;
auto savedRemapper = m_remapper;
// Set a different assertion handler for the induction case: we want to just emit them
// as assumptions.
m_builder.setAssertionHandler(
[&]( const z3::expr& expr, const z3::expr& exprToCheck, LocationId locationId )
{
m_builder.assume( expr );
return true;
} );
VerifyViz::Guard vvg( m_viz );
m_viz.beginLoopInduction();
m_remapper.setLoopEdgesOnly();
// Havoc all the addresses modified during the current loop and emit
// the first unrolling for the induction case.
m_remapper.nextLoopIteration();
uint32_t firstInductionIterationHeaderIndex = m_remapper.remapBBId( header );
m_builder.setCurrentBB( firstInductionIterationHeaderIndex );
if( ms_TraceMode )
cout << " == Havocing modified storage locations for loop " << header.index()
<< endl;
m_cfg->forEachDataLocationModifiedByLoop( header.index(),
[&]( auto&& type, auto&& sloc ) {
HavocDataLocation( m_builder, firstInductionIterationHeaderIndex, type, sloc );
} );
if( ms_TraceMode )
cout << " == Unrolling first induction case for loop " << header.index() << endl;
buildZ3Expressions( header, &parentWorkQueue );
// Emit the rest of the induction case.
uint32_t i = 1;
while( i < k )
{
if( ms_TraceMode )
cout << " == Unrolling induction case " << i << " for loop " << header.index()
<< endl;
m_viz.nextLoopIteration();
m_remapper.nextLoopIteration();
buildZ3Expressions( header, &parentWorkQueue );
++i;
}
// For the final loop instance, if we aren't at the last value of k before giving up,
// we want to resolve the assertions without emitting diagnostics:
// if they fail it means we need to increase k and try again.
// Otherwise, we want to use the regular assertion handler that will emit error
// messages for all failed assertions.
if( k == maxK )
m_builder.setAssertionHandler( origAssHandler );
else
{
m_builder.setAssertionHandler(
[&]( const z3::expr& expr, const z3::expr& exprToCheck, LocationId locationId )
{
if( ms_TraceMode )
return true;
z3::expr_vector exprVec( GetZ3Context() );
exprVec.push_back( exprToCheck );
bool result = m_solver.check( exprVec ) == z3::check_result::unsat;
if( ( !result && ms_DumpSolverOnFailure )
|| ( result && ms_DumpSolverOnSuccess ) )
cout << "Solver dump:\n"
<< m_solver << "check_unsat_ind: " << exprToCheck << endl
<< endl;
return result;
} );
}
m_viz.nextLoopIteration();
m_remapper.nextLoopIteration();
if( buildZ3Expressions( header, &parentWorkQueue ) && !m_builder.hasCheckFailed() )
{
vvg.commit();
completed = true;
// Emit the loop header one last time, but only with its exit edge
// so that the results of the induction are exposed to the
// rest of the verification process
m_viz.nextLoopIteration();
m_remapper.nextLoopIteration();
m_remapper.setExitEdgesOnly();
m_builder.setAssertionHandler(
[&]( const z3::expr& expr, const z3::expr& exprToCheck, LocationId locationId )
{ return true; } );
buildZ3Expressions( header, &parentWorkQueue );
m_builder.setAssertionHandler( origAssHandler );
break;
}
m_solver.pop();
m_remapper = move( savedRemapper );
m_builder = move( savedBuilder );
}
m_remapper.endLoopUnrolling();
m_viz.endLoopVerification();
if( ms_TraceMode )
cout << " == Finished checking loop " << header.index() << endl;
return completed && !m_builder.hasCheckFailed();
}
} // namespace goose::verify
|
Changes to bs/verify/phi.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "verify.h"
#include "builtins/builtins.h"
namespace goose::verify
{
bool BuildZ3Op( Builder& b, const Phi& instr )
{
const auto* remapper = b.remapper();
if( !remapper )
return false;
| > | | > | | | | > | | | | > | | | | | < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
#include "verify.h"
#include "builtins/builtins.h"
namespace goose::verify
{
bool BuildZ3Op( Builder& b, const Phi& instr )
{
const auto* remapper = b.remapper();
if( !remapper )
return false;
auto newVar =
BuildZ3ConstantFromType( b, instr.type().get(), format( "v{}", b.newUniqueId() ) );
auto& c = GetZ3Context();
uint32_t bbIndex = b.currentBBIndex();
// Model the ssa phi operation by creating a series of equality assertions, each implied by
// one of the possible incoming edge condition for the current basic block.
remapper->forEachIncomingEdge( bbIndex,
[&]( auto&& predIndex, auto&& predExpr )
{
uint32_t origPredIndex = remapper->getOriginalBBIndex( predIndex );
// Match the incoming edge with the correct one in the phi instruction.
instr.forAllIncomings(
[&]( auto&& bb, auto&& val )
{
if( bb->index() != origPredIndex )
return true;
auto zv = BuildZ3ExprFromValue( b, val );
b.add( z3::implies(
c.bool_const( format( "e{}_{}", predIndex, bbIndex ).c_str() ),
newVar.expr == Coerce( zv, newVar ) ) );
return true;
} );
} );
b.setTemporary( instr.destIndex(), move( newVar ) );
return true;
}
} // namespace goose::verify
|
Changes to bs/verify/propositions.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "verify.h"
#include "diagnostics/diagnostics.h"
#include "builtins/builtins.h"
using namespace goose::verify;
using namespace goose::diagnostics;
Propositions::Propositions( const sema::Context& c ) :
m_context( c ),
m_solver( GetZ3Context() ),
m_builder( m_context, m_solver, 0 )
| < > > | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#include "verify.h"
#include "diagnostics/diagnostics.h"
#include "builtins/builtins.h"
using namespace goose::verify;
using namespace goose::diagnostics;
Propositions::Propositions( const sema::Context& c ) :
m_context( c ),
m_solver( GetZ3Context() ),
m_builder( m_context, m_solver, 0 )
{
}
bool Propositions::addPropositions( const ValueVec& assList )
{
for( auto&& val : assList )
{
if( val.isPoison() )
return false;
if( val.type() != GetValueType< bool >() )
continue;
auto zv = TryBuildZ3ExprFromValue( m_builder, val );
if( !zv )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
val.locationId(), "invalid proposition." );
return false;
}
stringstream sstr;
sstr << '$' << m_nextIndex++;
auto boolCst = GetZ3Context().bool_const( sstr.str().c_str() );
|
| ︙ | ︙ | |||
54 55 56 57 58 59 60 |
auto unsatCore = m_solver.unsat_core();
llvm::SmallVector< LocationId, 8 > errorLocations;
for( auto&& unsatExpr : unsatCore )
{
auto it = find_if( m_idAndLocs.begin(), m_idAndLocs.end(),
| | | | | 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 unsatCore = m_solver.unsat_core();
llvm::SmallVector< LocationId, 8 > errorLocations;
for( auto&& unsatExpr : unsatCore )
{
auto it = find_if( m_idAndLocs.begin(), m_idAndLocs.end(),
[&]( auto&& x ) { return x.first == unsatExpr.id(); } );
if( it == m_idAndLocs.end() )
G_ERROR( "unknown id in unsat core" );
errorLocations.push_back( it->second );
}
// Sort the unsatisfiable propositions by location ids, because z3 doesn't
// provide them in a deterministic order.
sort( errorLocations.begin(), errorLocations.end() );
for( auto&& locId : errorLocations )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
locId, "this proposition is unsatisfiable." );
}
return false;
}
|
Changes to bs/verify/propositions.h.
1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_VERIFY_PROPOSITIONS_H
#define GOOSE_VERIFY_PROPOSITIONS_H
namespace goose::verify
{
// Checks that propositions are satisfiable,
// and emits any necessary diagnostic.
class Propositions
{
| | | | | | | | | | | | | | | | | | | 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 |
#ifndef GOOSE_VERIFY_PROPOSITIONS_H
#define GOOSE_VERIFY_PROPOSITIONS_H
namespace goose::verify
{
// Checks that propositions are satisfiable,
// and emits any necessary diagnostic.
class Propositions
{
public:
Propositions( const sema::Context& c );
// Emits a diagnostic and returns false if any of the expression
// can't be represented as a z3 expression.
bool addPropositions( const ValueVec& assList );
// Checks the satisfiability of the Propositionss. Emits diagnostics and return
// false if they aren't.
bool verify();
private:
const sema::Context& m_context;
z3::solver m_solver;
Builder m_builder;
// Keep track of the ids of bool constants we generated to track which
// Propositionss have failed, along with the location of the corresponding expressions.
vector< pair< uint32_t, LocationId > > m_idAndLocs;
uint32_t m_nextIndex = 0;
};
} // namespace goose::verify
#endif
|
Changes to bs/verify/remapper.cpp.
| ︙ | ︙ | |||
23 24 25 26 27 28 29 |
return bb.index();
auto& lus = m_loopUnrollingStack.back();
auto blUnrollId = make_pair( bb.index(), lus.currentUnrollIndex );
return remap( lus.loopRemapping, blUnrollId );
}
| > | | > | | | < | < | 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 |
return bb.index();
auto& lus = m_loopUnrollingStack.back();
auto blUnrollId = make_pair( bb.index(), lus.currentUnrollIndex );
return remap( lus.loopRemapping, blUnrollId );
}
bool Remapper::shouldIncludeEdge(
EdgeFilter filter, const cir::BasicBlock& currentBB, const cir::BasicBlock& succBB )
{
auto edgeCat = CFG::GetEdgeLoopCategory( currentBB, succBB );
if( edgeCat != CFG::EdgeLoopCategory::None )
{
if( filter == EdgeFilter::LoopOnly && edgeCat != CFG::EdgeLoopCategory::LoopEdge )
return false;
if( filter == EdgeFilter::ExitOnly && edgeCat != CFG::EdgeLoopCategory::LoopExitEdge )
return false;
}
return true;
}
uint32_t Remapper::remapOutgoingEdge(
const cir::BasicBlock& currentBB, const cir::BasicBlock& succBB )
{
if( !m_loopUnrollingStack.empty() )
{
const auto& lus = m_loopUnrollingStack.back();
if( !shouldIncludeEdge( lus.edgeFilter, currentBB, succBB ) )
return InvalidEdgeId;
}
if( succBB.isLoopHeader() )
{
// Exiting into a loop header. If it's an active loop, we are exiting into its next
// iteration. Otherwise, we are exiting into its first iteration.
auto it = find_if( m_loopUnrollingStack.rbegin(), m_loopUnrollingStack.rend(),
[&]( auto&& lus ) { return lus.loopId == succBB.index(); } );
if( it == m_loopUnrollingStack.rend() )
{
// First iteration of a new loop.
auto blUnrollId = make_pair( succBB.index(), 1 );
return remap( m_nextLoopRemapping, blUnrollId );
}
|
| ︙ | ︙ | |||
77 78 79 80 81 82 83 |
{
// Exiting towards the function's non loop code: no remapping necessary.
if( !succBB.loopId() )
return succBB.index();
// Exiting into a non loop header: if its part of an active loop, remap to the current
// iteration of that loop.
| | < | < | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
{
// Exiting towards the function's non loop code: no remapping necessary.
if( !succBB.loopId() )
return succBB.index();
// Exiting into a non loop header: if its part of an active loop, remap to the current
// iteration of that loop.
auto it = find_if( m_loopUnrollingStack.rbegin(), m_loopUnrollingStack.rend(),
[&]( auto&& lus ) { return lus.loopId == succBB.loopId(); } );
assert( it != m_loopUnrollingStack.rend() );
auto& lus = *it;
auto blUnrollId = make_pair( succBB.index(), lus.currentUnrollIndex );
return remap( lus.loopRemapping, blUnrollId );
}
}
|
| ︙ | ︙ |
Changes to bs/verify/remapper.h.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
using RemappingMap = unordered_map< BlockAndUnrollingId, uint32_t >;
// Helper class that tracks the state of loop unrolling
// and provides methods to remap basic block indices
// to their current unrolled instances.
class Remapper
{
| | | | < | > > | | > | | | | | | | < | | | | | | | < | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | 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 |
using RemappingMap = unordered_map< BlockAndUnrollingId, uint32_t >;
// Helper class that tracks the state of loop unrolling
// and provides methods to remap basic block indices
// to their current unrolled instances.
class Remapper
{
public:
Remapper( uint32_t firstRemappedId ) :
m_nextUniqueId( firstRemappedId )
{
}
static constexpr uint32_t InvalidEdgeId = ~0U;
uint32_t remapBBId( const cir::BasicBlock& bb );
uint32_t remapOutgoingEdge(
const cir::BasicBlock& currentBB, const cir::BasicBlock& succBB );
uint32_t getCurrentLoopId() const;
void beginLoopUnrolling( uint32_t loopId );
void endLoopUnrolling();
void nextLoopIteration();
void setExitEdgesOnly();
void setLoopEdgesOnly();
template< typename F > void forEachIncomingEdge( uint32_t bbid, F&& func ) const
{
auto begin = m_edges.lower_bound( { bbid, 0U } );
auto end = m_edges.upper_bound( { bbid, ~0U } );
for( auto it = begin; it != end; ++it )
func( it->first.second, it->second );
}
template< typename Z > void addEdge( uint32_t srcBBId, uint32_t destBBId, Z&& expr )
{
m_edges.emplace( pair{ destBBId, srcBBId }, forward< Z >( expr ) );
}
// Given a basic block index, if it's a remapped index (an invented index
// for an unrolled basic block), return the original BB index.
uint32_t getOriginalBBIndex( uint32_t remappedId ) const;
// Indicate whether all the (possibly unrolled) predecessors of a
// basic block have been processed by checking if the number of
// generated edges matches the number of incoming edges of the block.
bool areAllPredecessorsProcessed( const cir::BasicBlock& bb );
private:
enum class EdgeFilter
{
All,
LoopOnly,
ExitOnly
};
bool shouldIncludeEdge(
EdgeFilter filter, const cir::BasicBlock& currentBB, const cir::BasicBlock& succBB );
struct LoopUnrollingState
{
RemappingMap loopRemapping;
uint32_t loopId = 0;
uint32_t currentUnrollIndex = 0;
EdgeFilter edgeFilter = EdgeFilter::All;
};
uint32_t remap( RemappingMap& rm, BlockAndUnrollingId blUnrollId );
vector< LoopUnrollingState > m_loopUnrollingStack;
// The remapping state for the next loop before we enter it
// (so that we can store the remapping for its incoming edges before we begin
// checking it)
RemappingMap m_nextLoopRemapping;
// For each edge of the CFG, store the z3 boolean expression
// representing the condition for going through that edge.
map< pair< uint32_t, uint32_t >, z3::expr > m_edges;
// For each BB index "invented" by unrolling, store
// the original index.
unordered_map< uint32_t, uint32_t > m_originalBBIndices;
uint32_t m_nextUniqueId = 0;
};
} // namespace goose::verify
#endif
|
Changes to bs/verify/stack.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "verify.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::builtins;
namespace goose::verify
{
optional< Z3Val > Stack::PopAsZ3Val( Builder& b )
{
| > | | | | | | | | > | | | 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 |
#include "verify.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::builtins;
namespace goose::verify
{
optional< Z3Val > Stack::PopAsZ3Val( Builder& b )
{
auto result = visit(
[&]< typename T >( const T& elem ) -> optional< Z3Val >
{
if constexpr( is_same_v< T, Value > )
return BuildZ3ExprFromValue( b, elem );
else if constexpr( is_same_v< T, Address > )
return BuildAddressExpr( b, elem );
else
return nullopt;
},
m_stack.top() );
if( result )
m_stack.pop();
return result;
}
} // namespace goose::verify
|
Changes to bs/verify/stack.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_VERIFY_STACK_H
#define GOOSE_VERIFY_STACK_H
namespace goose::verify
{
class Stack
{
| | | | < | < < < | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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_VERIFY_STACK_H
#define GOOSE_VERIFY_STACK_H
namespace goose::verify
{
class Stack
{
public:
using Slot = variant< Value, Z3Val, Address, GhostFuncApplication, Term, StringId >;
template< typename T > void push( T&& x ) { m_stack.push( forward< T >( x ) ); }
template< typename T > optional< T > pop( Builder& b )
{
if( m_stack.empty() )
return nullopt;
if constexpr( is_same_v< T, Slot > )
{
optional< Slot > result = move( m_stack.top() );
m_stack.pop();
return result;
}
else
{
if( !holds_alternative< T >( m_stack.top() ) )
{
if constexpr( is_same_v< T, Z3Val > )
return PopAsZ3Val( b );
else
return nullopt;
}
optional< T > result = move( get< T >( m_stack.top() ) );
m_stack.pop();
return result;
}
}
private:
optional< Z3Val > PopAsZ3Val( Builder& b );
stack< Slot > m_stack;
};
} // namespace goose::verify
#endif
|
Changes to bs/verify/storage.cpp.
| ︙ | ︙ | |||
65 66 67 68 69 70 71 |
val = LoadFromBaseAddress( b, addr );
if( !val )
return nullopt;
for( uint32_t i = 1; i < addr.path().size(); ++i )
{
| > | | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
val = LoadFromBaseAddress( b, addr );
if( !val )
return nullopt;
for( uint32_t i = 1; i < addr.path().size(); ++i )
{
auto tinfo =
TypeCache::GetInstance().getTypeInfo( b.context(), ValueToEIR( val->type ) );
if( !tinfo )
return nullopt;
// The aggregate types that we handle here are:
// - tuples
// - addresses (we treat them as a tuple of (lifetime, origin, path))
//
|
| ︙ | ︙ | |||
122 123 124 125 126 127 128 |
}
optional< Z3Val > LoadFromAddress( Builder& b, const GhostFuncApplication& gfa )
{
return b.retrieveGFC( gfa );
}
| | > > | | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
}
optional< Z3Val > LoadFromAddress( Builder& b, const GhostFuncApplication& gfa )
{
return b.retrieveGFC( gfa );
}
optional< z3::expr > ModifyAggregate( Builder& b, const Z3Val& aggregate,
const SelectPath& path, uint32_t index, Z3Val&& valToStore )
{
auto tinfo =
TypeCache::GetInstance().getTypeInfo( b.context(), ValueToEIR( aggregate.type ) );
if( !tinfo )
return nullopt;
// The only aggregate type that we handle for now are tuples.
// TODO: arrays
auto elemCount = TupleTypeSize( aggregate.type );
|
| ︙ | ︙ | |||
147 148 149 150 151 152 153 |
auto elemExpr = tinfo->proj( aggregate.expr, i );
// Copy all elements from the existing value as is,
// except for the one pointed to by the current path index.
if( i == elemIndex )
{
// If we didn't reach the end of the path yet, recurse.
| | | | > | | 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
auto elemExpr = tinfo->proj( aggregate.expr, i );
// Copy all elements from the existing value as is,
// except for the one pointed to by the current path index.
if( i == elemIndex )
{
// If we didn't reach the end of the path yet, recurse.
// Otherwise, it means we finally reached the nested member that we wanted to
// modify, so push the new value.
if( ++index < path.size() )
{
auto newElem = ModifyAggregate( b,
Z3Val{ move( elemExpr ), *EIRToValue( elemType ), aggregate.loc }, path,
index, move( valToStore ) );
if( !newElem )
return nullopt;
args.push_back( move( *newElem ) );
}
else
args.push_back( valToStore.expr );
|
| ︙ | ︙ | |||
198 199 200 201 202 203 204 |
// it means we are storing into a member (of a member) of an aggregate type.
// In this case, we need to load the existing value of the aggregate,
// rebuild it with the modified member, and store it.
auto aggregate = LoadFromBaseAddress( b, addr );
if( !aggregate )
return;
| > | > | | 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
// it means we are storing into a member (of a member) of an aggregate type.
// In this case, we need to load the existing value of the aggregate,
// rebuild it with the modified member, and store it.
auto aggregate = LoadFromBaseAddress( b, addr );
if( !aggregate )
return;
if( auto newAggregateToStore =
ModifyAggregate( b, *aggregate, addr.path(), 1, move( val ) ) )
StoreToBaseAddress(
b, addr, Z3Val{ *newAggregateToStore, aggregate->type, addr.locationId() } );
}
void StoreToAddress( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val )
{
b.setGFC( gfa, move( val ) );
}
|
| ︙ | ︙ | |||
233 234 235 236 237 238 239 |
uint32_t i = 0;
for( auto&& index : addr.path() )
{
auto zindex = zc.int_val( index ).unit();
pathVec.set( i++, zindex );
}
| < < | < | < < | < < | > > | | | < | < | < | | | > | > | | | | | | | | | | | < | > | > | 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 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
uint32_t i = 0;
for( auto&& index : addr.path() )
{
auto zindex = zc.int_val( index ).unit();
pathVec.set( i++, zindex );
}
return { TypeCache::GetInstance().addrCtor()( ltExpr,
zc.int_val( static_cast< int >( addr.origin() ) ), z3::concat( pathVec ) ),
*EIRToValue( GetValueType< cir::Address >() ), addr.locationId() };
}
optional< verify::GhostFuncApplication > CIRGFAToVerifyGFA(
Builder& b, const cir::GhostFuncApplication& cgfa )
{
verify::GhostFuncApplication vgfa( cgfa.func(), cgfa.locationId() );
vgfa.args().reserve( cgfa.args().size() );
for( auto&& a : cgfa.args() )
{
visit(
[&]< typename T >( const T& x )
{
if constexpr( is_same_v< T, eir::Value > )
vgfa.args().emplace_back( BuildZ3ExprFromValue( b, x ) );
else if constexpr( is_same_v< T, Address > )
vgfa.args().emplace_back( BuildAddressExpr( b, x ) );
},
a );
}
if( vgfa.args().size() != cgfa.args().size() )
return nullopt;
return vgfa;
}
void HavocDataLocation(
Builder& b, uint32_t bbIndex, const Term& type, const DataLocation& sloc )
{
auto valToStore = BuildZ3ConstantFromType( b, type, format( "v{}", b.newUniqueId() ) );
visit(
[&]< typename T >( const T& x )
{
if constexpr( is_same_v< T, Address > )
{
StoreToAddress( b, x, move( valToStore ) );
}
else if constexpr( is_same_v< T, cir::GhostFuncApplication > )
{
if( auto vgfa = CIRGFAToVerifyGFA( b, x ) )
StoreToAddress( b, *vgfa, move( valToStore ) );
}
},
sloc );
}
} // namespace goose::verify
|
Changes to bs/verify/storage.h.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 |
extern optional< Z3Val > LoadFromAddress( Builder& b, const GhostFuncApplication& gfa );
extern void StoreToAddress( Builder& b, const Address& addr, Z3Val&& val );
extern void StoreToAddress( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val );
extern Z3Val BuildAddressExpr( Builder& b, const Address& addr );
| > | < > | 19 20 21 22 23 24 25 26 27 28 29 30 |
extern optional< Z3Val > LoadFromAddress( Builder& b, const GhostFuncApplication& gfa );
extern void StoreToAddress( Builder& b, const Address& addr, Z3Val&& val );
extern void StoreToAddress( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val );
extern Z3Val BuildAddressExpr( Builder& b, const Address& addr );
extern void HavocDataLocation(
Builder& b, uint32_t bbIndex, const Term& type, const DataLocation& sloc );
} // namespace goose::verify
#endif
|
Changes to bs/verify/terminator.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "verify.h"
#include "builtins/builtins.h"
#include "diagnostics/diagnostics.h"
#include "helpers.inl"
using namespace goose::diagnostics;
namespace goose::verify
{
bool Func::handleTerminator( uint32_t bbIndex, const cir::Terminator& t )
{
| | < < < | > | | | | | | | | | | 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 |
#include "verify.h"
#include "builtins/builtins.h"
#include "diagnostics/diagnostics.h"
#include "helpers.inl"
using namespace goose::diagnostics;
namespace goose::verify
{
bool Func::handleTerminator( uint32_t bbIndex, const cir::Terminator& t )
{
return visit( [&]( auto&& t ) { return handleTerminator( bbIndex, t ); }, t.content() );
}
bool Func::handleTerminator( uint32_t bbIndex, const cir::RetVoid& tr )
{
// Emit the "ensures" expressions as assertions.
if( !m_func )
return true;
DiagnosticsContext dc( tr.locationId(), "When returning here." );
Builder cb = m_builder;
const auto& fvi = m_func->type().verifInfos();
if( !fvi )
return true;
const auto& postConds = fvi->postConds();
bool success = true;
postConds->forEach(
[&]( auto&& cond )
{
// Don't do any error handling here, it should already have been taken care of
// by the condition verifier.
if( auto zv = TryBuildZ3ExprFromValue( cb, cond ) )
{
bool succ = m_builder.checkAssertion( zv->expr, cond.locationId() );
success = success && succ;
}
} );
return success;
}
bool Func::handleTerminator( uint32_t bbIndex, const cir::Ret& tr )
{
auto retExpr = m_builder.pop();
|
| ︙ | ︙ | |||
60 61 62 63 64 65 66 |
if( retExpr )
{
// Set the @result placeholder for the func's post conditions
cb.setPlaceholder( "@result"_sid, retExpr->expr );
// Emit the return type's predicates as assertions.
| | < | < | > | | | | | | | | | | 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 |
if( retExpr )
{
// Set the @result placeholder for the func's post conditions
cb.setPlaceholder( "@result"_sid, retExpr->expr );
// Emit the return type's predicates as assertions.
ForEachPredicate( cb, m_retType, retExpr->expr,
[&]( auto&& z3expr, auto locId ) { m_builder.checkAssertion( z3expr, locId ); } );
}
const auto& fvi = m_func->type().verifInfos();
if( !fvi )
return true;
const auto& postConds = fvi->postConds();
bool success = true;
postConds->forEach(
[&]( auto&& cond )
{
// Don't do any error handling here, it should already have been taken care of
// by the condition verifier.
if( auto zv = TryBuildZ3ExprFromValue( cb, cond ) )
{
bool succ = m_builder.checkAssertion( zv->expr, cond.locationId() );
success = success && succ;
}
} );
return success;
}
bool Func::handleTerminator( uint32_t bbIndex, const cir::Branch& t )
{
const auto& bb = m_cfg->getBB( bbIndex );
|
| ︙ | ︙ | |||
147 148 149 150 151 152 153 |
uint32_t destBBId = m_remapper.remapOutgoingEdge( *bb, *pDestBB );
m_viz.addEdge( srcBBId, destBBId );
m_remapper.addEdge( srcBBId, destBBId, move( cond ) );
return true;
}
| | | 144 145 146 147 148 149 150 151 |
uint32_t destBBId = m_remapper.remapOutgoingEdge( *bb, *pDestBB );
m_viz.addEdge( srcBBId, destBBId );
m_remapper.addEdge( srcBBId, destBBId, move( cond ) );
return true;
}
} // namespace goose::verify
|
Changes to bs/verify/type.cpp.
| ︙ | ︙ | |||
54 55 56 57 58 59 60 |
m_typeInfos.emplace( type, *tinfo );
return tinfo;
}
const TypeInfo& TypeCache::uninterpretedTypeInfo() const
{
| | > | > | | < | < < | < < | < < < | < < | < | < | < < | < | > | < | | < > | | > | | | | < < | < | < | | < | > | < | 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 |
m_typeInfos.emplace( type, *tinfo );
return tinfo;
}
const TypeInfo& TypeCache::uninterpretedTypeInfo() const
{
static TypeInfo UnintTypeInfo{ m_uninterpretedSort,
[&]( auto&& b )
{
return GetZ3Context().constant(
format( "uc{}", b.newUniqueId() ).c_str(), m_uninterpretedSort );
},
[&]( auto&& b, auto&& val )
{
return GetZ3Context().constant(
format( "uv{}", b.newUniqueId() ).c_str(), m_uninterpretedSort );
},
{}, {} };
return UnintTypeInfo;
}
optional< TypeInfo > TypeCache::createTypeInfo( const sema::Context& c, const Term& type )
{
// TODO: some types are missing and will be dealt with later on.
if( type == GetValueType< bool >() )
{
return TypeInfo{ GetZ3Context().bool_sort(), []( auto&& b )
{ return GetZ3Context().bool_const( format( "u{}", b.newUniqueId() ).c_str() ); },
[]( auto&& b, auto&& val )
{ return GetZ3Context().bool_val( *FromValue< bool >( val ) ); }, {}, {} };
}
if( type == GetValueType< BigInt >() )
{
return TypeInfo{ GetZ3Context().int_sort(), []( auto&& b )
{ return GetZ3Context().int_const( format( "bi{}", b.newUniqueId() ).c_str() ); },
[]( auto&& b, auto&& val )
{
auto intVal = FromValue< BigInt >( val );
if( intVal->fitsAsS64() )
return GetZ3Context().int_val( intVal->toS64() );
else
return GetZ3Context().int_val( intVal->toString().c_str() );
},
{}, {} };
}
if( type == GetValueType< string >() )
{
return TypeInfo{ GetZ3Context().string_sort(), []( auto&& b )
{ return GetZ3Context().string_const( format( "s{}", b.newUniqueId() ).c_str() ); },
[]( auto&& b, auto&& val ) -> z3::expr
{
// TODO need to see how unicode convcersion works, does z3 do
// it automatically?
// we'll have to see when we start verifying strings in earnest
// (for now it's just there so the verifier doesn't fail when
// ecountering a string)
if( val.isConstant() )
return GetZ3Context().string_val( *FromValue< string >( val ) );
if( !BuildZ3Op( b, *val.cir() ) )
return GetZ3Context().string_const(
format( "str{}", b.newUniqueId() ).c_str() );
return b.pop()->expr;
},
{}, {} };
}
auto typeVal = EIRToValue( type );
if( !typeVal )
return nullopt;
// Lower pointer types into addresses: we're only interested in knowing about aliasing, for now.
if( type == GetValueType< Address >() || FromValue< PointerType >( *typeVal ) )
{
return TypeInfo{ m_addressSort,
[&]( auto&& b ) {
return GetZ3Context().constant(
format( "ml{}", b.newUniqueId() ).c_str(), m_addressSort );
},
[&]( auto&& b, auto&& val )
{
// TODO: handle constant addresses (not needed for now, perhaps we'll never need it)
// G_VAL_ASSERT( val, !val.isConstant() );
if( val.isConstant() || !BuildZ3Op( b, *val.cir() ) )
return GetZ3Context().constant(
format( "ml{}", b.newUniqueId() ).c_str(), m_addressSort );
return b.pop()->expr;
},
[&]( auto&& tup, auto&& index )
{
assert( index < m_addressProjs.size() );
return m_addressProjs[index]( tup );
},
{} };
}
if( IsTupleType( *typeVal ) )
return createTupleTypeInfo( c, *typeVal );
if( IsSequenceType( *typeVal ) )
return createSequenceTypeInfo( c, *typeVal );
if( auto intType = FromValue< builtins::IntegerType >( *typeVal ) )
{
if( intType->m_signed )
{
return TypeInfo{ GetZ3Context().int_sort(), []( auto&& b )
{ return GetZ3Context().int_const( format( "u{}", b.newUniqueId() ).c_str() ); },
[]( auto&& b, auto&& val )
{
auto intVal = FromValue< APSInt >( val );
return GetZ3Context().int_val( intVal->getExtValue() );
},
{}, {} };
}
else
{
auto numBits = intType->m_numBits;
return TypeInfo{ GetZ3Context().bv_sort( intType->m_numBits ),
[numBits]( auto&& b ) {
return GetZ3Context().bv_const(
format( "u{}", b.newUniqueId() ).c_str(), numBits );
},
[numBits]( auto&& b, auto&& val )
{
auto intVal = FromValue< APSInt >( val );
return GetZ3Context().bv_val( intVal->getExtValue(), numBits );
},
{}, {} };
}
}
return nullopt;
}
optional< TypeInfo > TypeCache::createTupleTypeInfo( const sema::Context& c, const Value& tupType )
|
| ︙ | ︙ | |||
228 229 230 231 232 233 234 |
auto tinfo = getTypeInfo( c, elemType );
if( !tinfo )
return nullopt;
elemTInfos.push_back( *tinfo );
elemNames.push_back( format( "{}_{}", sortName, i ) );
| | > | | 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
auto tinfo = getTypeInfo( c, elemType );
if( !tinfo )
return nullopt;
elemTInfos.push_back( *tinfo );
elemNames.push_back( format( "{}_{}", sortName, i ) );
assert( tinfo->sort );
elemSorts.push_back( *tinfo->sort );
}
llvm::SmallVector< z3::func_decl, 8 > projs;
auto&& [tupSort, tupCtor] =
mkZ3TupleSort( sortName.c_str(), size, elemNames, elemSorts, projs );
const auto& ctor = tupCtor;
auto undefined = [size, elemTInfos, ctor]( auto&& b )
{
llvm::SmallVector< z3::expr, 8 > args;
args.reserve( size );
|
| ︙ | ︙ | |||
258 259 260 261 262 263 264 |
};
auto build = [size, elemTInfos, ctor]( auto&& b, auto&& val )
{
llvm::SmallVector< z3::expr, 8 > args;
args.reserve( size );
| | > | | | | | | | < < < | | > | | | | | < | < | < | 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 282 283 284 285 286 287 |
};
auto build = [size, elemTInfos, ctor]( auto&& b, auto&& val )
{
llvm::SmallVector< z3::expr, 8 > args;
args.reserve( size );
ForEachInTuple( val,
[&]( auto&& elemVal )
{
auto elem = BuildZ3ExprFromValue( b, elemVal );
args.push_back( move( elem.expr ) );
return true;
} );
return ctor( args.size(), &args.front() );
};
auto proj = [projs]( auto&& tup, auto&& index )
{
assert( index < projs.size() );
return projs[index]( tup );
};
auto ctorWrapper = [ctor]( auto&& args ) { return ctor( args.size(), &args.front() ); };
return TypeInfo{ tupSort, undefined, build, proj, ctorWrapper };
}
optional< TypeInfo > TypeCache::createSequenceTypeInfo(
const sema::Context& c, const Value& seqType )
{
auto subTypeInfo = createTypeInfo( c, GetSequenceSubType( seqType ) );
if( !subTypeInfo || !subTypeInfo->sort )
return nullopt;
auto sort = GetZ3Context().seq_sort( *subTypeInfo->sort );
// TODO: implement build, but need to implement bridge for seq values and to decide on a
// representation! Not needed until we actually use compilation time sequences for more than
// representing calculated addresses during verification). It will be needed some day, but not
// yet.
return TypeInfo{ sort, [sort]( auto&& b )
{ return GetZ3Context().constant( format( "seq{}", b.newUniqueId() ).c_str(), sort ); }, {},
{}, {} };
}
|
Changes to bs/verify/type.h.
1 2 3 4 5 6 7 8 |
#ifndef GOOSE_VERIFY_TYPE_H
#define GOOSE_VERIFY_TYPE_H
namespace goose::verify
{
struct TypeInfo
{
optional< z3::sort > sort;
| | | | | | | | | < | | | < | | < < < | | < < < | < < < | | | | | | | | | | | | | | 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 |
#ifndef GOOSE_VERIFY_TYPE_H
#define GOOSE_VERIFY_TYPE_H
namespace goose::verify
{
struct TypeInfo
{
optional< z3::sort > sort;
function< z3::expr( Builder& ) > undefined;
function< z3::expr( Builder&, const Value& ) > build;
function< z3::expr( const z3::expr&, uint32_t index ) > proj;
function< z3::expr( llvm::SmallVector< z3::expr, 8 > ) > ctor;
};
// For complex types that require the creation of custom constructor funcs in z3
// (for instances tuples), we keep a cache to avoid recreating them multiple times.
class TypeCache
{
public:
static TypeCache& GetInstance();
optional< TypeInfo > getTypeInfo( const sema::Context& c, const Term& type );
const TypeInfo& uninterpretedTypeInfo() const;
const z3::func_decl& addrCtor() const { return m_addressCtor; }
const z3::func_decl& addrLifetimeGetter() const { return m_addressProjs[0]; }
const z3::func_decl& addrOriginGetter() const { return m_addressProjs[1]; }
const z3::func_decl& addrPathGetter() const { return m_addressProjs[2]; }
private:
TypeCache();
optional< TypeInfo > createTupleTypeInfo( const sema::Context& c, const Value& tupType );
optional< TypeInfo > createSequenceTypeInfo( const sema::Context& c, const Value& seqType );
optional< TypeInfo > createTypeInfo( const sema::Context& c, const Term& type );
unordered_map< Term, TypeInfo > m_typeInfos;
z3::sort m_memPathSort;
z3::sort m_addressSort;
z3::func_decl m_addressCtor;
llvm::SmallVector< z3::func_decl, 3 > m_addressProjs;
z3::sort m_uninterpretedSort;
static uint32_t ms_nextUniqueId;
};
} // namespace goose::verify
#endif
|
Changes to bs/verify/value.cpp.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), val.type() );
if( !tinfo )
tinfo = TypeCache::GetInstance().uninterpretedTypeInfo();
auto zexpr = tinfo->build( b, val );
| | | | > | > | > | > | > | 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 |
tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), val.type() );
if( !tinfo )
tinfo = TypeCache::GetInstance().uninterpretedTypeInfo();
auto zexpr = tinfo->build( b, val );
return Z3Val{ move( zexpr ), *EIRToValue( val.type() ), val.locationId() };
}
optional< Z3Val > TryBuildZ3ValFromConstant( Builder& b, const Value& val )
{
auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), val.type() );
if( !tinfo )
return nullopt;
auto zexpr = tinfo->build( b, val );
return Z3Val{ move( zexpr ), *EIRToValue( val.type() ), val.locationId() };
}
Z3Val BuildZ3ConstantFromType( Builder& b, const Value& type, const string& name )
{
auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), ValueToEIR( type ) );
if( !tinfo )
tinfo = TypeCache::GetInstance().uninterpretedTypeInfo();
assert( tinfo->sort );
return Z3Val{ GetZ3Context().constant( name.c_str(), *tinfo->sort ), type,
type.locationId() };
}
optional< Z3Val > TryBuildZ3ConstantFromType(
Builder& b, const Value& type, const string& name )
{
auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), ValueToEIR( type ) );
if( !tinfo )
return nullopt;
assert( tinfo->sort );
return Z3Val{ GetZ3Context().constant( name.c_str(), *tinfo->sort ), type,
type.locationId() };
}
Z3Val BuildZ3ConstantFromType( Builder& b, const Term& type, const string& name )
{
auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), type );
if( !tinfo )
tinfo = TypeCache::GetInstance().uninterpretedTypeInfo();
assert( tinfo->sort );
auto tval = *EIRToValue( type );
return Z3Val{ GetZ3Context().constant( name.c_str(), *tinfo->sort ), tval,
tval.locationId() };
}
optional< Z3Val > TryBuildZ3ConstantFromType( Builder& b, const Term& type, const string& name )
{
auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), type );
if( !tinfo )
return nullopt;
assert( tinfo->sort );
auto tval = *EIRToValue( type );
return Z3Val{ GetZ3Context().constant( name.c_str(), *tinfo->sort ), tval,
tval.locationId() };
}
z3::expr GetAsBitVec( const z3::expr& expr, const Value& type )
{
if( expr.is_bv() )
return expr;
|
| ︙ | ︙ | |||
118 119 120 121 122 123 124 |
return GetAsInt( expr, to.type );
else if( to.expr.is_bv() )
return GetAsBitVec( expr, to.type );
else
return expr;
}
| | < | < > | | > | > | > | > > | | > | > | > | > | > | 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 |
return GetAsInt( expr, to.type );
else if( to.expr.is_bv() )
return GetAsBitVec( expr, to.type );
else
return expr;
}
template< typename I, typename F > bool BuildZ3UnaryExpr( Builder& b, const I& instr, F&& func )
{
auto operand = b.pop();
if( !operand )
return false;
b.push( Z3Val{ func( operand->expr ), operand->type, instr.locationId() } );
return true;
}
template< typename I, typename F > bool BuildZ3BinExpr( Builder& b, const I& instr, F&& func )
{
auto rhs = b.pop();
if( !rhs )
return false;
auto lhs = b.pop();
if( !lhs )
return false;
if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )
{
b.push(
Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), lhs->type, instr.locationId() } );
return true;
}
// If we are trying to do an operation on a mix of bitvec and int,
// convert the int to a bitvec first.
if( lhs->expr.is_bv() )
{
assert( rhs->expr.is_int() );
b.push( Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), lhs->type,
instr.locationId() } );
return true;
}
else
{
assert( lhs->expr.is_int() );
b.push( Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), lhs->type,
instr.locationId() } );
return true;
}
return false;
}
template< typename I, typename F >
bool BuildZ3BinBitwiseExpr( Builder& b, const I& instr, F&& func )
{
auto rhs = b.pop();
if( !rhs )
return false;
auto lhs = b.pop();
if( !lhs )
return false;
if( ValueToEIR( lhs->type ) == GetValueType< BigInt >() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
0, "verifier error: bitwise operands can't be ct_int." );
return false;
}
if( ValueToEIR( rhs->type ) == GetValueType< BigInt >() )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
0, "verifier error: bitwise operands can't be ct_int." );
return false;
}
if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )
{
b.push(
Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), lhs->type, instr.locationId() } );
return true;
}
// If we are trying to do an operation on a mix of bitvec and int,
// convert the int to a bitvec first.
if( lhs->expr.is_bv() )
{
assert( rhs->expr.is_int() );
b.push( Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), lhs->type,
instr.locationId() } );
return true;
}
else
{
assert( lhs->expr.is_int() );
b.push( Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), lhs->type,
instr.locationId() } );
return true;
}
return false;
}
template< typename I, typename F >
bool BuildZ3BinBoolExpr( Builder& b, const I& instr, F&& func )
{
auto rhs = b.pop();
if( !rhs )
return false;
auto lhs = b.pop();
if( !lhs )
return false;
if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )
{
b.push( Z3Val{ func( lhs->expr, rhs->expr, lhs->type ),
*EIRToValue( GetValueType< bool >() ), instr.locationId() } );
return true;
}
// If we are trying to do an operation on a mix of bitvec and int,
// convert the int to a bitvec first.
if( lhs->expr.is_bv() )
{
assert( rhs->expr.is_int() );
b.push( Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ),
*EIRToValue( GetValueType< bool >() ), instr.locationId() } );
return true;
}
else
{
cout << lhs->expr << endl;
assert( lhs->expr.is_int() );
b.push( Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ),
*EIRToValue( GetValueType< bool >() ), instr.locationId() } );
return true;
}
return false;
}
template< typename I, typename F >
|
| ︙ | ︙ | |||
264 265 266 267 268 269 270 |
if( !lhs->expr.is_bool() )
return false;
if( !rhs->expr.is_bool() )
return false;
| | > < < | < < < | > | | < | < | > | | | | | | | | | | | 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 |
if( !lhs->expr.is_bool() )
return false;
if( !rhs->expr.is_bool() )
return false;
b.push( Z3Val{ func( lhs->expr, rhs->expr, lhs->type ),
*EIRToValue( GetValueType< bool >() ), instr.locationId() } );
return true;
}
template< typename RT, typename I, typename F >
bool BuildZ3BinSeqExpr( Builder& b, const I& instr, F&& func )
{
auto rhs = b.pop();
if( !rhs )
return false;
auto lhs = b.pop();
if( !lhs )
return false;
if( !lhs->expr.is_seq() )
return false;
if( !rhs->expr.is_seq() )
return false;
b.push( Z3Val{ func( lhs->expr, rhs->expr ), *EIRToValue( GetValueType< RT >() ),
instr.locationId() } );
return true;
}
bool BuildZ3Op( Builder& b, const cir::InstrSeq& is )
{
for( const auto& instr : is )
if( !BuildZ3Op( b, instr ) )
return false;
return true;
}
template< typename T > bool BuildZ3Op( Builder& b, const T& instr )
{
return false;
}
bool BuildZ3Op( Builder& b, const Load& instr )
{
optional< Z3Val > zv;
if( auto cstAddr = b.pop< Address >() )
zv = LoadFromAddress( b, *cstAddr, instr.type().get() );
else if( auto gfa = b.pop< GhostFuncApplication >() )
zv = LoadFromAddress( b, *gfa );
else if( auto symAddr = b.pop< Z3Val >() )
zv = BuildZ3ConstantFromType(
b, instr.type().get(), format( "val{}", b.newUniqueId() ) );
if( !zv )
return false;
if( b.mustLoadAssume() )
ForEachPredicate( b, instr.type().get(), zv->expr,
[&]( auto&& z3expr, auto locId ) { b.assume( z3expr ); } );
zv->loc = instr.locationId();
b.push( move( *zv ) );
return true;
}
bool BuildZ3Op( Builder& b, const Store& instr )
{
auto addr = b.pop< Stack::Slot >();
if( !addr )
return false;
auto zv = b.pop();
if( !zv )
return false;
ForEachPredicate( b, instr.type().get(), zv->expr,
[&]( auto&& z3expr, auto locId )
{
if( !instr.srcLocId().invalid() && !instr.destLocId().invalid() )
{
DiagnosticsContext dc( instr.destLocId(), "...to this." );
DiagnosticsContext dc2( instr.srcLocId(), "When assigning this..." );
b.checkAssertion( z3expr, locId );
}
else
b.checkAssertion( z3expr, locId );
} );
if( const auto* cstAddr = get_if< Address >( &*addr ) )
StoreToAddress( b, *cstAddr, move( *zv ) );
else if( const auto* gfa = get_if< GhostFuncApplication >( &*addr ) )
StoreToAddress( b, *gfa, move( *zv ) );
return true;
|
| ︙ | ︙ | |||
394 395 396 397 398 399 400 |
bool BuildZ3Op( Builder& b, const AllocVar& instr )
{
auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), instr.type().get() );
if( !tinfo )
tinfo = TypeCache::GetInstance().uninterpretedTypeInfo();
| > | | > | | | | | | | > | | | | | | | | > | | | | | | | | > | | | | | | | | < | < | < | < | > | | | | | | > | | | | | | > | | | | | > | > | > | > | > | > | | > | | | | | > | > | > | > | > | > | > | > | > | > | | 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 |
bool BuildZ3Op( Builder& b, const AllocVar& instr )
{
auto tinfo = TypeCache::GetInstance().getTypeInfo( b.context(), instr.type().get() );
if( !tinfo )
tinfo = TypeCache::GetInstance().uninterpretedTypeInfo();
b.setVar( instr.index(),
Z3Val{ tinfo->undefined( b ), *EIRToValue( instr.type().get() ), instr.locationId() } );
return true;
}
// Implemented in phi.cpp
bool BuildZ3Op( Builder& b, const Phi& instr );
bool BuildZ3Op( Builder& b, const Not& instr )
{
return BuildZ3UnaryExpr( b, instr,
[]( auto&& operand )
{
if( operand.is_bool() )
return !operand;
assert( operand.is_bv() );
return ~operand;
} );
}
bool BuildZ3Op( Builder& b, const And& instr )
{
return BuildZ3BinBitwiseExpr( b, instr,
[]( auto&& lhs, auto&& rhs, auto&& type )
{
if( lhs.is_bool() && rhs.is_bool() )
return lhs && rhs;
auto lhsBV = GetAsBitVec( lhs, type );
auto rhsBV = GetAsBitVec( rhs, type );
return lhsBV & rhsBV;
} );
}
bool BuildZ3Op( Builder& b, const Or& instr )
{
return BuildZ3BinBitwiseExpr( b, instr,
[]( auto&& lhs, auto&& rhs, auto&& type )
{
if( lhs.is_bool() && rhs.is_bool() )
return lhs || rhs;
auto lhsBV = GetAsBitVec( lhs, type );
auto rhsBV = GetAsBitVec( rhs, type );
return lhsBV | rhsBV;
} );
}
bool BuildZ3Op( Builder& b, const Xor& instr )
{
return BuildZ3BinBitwiseExpr( b, instr,
[]( auto&& lhs, auto&& rhs, auto&& type )
{
if( lhs.is_bool() && rhs.is_bool() )
return lhs ^ rhs;
auto lhsBV = GetAsBitVec( lhs, type );
auto rhsBV = GetAsBitVec( rhs, type );
return lhsBV ^ rhsBV;
} );
}
bool BuildZ3Op( Builder& b, const Implies& instr )
{
return BuildZ3BinLogicExpr( b, instr,
[]( auto&& lhs, auto&& rhs, auto&& type ) { return z3::implies( lhs, rhs ); } );
}
bool BuildZ3Op( Builder& b, const IsPrefixOf& instr )
{
return BuildZ3BinSeqExpr< bool >(
b, instr, []( auto&& lhs, auto&& rhs ) { return z3::prefixof( lhs, rhs ); } );
}
bool BuildZ3Op( Builder& b, const Shl& instr )
{
return BuildZ3BinBitwiseExpr( b, instr,
[]( auto&& lhs, auto&& rhs, auto&& type )
{
auto lhsBV = GetAsBitVec( lhs, type );
auto rhsBV = GetAsBitVec( rhs, type );
return z3::shl( lhsBV, rhsBV );
} );
}
bool BuildZ3Op( Builder& b, const LShr& instr )
{
return BuildZ3BinBitwiseExpr( b, instr,
[]( auto&& lhs, auto&& rhs, auto&& type )
{
auto lhsBV = GetAsBitVec( lhs, type );
auto rhsBV = GetAsBitVec( rhs, type );
return z3::lshr( lhsBV, rhsBV );
} );
}
bool BuildZ3Op( Builder& b, const AShr& instr )
{
return BuildZ3BinBitwiseExpr( b, instr,
[]( auto&& lhs, auto&& rhs, auto&& type )
{
auto lhsBV = GetAsBitVec( lhs, type );
auto rhsBV = GetAsBitVec( rhs, type );
return z3::ashr( lhsBV, rhsBV );
} );
}
bool BuildZ3Op( Builder& b, const Add& instr )
{
return BuildZ3BinExpr(
b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs + rhs; } );
}
bool BuildZ3Op( Builder& b, const Sub& instr )
{
return BuildZ3BinExpr(
b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs - rhs; } );
}
bool BuildZ3Op( Builder& b, const Mul& instr )
{
return BuildZ3BinExpr(
b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs * rhs; } );
}
bool BuildZ3Op( Builder& b, const UDiv& instr )
{
return BuildZ3BinExpr(
b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::udiv( lhs, rhs ); } );
}
bool BuildZ3Op( Builder& b, const SDiv& instr )
{
return BuildZ3BinExpr(
b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs / rhs; } );
}
bool BuildZ3Op( Builder& b, const URem& instr )
{
return BuildZ3BinExpr(
b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::urem( lhs, rhs ); } );
}
bool BuildZ3Op( Builder& b, const SRem& instr )
{
return BuildZ3BinExpr( b, instr,
[]( auto&& lhs, auto&& rhs, auto&& type )
{
if( lhs.is_bv() )
return z3::srem( lhs, rhs );
return z3::rem( lhs, rhs );
} );
}
bool BuildZ3Op( Builder& b, const Eq& instr )
{
return BuildZ3BinBoolExpr(
b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs == rhs; } );
}
bool BuildZ3Op( Builder& b, const Neq& instr )
{
return BuildZ3BinBoolExpr(
b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs != rhs; } );
}
bool BuildZ3Op( Builder& b, const UGT& instr )
{
return BuildZ3BinBoolExpr(
b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::ugt( lhs, rhs ); } );
}
bool BuildZ3Op( Builder& b, const UGE& instr )
{
return BuildZ3BinBoolExpr(
b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::uge( lhs, rhs ); } );
}
bool BuildZ3Op( Builder& b, const ULT& instr )
{
return BuildZ3BinBoolExpr(
b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::ult( lhs, rhs ); } );
}
bool BuildZ3Op( Builder& b, const ULE& instr )
{
return BuildZ3BinBoolExpr(
b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::ule( lhs, rhs ); } );
}
bool BuildZ3Op( Builder& b, const SGT& instr )
{
return BuildZ3BinBoolExpr(
b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs > rhs; } );
}
bool BuildZ3Op( Builder& b, const SGE& instr )
{
return BuildZ3BinBoolExpr(
b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs >= rhs; } );
}
bool BuildZ3Op( Builder& b, const SLT& instr )
{
return BuildZ3BinBoolExpr(
b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs < rhs; } );
}
bool BuildZ3Op( Builder& b, const SLE& instr )
{
return BuildZ3BinBoolExpr(
b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs <= rhs; } );
}
bool BuildZ3Op( Builder& b, const Assert& instr )
{
auto cond = b.pop();
if( !cond )
return false;
|
| ︙ | ︙ | |||
608 609 610 611 612 613 614 |
const auto* expr = b.retrievePlaceholder( instr.name() );
if( expr )
{
b.push( Z3Val{ *expr, *EIRToValue( instr.type().get() ), instr.locationId() } );
return true;
}
| > | | 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 |
const auto* expr = b.retrievePlaceholder( instr.name() );
if( expr )
{
b.push( Z3Val{ *expr, *EIRToValue( instr.type().get() ), instr.locationId() } );
return true;
}
auto result =
BuildZ3ConstantFromType( b, instr.type().get(), format( "p{}", instr.name().str() ) );
b.push( move( result ) );
return true;
}
bool BuildZ3Op( Builder& b, const PHOverrideSet& instr )
{
auto phExpr = b.pop();
|
| ︙ | ︙ | |||
658 659 660 661 662 663 664 |
return !!b.setVar( instr.tempIndex(), move( *initVal ) );
}
bool BuildZ3Op( Builder& b, const Select& instr )
{
if( auto cstBaseLoc = b.pop< Address >() )
{
| > | | < | < < | < | < < | 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 |
return !!b.setVar( instr.tempIndex(), move( *initVal ) );
}
bool BuildZ3Op( Builder& b, const Select& instr )
{
if( auto cstBaseLoc = b.pop< Address >() )
{
b.push(
Address::Select( move( *cstBaseLoc ), instr.memberIndex(), instr.locationId() ) );
return true;
}
auto symBaseLoc = b.pop();
if( !symBaseLoc )
return false;
const auto& tc = TypeCache::GetInstance();
auto ltExpr = tc.addrLifetimeGetter()( symBaseLoc->expr );
auto originExpr = tc.addrOriginGetter()( symBaseLoc->expr );
auto pathExpr = tc.addrPathGetter()( symBaseLoc->expr );
b.push( Z3Val{
tc.addrCtor()( ltExpr, originExpr,
z3::concat( pathExpr, GetZ3Context().int_val( instr.memberIndex() ).unit() ) ),
*EIRToValue( GetValueType< cir::Address >() ), instr.locationId() } );
return true;
}
bool BuildZ3Op( Builder& b, const GhostCall& instr )
{
auto gfunc = b.pop< Value >();
|
| ︙ | ︙ | |||
767 768 769 770 771 772 773 |
{
G_ERROR( "InlineCall encountered during verification" );
return false;
}
bool BuildZ3Op( Builder& b, const cir::Instruction& instr )
{
| | < < < | 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 |
{
G_ERROR( "InlineCall encountered during verification" );
return false;
}
bool BuildZ3Op( Builder& b, const cir::Instruction& instr )
{
return visit( [&]( auto&& e ) { return BuildZ3Op( b, e ); }, instr );
}
Z3Val BuildZ3ExprFromValue( Builder& b, const Value& val )
{
if( val.isConstant() )
return BuildZ3ValFromConstant( b, val );
|
| ︙ | ︙ | |||
801 802 803 804 805 806 807 |
return TryBuildZ3ValFromConstant( b, val );
if( BuildZ3Op( b, *val.cir() ) )
return b.pop();
return TryBuildZ3ConstantFromType( b, val.type(), format( "val{}", b.newUniqueId() ) );
}
| | | 825 826 827 828 829 830 831 832 |
return TryBuildZ3ValFromConstant( b, val );
if( BuildZ3Op( b, *val.cir() ) )
return b.pop();
return TryBuildZ3ConstantFromType( b, val.type(), format( "val{}", b.newUniqueId() ) );
}
} // namespace goose::verify
|
Changes to bs/verify/value.h.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 |
extern Z3Val BuildZ3ValFromConstant( Builder& b, const Value& val );
extern Z3Val BuildZ3ExprFromValue( Builder& b, const Value& val );
extern Z3Val BuildZ3ConstantFromType( Builder& b, const Value& type, const string& name );
extern Z3Val BuildZ3ConstantFromType( Builder& b, const Term& type, const string& name );
extern optional< Z3Val > TryBuildZ3ValFromConstant( Builder& b, const Value& val );
extern optional< Z3Val > TryBuildZ3ExprFromValue( Builder& b, const Value& val );
| | > | > | | 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 |
extern Z3Val BuildZ3ValFromConstant( Builder& b, const Value& val );
extern Z3Val BuildZ3ExprFromValue( Builder& b, const Value& val );
extern Z3Val BuildZ3ConstantFromType( Builder& b, const Value& type, const string& name );
extern Z3Val BuildZ3ConstantFromType( Builder& b, const Term& type, const string& name );
extern optional< Z3Val > TryBuildZ3ValFromConstant( Builder& b, const Value& val );
extern optional< Z3Val > TryBuildZ3ExprFromValue( Builder& b, const Value& val );
extern optional< Z3Val > TryBuildZ3ConstantFromType(
Builder& b, const Value& type, const string& name );
extern optional< Z3Val > TryBuildZ3ConstantFromType(
Builder& b, const Term& type, const string& name );
extern bool BuildZ3Op( Builder& b, const cir::Instruction& instr );
extern bool BuildZ3Op( Builder& b, const cir::InstrSeq& is );
extern z3::expr GetAsBitVec( const z3::expr& expr, const Value& type );
extern z3::expr GetAsBitVec( const Z3Val& zv );
extern z3::expr GetAsInt( const z3::expr& expr, const Value& type );
extern z3::expr GetAsInt( const Z3Val& zv );
extern z3::expr Coerce( const Z3Val& val, const Z3Val& to );
extern z3::expr Coerce( const z3::expr& expr, const Z3Val& to );
} // namespace goose::verify
#endif
|
Changes to bs/verify/vartracker.cpp.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
const auto* vs = m_varStorage.get( index );
if( !vs )
return nullopt;
if( vs->size() <= bbIndex )
return nullopt;
| | | > | > | | | 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 |
const auto* vs = m_varStorage.get( index );
if( !vs )
return nullopt;
if( vs->size() <= bbIndex )
return nullopt;
return ( *vs )[bbIndex];
}
optional< Z3Val > VarTracker::setForBasicBlock(
Builder& b, uint32_t bbIndex, uint32_t index, Z3Val&& val )
{
auto* vs = m_varStorage.get( index );
if( !vs )
{
VarState newVS;
newVS.resize( bbIndex + 1 );
auto& vsRef = m_varStorage.set( index, move( newVS ) );
vs = &vsRef;
}
else if( vs->size() <= bbIndex )
vs->resize( bbIndex + 1 );
auto& c = GetZ3Context();
auto predicate = c.bool_const( format( "b{}", bbIndex ).c_str() );
auto lhsExpr = BuildZ3ConstantFromType(
b, val.type, format( "v{}_{}_{}", index, bbIndex, b.newUniqueId() ) );
b.add( z3::implies( predicate, lhsExpr.expr == Coerce( val, lhsExpr ) ) );
( *vs )[bbIndex] = move( lhsExpr );
return *( *vs )[bbIndex];
}
optional< Z3Val > VarTracker::retrieve( Builder& b, uint32_t bbIndex, uint32_t index )
{
if( bbIndex == b.currentBBIndex() )
return nullopt;
|
| ︙ | ︙ | |||
58 59 60 61 62 63 64 |
// Model the ssa phi operation by creating a series of equality assertions, each implied by
// one of the possible incoming edge condition for the current basic block.
if( b.remapper() )
{
auto& c = GetZ3Context();
| | > | | | | | > > | | | | | 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 |
// Model the ssa phi operation by creating a series of equality assertions, each implied by
// one of the possible incoming edge condition for the current basic block.
if( b.remapper() )
{
auto& c = GetZ3Context();
b.remapper()->forEachIncomingEdge( bbIndex,
[&]( auto&& predIndex, auto&& expr )
{
if( auto predVal = retrieve( b, predIndex, index ) )
{
if( !lhsExpr )
lhsExpr = BuildZ3ConstantFromType( b, predVal->type,
format( "v{}_{}_{}", index, bbIndex, b.newUniqueId() ) );
b.add(
z3::implies( c.bool_const( format( "e{}_{}", predIndex, bbIndex ).c_str() ),
lhsExpr->expr == Coerce( *predVal, *lhsExpr ) ) );
}
} );
}
if( !lhsExpr )
return nullopt;
return setForBasicBlock( b, bbIndex, index, move( *lhsExpr ) );
}
optional< Z3Val > VarTracker::set( Builder& b, uint32_t index, Z3Val&& val )
{
return setForBasicBlock( b, b.currentBBIndex(), index, move( val ) );
}
|
Changes to bs/verify/vartracker.h.
1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_VERIFY_VARTRACKER_H
#define GOOSE_VERIFY_VARTRACKER_H
namespace goose::verify
{
class Builder;
class VarTracker
{
| | | | < | > > | | | | | > | | | | < > | 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 |
#ifndef GOOSE_VERIFY_VARTRACKER_H
#define GOOSE_VERIFY_VARTRACKER_H
namespace goose::verify
{
class Builder;
class VarTracker
{
public:
VarTracker( size_t size ) :
m_varStorage( size )
{
}
optional< Z3Val > retrieve( Builder& b, uint32_t bbIndex, uint32_t index );
optional< Z3Val > set( Builder& b, uint32_t index, Z3Val&& val );
private:
optional< Z3Val > getForBasicBlock( uint32_t bbIndex, uint32_t index ) const;
optional< Z3Val > setForBasicBlock(
Builder& b, uint32_t bbIndex, uint32_t index, Z3Val&& val );
// For each variable, store its last value as a z3 expression.
using VarState = llvm::SmallVector< optional< Z3Val >, 16 >;
using VarStorage = cir::TempStorage< VarState >;
VarStorage m_varStorage;
};
} // namespace goose::verify
#endif
|
Changes to bs/verify/verify.cpp.
1 2 3 4 5 6 7 8 9 |
#include "verify.h"
namespace goose::verify
{
z3::context& GetZ3Context()
{
static z3::context z3Context;
return z3Context;
}
| | | 1 2 3 4 5 6 7 8 9 10 |
#include "verify.h"
namespace goose::verify
{
z3::context& GetZ3Context()
{
static z3::context z3Context;
return z3Context;
}
} // namespace goose::verify
|
Changes to bs/verify/verify.h.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 |
{
Pass,
Fails,
Unverifiable
};
extern Result VerifyInstrSeq( const sema::Context& c, const InstrSeq& is );
| | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
{
Pass,
Fails,
Unverifiable
};
extern Result VerifyInstrSeq( const sema::Context& c, const InstrSeq& is );
} // namespace goose::verify
#include "helpers.h"
#include "verifyviz.h"
#include "type.h"
#include "value.h"
#include "ghostfunc.h"
|
| ︙ | ︙ |
Changes to bs/verify/verifyviz.cpp.
| ︙ | ︙ | |||
15 16 17 18 19 20 21 |
ostream& indent( ostream& out, int indentation )
{
while( indentation-- )
out << '\t';
return out;
}
| | | | < | | | > > | > > | > > | | | > > | > > | | | > > | | < | | | 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 |
ostream& indent( ostream& out, int indentation )
{
while( indentation-- )
out << '\t';
return out;
}
} // namespace
VerifyViz::~VerifyViz()
{
#ifdef VERIFY_VIZ_FAIL_ONLY
if( !m_assertionFailed )
return;
#endif
if( m_filename.empty() )
m_filename = "anon_func.dot";
ofstream out( m_filename );
out << "digraph \"goose\"\n"
"{\n"
"\tcolor=\"#"
<< colors::base05
<< "\";\n"
"\tfontcolor=\"#"
<< colors::base05
<< "\";\n"
"\tbgcolor=\"#"
<< colors::base00
<< "\";\n"
"\trankdir=TB;\n"
"\tnode [\n"
"\t\tcolor=\"#"
<< colors::base05
<< "\";\n"
"\t\tfontcolor=\"#"
<< colors::base05
<< "\";\n"
"\t];\n"
"\tedge [\n"
"\t\tcolor=\"#"
<< colors::base03
<< "\";\n"
"\t];\n";
if( m_assertionFailed )
indent( out, 1 ) << *m_assertionFailed << "[color=\"#" << colors::base08 << "\"];\n";
for( auto&& [id, oid] : m_root->m_blocks )
{
out << '\n';
if( id != oid )
indent( out, 1 ) << id << "[ label=\"" << id << ':' << oid << "\"];";
else
indent( out, 1 ) << id << ';';
}
for( auto&& x : m_root->m_children )
{
out << '\n';
GenerateSubGraph( out, x, 1 );
}
for( auto&& [from, to] : m_edges )
indent( out, 1 ) << from << "->" << to << ";\n";
out << "}\n";
}
void VerifyViz::GenerateSubGraph( ostream& out, ptr< SubGraph > sg, int indentation ) const
{
indent( out, indentation ) << "subgraph cluster_" << sg.get() << endl;
indent( out, indentation ) << "{\n";
indent( out, indentation + 1 ) << "color = \"#" << colors::base09 << "\";\n";
for( auto&& [id, oid] : sg->m_blocks )
{
out << '\n';
if( id != oid )
indent( out, indentation + 1 ) << id << "[ label=\"" << id << ':' << oid << "\"];";
else
indent( out, indentation + 1 ) << id << ';';
}
|
| ︙ | ︙ |
Changes to bs/verify/verifyviz.h.
1 2 3 4 5 6 7 8 9 | #ifndef GOOSE_VERIFY_VIZ_H #define GOOSE_VERIFY_VIZ_H #include <z3++.h> #include "cir/cir.h" #include "sema/sema.h" // Enable the generation of a dot file of the unrolled CFG generated to // verify each function, saved as "mangled_func_name.dot" | | | | | > | > | | | > | | | | | > | | | | | | | | | | | | | | | | | < < < | | | | | | | | | | | | | | | | | | | | 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 |
#ifndef GOOSE_VERIFY_VIZ_H
#define GOOSE_VERIFY_VIZ_H
#include <z3++.h>
#include "cir/cir.h"
#include "sema/sema.h"
// Enable the generation of a dot file of the unrolled CFG generated to
// verify each function, saved as "mangled_func_name.dot"
// #define VERIFY_VIZ
namespace goose::verify
{
#ifndef VERIFY_VIZ
class VerifyViz
{
public:
void setFunctionIdentity( const Term& id ) {}
void beginLoopVerification() {}
void beginLoopInduction() {}
void endLoopVerification() {}
void nextLoopIteration() {}
void addBlock( uint32_t id, uint32_t originalId ) {}
void addEdge( uint32_t from, uint32_t to ) {}
void assertionFailed() {}
struct Guard
{
Guard( VerifyViz& vv ) {}
void commit() {}
};
};
#else
class VerifyViz
{
struct SubGraph
{
ptr< SubGraph > deepCopy() const;
vector< pair< uint32_t, uint32_t > > m_blocks;
list< ptr< SubGraph > > m_children;
wptr< SubGraph > m_parent;
};
public:
~VerifyViz();
void setFunctionIdentity( const Term& id );
void beginLoopVerification();
void beginLoopInduction();
void endLoopVerification();
void nextLoopIteration();
void addBlock( uint32_t id, uint32_t originalId );
void addEdge( uint32_t from, uint32_t to );
void assertionFailed() { m_assertionFailed = m_lastBlockId; }
struct Guard
{
Guard( VerifyViz& vv );
~Guard();
void commit() { m_commit = true; }
ptr< SubGraph > deepCopyGraph( const ptr< SubGraph >& pNode );
VerifyViz& m_vv;
ptr< SubGraph > m_savedRoot;
ptr< SubGraph > m_savedCurrentSubGraph;
vector< pair< uint32_t, uint32_t > > m_savedEdges;
uint32_t m_savedLastBlockId = 0;
bool m_commit = false;
};
private:
void pushSubGraph();
void popSubGraph();
void GenerateSubGraph( ostream& out, ptr< SubGraph > sg, int indentation ) const;
string m_filename;
ptr< SubGraph > m_root = make_shared< SubGraph >();
ptr< SubGraph > m_currentSubGraph = m_root;
vector< pair< uint32_t, uint32_t > > m_edges;
optional< uint32_t > m_assertionFailed;
uint32_t m_lastBlockId = 0;
};
#endif
} // namespace goose::verify
#endif
|