From 36e197f903ba19229b35cdd7c5bea045ca34dcdc Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 25 Sep 2016 21:23:07 +0800 Subject: Parse+AST - Support capturing :item (requires cloning AST items) --- src/ast/ast.cpp | 188 +++++++++++++++++++++++++++++------- src/ast/ast.hpp | 23 ++++- src/ast/expr.cpp | 24 +++++ src/ast/expr.hpp | 31 +----- src/ast/expr_ptr.hpp | 33 +++++++ src/ast/types.cpp | 3 +- src/expand/cfg.cpp | 4 +- src/expand/concat.cpp | 1 + src/expand/format_args.cpp | 1 + src/expand/macro_rules.cpp | 2 +- src/macro_rules/eval.cpp | 6 +- src/macro_rules/macro_rules.hpp | 2 +- src/main.cpp | 4 + src/parse/common.hpp | 1 + src/parse/eTokenType.enum.h | 1 + src/parse/expr.cpp | 3 +- src/parse/interpolated_fragment.cpp | 12 +++ src/parse/interpolated_fragment.hpp | 5 + src/parse/root.cpp | 3 +- src/parse/token.cpp | 16 ++- src/resolve/absolute.cpp | 1 + 21 files changed, 289 insertions(+), 75 deletions(-) create mode 100644 src/ast/expr_ptr.hpp (limited to 'src') diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 4de02843..60c813d4 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -3,6 +3,7 @@ #include "ast.hpp" #include "crate.hpp" #include "types.hpp" +#include "expr.hpp" #include "../common.hpp" #include #include "../parse/parseerror.hpp" @@ -64,6 +65,125 @@ MetaItem MetaItem::clone() const throw ::std::runtime_error("MetaItem::clone - Fell off end"); } +StructItem StructItem::clone() const +{ + return StructItem(m_attrs.clone(), m_is_public, m_name, m_type); +} +TupleItem TupleItem::clone() const +{ + return TupleItem(m_attrs.clone(), m_is_public, m_type); +} + + +TypeAlias TypeAlias::clone() const +{ + return TypeAlias( m_params, m_type ); +} +Static Static::clone() const +{ + return Static( m_class, m_type, m_value.is_valid() ? AST::Expr( m_value.node().clone() ) : AST::Expr() ); +} + +Function::Function(Span sp, GenericParams params, TypeRef ret_type, Arglist args): + m_span(sp), + m_params( move(params) ), + m_rettype( move(ret_type) ), + m_args( move(args) ) +{ +} +Function Function::clone() const +{ + decltype(m_args) new_args; + for(const auto& arg : m_args) + new_args.push_back( ::std::make_pair( arg.first.clone(), arg.second ) ); + + auto rv = Function( m_span, m_params, m_rettype, mv$(new_args) ); + if( m_code.is_valid() ) + { + rv.m_code = AST::Expr( m_code.node().clone() ); + } + return rv; +} + +void Trait::add_type(::std::string name, TypeRef type) { + m_items.push_back( Named(mv$(name), Item::make_Type({TypeAlias(GenericParams(), mv$(type))}), true) ); +} +void Trait::add_function(::std::string name, Function fcn) { + DEBUG("trait fn " << name); + m_items.push_back( Named(mv$(name), Item::make_Function({mv$(fcn)}), true) ); +} +void Trait::add_static(::std::string name, Static v) { + m_items.push_back( Named(mv$(name), Item::make_Static({mv$(v)}), true) ); +} +void Trait::set_is_marker() { + m_is_marker = true; +} +bool Trait::is_marker() const { + return m_is_marker; +} +bool Trait::has_named_item(const ::std::string& name, bool& out_is_fcn) const +{ + for( const auto& i : m_items ) + { + if( i.name == name ) { + out_is_fcn = i.data.is_Function(); + return true; + } + } + return false; +} + +Trait Trait::clone() const +{ + auto rv = Trait(m_params, m_supertraits); + for(const auto& item : m_items) + { + rv.m_items.push_back( Named { item.name, item.data.clone(), item.is_pub } ); + } + return rv; +} + +Enum Enum::clone() const +{ + decltype(m_variants) new_variants; + for(const auto& var : m_variants) + { + TU_MATCHA( (var.m_data), (e), + (Value, + new_variants.push_back( EnumVariant(var.m_attrs.clone(), var.m_name, e.m_value.clone()) ); + ), + (Tuple, + new_variants.push_back( EnumVariant(var.m_attrs.clone(), var.m_name, e.m_sub_types) ); + ), + (Struct, + decltype(e.m_fields) new_fields; + for(const auto& f : e.m_fields) + new_fields.push_back( f.clone() ); + new_variants.push_back( EnumVariant(var.m_attrs.clone(), var.m_name, mv$(new_fields)) ); + ) + ) + } + return Enum(m_params, mv$(new_variants)); +} +Struct Struct::clone() const +{ + TU_MATCHA( (m_data), (e), + (Tuple, + decltype(e.ents) new_fields; + for(const auto& f : e.ents) + new_fields.push_back( f.clone() ); + return Struct(m_params, mv$(new_fields)); + ), + (Struct, + decltype(e.ents) new_fields; + for(const auto& f : e.ents) + new_fields.push_back( f.clone() ); + return Struct(m_params, mv$(new_fields)); + ) + ) + throw ""; +} + ::std::ostream& operator<<(::std::ostream& os, const ImplDef& impl) { return os << "impl<" << impl.m_params << "> " << impl.m_trait.ent << " for " << impl.m_type << ""; @@ -282,42 +402,44 @@ Module::ItemRef Module::find_item(const ::std::string& needle, bool allow_leaves return Module::ItemRef(); } - -Function::Function(Span sp, GenericParams params, TypeRef ret_type, Arglist args): - m_span(sp), - m_params( move(params) ), - m_rettype( move(ret_type) ), - m_args( move(args) ) +Item Item::clone() const { + TU_MATCHA( (*this), (e), + (None, + return AST::Item(e); + ), + (MacroInv, + TODO(Span(), "Clone on Item::MacroInv"); + ), + (Module, + TODO(Span(), "Clone on Item::Module"); + ), + (Crate, + return AST::Item(e); + ), + (Type, + return AST::Item(e.clone()); + ), + (Struct, + return AST::Item(e.clone()); + ), + (Enum, + return AST::Item(e.clone()); + ), + (Trait, + return AST::Item(e.clone()); + ), + + (Function, + return AST::Item(e.clone()); + ), + (Static, + return AST::Item(e.clone()); + ) + ) + throw ""; } -void Trait::add_type(::std::string name, TypeRef type) { - m_items.push_back( Named(mv$(name), Item::make_Type({TypeAlias(GenericParams(), mv$(type))}), true) ); -} -void Trait::add_function(::std::string name, Function fcn) { - DEBUG("trait fn " << name); - m_items.push_back( Named(mv$(name), Item::make_Function({mv$(fcn)}), true) ); -} -void Trait::add_static(::std::string name, Static v) { - m_items.push_back( Named(mv$(name), Item::make_Static({mv$(v)}), true) ); -} -void Trait::set_is_marker() { - m_is_marker = true; -} -bool Trait::is_marker() const { - return m_is_marker; -} -bool Trait::has_named_item(const ::std::string& name, bool& out_is_fcn) const -{ - for( const auto& i : m_items ) - { - if( i.name == name ) { - out_is_fcn = i.data.is_Function(); - return true; - } - } - return false; -} ::std::ostream& operator<<(::std::ostream& os, const TypeParam& tp) diff --git a/src/ast/ast.hpp b/src/ast/ast.hpp index 8f9ff8f6..99359d6d 100644 --- a/src/ast/ast.hpp +++ b/src/ast/ast.hpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include @@ -70,6 +70,8 @@ struct StructItem friend ::std::ostream& operator<<(::std::ostream& os, const StructItem& x) { return os << (x.m_is_public ? "pub " : "") << x.m_name << ": " << x.m_type; } + + StructItem clone() const; }; struct TupleItem @@ -92,6 +94,8 @@ struct TupleItem friend ::std::ostream& operator<<(::std::ostream& os, const TupleItem& x) { return os << (x.m_is_public ? "pub " : "") << x.m_type; } + + TupleItem clone() const; }; class TypeAlias @@ -110,6 +114,8 @@ public: GenericParams& params() { return m_params; } TypeRef& type() { return m_type; } + + TypeAlias clone() const; }; class Static @@ -141,6 +147,8 @@ public: TypeRef& type() { return m_type; } Expr& value() { return m_value; } + + Static clone() const; }; class Function @@ -155,6 +163,7 @@ private: Expr m_code; TypeRef m_rettype; Arglist m_args; + // TODO: ABI, const, and unsafe public: Function() {} @@ -174,6 +183,8 @@ public: TypeRef& rettype() { return m_rettype; } const Arglist& args() const { return m_args; } Arglist& args() { return m_args; } + + Function clone() const; }; class Trait @@ -210,6 +221,8 @@ public: bool is_marker() const; bool has_named_item(const ::std::string& name, bool& out_is_fcn) const; + + Trait clone() const; }; TAGGED_UNION_EX(EnumVariantData, (), Value, @@ -291,10 +304,11 @@ public: {} const GenericParams& params() const { return m_params; } + GenericParams& params() { return m_params; } const ::std::vector& variants() const { return m_variants; } + ::std::vector& variants() { return m_variants; } - GenericParams& params() { return m_params; } - ::std::vector& variants() { return m_variants; } + Enum clone() const; }; TAGGED_UNION_EX(StructData, (), Struct, @@ -333,6 +347,7 @@ public: TypeRef get_field_type(const char *name, const ::std::vector& args); + Struct clone() const; }; class ImplDef @@ -634,6 +649,8 @@ TAGGED_UNION_EX(Item, (), None, public: MetaItems attrs; Span span; + + Item clone() const; ) ); diff --git a/src/ast/expr.cpp b/src/ast/expr.cpp index 780b480c..ecd60def 100644 --- a/src/ast/expr.cpp +++ b/src/ast/expr.cpp @@ -5,6 +5,19 @@ namespace AST { + +Expr::Expr(unique_ptr node): + m_node(node.release()) +{ +} +Expr::Expr(ExprNode* node): + m_node(node) +{ +} +Expr::Expr(): + m_node(nullptr) +{ +} void Expr::visit_nodes(NodeVisitor& v) { if( m_node ) @@ -21,6 +34,17 @@ void Expr::visit_nodes(NodeVisitor& v) const m_node->visit(v); } } + +Expr Expr::clone() const +{ + if( m_node ) { + return Expr( m_node->clone() ); + } + else { + return Expr(); + } +} + ::std::ostream& operator<<(::std::ostream& os, const Expr& pat) { if( pat.m_node.get() ) diff --git a/src/ast/expr.hpp b/src/ast/expr.hpp index 0d503b81..00c1154b 100644 --- a/src/ast/expr.hpp +++ b/src/ast/expr.hpp @@ -40,11 +40,9 @@ public: MetaItems& attrs() { return m_attrs; } TypeRef& get_res_type() { return m_res_type; } - - friend ::std::ostream& operator<<(::std::ostream& os, const ExprNode& node); static ::std::unique_ptr from_deserialiser(Deserialiser& d); }; -typedef ::std::unique_ptr ExprNodeP; +typedef ::std::unique_ptr ExprNodeP; #define NODE_METHODS() \ void visit(NodeVisitor& nv) override;\ @@ -685,33 +683,6 @@ public: #undef NT }; -class Expr -{ - ::std::shared_ptr m_node; -public: - Expr(unique_ptr node): - m_node(node.release()) - { - } - Expr(ExprNode* node): - m_node(node) - { - } - Expr(): - m_node(nullptr) - { - } - - bool is_valid() const { return m_node.get() != nullptr; } - ExprNode& node() { assert(m_node.get()); return *m_node; } - const ExprNode& node() const { assert(m_node.get()); return *m_node; } - ::std::shared_ptr take_node() { assert(m_node.get()); return ::std::move(m_node); } - void visit_nodes(NodeVisitor& v); - void visit_nodes(NodeVisitor& v) const; - - friend ::std::ostream& operator<<(::std::ostream& os, const Expr& pat); -}; - } #endif diff --git a/src/ast/expr_ptr.hpp b/src/ast/expr_ptr.hpp new file mode 100644 index 00000000..eb60c49f --- /dev/null +++ b/src/ast/expr_ptr.hpp @@ -0,0 +1,33 @@ +/* + */ +#include + +namespace AST { + +class ExprNode; +class NodeVisitor; + +typedef ::std::unique_ptr ExprNodeP; +extern ::std::ostream& operator<<(::std::ostream& os, const ExprNode& node); + +class Expr +{ + ::std::shared_ptr m_node; +public: + Expr(unique_ptr node); + Expr(ExprNode* node); + Expr(); + + bool is_valid() const { return m_node.get() != nullptr; } + const ExprNode& node() const { assert(m_node.get()); return *m_node; } + ExprNode& node() { assert(m_node.get()); return *m_node; } + ::std::shared_ptr take_node() { assert(m_node.get()); return ::std::move(m_node); } + void visit_nodes(NodeVisitor& v); + void visit_nodes(NodeVisitor& v) const; + + Expr clone() const; + + friend ::std::ostream& operator<<(::std::ostream& os, const Expr& pat); +}; + +} diff --git a/src/ast/types.cpp b/src/ast/types.cpp index 293d9db0..1c6cc568 100644 --- a/src/ast/types.cpp +++ b/src/ast/types.cpp @@ -9,7 +9,8 @@ */ #include "types.hpp" #include "ast/ast.hpp" -#include "ast/crate.hpp" +#include +#include /// Mappings from internal type names to the core type enum static const struct { diff --git a/src/expand/cfg.cpp b/src/expand/cfg.cpp index 2a629432..c79e9872 100644 --- a/src/expand/cfg.cpp +++ b/src/expand/cfg.cpp @@ -1,9 +1,11 @@ - +/* + */ #include #include #include #include #include "cfg.hpp" +#include // Needed to clear a ExprNodeP #include #include diff --git a/src/expand/concat.cpp b/src/expand/concat.cpp index 1838df30..61840065 100644 --- a/src/expand/concat.cpp +++ b/src/expand/concat.cpp @@ -5,6 +5,7 @@ #include "../parse/parseerror.hpp" #include "../parse/tokentree.hpp" #include "../parse/lex.hpp" +#include class CConcatExpander: public ExpandProcMacro diff --git a/src/expand/format_args.cpp b/src/expand/format_args.cpp index 3e74e076..b25cc595 100644 --- a/src/expand/format_args.cpp +++ b/src/expand/format_args.cpp @@ -6,6 +6,7 @@ #include "../parse/tokentree.hpp" #include "../parse/lex.hpp" #include // for m_load_std +#include // for ExprNode_* namespace { diff --git a/src/expand/macro_rules.cpp b/src/expand/macro_rules.cpp index 359b9865..1a3fe246 100644 --- a/src/expand/macro_rules.cpp +++ b/src/expand/macro_rules.cpp @@ -101,7 +101,7 @@ class CMacroUseHandler: ::std::unique_ptr Macro_Invoke(const char* name, const MacroRules& rules, const TokenTree& tt, AST::Module& mod) { - return Macro_InvokeRules(name, rules, tt); + return Macro_InvokeRules(name, rules, tt, mod); } diff --git a/src/macro_rules/eval.cpp b/src/macro_rules/eval.cpp index f850ae95..966c1149 100644 --- a/src/macro_rules/eval.cpp +++ b/src/macro_rules/eval.cpp @@ -13,6 +13,7 @@ #include #include "pattern_checks.hpp" #include +#include class ParameterMappings { @@ -499,7 +500,7 @@ void Macro_HandlePatternCap(TTStream& lex, unsigned int index, MacroPatEnt::Type bound_tts.insert( index, iterations, InterpolatedFragment( Parse_MetaItem(lex) ) ); break; case MacroPatEnt::PAT_ITEM: - TODO(lex.getPosition(), "Parse :item"); + bound_tts.insert( index, iterations, InterpolatedFragment( Parse_Mod_Item_S(lex, false, "!", lex.parse_state().module->path(), false, AST::MetaItems{}) ) ); break; case MacroPatEnt::PAT_IDENT: GET_CHECK_TOK(tok, lex, TOK_IDENT); @@ -562,7 +563,7 @@ bool Macro_HandlePattern(TTStream& lex, const MacroPatEnt& pat, ::std::vector Macro_InvokeRules(const char *name, const MacroRules& rules, const TokenTree& input) +::std::unique_ptr Macro_InvokeRules(const char *name, const MacroRules& rules, const TokenTree& input, AST::Module& mod) { TRACE_FUNCTION; Span sp;// = input @@ -579,6 +580,7 @@ bool Macro_HandlePattern(TTStream& lex, const MacroPatEnt& pat, ::std::vector Macro_InvokeRules(const char *name, const MacroRules& rules, const TokenTree& input); +extern ::std::unique_ptr Macro_InvokeRules(const char *name, const MacroRules& rules, const TokenTree& input, AST::Module& mod); extern MacroRulesPtr Parse_MacroRules(TokenStream& lex); #endif // MACROS_HPP_INCLUDED diff --git a/src/main.cpp b/src/main.cpp index 439ccbaf..eb4bb478 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -160,6 +160,10 @@ int main(int argc, char *argv[]) return false; }); + // TODO: This is for liblibc - should be a command-line option + //Cfg_SetFlag("stdbuild"); + + try { diff --git a/src/parse/common.hpp b/src/parse/common.hpp index 12d56eb3..a4420b54 100644 --- a/src/parse/common.hpp +++ b/src/parse/common.hpp @@ -46,6 +46,7 @@ extern AST::Pattern Parse_Pattern(TokenStream& lex, bool is_refutable); extern void Parse_Impl_Item(TokenStream& lex, AST::Impl& impl); extern void Parse_Mod_Item(TokenStream& lex, bool file_controls_dir, const ::std::string& file_path, AST::Module& mod, bool is_public, AST::MetaItems meta_items); +extern ::AST::Named<::AST::Item> Parse_Mod_Item_S(TokenStream& lex, bool file_controls_dir, const ::std::string& file_path, const ::AST::Path& mod_path, bool is_public, AST::MetaItems meta_items); extern void Parse_ModRoot_Items(TokenStream& lex, AST::Module& mod, bool file_controls_dir, const ::std::string& path); diff --git a/src/parse/eTokenType.enum.h b/src/parse/eTokenType.enum.h index f7157805..21bbb1d5 100644 --- a/src/parse/eTokenType.enum.h +++ b/src/parse/eTokenType.enum.h @@ -12,6 +12,7 @@ _(TOK_INTERPOLATED_EXPR) _(TOK_INTERPOLATED_STMT) _(TOK_INTERPOLATED_BLOCK) _(TOK_INTERPOLATED_META) +_(TOK_INTERPOLATED_ITEM) // Value tokens _(TOK_IDENT) diff --git a/src/parse/expr.cpp b/src/parse/expr.cpp index ffc2fa19..aa016cd2 100644 --- a/src/parse/expr.cpp +++ b/src/parse/expr.cpp @@ -11,7 +11,8 @@ * - Parse_Expr0 : Parse a single expression */ #include "parseerror.hpp" -#include "../ast/ast.hpp" +#include +#include #include "common.hpp" #include #include "tokentree.hpp" diff --git a/src/parse/interpolated_fragment.cpp b/src/parse/interpolated_fragment.cpp index e907978b..f0d7c19c 100644 --- a/src/parse/interpolated_fragment.cpp +++ b/src/parse/interpolated_fragment.cpp @@ -3,6 +3,7 @@ #include #include "interpolated_fragment.hpp" #include +#include // For definition of ExprNode InterpolatedFragment::~InterpolatedFragment() { @@ -22,6 +23,9 @@ InterpolatedFragment::~InterpolatedFragment() case InterpolatedFragment::META: delete reinterpret_cast(m_ptr); break; + case InterpolatedFragment::ITEM: + delete reinterpret_cast*>(m_ptr); + break; } } } @@ -48,6 +52,11 @@ InterpolatedFragment::InterpolatedFragment(AST::MetaItem v): m_ptr( new AST::MetaItem(mv$(v)) ) { } +InterpolatedFragment::InterpolatedFragment(::AST::Named<::AST::Item> v): + m_type( InterpolatedFragment::ITEM ), + m_ptr( new ::AST::Named<::AST::Item>( mv$(v) ) ) +{ +} InterpolatedFragment::InterpolatedFragment(TokenTree v): m_type( InterpolatedFragment::TT ), m_ptr( new TokenTree(mv$(v)) ) @@ -99,6 +108,9 @@ InterpolatedFragment::InterpolatedFragment(TypeRef v): case InterpolatedFragment::META: os << "meta[" << *reinterpret_cast(x.m_ptr) << "]"; break; + case InterpolatedFragment::ITEM: + os << "item[?]"; + break; } return os; } diff --git a/src/parse/interpolated_fragment.hpp b/src/parse/interpolated_fragment.hpp index 7d9ee7c8..b0be2efe 100644 --- a/src/parse/interpolated_fragment.hpp +++ b/src/parse/interpolated_fragment.hpp @@ -11,8 +11,11 @@ namespace AST { class Path; class ExprNode; class MetaItem; + template class Named; + class Item; }; + class InterpolatedFragment { public: @@ -28,6 +31,7 @@ public: BLOCK, META, + ITEM, } m_type; // Owned type-pruned pointer @@ -41,6 +45,7 @@ public: InterpolatedFragment(::AST::Path); InterpolatedFragment(::TypeRef); InterpolatedFragment(::AST::MetaItem ); + InterpolatedFragment(::AST::Named ); ~InterpolatedFragment(); InterpolatedFragment(Type , ::AST::ExprNode*); diff --git a/src/parse/root.cpp b/src/parse/root.cpp index 0140f898..b5fc9c09 100644 --- a/src/parse/root.cpp +++ b/src/parse/root.cpp @@ -1675,7 +1675,7 @@ void Parse_ModRoot_Items(TokenStream& lex, AST::Module& mod, bool file_controls_ PUTBACK(tok, lex); DEBUG("meta_items = " << meta_items); - // root-level macros + // Root-level macro invocations auto ps = lex.start_span(); if( GET_TOK(tok, lex) == TOK_MACRO ) { @@ -1692,6 +1692,7 @@ void Parse_ModRoot_Items(TokenStream& lex, AST::Module& mod, bool file_controls_ } // Module visibility + // TODO: Move this into Parse_Mod_Item? bool is_public = false; if( GET_TOK(tok, lex) == TOK_RWORD_PUB ) { is_public = true; diff --git a/src/parse/token.cpp b/src/parse/token.cpp index ee884d3b..b261f248 100644 --- a/src/parse/token.cpp +++ b/src/parse/token.cpp @@ -8,6 +8,7 @@ #include "interpolated_fragment.hpp" #include #include +#include // for reasons Token::~Token() { @@ -93,6 +94,12 @@ Token::Token(const InterpolatedFragment& frag) m_type = TOK_INTERPOLATED_META; m_data = new AST::MetaItem( reinterpret_cast(frag.m_ptr)->clone() ); break; + case InterpolatedFragment::ITEM: { + m_type = TOK_INTERPOLATED_ITEM; + const auto& named = *reinterpret_cast*>(frag.m_ptr); + ::AST::Item item = named.data.clone(); + m_data = new AST::Named( named.name, mv$(item), named.is_pub ); + break; } } } Token::Token(TagTakeIP, InterpolatedFragment frag) @@ -122,9 +129,15 @@ Token::Token(TagTakeIP, InterpolatedFragment frag) m_data = reinterpret_cast(frag.m_ptr); frag.m_ptr = nullptr; break; + case InterpolatedFragment::ITEM: + m_type = TOK_INTERPOLATED_ITEM; + m_data = frag.m_ptr; + frag.m_ptr = nullptr; + break; case InterpolatedFragment::META: m_type = TOK_INTERPOLATED_META; - m_data = new AST::MetaItem( mv$(*reinterpret_cast(frag.m_ptr)) ); + m_data = frag.m_ptr; + frag.m_ptr = nullptr; break; } } @@ -257,6 +270,7 @@ struct EscapedString { case TOK_INTERPOLATED_STMT: return "/*:stmt*/"; case TOK_INTERPOLATED_BLOCK: return "/*:block*/"; case TOK_INTERPOLATED_META: return "/*:meta*/"; + case TOK_INTERPOLATED_ITEM: return "/*:item*/"; // Value tokens case TOK_IDENT: return m_data.as_String(); case TOK_MACRO: return m_data.as_String() + "!"; diff --git a/src/resolve/absolute.cpp b/src/resolve/absolute.cpp index 76b4160f..5666cdd7 100644 --- a/src/resolve/absolute.cpp +++ b/src/resolve/absolute.cpp @@ -8,6 +8,7 @@ */ #include #include +#include #include #include -- cgit v1.2.3