From 86312a017590ce285f85fe386df22940265e9048 Mon Sep 17 00:00:00 2001 From: Robin Sommer Date: Tue, 27 Jun 2023 08:42:32 +0200 Subject: [PATCH] Move all modules into a single AST and reactivate most visitors. There's now a single `AST Root` node that all modules are children of. AST management and processing has moved into the `ASTContext` class. The driver's units now just refer to the modules inside that single, joint AST; they are no longer an owner of the module AST. Most visitors are reactivated to operate on that single AST, but many don't have any actual functionality yet. --- hilti/toolchain/CMakeLists.txt | 36 +- hilti/toolchain/include/ast/ast-context.h | 112 ++- hilti/toolchain/include/ast/builder/builder.h | 2 +- .../ast/builder/node-factory.autogen.h | 46 +- .../include/ast/builder/node-factory.h | 6 +- .../ast/declarations/imported-module.h | 14 +- hilti/toolchain/include/ast/expressions/all.h | 2 +- .../include/ast/expressions/{id.h => name.h} | 0 .../ast/expressions/resolved-operator.h | 6 +- hilti/toolchain/include/ast/forward.h | 4 +- hilti/toolchain/include/ast/module.h | 92 ++- hilti/toolchain/include/ast/node-range.h | 10 + hilti/toolchain/include/ast/node.h | 68 +- hilti/toolchain/include/ast/nodes.decl | 111 --- hilti/toolchain/include/ast/operators/bytes.h | 2 +- .../include/ast/operators/function.h | 2 +- .../toolchain/include/ast/operators/stream.h | 2 +- hilti/toolchain/include/ast/scope-lookup.h | 95 --- hilti/toolchain/include/ast/scope.h | 8 +- .../toolchain/include/ast/statements/switch.h | 2 +- .../include/ast/visitor-dispatcher.h | 287 ++++---- hilti/toolchain/include/ast/visitor.h | 37 +- hilti/toolchain/include/base/logger.h | 50 +- hilti/toolchain/include/compiler/coercion.h | 46 +- hilti/toolchain/include/compiler/context.h | 99 +-- .../include/compiler/detail/codegen/codegen.h | 45 +- .../include/compiler/detail/parser/driver.h | 6 +- .../include/compiler/detail/visitors.h | 29 +- hilti/toolchain/include/compiler/driver.h | 38 +- hilti/toolchain/include/compiler/plugin.h | 50 +- hilti/toolchain/include/compiler/unit.h | 247 +------ hilti/toolchain/include/global.h | 2 +- hilti/toolchain/src/ast/ast-context.cc | 470 +++++++++++++ hilti/toolchain/src/ast/dispatcher.cc | 1 + .../src/ast/expressions/{id.cc => name.cc} | 13 +- hilti/toolchain/src/ast/module.cc | 12 +- hilti/toolchain/src/ast/node.cc | 86 ++- hilti/toolchain/src/ast/scope-lookup.cc | 77 --- hilti/toolchain/src/ast/scope.cc | 19 +- .../toolchain/src/compiler/codegen/codegen.cc | 145 ++-- .../src/compiler/codegen/coercions.cc | 29 +- hilti/toolchain/src/compiler/codegen/ctors.cc | 16 +- .../src/compiler/codegen/expressions.cc | 16 +- .../src/compiler/codegen/operators.cc | 20 +- .../src/compiler/codegen/statements.cc | 19 +- hilti/toolchain/src/compiler/codegen/types.cc | 103 ++- .../toolchain/src/compiler/codegen/unpack.cc | 41 +- hilti/toolchain/src/compiler/coercion.cc | 306 ++++---- hilti/toolchain/src/compiler/context.cc | 136 +--- hilti/toolchain/src/compiler/cxx/unit.cc | 2 +- hilti/toolchain/src/compiler/driver.cc | 478 ++----------- hilti/toolchain/src/compiler/parser/driver.cc | 4 +- hilti/toolchain/src/compiler/parser/parser.yy | 5 +- hilti/toolchain/src/compiler/plugin.cc | 36 +- hilti/toolchain/src/compiler/unit.cc | 419 ++--------- .../src/compiler/visitors/coercer.cc | 23 +- .../src/compiler/visitors/constant-folder.cc | 51 +- .../src/compiler/visitors/normalizer.cc | 56 +- .../src/compiler/visitors/printer.cc | 651 +++++++++--------- .../src/compiler/visitors/renderer.cc | 4 +- .../src/compiler/visitors/resolver.cc | 42 +- .../src/compiler/visitors/scope-builder.cc | 102 +-- .../src/compiler/visitors/validator.cc | 38 +- 63 files changed, 2192 insertions(+), 2784 deletions(-) rename hilti/toolchain/include/ast/expressions/{id.h => name.h} (100%) delete mode 100644 hilti/toolchain/include/ast/nodes.decl delete mode 100644 hilti/toolchain/include/ast/scope-lookup.h create mode 100644 hilti/toolchain/src/ast/ast-context.cc rename hilti/toolchain/src/ast/expressions/{id.cc => name.cc} (90%) delete mode 100644 hilti/toolchain/src/ast/scope-lookup.cc diff --git a/hilti/toolchain/CMakeLists.txt b/hilti/toolchain/CMakeLists.txt index 61263492e..c3831a4a9 100644 --- a/hilti/toolchain/CMakeLists.txt +++ b/hilti/toolchain/CMakeLists.txt @@ -35,6 +35,7 @@ autogen_dispatchers( ${CMAKE_CURRENT_SOURCE_DIR}/include/hilti/ast/nodes.decl ${AUTOGEN_H}/operators.decl) set(SOURCES + src/ast/ast-context.cc src/ast/attribute.cc src/ast/builder/builder.cc # src/ast/builder/type.cc @@ -47,16 +48,14 @@ set(SOURCES src/ast/expression.cc src/ast/function.cc # src/ast/expressions - # src/ast/expressions/id.cc + src/ast/expressions/name.cc src/ast/location.cc src/ast/meta.cc src/ast/module.cc src/ast/node.cc src/ast/statement.cc src/ast/type.cc - # src/ast/node_ref.cc src/ast/scope.cc - # src/ast/scope-lookup.cc src/ast/statements/switch.cc src/ast/statements/try.cc # src/ast/type.cc @@ -71,16 +70,15 @@ set(SOURCES src/base/preprocessor.cc src/base/timing.cc src/base/util.cc - # src/compiler/codegen/codegen.cc - # src/compiler/codegen/coercions.cc - # src/compiler/codegen/ctors.cc - # src/compiler/codegen/expressions.cc - # src/compiler/codegen/expressions.cc - # src/compiler/codegen/operators.cc - # src/compiler/codegen/statements.cc - # src/compiler/codegen/types.cc - # src/compiler/codegen/unpack.cc - # src/compiler/coercion.cc + src/compiler/codegen/codegen.cc + src/compiler/codegen/coercions.cc + src/compiler/codegen/ctors.cc + src/compiler/codegen/expressions.cc + src/compiler/codegen/operators.cc + src/compiler/codegen/statements.cc + src/compiler/codegen/types.cc + src/compiler/codegen/unpack.cc + src/compiler/coercion.cc src/compiler/context.cc src/compiler/cxx/elements.cc src/compiler/cxx/formatter.cc @@ -93,14 +91,14 @@ set(SOURCES src/compiler/parser/driver.cc src/compiler/plugin.cc src/compiler/unit.cc - # src/compiler/visitors/coercer.cc - # src/compiler/visitors/constant-folder.cc - # src/compiler/visitors/normalizer.cc + src/compiler/visitors/coercer.cc + src/compiler/visitors/constant-folder.cc + src/compiler/visitors/normalizer.cc src/compiler/visitors/printer.cc src/compiler/visitors/renderer.cc - # src/compiler/visitors/resolver.cc - # src/compiler/visitors/scope-builder.cc - # src/compiler/visitors/validator.cc + src/compiler/visitors/resolver.cc + src/compiler/visitors/scope-builder.cc + src/compiler/visitors/validator.cc src/global.cc # # Already included in hilti-rt, which we pull in. # # src/3rdparty/utf8proc/utf8proc.c diff --git a/hilti/toolchain/include/ast/ast-context.h b/hilti/toolchain/include/ast/ast-context.h index c4d905188..1b3108764 100644 --- a/hilti/toolchain/include/ast/ast-context.h +++ b/hilti/toolchain/include/ast/ast-context.h @@ -2,11 +2,119 @@ #pragma once +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + namespace hilti { -// Place-holder we'll use later for maintaining AST-global state. -class ASTContext { +class Context; +class ASTContext; +struct Plugin; + +/** Root node for the AST inside an AST context. This will always be present exactly once. */ +class ASTRoot : public Node { public: + ~ASTRoot() override; + + static auto create(ASTContext* ctx) { return NodeDerivedPtr(new ASTRoot()); } + +protected: + ASTRoot() : Node({}, Meta(Location(""))) {} + + std::string _render() const override; + + bool isEqual(const Node& other) const override { return other.isA(); } + + HILTI_NODE(ASTRoot); +}; + +// Container for AST-wide state. +class ASTContext : public std::enable_shared_from_this { +public: + enum ASTState { Modified, NotModified }; + + ASTContext(Context* context); + + auto root() const { return _root; } + + Result parseSource(const hilti::rt::filesystem::path& path, + std::optional process_extension = {}); + + Result importModule(const ID& id, const std::optional& scope, + const hilti::rt::filesystem::path& parse_extension, + const std::optional& process_extension, + std::vector search_dirs); + + ModulePtr getModule(const module::UID& uid) const { + if ( auto m = _modules_by_uid.find(uid); m != _modules_by_uid.end() ) + return m->second; + else + return nullptr; + } + + Result processAST(); // top-level entry point for resolving/validating/optimizing AST + + bool isFullyResolved() const { return _resolved; } + + /** + * Returns direct & indirect dependencies that a module imports. This + * information will be available only once the AST has been resolved + * successfully. + * + * @param uid UID of module to return dependencies for; the module must be known, otherwise an internal error is + * reported + * @param recursive if true, return the transitive closure of all + * dependent units, vs just direct dependencies of the current unit + * @return set of dependencies, or empty vector if not yet resolved + */ + std::vector dependencies(const module::UID& uid, bool recursive = false) const; + + /** + * Dumps the current total AST of all modules to a debug stream. + * + * @param stream debug stream to write to + */ + void dumpAST(const hilti::logging::DebugStream& stream, const std::string& prefix); + +private: + Result _parseSource(const hilti::rt::filesystem::path& path, const std::optional& scope, + std::optional process_extension = {}); + module::UID _addModuleToAST(ModulePtr module); + Result _buildScopes(const Plugin& plugin); + Result _resolve(const Plugin& plugin); + Result _validate(const Plugin& plugin, bool pre_resolver); + Result _optimize(const Plugin& plugin); + + void _saveIterationAST(const Plugin& plugin, const std::string& prefix, int round = 0); + void _saveIterationAST(const Plugin& plugin, const std::string& prefix, const std::string& tag); + + void _dumpAST(const hilti::logging::DebugStream& stream, const Plugin& plugin, const std::string& prefix, + int round); + void _dumpAST(std::ostream& stream, const Plugin& plugin, const std::string& prefix, int round); + void _dumpDeclarations(const Plugin& plugin); + + // Reports any errors recorded in the AST to stderr. + // + // @returns false if there were errors, true if the AST is all good + bool _reportErrors(); + + Context* _context; + NodeDerivedPtr _root; + bool _rebuild_scopes = true; + bool _resolved = false; + + std::unordered_map _modules_by_uid; + std::unordered_map _modules_by_path; + std::map, ModulePtr> _modules_by_id_and_scope; }; } // namespace hilti diff --git a/hilti/toolchain/include/ast/builder/builder.h b/hilti/toolchain/include/ast/builder/builder.h index 3dfd107e3..1641957f1 100644 --- a/hilti/toolchain/include/ast/builder/builder.h +++ b/hilti/toolchain/include/ast/builder/builder.h @@ -11,7 +11,7 @@ namespace hilti { class Builder : public builder::NodeFactory { public: - Builder(std::shared_ptr context) : NodeFactory(std::move(context)) {} + Builder(ASTContext* ctx) : NodeFactory(ctx) {} }; using BuilderPtr = std::shared_ptr; diff --git a/hilti/toolchain/include/ast/builder/node-factory.autogen.h b/hilti/toolchain/include/ast/builder/node-factory.autogen.h index 036519613..ff1825328 100644 --- a/hilti/toolchain/include/ast/builder/node-factory.autogen.h +++ b/hilti/toolchain/include/ast/builder/node-factory.autogen.h @@ -6,7 +6,7 @@ auto attribute(std::string tag, const NodePtr& v, Meta m = Meta()) { } // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/attribute.h:73:5 auto attributeSet(Attributes attrs, Meta m = Meta()) { return hilti::AttributeSet::create(context(), attrs, m); -} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/attribute.h:151:5 +} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/attribute.h:154:5 auto ctorAddress(hilti::rt::Address v, const Meta& meta = {}) { return hilti::ctor::Address::create(context(), v, meta); } // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/ctors/address.h:29:5 @@ -122,6 +122,9 @@ auto ctorUnion(const QualifiedTypePtr& type, const ExpressionPtr& value, const M auto ctorUnsignedInteger(uint64_t value, int width, const Meta& meta = {}) { return hilti::ctor::UnsignedInteger::create(context(), value, width, meta); } // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/ctors/integer.h:70:5 +auto ctorUnsignedInteger(uint64_t value, int width, const UnqualifiedTypePtr& t, const Meta& meta = {}) { + return hilti::ctor::UnsignedInteger::create(context(), value, width, t, meta); +} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/ctors/integer.h:76:5 auto ctorValueReference(const ExpressionPtr& expr, const Meta& meta = {}) { return hilti::ctor::ValueReference::create(context(), expr, meta); } // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/ctors/reference.h:64:5 @@ -319,15 +322,16 @@ auto function(const ID& id, const type::FunctionPtr& ftype, const StatementPtr& const AttributeSetPtr& attrs = nullptr, const Meta& meta = {}) { return hilti::Function::create(context(), id, ftype, body, cc, attrs, meta); } // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/function.h:66:5 -auto module(ID id = {}, const Meta& meta = {}) { - return hilti::Module::create(context(), id, meta); -} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/module.h:89:5 -auto module(ID id, const Declarations& decls, Statements stmts, const Meta& meta = {}) { - return hilti::Module::create(context(), id, decls, stmts, meta); -} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/module.h:81:5 -auto module(ID id, const Declarations& decls, const Meta& meta = {}) { - return hilti::Module::create(context(), id, decls, meta); -} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/module.h:93:5 +auto module(const module::UID& uid, const ID& scope = {}, const Meta& meta = {}) { + return hilti::Module::create(context(), uid, scope, meta); +} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/module.h:142:5 +auto module(const module::UID& uid, const ID& scope, const Declarations& decls, Statements stmts, + const Meta& meta = {}) { + return hilti::Module::create(context(), uid, scope, decls, stmts, meta); +} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/module.h:133:5 +auto module(const module::UID& uid, const ID& scope, const Declarations& decls, const Meta& meta = {}) { + return hilti::Module::create(context(), uid, scope, decls, meta); +} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/module.h:146:5 auto qualifiedType(const UnqualifiedTypePtr& t, bool is_constant, Meta m = Meta()) { return hilti::QualifiedType::create(context(), t, is_constant, m); } // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/type.h:160:5 @@ -434,10 +438,10 @@ auto typeBool(Meta meta = {}) { } // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/bool.h:15:5 auto typeBytes(const Meta& meta = {}) { return hilti::type::Bytes::create(context(), meta); -} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/bytes.h:48:5 +} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/bytes.h:46:5 auto typeBytesIterator(Meta meta = {}) { return hilti::type::bytes::Iterator::create(context(), meta); -} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/bytes.h:23:5 +} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/bytes.h:21:5 auto typeDocOnly(std::string description, Meta meta = {}) { return hilti::type::DocOnly::create(context(), description, meta); } // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/doc-only.h:22:5 @@ -508,6 +512,9 @@ auto typeMember(const ID& id, Meta meta = {}) { auto typeMember(type::Wildcard _, Meta m = Meta()) { return hilti::type::Member::create(context(), _, m); } // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/member.h:27:5 +auto typeName(ID id, Meta meta = {}) { + return hilti::type::Name::create(context(), id, meta); +} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/unresolved-id.h:16:5 auto typeNetwork(Meta meta = {}) { return hilti::type::Network::create(context(), meta); } // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/network.h:14:5 @@ -553,12 +560,12 @@ auto typeSetIterator(const QualifiedTypePtr& etype, Meta meta = {}) { auto typeSetIterator(type::Wildcard _, Meta m = Meta()) { return hilti::type::set::Iterator::create(context(), _, m); } // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/set.h:25:5 -auto typeSignedInteger(int width, Meta m = Meta()) { +auto typeSignedInteger(int width, const Meta& m = Meta()) { return hilti::type::SignedInteger::create(context(), width, m); -} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/integer.h:43:5 +} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/integer.h:46:5 auto typeSignedInteger(type::Wildcard _, Meta m = Meta()) { return hilti::type::SignedInteger::create(context(), _, m); -} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/integer.h:47:5 +} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/integer.h:48:5 auto typeStream(const Meta& meta = {}) { return hilti::type::Stream::create(context(), meta); } // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/stream.h:72:5 @@ -628,15 +635,12 @@ auto typeUnion(type::Wildcard _, const Meta& meta = {}) { auto typeUnknown(Meta meta = {}) { return hilti::type::Unknown::create(context(), meta); } // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/unknown.h:14:5 -auto typeUnresolvedID(ID id, Meta meta = {}) { - return hilti::type::Name::create(context(), id, meta); -} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/unresolved-id.h:16:5 -auto typeUnsignedInteger(int width, Meta m = Meta()) { +auto typeUnsignedInteger(int width, const Meta& m = Meta()) { return hilti::type::UnsignedInteger::create(context(), width, m); -} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/integer.h:67:5 +} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/integer.h:68:5 auto typeUnsignedInteger(type::Wildcard _, Meta m = Meta()) { return hilti::type::UnsignedInteger::create(context(), _, m); -} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/integer.h:71:5 +} // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/integer.h:70:5 auto typeValueReference(const QualifiedTypePtr& type, Meta meta = {}) { return hilti::type::ValueReference::create(context(), type, meta); } // /Users/robin/work/spicy/node-rewrite/hilti/toolchain/include/hilti/ast/types/reference.h:85:5 diff --git a/hilti/toolchain/include/ast/builder/node-factory.h b/hilti/toolchain/include/ast/builder/node-factory.h index 46779fd35..445472c98 100644 --- a/hilti/toolchain/include/ast/builder/node-factory.h +++ b/hilti/toolchain/include/ast/builder/node-factory.h @@ -13,12 +13,12 @@ namespace hilti::builder { class NodeFactory { public: - NodeFactory(std::shared_ptr context) : _context(std::move(context)) {} + NodeFactory(ASTContext* context) : _context(context) {} - ASTContext* context() const { return _context.get(); } + ASTContext* context() const { return _context; } private: - const std::shared_ptr _context; + ASTContext* _context; public: #include diff --git a/hilti/toolchain/include/ast/declarations/imported-module.h b/hilti/toolchain/include/ast/declarations/imported-module.h index 3025349cd..e47b222f4 100644 --- a/hilti/toolchain/include/ast/declarations/imported-module.h +++ b/hilti/toolchain/include/ast/declarations/imported-module.h @@ -9,6 +9,8 @@ #include +#include "ast/module.h" + namespace hilti { class Unit; @@ -27,14 +29,13 @@ namespace declaration { */ class ImportedModule : public Declaration { public: - hilti::rt::filesystem::path parseExtension() const { return _parse_extension; } - const auto& path() const { return _path; } const auto& scope() const { return _scope; } const auto& searchDirectories() const { return _dirs; } - auto unit() const { return _unit.lock(); } + const auto& parseExtension() const { return _parse_extension; } - void setUnit(const std::shared_ptr& unit) { _unit = unit; } + auto uid() const { return _uid; } + void setUID(module::UID uid) { _uid = std::move(uid); } std::string displayName() const final { return "imported module"; } @@ -44,6 +45,7 @@ class ImportedModule : public Declaration { {"ext", _parse_extension.native()}, {"scope", _scope ? _scope->str() : std::string("")}, {"dirs", util::join(_dirs)}, + {"uid", _uid ? _uid->str() : std::string("")}, }; return Declaration::properties() + p; } @@ -79,7 +81,7 @@ class ImportedModule : public Declaration { return false; return Declaration::isEqual(other) && _parse_extension == n->_parse_extension && _path == n->_path && - _scope == n->_scope && _dirs == n->_dirs; + _scope == n->_scope && _dirs == n->_dirs && _uid == n->_uid; } HILTI_NODE(ImportedModule) @@ -90,7 +92,7 @@ class ImportedModule : public Declaration { std::optional _scope; std::vector _dirs; - std::weak_ptr _unit; + std::optional _uid; }; } // namespace declaration diff --git a/hilti/toolchain/include/ast/expressions/all.h b/hilti/toolchain/include/ast/expressions/all.h index fb45bc459..96fb57170 100644 --- a/hilti/toolchain/include/ast/expressions/all.h +++ b/hilti/toolchain/include/ast/expressions/all.h @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -16,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/hilti/toolchain/include/ast/expressions/id.h b/hilti/toolchain/include/ast/expressions/name.h similarity index 100% rename from hilti/toolchain/include/ast/expressions/id.h rename to hilti/toolchain/include/ast/expressions/name.h diff --git a/hilti/toolchain/include/ast/expressions/resolved-operator.h b/hilti/toolchain/include/ast/expressions/resolved-operator.h index a0e64ffe1..c9aa12154 100644 --- a/hilti/toolchain/include/ast/expressions/resolved-operator.h +++ b/hilti/toolchain/include/ast/expressions/resolved-operator.h @@ -16,7 +16,7 @@ namespace hilti::expression { * * @note Typically, one derives from this only by using the `__BEGIN_OPERATOR` macro. */ -class ResolvedOperatorBase : public Expression { +class ResolvedOperator : public Expression { public: const auto& operator_() const { return *_operator; } auto kind() const { return _operator->kind(); } @@ -47,14 +47,14 @@ class ResolvedOperatorBase : public Expression { } protected: - ResolvedOperatorBase(Nodes children, OperatorPtr op, Meta meta) + ResolvedOperator(Nodes children, OperatorPtr op, Meta meta) : Expression(std::move(children), std::move(meta)), _operator(std::move(op)) { setChild(0, _operator->result(Node::children(1, -1))); // TODO: Right? type::pruneWalk(child(0)->as()); } bool isEqual(const Node& other) const override { - auto n = other.tryAs(); + auto n = other.tryAs(); if ( ! n ) return false; diff --git a/hilti/toolchain/include/ast/forward.h b/hilti/toolchain/include/ast/forward.h index f42baf6f9..56e6649d4 100644 --- a/hilti/toolchain/include/ast/forward.h +++ b/hilti/toolchain/include/ast/forward.h @@ -10,6 +10,7 @@ namespace hilti { class Node; class Operator; +class ASTRoot; class Attribute; class AttributeSet; class Ctor; @@ -219,6 +220,7 @@ class Label; template using NodeDerivedPtr = std::shared_ptr; +using ASTRootPtr = std::shared_ptr; using AttributePtr = std::shared_ptr; using AttributeSetPtr = std::shared_ptr; using CtorPtr = std::shared_ptr; @@ -243,7 +245,5 @@ using UnqualifiedTypes = std::vector; class Builder; class ASTContext; class Nodes; -class Visitor; - } // namespace hilti diff --git a/hilti/toolchain/include/ast/module.h b/hilti/toolchain/include/ast/module.h index 03ccce6b7..ce44e0dc5 100644 --- a/hilti/toolchain/include/ast/module.h +++ b/hilti/toolchain/include/ast/module.h @@ -6,6 +6,8 @@ #include #include +#include + #include #include #include @@ -14,12 +16,63 @@ namespace hilti { +namespace module { + +// Globally unique ID for a module that can be used to name it unambiguously. +struct UID { + ID id; /**< module name */ + hilti::rt::filesystem::path + path; /**< path to module's source code on disk; will be set to a unique place-holder if no file exists */ + hilti::rt::filesystem::path parse_extension; /**< language extension determining how to *parse* this module, usually + derive from file name */ + hilti::rt::filesystem::path + process_extension; /**< language extension determining how to process this module *after* parsing */ + + UID(ID id, const hilti::rt::filesystem::path& path) + : id(std::move(id)), + path(util::normalizePath(path)), + parse_extension(path.extension()), + process_extension(path.extension()) {} + + UID(const ID& id, hilti::rt::filesystem::path parse_extension, const hilti::rt::filesystem::path& process_extension) + : id(id), parse_extension(std::move(parse_extension)), process_extension(process_extension) { + path = util::fmt("/tmp/hilti/%s.%" PRIu64 ".%s.%s", id, ++_no_file_counter, process_extension, parse_extension); + } + + size_t hash() const { + return rt::hashCombine(std::hash{}(id.str()), std::hash{}(path.native()), + std::hash{}(parse_extension.native()), + std::hash{}(process_extension.native())); + } + + bool operator==(const UID& other) const { + return id == other.id && path == other.path && parse_extension == other.parse_extension && + process_extension == other.process_extension; + } + + bool operator!=(const UID& other) const { return ! (*this == other); } + + std::string str() const { return util::fmt("%s_%x", id, hash() % 0xfff); } // should be unique "enough" + + operator std::string() const { return str(); } + explicit operator bool() const { return ! id.empty(); } + +private: + inline static uint64_t _no_file_counter = 0; +}; + +inline std::ostream& operator<<(std::ostream& stream, const UID& uid) { return stream << uid.str(); } + +} // namespace module + /** Base class for Module nodes. */ class Module : public Node, public node::WithDocString { public: ~Module() override; - const auto& id() const { return _id; } + const auto& uid() const { return _uid; } + const auto& id() const { return _uid.id; } + const auto& scopePath() const { return _scope_path; } auto statements() const { return child(0); } auto declarations() const { return childrenOfType(); } @@ -73,29 +126,38 @@ class Module : public Node, public node::WithDocString { */ void add(StatementPtr s) { child(0)->add(std::move(s)); } + void setScopePath(const ID& scope) { _scope_path = scope; } + void setProcessExtension(const hilti::rt::filesystem::path& ext) { _uid.process_extension = ext; } + node::Properties properties() const override { - auto p = node::Properties{{"id", _id}}; + auto p = node::Properties{{"id", _uid.id}, + {"path", _uid.path.native()}, + {"ext", _uid.process_extension.native()}, + {"scope", _scope_path}}; return Node::properties() + p; } - static auto create(ASTContext* ctx, ID id, const Declarations& decls, Statements stmts, const Meta& meta = {}) { + static auto create(ASTContext* ctx, const module::UID& uid, const ID& scope, const Declarations& decls, + Statements stmts, const Meta& meta = {}) { Nodes nodes = {statement::Block::create(ctx, std::move(stmts), meta)}; for ( auto d : decls ) nodes.push_back(std::move(d)); - return NodeDerivedPtr(new Module(std::move(nodes), std::move(id), meta)); + return NodeDerivedPtr(new Module(std::move(nodes), uid, scope, meta)); } - static auto create(ASTContext* ctx, ID id = {}, const Meta& meta = {}) { - return create(ctx, std::move(id), {}, {}, meta); + static auto create(ASTContext* ctx, const module::UID& uid, const ID& scope = {}, const Meta& meta = {}) { + return create(ctx, uid, scope, {}, {}, meta); } - static auto create(ASTContext* ctx, ID id, const Declarations& decls, const Meta& meta = {}) { - return create(ctx, std::move(id), decls, {}, meta); + static auto create(ASTContext* ctx, const module::UID& uid, const ID& scope, const Declarations& decls, + const Meta& meta = {}) { + return create(ctx, uid, scope, decls, {}, meta); } protected: - Module(Nodes children, ID id, Meta meta = {}) : Node(std::move(children), std::move(meta)), _id(std::move(id)) {} + Module(Nodes children, module::UID uid, ID scope, Meta meta = {}) + : Node(std::move(children), std::move(meta)), _uid(std::move(uid)), _scope_path(std::move(scope)) {} std::string _render() const override; @@ -104,13 +166,21 @@ class Module : public Node, public node::WithDocString { if ( ! n ) return false; - return Node::isEqual(other) && _id == n->_id; + return Node::isEqual(other) && _uid == n->_uid && _scope_path == n->_scope_path; } HILTI_NODE(Module); private: - ID _id; + module::UID _uid; + ID _scope_path; }; } // namespace hilti + +namespace std { +template<> +struct hash { + size_t operator()(const hilti::module::UID& x) const { return x.hash(); } +}; +} // namespace std diff --git a/hilti/toolchain/include/ast/node-range.h b/hilti/toolchain/include/ast/node-range.h index ec92bcfde..36d5dfad7 100644 --- a/hilti/toolchain/include/ast/node-range.h +++ b/hilti/toolchain/include/ast/node-range.h @@ -9,6 +9,8 @@ #include +#include "ast/forward.h" + namespace hilti { namespace node { @@ -33,6 +35,8 @@ class RangeIterator { using iterator_category = BaseIterator::iterator_category; explicit RangeIterator(BaseIterator i) : _iter(i) {} + explicit RangeIterator(typename std::vector>::const_iterator i) + : _iter(reinterpret_cast(i)) {} RangeIterator(const RangeIterator& other) = default; RangeIterator(RangeIterator&& other) noexcept = default; RangeIterator() {} @@ -94,6 +98,12 @@ class Range { using const_iterator = RangeIterator; explicit Range() {} + Range(typename std::vector>::const_iterator begin, + typename std::vector>::const_iterator end) + : _begin(begin), _end(end) {} + + explicit Range(const std::vector>& nodes) : Range(nodes.begin(), nodes.end()) {} + Range(std::vector::const_iterator begin, std::vector::const_iterator end) : _begin(begin), _end(end) {} diff --git a/hilti/toolchain/include/ast/node.h b/hilti/toolchain/include/ast/node.h index b84f55304..b8665c8fe 100644 --- a/hilti/toolchain/include/ast/node.h +++ b/hilti/toolchain/include/ast/node.h @@ -28,12 +28,12 @@ friend class hilti::builder::NodeBuilder; #define HILTI_NODE_IMPLEMENTATION_0(CLASS) \ - void ::hilti::CLASS::dispatch(visitor::Dispatcher& v) { v(*this); } + void ::hilti::CLASS::dispatch(visitor::Dispatcher& v) { v(this); } #define HILTI_NODE_IMPLEMENTATION_1(CLASS, BASE) \ void ::hilti::CLASS::dispatch(visitor::Dispatcher& v) { \ - v(*static_cast(this)); \ - v(*this); \ + v(static_cast(this)); \ + v(this); \ } namespace hilti { @@ -128,31 +128,39 @@ class Node : public std::enable_shared_from_this { virtual node::Properties properties() const { return {}; } const auto& children() const { return _children; } - NodePtr parent() { return _parent ? _parent->shared_from_this() : NodePtr(); } + Node* parent() const { return _parent; } const auto& meta() const { return _meta; } void setMeta(Meta m) { _meta = std::move(m); } /** - * Returns the scope associated with the node. All nodes have a scope - * used for ID resolution. Initially, a new node receive its own, empty - * scope. However, scopes can be shared across nodes through - * `setScope()`. + * Returns the scope associated with the node, if any. */ - auto scope() const { + auto scope() const { return _scope.get(); } + + /** + * Returns the node's direct scope if already created, or creates one if + * not yet. + */ + auto getOrCreateScope() { if ( ! _scope ) - _scope = std::make_shared(); + _scope = std::make_unique(); - return _scope; + return _scope.get(); } + /** Clears out the current scope. */ + void clearScope() { _scope.reset(); } + /** - * Resets the node's scope to point to another one. + * Looks an ID in the node's chain of scope, following HILTI's scoping and visibility rules. + * + * @param id id to look up + * @param what description of what we're looking for, for error reporting + * @return if found, a pair of the declaration the ID refers to plus the declarations canonical ID; if not found an + * error message appropriate for reporting to the user */ - void setScope(std::shared_ptr new_scope) { _scope = std::move(new_scope); } - - /** Clears out the current scope. */ - void clearScope() { _scope = nullptr; } + Result> lookupID(const ID& id, const std::string_view& what) const; auto pruneWalk() const { return _prune_walk; } void setPruneWalk(bool prune) { _prune_walk = prune; } @@ -283,8 +291,8 @@ class Node : public std::enable_shared_from_this { } template - auto& as() const { - if ( auto p = std::dynamic_pointer_cast(shared_from_this()) ) + auto as() const { + if ( const auto p = std::dynamic_pointer_cast(shared_from_this()) ) return p; std::cerr << hilti::util::fmt("internal error: unexpected type, want %s but have %s", @@ -410,6 +418,8 @@ class Node : public std::enable_shared_from_this { virtual void dispatch(visitor::Dispatcher& v) = 0; + Node& operator=(const Node& other) = delete; + protected: Node(Nodes children, Meta meta) : _children(std::move(children)), _meta(std::move(meta)) { for ( auto& c : _children ) { @@ -419,13 +429,19 @@ class Node : public std::enable_shared_from_this { } Node(Meta meta) : _meta(std::move(meta)) {} - Node(const Node& other) = default; Node(Node&& other) = default; + Node(const Node& other) { + _meta = other._meta; + _prune_walk = other._prune_walk; + _inherit_scope = other._inherit_scope; + _children = other._children; // shallow copy by default; deepClone() will recreate this if needed + } + void clearChildren() { for ( auto& c : _children ) { if ( c ) - c->clearParent(); + c->_clearParent(); } _children.clear(); @@ -446,15 +462,15 @@ class Node : public std::enable_shared_from_this { virtual NodePtr _clone(ASTContext* ctx) const = 0; // shallow copy virtual std::string _render() const { return ""; } + friend bool operator==(const Node& x, const Node& y) { return x.isEqual(y); } + private: friend NodePtr node::clone(ASTContext* ctx, const NodePtr& n); friend NodePtr node::deepClone(ASTContext* ctx, const NodePtr& n); - void clearParent() { _parent = nullptr; } - + void _clearParent() { _parent = nullptr; } void _destroyChildrenRecursively(Node* n); - Node& operator=(const Node& other) = default; Node& operator=(Node&& other) noexcept = default; Node* _parent = nullptr; @@ -463,7 +479,7 @@ class Node : public std::enable_shared_from_this { bool _prune_walk = false; bool _inherit_scope = true; - mutable std::shared_ptr _scope = nullptr; + std::unique_ptr _scope = nullptr; std::vector _errors; }; @@ -576,7 +592,7 @@ auto filter(const hilti::node::Set& x, F f) { */ template auto transform(const hilti::node::Range& x, F f) { - using Y = std::invoke_result_t; + using Y = std::invoke_result_t&>; std::vector y; y.reserve(x.size()); for ( const auto& i : x ) @@ -591,7 +607,7 @@ auto transform(const hilti::node::Range& x, F f) { */ template auto transform(const hilti::node::Set& x, F f) { - using Y = std::invoke_result_t; + using Y = std::invoke_result_t&>; std::vector y; y.reserve(x.size()); for ( const auto& i : x ) diff --git a/hilti/toolchain/include/ast/nodes.decl b/hilti/toolchain/include/ast/nodes.decl deleted file mode 100644 index 9a1a00a30..000000000 --- a/hilti/toolchain/include/ast/nodes.decl +++ /dev/null @@ -1,111 +0,0 @@ -trait hilti::Declaration isDeclaration -trait hilti::Node isNode -trait hilti::TypePtr isType -trait hilti::Statement isStatement -trait hilti::Expression isExpression -trait hilti::Ctor isCtor -trait hilti::expression::ResolvedOperator isResolvedOperator - -hilti::Attribute : isNode -hilti::AttributeSet : isNode -hilti::Ctor : isNode -hilti::Declaration : isNode -hilti::Expression : isNode -hilti::Function : isNode -hilti::ID : isNode -hilti::Module : isNode -hilti::Statement : isNode -hilti::TypePtr : isNode -hilti::type::function::Result : isNode -hilti::type::function::Parameter : isNode -hilti::statement::switch_::Case : isNode -hilti::statement::try_::Catch : isNode -hilti::type::enum_::Label : isNode -hilti::type::tuple::Element : isNode - -hilti::ctor::Address : isCtor -hilti::ctor::Bool : isCtor -hilti::ctor::Bytes : isCtor -hilti::ctor::Coerced : isCtor -hilti::ctor::Default : isCtor -hilti::ctor::Enum : isCtor -hilti::ctor::Error : isCtor -hilti::ctor::Exception : isCtor -hilti::ctor::Interval : isCtor -hilti::ctor::List : isCtor -hilti::ctor::Library : isCtor -hilti::ctor::Map : isCtor -hilti::ctor::map::Element : isNode -hilti::ctor::Network : isCtor -hilti::ctor::Null : isCtor -hilti::ctor::Optional : isCtor -hilti::ctor::Port : isCtor -hilti::ctor::Real : isCtor -hilti::ctor::RegExp : isCtor -hilti::ctor::Result : isCtor -hilti::ctor::Set : isCtor -hilti::ctor::SignedInteger : isCtor -hilti::ctor::Stream : isCtor -hilti::ctor::String : isCtor -hilti::ctor::StrongReference : isCtor -hilti::ctor::Struct : isCtor -hilti::ctor::Time : isCtor -hilti::ctor::Tuple : isCtor -hilti::ctor::UnsignedInteger : isCtor -hilti::ctor::ValueReference : isCtor -hilti::ctor::Vector : isCtor -hilti::ctor::WeakReference : isCtor -hilti::ctor::ValueReference : isCtor - -hilti::declaration::Constant : isDeclaration -hilti::declaration::Expression : isDeclaration -hilti::declaration::Field : isDeclaration -hilti::declaration::Function : isDeclaration -hilti::declaration::GlobalVariable : isDeclaration -hilti::declaration::ImportedModule : isDeclaration -hilti::declaration::LocalVariable : isDeclaration -hilti::declaration::Module : isDeclaration -hilti::declaration::Parameter : isDeclaration -hilti::declaration::Property : isDeclaration -hilti::declaration::Type : isDeclaration - -hilti::expression::Assign : isExpression -hilti::expression::BuiltinFunction : isExpression -hilti::expression::Coerced : isExpression -hilti::expression::Ctor : isExpression -hilti::expression::Deferred : isExpression -hilti::expression::Grouping : isExpression -hilti::expression::Keyword : isExpression -hilti::expression::ListComprehension : isExpression -hilti::expression::PendingCoerced : isExpression -hilti::expression::LogicalAnd : isExpression -hilti::expression::LogicalOr : isExpression -hilti::expression::LogicalNot : isExpression -hilti::expression::Member : isExpression -hilti::expression::Move : isExpression -hilti::expression::ResolvedID : isExpression -hilti::expression::ResolvedOperator : isExpression -hilti::expression::Ternary : isExpression -hilti::expression::Type_ : isExpression -hilti::expression::TypeInfo : isExpression -hilti::expression::TypeWrapped : isExpression -hilti::expression::UnresolvedID : isExpression -hilti::expression::UnresolvedOperator : isExpression -hilti::expression::Void : isExpression - -hilti::statement::Assert : isStatement -hilti::statement::Block : isStatement -hilti::statement::Break : isStatement -hilti::statement::Comment : isStatement -hilti::statement::Continue : isStatement -hilti::statement::Declaration : isStatement -hilti::statement::Expression : isStatement -hilti::statement::For : isStatement -hilti::statement::If : isStatement -hilti::statement::Return : isStatement -hilti::statement::SetLocation : isStatement -hilti::statement::Switch : isStatement -hilti::statement::Throw : isStatement -hilti::statement::Try : isStatement -hilti::statement::While : isStatement -hilti::statement::Yield : isStatement diff --git a/hilti/toolchain/include/ast/operators/bytes.h b/hilti/toolchain/include/ast/operators/bytes.h index 2d0bd7e9e..a0276b1d2 100644 --- a/hilti/toolchain/include/ast/operators/bytes.h +++ b/hilti/toolchain/include/ast/operators/bytes.h @@ -3,7 +3,7 @@ #pragma once #include -#include +#include #include #include #include diff --git a/hilti/toolchain/include/ast/operators/function.h b/hilti/toolchain/include/ast/operators/function.h index 459a7e840..d1738bb83 100644 --- a/hilti/toolchain/include/ast/operators/function.h +++ b/hilti/toolchain/include/ast/operators/function.h @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include #include diff --git a/hilti/toolchain/include/ast/operators/stream.h b/hilti/toolchain/include/ast/operators/stream.h index bc5d999e0..25b230cf6 100644 --- a/hilti/toolchain/include/ast/operators/stream.h +++ b/hilti/toolchain/include/ast/operators/stream.h @@ -2,7 +2,7 @@ #pragma once -#include +#include #include #include #include diff --git a/hilti/toolchain/include/ast/scope-lookup.h b/hilti/toolchain/include/ast/scope-lookup.h deleted file mode 100644 index c7b420af8..000000000 --- a/hilti/toolchain/include/ast/scope-lookup.h +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2020-2023 by the Zeek Project. See LICENSE for details. -// -// Functionality factored out from scope.h to avoid header loops. - -#pragma once - -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace hilti::scope { - -namespace detail { -/** Internal backend to `hilti::lookupID()`. */ -std::pair>> lookupID(const ID& id, const Node& n); -} // namespace detail - -/** - * Looks up a still unresolved ID inside an AST. The ID is expected to - * resolve to exactly one declaration of an expected type, and must be - * exported if inside another module; otherwise an error is flagged. - * - * @tparam D class implementing the `Declaration` interface that we expecting the ID to resolve to - * @param id id to look up - * @param p AST position where to start the lookup; we'll traverse up the AST from there - * @param what textual description of what we're looking for (i.e., of *D*); used in error messages - * @return node if resolved, or an appropriate error if not - */ -template -Result> lookupID(const ID& id, const visitor::Position& p, const std::string_view& what) { - if ( ! id ) - logger().internalError("lookupID() called with empty ID"); - - for ( auto i = p.path.rbegin(); i != p.path.rend(); ++i ) { - auto [stop, resolved] = detail::lookupID(id, **i); - - if ( resolved ) { - if ( auto d = (*resolved).first->tryAs() ) { - if ( ! resolved->second.namespace_() ) { - // If it's from module's scope, qualify the ID. - if ( auto m = (*i)->tryAs() ) - return std::make_pair(resolved->first, ID(m->id(), resolved->second)); - } - - else - return std::move(resolved); - } - else - return result::Error(util::fmt("ID '%s' does not resolve to a %s (but to a %s)", id, what, - (*resolved).first->as().displayName())); - } - - if ( stop ) - // Pass back error. - return std::move(resolved); - - // If the type has the NoInheritScope flag, we skip everything else - // in remainder of the path except for the top-level module, to which - // we then jump directly. One exception: If the type is part of a - // type declaration, we need to check the declaration's scope still - // as well; that's the "if" clause below allowing to go one further - // step up, and the "else" clause then stopping during the next - // round. - bool skip_to_module = false; - - if ( auto t = (*i)->tryAs(); t && t->hasFlag(type::Flag::NoInheritScope) ) { - if ( auto x = i; ++x != p.path.rend() && (*x)->tryAs() ) - // Ignore, we'll cover this in next round in the case below. - continue; - - skip_to_module = true; - } - else if ( auto t = (*i)->tryAs(); t && t->type().hasFlag(type::Flag::NoInheritScope) ) - skip_to_module = true; - - if ( skip_to_module ) { - // Advance to module scope directly. - while ( ++i != p.path.rend() ) { - if ( (*i)->isA() ) - break; - } - --i; // for-loop will increase - } - } - - return result::Error(util::fmt("unknown ID '%s'", id)); -} - -} // namespace hilti::scope diff --git a/hilti/toolchain/include/ast/scope.h b/hilti/toolchain/include/ast/scope.h index fb1e97e35..68e61b57d 100644 --- a/hilti/toolchain/include/ast/scope.h +++ b/hilti/toolchain/include/ast/scope.h @@ -28,8 +28,8 @@ class Scope { Scope() = default; ~Scope() = default; - void insert(NodePtr&& n); - void insert(const ID& id, NodePtr&& n); + void insert(DeclarationPtr&& d); + void insert(const ID& id, DeclarationPtr&& d); void insertNotFound(const ID& id); /** Returns if there's at least one mapping for an ID. */ @@ -37,7 +37,7 @@ class Scope { /** Result type for the lookup methods. */ struct Referee { - NodePtr node; /**< node that ID maps to */ + DeclarationPtr node; /**< node that ID maps to */ std::string qualified; /**< qualified ID with full path used to find it */ bool external{}; /**< true if found in a different (imported) module */ }; @@ -73,7 +73,7 @@ class Scope { Scope& operator=(Scope&& other) = delete; private: - using ItemMap = std::map>; + using ItemMap = std::map>; std::vector _findID(const ID& id, bool external = false) const; std::vector _findID(const Scope* scope, const ID& id, bool external = false) const; diff --git a/hilti/toolchain/include/ast/statements/switch.h b/hilti/toolchain/include/ast/statements/switch.h index 5dc8d40fa..1ddbe5479 100644 --- a/hilti/toolchain/include/ast/statements/switch.h +++ b/hilti/toolchain/include/ast/statements/switch.h @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/hilti/toolchain/include/ast/visitor-dispatcher.h b/hilti/toolchain/include/ast/visitor-dispatcher.h index fe66fd588..ee353938c 100644 --- a/hilti/toolchain/include/ast/visitor-dispatcher.h +++ b/hilti/toolchain/include/ast/visitor-dispatcher.h @@ -8,149 +8,150 @@ namespace hilti::visitor { class Dispatcher { public: - virtual void operator()(const hilti::Attribute&) {} - virtual void operator()(const hilti::AttributeSet&) {} - virtual void operator()(const hilti::Ctor&) {} - virtual void operator()(const hilti::Declaration&) {} - virtual void operator()(const hilti::Expression&) {} - virtual void operator()(const hilti::Function&) {} - virtual void operator()(const hilti::Module&) {} - virtual void operator()(const hilti::QualifiedType&) {} - virtual void operator()(const hilti::Statement&) {} - virtual void operator()(const hilti::UnqualifiedType&) {} - virtual void operator()(const hilti::ctor::Address&) {} - virtual void operator()(const hilti::ctor::Bool&) {} - virtual void operator()(const hilti::ctor::Bytes&) {} - virtual void operator()(const hilti::ctor::Coerced&) {} - virtual void operator()(const hilti::ctor::Default&) {} - virtual void operator()(const hilti::ctor::Enum&) {} - virtual void operator()(const hilti::ctor::Error&) {} - virtual void operator()(const hilti::ctor::Exception&) {} - virtual void operator()(const hilti::ctor::Interval&) {} - virtual void operator()(const hilti::ctor::Library&) {} - virtual void operator()(const hilti::ctor::List&) {} - virtual void operator()(const hilti::ctor::Map&) {} - virtual void operator()(const hilti::ctor::Network&) {} - virtual void operator()(const hilti::ctor::Null&) {} - virtual void operator()(const hilti::ctor::Optional&) {} - virtual void operator()(const hilti::ctor::Port&) {} - virtual void operator()(const hilti::ctor::Real&) {} - virtual void operator()(const hilti::ctor::RegExp&) {} - virtual void operator()(const hilti::ctor::Result&) {} - virtual void operator()(const hilti::ctor::Set&) {} - virtual void operator()(const hilti::ctor::SignedInteger&) {} - virtual void operator()(const hilti::ctor::Stream&) {} - virtual void operator()(const hilti::ctor::String&) {} - virtual void operator()(const hilti::ctor::StrongReference&) {} - virtual void operator()(const hilti::ctor::Struct&) {} - virtual void operator()(const hilti::ctor::Time&) {} - virtual void operator()(const hilti::ctor::Tuple&) {} - virtual void operator()(const hilti::ctor::Union&) {} - virtual void operator()(const hilti::ctor::UnsignedInteger&) {} - virtual void operator()(const hilti::ctor::ValueReference&) {} - virtual void operator()(const hilti::ctor::Vector&) {} - virtual void operator()(const hilti::ctor::WeakReference&) {} - virtual void operator()(const hilti::ctor::map::Element&) {} - virtual void operator()(const hilti::ctor::struct_::Field&) {} - virtual void operator()(const hilti::declaration::Constant&) {} - virtual void operator()(const hilti::declaration::Expression&) {} - virtual void operator()(const hilti::declaration::Field&) {} - virtual void operator()(const hilti::declaration::Function&) {} - virtual void operator()(const hilti::declaration::GlobalVariable&) {} - virtual void operator()(const hilti::declaration::ImportedModule&) {} - virtual void operator()(const hilti::declaration::LocalVariable&) {} - virtual void operator()(const hilti::declaration::Module&) {} - virtual void operator()(const hilti::declaration::Property&) {} - virtual void operator()(const hilti::declaration::Type&) {} - virtual void operator()(const hilti::expression::Assign&) {} - virtual void operator()(const hilti::expression::BuiltInFunction&) {} - virtual void operator()(const hilti::expression::Coerced&) {} - virtual void operator()(const hilti::expression::Ctor&) {} - virtual void operator()(const hilti::expression::Deferred&) {} - virtual void operator()(const hilti::expression::Grouping&) {} - virtual void operator()(const hilti::expression::Keyword&) {} - virtual void operator()(const hilti::expression::ListComprehension&) {} - virtual void operator()(const hilti::expression::LogicalAnd&) {} - virtual void operator()(const hilti::expression::LogicalNot&) {} - virtual void operator()(const hilti::expression::LogicalOr&) {} - virtual void operator()(const hilti::expression::Member&) {} - virtual void operator()(const hilti::expression::Move&) {} - virtual void operator()(const hilti::expression::Name&) {} - virtual void operator()(const hilti::expression::PendingCoerced&) {} - virtual void operator()(const hilti::expression::Ternary&) {} - virtual void operator()(const hilti::expression::TypeInfo&) {} - virtual void operator()(const hilti::expression::TypeWrapped&) {} - virtual void operator()(const hilti::expression::Type_&) {} - virtual void operator()(const hilti::expression::UnresolvedOperator&) {} - virtual void operator()(const hilti::expression::Void&) {} - virtual void operator()(const hilti::statement::Assert&) {} - virtual void operator()(const hilti::statement::Block&) {} - virtual void operator()(const hilti::statement::Break&) {} - virtual void operator()(const hilti::statement::Comment&) {} - virtual void operator()(const hilti::statement::Continue&) {} - virtual void operator()(const hilti::statement::Declaration&) {} - virtual void operator()(const hilti::statement::Expression&) {} - virtual void operator()(const hilti::statement::For&) {} - virtual void operator()(const hilti::statement::If&) {} - virtual void operator()(const hilti::statement::Return&) {} - virtual void operator()(const hilti::statement::SetLocation&) {} - virtual void operator()(const hilti::statement::Switch&) {} - virtual void operator()(const hilti::statement::Throw&) {} - virtual void operator()(const hilti::statement::Try&) {} - virtual void operator()(const hilti::statement::While&) {} - virtual void operator()(const hilti::statement::Yield&) {} - virtual void operator()(const hilti::statement::switch_::Case&) {} - virtual void operator()(const hilti::statement::try_::Catch&) {} - virtual void operator()(const hilti::type::Address&) {} - virtual void operator()(const hilti::type::Any&) {} - virtual void operator()(const hilti::type::Auto&) {} - virtual void operator()(const hilti::type::Bool&) {} - virtual void operator()(const hilti::type::Bytes&) {} - virtual void operator()(const hilti::type::DocOnly&) {} - virtual void operator()(const hilti::type::Enum&) {} - virtual void operator()(const hilti::type::Error&) {} - virtual void operator()(const hilti::type::Exception&) {} - virtual void operator()(const hilti::type::Function&) {} - virtual void operator()(const hilti::type::Interval&) {} - virtual void operator()(const hilti::type::Library&) {} - virtual void operator()(const hilti::type::List&) {} - virtual void operator()(const hilti::type::Map&) {} - virtual void operator()(const hilti::type::Member&) {} - virtual void operator()(const hilti::type::Name&) {} - virtual void operator()(const hilti::type::Network&) {} - virtual void operator()(const hilti::type::Null&) {} - virtual void operator()(const hilti::type::OperandList&) {} - virtual void operator()(const hilti::type::Optional&) {} - virtual void operator()(const hilti::type::Port&) {} - virtual void operator()(const hilti::type::Real&) {} - virtual void operator()(const hilti::type::RegExp&) {} - virtual void operator()(const hilti::type::Result&) {} - virtual void operator()(const hilti::type::Set&) {} - virtual void operator()(const hilti::type::SignedInteger&) {} - virtual void operator()(const hilti::type::Stream&) {} - virtual void operator()(const hilti::type::String&) {} - virtual void operator()(const hilti::type::StrongReference&) {} - virtual void operator()(const hilti::type::Struct&) {} - virtual void operator()(const hilti::type::Time&) {} - virtual void operator()(const hilti::type::Tuple&) {} - virtual void operator()(const hilti::type::Type_&) {} - virtual void operator()(const hilti::type::Union&) {} - virtual void operator()(const hilti::type::Unknown&) {} - virtual void operator()(const hilti::type::UnsignedInteger&) {} - virtual void operator()(const hilti::type::ValueReference&) {} - virtual void operator()(const hilti::type::Vector&) {} - virtual void operator()(const hilti::type::Void&) {} - virtual void operator()(const hilti::type::WeakReference&) {} - virtual void operator()(const hilti::type::bytes::Iterator&) {} - virtual void operator()(const hilti::type::enum_::Label&) {} - virtual void operator()(const hilti::type::function::Parameter&) {} - virtual void operator()(const hilti::type::list::Iterator&) {} - virtual void operator()(const hilti::type::map::Iterator&) {} - virtual void operator()(const hilti::type::set::Iterator&) {} - virtual void operator()(const hilti::type::stream::Iterator&) {} - virtual void operator()(const hilti::type::stream::View&) {} - virtual void operator()(const hilti::type::tuple::Element&) {} - virtual void operator()(const hilti::type::vector::Iterator&) {} + virtual void operator()(hilti::ASTRoot*) {} + virtual void operator()(hilti::Attribute*) {} + virtual void operator()(hilti::AttributeSet*) {} + virtual void operator()(hilti::Ctor*) {} + virtual void operator()(hilti::Declaration*) {} + virtual void operator()(hilti::Expression*) {} + virtual void operator()(hilti::Function*) {} + virtual void operator()(hilti::Module*) {} + virtual void operator()(hilti::QualifiedType*) {} + virtual void operator()(hilti::Statement*) {} + virtual void operator()(hilti::UnqualifiedType*) {} + virtual void operator()(hilti::ctor::Address*) {} + virtual void operator()(hilti::ctor::Bool*) {} + virtual void operator()(hilti::ctor::Bytes*) {} + virtual void operator()(hilti::ctor::Coerced*) {} + virtual void operator()(hilti::ctor::Default*) {} + virtual void operator()(hilti::ctor::Enum*) {} + virtual void operator()(hilti::ctor::Error*) {} + virtual void operator()(hilti::ctor::Exception*) {} + virtual void operator()(hilti::ctor::Interval*) {} + virtual void operator()(hilti::ctor::Library*) {} + virtual void operator()(hilti::ctor::List*) {} + virtual void operator()(hilti::ctor::Map*) {} + virtual void operator()(hilti::ctor::Network*) {} + virtual void operator()(hilti::ctor::Null*) {} + virtual void operator()(hilti::ctor::Optional*) {} + virtual void operator()(hilti::ctor::Port*) {} + virtual void operator()(hilti::ctor::Real*) {} + virtual void operator()(hilti::ctor::RegExp*) {} + virtual void operator()(hilti::ctor::Result*) {} + virtual void operator()(hilti::ctor::Set*) {} + virtual void operator()(hilti::ctor::SignedInteger*) {} + virtual void operator()(hilti::ctor::Stream*) {} + virtual void operator()(hilti::ctor::String*) {} + virtual void operator()(hilti::ctor::StrongReference*) {} + virtual void operator()(hilti::ctor::Struct*) {} + virtual void operator()(hilti::ctor::Time*) {} + virtual void operator()(hilti::ctor::Tuple*) {} + virtual void operator()(hilti::ctor::Union*) {} + virtual void operator()(hilti::ctor::UnsignedInteger*) {} + virtual void operator()(hilti::ctor::ValueReference*) {} + virtual void operator()(hilti::ctor::Vector*) {} + virtual void operator()(hilti::ctor::WeakReference*) {} + virtual void operator()(hilti::ctor::map::Element*) {} + virtual void operator()(hilti::ctor::struct_::Field*) {} + virtual void operator()(hilti::declaration::Constant*) {} + virtual void operator()(hilti::declaration::Expression*) {} + virtual void operator()(hilti::declaration::Field*) {} + virtual void operator()(hilti::declaration::Function*) {} + virtual void operator()(hilti::declaration::GlobalVariable*) {} + virtual void operator()(hilti::declaration::ImportedModule*) {} + virtual void operator()(hilti::declaration::LocalVariable*) {} + virtual void operator()(hilti::declaration::Module*) {} + virtual void operator()(hilti::declaration::Property*) {} + virtual void operator()(hilti::declaration::Type*) {} + virtual void operator()(hilti::expression::Assign*) {} + virtual void operator()(hilti::expression::BuiltInFunction*) {} + virtual void operator()(hilti::expression::Coerced*) {} + virtual void operator()(hilti::expression::Ctor*) {} + virtual void operator()(hilti::expression::Deferred*) {} + virtual void operator()(hilti::expression::Grouping*) {} + virtual void operator()(hilti::expression::Keyword*) {} + virtual void operator()(hilti::expression::ListComprehension*) {} + virtual void operator()(hilti::expression::LogicalAnd*) {} + virtual void operator()(hilti::expression::LogicalNot*) {} + virtual void operator()(hilti::expression::LogicalOr*) {} + virtual void operator()(hilti::expression::Member*) {} + virtual void operator()(hilti::expression::Move*) {} + virtual void operator()(hilti::expression::Name*) {} + virtual void operator()(hilti::expression::PendingCoerced*) {} + virtual void operator()(hilti::expression::Ternary*) {} + virtual void operator()(hilti::expression::TypeInfo*) {} + virtual void operator()(hilti::expression::TypeWrapped*) {} + virtual void operator()(hilti::expression::Type_*) {} + virtual void operator()(hilti::expression::UnresolvedOperator*) {} + virtual void operator()(hilti::expression::Void*) {} + virtual void operator()(hilti::statement::Assert*) {} + virtual void operator()(hilti::statement::Block*) {} + virtual void operator()(hilti::statement::Break*) {} + virtual void operator()(hilti::statement::Comment*) {} + virtual void operator()(hilti::statement::Continue*) {} + virtual void operator()(hilti::statement::Declaration*) {} + virtual void operator()(hilti::statement::Expression*) {} + virtual void operator()(hilti::statement::For*) {} + virtual void operator()(hilti::statement::If*) {} + virtual void operator()(hilti::statement::Return*) {} + virtual void operator()(hilti::statement::SetLocation*) {} + virtual void operator()(hilti::statement::Switch*) {} + virtual void operator()(hilti::statement::Throw*) {} + virtual void operator()(hilti::statement::Try*) {} + virtual void operator()(hilti::statement::While*) {} + virtual void operator()(hilti::statement::Yield*) {} + virtual void operator()(hilti::statement::switch_::Case*) {} + virtual void operator()(hilti::statement::try_::Catch*) {} + virtual void operator()(hilti::type::Address*) {} + virtual void operator()(hilti::type::Any*) {} + virtual void operator()(hilti::type::Auto*) {} + virtual void operator()(hilti::type::Bool*) {} + virtual void operator()(hilti::type::Bytes*) {} + virtual void operator()(hilti::type::DocOnly*) {} + virtual void operator()(hilti::type::Enum*) {} + virtual void operator()(hilti::type::Error*) {} + virtual void operator()(hilti::type::Exception*) {} + virtual void operator()(hilti::type::Function*) {} + virtual void operator()(hilti::type::Interval*) {} + virtual void operator()(hilti::type::Library*) {} + virtual void operator()(hilti::type::List*) {} + virtual void operator()(hilti::type::Map*) {} + virtual void operator()(hilti::type::Member*) {} + virtual void operator()(hilti::type::Name*) {} + virtual void operator()(hilti::type::Network*) {} + virtual void operator()(hilti::type::Null*) {} + virtual void operator()(hilti::type::OperandList*) {} + virtual void operator()(hilti::type::Optional*) {} + virtual void operator()(hilti::type::Port*) {} + virtual void operator()(hilti::type::Real*) {} + virtual void operator()(hilti::type::RegExp*) {} + virtual void operator()(hilti::type::Result*) {} + virtual void operator()(hilti::type::Set*) {} + virtual void operator()(hilti::type::SignedInteger*) {} + virtual void operator()(hilti::type::Stream*) {} + virtual void operator()(hilti::type::String*) {} + virtual void operator()(hilti::type::StrongReference*) {} + virtual void operator()(hilti::type::Struct*) {} + virtual void operator()(hilti::type::Time*) {} + virtual void operator()(hilti::type::Tuple*) {} + virtual void operator()(hilti::type::Type_*) {} + virtual void operator()(hilti::type::Union*) {} + virtual void operator()(hilti::type::Unknown*) {} + virtual void operator()(hilti::type::UnsignedInteger*) {} + virtual void operator()(hilti::type::ValueReference*) {} + virtual void operator()(hilti::type::Vector*) {} + virtual void operator()(hilti::type::Void*) {} + virtual void operator()(hilti::type::WeakReference*) {} + virtual void operator()(hilti::type::bytes::Iterator*) {} + virtual void operator()(hilti::type::enum_::Label*) {} + virtual void operator()(hilti::type::function::Parameter*) {} + virtual void operator()(hilti::type::list::Iterator*) {} + virtual void operator()(hilti::type::map::Iterator*) {} + virtual void operator()(hilti::type::set::Iterator*) {} + virtual void operator()(hilti::type::stream::Iterator*) {} + virtual void operator()(hilti::type::stream::View*) {} + virtual void operator()(hilti::type::tuple::Element*) {} + virtual void operator()(hilti::type::vector::Iterator*) {} }; } // namespace hilti::visitor diff --git a/hilti/toolchain/include/ast/visitor.h b/hilti/toolchain/include/ast/visitor.h index 6a1792b77..1e6a6fabe 100644 --- a/hilti/toolchain/include/ast/visitor.h +++ b/hilti/toolchain/include/ast/visitor.h @@ -171,14 +171,8 @@ class Visitor : public ::hilti::visitor::Dispatcher { /** Execute matching dispatch methods for a single node. */ void dispatch(const NodePtr& n) { n->dispatch(*this); } - /** Execute matching dispatch methods for a single node. */ - void dispatch(Node& n) { n.dispatch(*this); } - - /** * Walk the AST recursively and call dispatch for each node. */ - void visitAll(const NodePtr& root) { - for ( auto i : walk(root) ) - dispatch(i); - } + // /** Execute matching dispatch methods for a single node. */ + // void dispatch(Node& n) { n.dispatch(*this); } /** * Return a range that iterates over AST, returning each node successively. @@ -212,5 +206,32 @@ using PostOrder = detail::visitor::Visitor; */ using RangePostOrder = detail::visitor::Range; +/** Walk the AST recursively and call dispatch for each node. */ +template +auto visit(Visitor&& visitor, NodePtr& root) { + for ( auto i : visitor.walk(root) ) + visitor.dispatch(i); +} + +/** Walk the AST recursively and call dispatch for each node, then run callback to retrieve a result. */ +template +auto visit(Visitor&& visitor, NodePtr& root, ResultFunc result) { + for ( auto i : visitor.walk(root) ) + visitor.dispatch(i); + + return result(visitor); +} + +template +void dispatch(Visitor&& visitor, const NodePtr& node, const NodePtr& n) { + n->dispatch(visitor); +} + +template +auto dispatch(Visitor&& visitor, const NodePtr& node, ResultFunc result) { + node->dispatch(visitor); + return result(visitor); +} + } // namespace visitor } // namespace hilti diff --git a/hilti/toolchain/include/base/logger.h b/hilti/toolchain/include/base/logger.h index 3f90bbce7..580371b8c 100644 --- a/hilti/toolchain/include/base/logger.h +++ b/hilti/toolchain/include/base/logger.h @@ -130,49 +130,49 @@ class Logger { /** Use HILTI_DEBUG(...) instead. */ void _debug(const logging::DebugStream& dbg, const std::string& msg, const Location& l = location::None); - template - void log(std::string msg, const T& n) { - log(msg, n.location()); + template + void log(std::string msg, const NodeDerivedPtr& n) { + log(msg, n->location()); } - template - void info(std::string msg, const T& n) { - info(msg, n.location()); + template + void info(std::string msg, const NodeDerivedPtr& n) { + info(msg, n->location()); } - template - void warning(std::string msg, const T& n) { - warning(msg, n.location()); + template + void warning(std::string msg, const NodeDerivedPtr& n) { + warning(msg, n->location()); } - template - void error(std::string msg, const T& n) { - error(msg, n.location()); + template + void error(std::string msg, const NodeDerivedPtr& n) { + error(msg, n->location()); } - template - void error(std::string msg, std::vector context, const T& n) { + template + void error(std::string msg, std::vector context, const NodeDerivedPtr& n) { error(msg, context, n.location()); } - template - void error(Result r, const T& n) { + template + void error(Result r, const NodeDerivedPtr& n) { error(r.error().description(), n.location()); } - template - __attribute__((noreturn)) void fatalError(std::string msg, const T& n) { - fatalError(msg, n.location()); + template + __attribute__((noreturn)) void fatalError(std::string msg, const NodeDerivedPtr& n) { + fatalError(msg, n->location()); } - template - __attribute__((noreturn)) void internalError(std::string msg, const T& n) { - internalError(msg, n.location()); + template + __attribute__((noreturn)) void internalError(std::string msg, const NodeDerivedPtr& n) { + internalError(msg, n->location()); } - template - void debug(const logging::DebugStream& dbg, std::string msg, const T& n) { - debug(dbg, msg, n.location()); + template + void debug(const logging::DebugStream& dbg, std::string msg, const NodeDerivedPtr& n) { + debug(dbg, msg, n->location()); } void debugEnable(const logging::DebugStream& dbg); diff --git a/hilti/toolchain/include/compiler/coercion.h b/hilti/toolchain/include/compiler/coercion.h index c9c35379d..d8e767f39 100644 --- a/hilti/toolchain/include/compiler/coercion.h +++ b/hilti/toolchain/include/compiler/coercion.h @@ -4,7 +4,6 @@ #include #include -#include #include @@ -113,7 +112,7 @@ enableEnumClassBitmask(hilti::CoercionStyle); // Must be in global scope namespace hilti { /** Return type for the functions doing expression coercion. */ -struct CoercedExpressionPtr { +struct CoercedExpression { /** Returns true if coercion was successful. */ operator bool() const { return coerced.hasValue(); } @@ -128,7 +127,7 @@ struct CoercedExpressionPtr { * Coerced expression if successful and the coerced expression is not * identical to original one; unset otherwise. */ - std::optional nexpr = {}; + ExpressionPtr nexpr = {}; /** * If coerced is set, true if type of new expression's type is to be @@ -150,7 +149,7 @@ struct CoercedExpressionPtr { * * @param src the original source expression */ - CoercedExpressionPtr(const ExpressionPtr& src) : coerced(src) {} + CoercedExpression(const ExpressionPtr& src) : coerced(src) {} /** * Represents a successful coercion that led to a new expression @@ -159,17 +158,17 @@ struct CoercedExpressionPtr { * @param src the original source expression's type * @param coerced the resulting expression that *src* was coerced to */ - CoercedExpressionPtr(const QualifiedTypePtr& src, ExpressionPtr coerced) + CoercedExpression(const QualifiedTypePtr& src, const ExpressionPtr& coerced) : coerced(coerced), nexpr(coerced), consider_type_changed(src->typename_() != coerced->type()->typename_()) {} /** Represents an unsuccessful coercion. */ - CoercedExpressionPtr() = default; + CoercedExpression() = default; /** * Represents an unsuccessful coercion, carrying an error message along * explaining why it failed. */ - CoercedExpressionPtr(const result::Error& error) : coerced(error) {} + CoercedExpression(const result::Error& error) : coerced(error) {} }; /** @@ -183,6 +182,7 @@ struct CoercedExpressionPtr { * later carry out the coercion (usually that'll be a `expression::Coerced`` * node). * + * @param builder builder to use * @param e expression to coerce * @param dst target type * @param style coercion style to use, given as a bitmask of any style @@ -190,9 +190,8 @@ struct CoercedExpressionPtr { * @return the *result* will evaluate to true if coercion was successful; if * so, the contained fields will provide more information */ -CoercedExpressionPtr coerceExpressionPtr(const ExpressionPtr& e, const QualifiedTypePtr& dst, - bitmask style = CoercionStyle::TryAllForAssignment, - bool lhs = false); +CoercedExpression coerceExpression(Builder* builder, const ExpressionPtr& e, const QualifiedTypePtr& dst, + bitmask style = CoercionStyle::TryAllForAssignment, bool lhs = false); /** * Coerces an expression to a given target type. This returns a struct with @@ -205,6 +204,7 @@ CoercedExpressionPtr coerceExpressionPtr(const ExpressionPtr& e, const Qualified * later carry out the coercion (usually that'll be a `expression::Coerced`` * node). * + * @param builder builder to use * @param e expression to coerce * @param src explicitly specified source type; this can be different from * the type of *e* and will be used instead of that @@ -214,16 +214,16 @@ CoercedExpressionPtr coerceExpressionPtr(const ExpressionPtr& e, const Qualified * @return the *result* will evaluate to true if coercion was successful; if * so, the contained fields will provide more information */ -CoercedExpressionPtr coerceExpressionPtr(const ExpressionPtr& e, const QualifiedTypePtr& src_, - const QualifiedTypePtr& dst_, - bitmask style = CoercionStyle::TryAllForAssignment, - bool lhs = false); +CoercedExpression coerceExpression(Builder* builder, const ExpressionPtr& e, const QualifiedTypePtr& src_, + const QualifiedTypePtr& dst_, + bitmask style = CoercionStyle::TryAllForAssignment, bool lhs = false); /** * Matches a set of expressions against a set of operands, coercing them as * needed. This takes into account specifics of the operands, such as them * being optional or having defaults. * + * @param builder builder to use * @param exprs source expressions to match against the operands * @param operands operands to match against * @param style coercion style to use for each expression's coercion to its @@ -236,41 +236,43 @@ CoercedExpressionPtr coerceExpressionPtr(const ExpressionPtr& e, const Qualified * available (missing expressions for optional operands without defaults will * remain left out). If unsuccessful, an error. */ -Result>> coerceOperands(const hilti::node::Range& exprs, - const std::vector& operands, - bitmask style); +Result> coerceOperands(Builder* builder, const hilti::node::Range& exprs, + const operator_::Operands& operands, bitmask style); /** * Coerces a constructor to a given target type. This returns the coerced * constructor, now of the new type. If the constructor is already of the * right type, it will just be returned back. * + * @param builder builder to use * @param c ctor to coerce * @param dst target type * @param style coercion style to use * @return if the coercion was successful, the returned new value (which may be the same as the old) */ -Result coerceCtorPtr(CtorPtr c, const QualifiedTypePtr& dst, - bitmask style = CoercionStyle::TryAllForAssignment); +Result coerceCtor(Builder* builder, CtorPtr c, const QualifiedTypePtr& dst, + bitmask style = CoercionStyle::TryAllForAssignment); /** * Coerces a source type to a given target type. This returns the coerced * type. If the type is already of the right type, it will just be returned * back. * + * @param builder builder to use * @param c ctor to coerce * @param dst target type * @param style coercion style to use * @return if the coercion was successful, the returned new value (which may be the same as the old) */ -Result coerceType(const QualifiedTypePtr& src_, const QualifiedTypePtr& dst_, +Result coerceType(Builder* builder, const QualifiedTypePtr& src_, const QualifiedTypePtr& dst_, bitmask style = CoercionStyle::TryAllForAssignment); namespace detail { /** Implements the corresponding functionality for the default HILTI compiler plugin. */ -std::optional coerceCtorPtr(CtorPtr c, const QualifiedTypePtr& dst, bitmask style); +CtorPtr coerceCtor(Builder* builder, const CtorPtr& c, const QualifiedTypePtr& dst, bitmask style); /** Implements the corresponding functionality for the default HILTI compiler plugin. */ -QualifiedTypePtr coerceType(QualifiedTypePtr t, const QualifiedTypePtr& dst, bitmask style); +QualifiedTypePtr coerceType(Builder* builder, const QualifiedTypePtr& t, const QualifiedTypePtr& dst, + bitmask style); } // namespace detail } // namespace hilti diff --git a/hilti/toolchain/include/compiler/context.h b/hilti/toolchain/include/compiler/context.h index e393e0f3b..740f172f1 100644 --- a/hilti/toolchain/include/compiler/context.h +++ b/hilti/toolchain/include/compiler/context.h @@ -99,40 +99,7 @@ struct Options { std::map _aux_options; }; -namespace context { - -/** - * Index into the context's cache of already processed modules. Note that we - * use ID and path interchangeably, a module can be accessed by *either*, - * meaning that the mapping from path to ID must be consistent throughout all - * processing. - */ -struct CacheIndex { - ID id; /**< module ID */ - ID scope; /**< import scope */ - hilti::rt::filesystem::path path; /**< path to module's source code on disk; can be left empty if no file exists */ - - auto scopedID() const { return scope + id; } - - CacheIndex() = default; - CacheIndex(ID id, const std::optional& scope, const hilti::rt::filesystem::path& path) - : id(std::move(id)), scope(scope ? *scope : ID()), path(util::normalizePath(path)) {} -}; - -/** - * Caches information about an already processed module. Note that these are - * "living" objects that keep being updated during AST processing. Only once - * "final" is set, the information is assumed to correct and no longer - * changing. - */ -struct CacheEntry { - std::shared_ptr unit; /**< cached unit */ - - CacheEntry() = default; - CacheEntry(std::shared_ptr unit) : unit(std::move(unit)) {} -}; - -} // namespace context +namespace context {} // namespace context /** Context storing compiler-wide state */ class Context { @@ -151,73 +118,9 @@ class Context { /** Returns the global AST context. */ std::shared_ptr astContext() const { return _ast_context; } - /** - * Caches a code unit inside the context. The cache uses a unit's `(ID, - * path)` tuple as the index. Any previously cached unit with the same - * index tuple will be replaced. - * - * @param unit unit to cache - * @return the meta data associated with the newly registered module - */ - void cacheUnit(const std::shared_ptr& unit); - - /** - * Looks up a previously cached unit by its ID. - * - * @param id module ID look up a unit for - * @param scope import-from scope associated with the import operation, if any - * @param extension a file extension expected for the unit, indicating its - * source language; a cached unit will only be returned if its extension - * matches - * @return the cache entry associated with the path if found - */ - std::optional lookupUnit(const ID& id, const std::optional& scope, - const hilti::rt::filesystem::path& extension); - - /** - * Looks up a previously cached unit by its path. It will only return a - * cached module if its extension matches that of the given path. - * - * @param path path to look up a unit for - * @param scope import-from scope associated with the path being imported, if any - * @return the cache entry associated with the path if found - */ - std::optional lookupUnit(const hilti::rt::filesystem::path& path, - const std::optional& scope, - std::optional ast_extension = {}); - - /** - * Looks up a previously cached unit using an existing cache index as the key. - * - * @param idx cache index to look up - * @return the cache entry associated with the path if found - */ - std::optional lookupUnit(const context::CacheIndex& idx, - const std::optional& ast_extension = {}); - - /** - * Returns all (direct & indirect) dependencies that a module imports. This - * information will be complete only once all AST have been fully resolved. - * - * @param idx cache index for the module which to return dependencies for - * @return set of dependencies - */ - std::vector> lookupDependenciesForUnit(const context::CacheIndex& idx, - const hilti::rt::filesystem::path& extension); - - /** - * Dumps the current state of the unit cache to a debug stream. - * - * @param stream debug stream to write to - */ - void dumpUnitCache(const hilti::logging::DebugStream& stream); - private: Options _options; std::shared_ptr _ast_context; - - std::unordered_map> _unit_cache_by_id; - std::unordered_map> _unit_cache_by_path; }; } // namespace hilti diff --git a/hilti/toolchain/include/compiler/detail/codegen/codegen.h b/hilti/toolchain/include/compiler/detail/codegen/codegen.h index 626ff1bd1..65ca8d710 100644 --- a/hilti/toolchain/include/compiler/detail/codegen/codegen.h +++ b/hilti/toolchain/include/compiler/detail/codegen/codegen.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -72,39 +73,37 @@ class CodeGen { const Options& options() const { return context()->options(); } // These must be called only while a module is being compiled. - std::optional typeDeclaration(const hilti::QualifiedTypePtr& t); - std::list typeDependencies(const hilti::QualifiedTypePtr& t); - cxx::Type compile(const hilti::QualifiedTypePtr& t, codegen::TypeUsage usage); - cxx::Expression compile(const hilti::Expression& e, bool lhs = false); - cxx::Expression compile(const hilti::Ctor& c, bool lhs = false); -#if 0 - cxx::Expression compile(const hilti::expression::ResolvedOperator& o, bool lhs = false); -#endif - cxx::Block compile(const hilti::Statement& s, cxx::Block* b = nullptr); - cxx::declaration::Function compile(const ID& id, type::Function ft, declaration::Linkage linkage, + std::optional typeDeclaration(const QualifiedTypePtr& t); + std::list typeDependencies(const QualifiedTypePtr& t); + cxx::Type compile(const QualifiedTypePtr& t, codegen::TypeUsage usage); + cxx::Expression compile(const hilti::ExpressionPtr& e, bool lhs = false); + cxx::Expression compile(const hilti::CtorPtr& c, bool lhs = false); + cxx::Expression compile(const NodeDerivedPtr& o, bool lhs = false); + cxx::Block compile(const hilti::StatementPtr& s, cxx::Block* b = nullptr); + cxx::declaration::Function compile(const ID& id, const NodeDerivedPtr& ft, + declaration::Linkage linkage, function::CallingConvention cc = function::CallingConvention::Standard, - const std::optional& fattrs = {}, - std::optional namespace_ = {}); + const AttributeSetPtr& fattrs = {}, std::optional namespace_ = {}); std::vector compileCallArguments(const hilti::node::Range& args, const hilti::node::Set& params); std::vector compileCallArguments(const hilti::node::Range& args, const hilti::node::Range& params); - std::optional typeDefaultValue(const hilti::QualifiedTypePtr& t); + std::optional typeDefaultValue(const QualifiedTypePtr& t); codegen::TypeUsage parameterKindToTypeUsage(declaration::parameter::Kind); - cxx::Expression typeInfo(const hilti::QualifiedTypePtr& t); - void addTypeInfoDefinition(const hilti::QualifiedTypePtr& t); + cxx::Expression typeInfo(const QualifiedTypePtr& t); + void addTypeInfoDefinition(const QualifiedTypePtr& t); cxx::Expression coerce(const cxx::Expression& e, const QualifiedTypePtr& src, const QualifiedTypePtr& dst); // only for supported coercions - cxx::Expression pack(const Expression& data, const std::vector& args); - cxx::Expression pack(const hilti::QualifiedTypePtr& t, const cxx::Expression& data, + cxx::Expression pack(const ExpressionPtr& data, const Expressions& args); + cxx::Expression pack(const QualifiedTypePtr& t, const cxx::Expression& data, const std::vector& args); - cxx::Expression unpack(const hilti::QualifiedTypePtr& t, const Expression& data, - const std::vector& args, bool throw_on_error); - cxx::Expression unpack(const hilti::QualifiedTypePtr& t, const cxx::Expression& data, + cxx::Expression unpack(const QualifiedTypePtr& t, const ExpressionPtr& data, const Expressions& args, + bool throw_on_error); + cxx::Expression unpack(const QualifiedTypePtr& t, const cxx::Expression& data, const std::vector& args, bool throw_on_error); - void addDeclarationFor(const hilti::QualifiedTypePtr& t) { _need_decls.push_back(t); } + void addDeclarationFor(const QualifiedTypePtr& t) { _need_decls.push_back(t); } cxx::Expression addTmp(const std::string& prefix, const cxx::Type& t); cxx::Expression addTmp(const std::string& prefix, const cxx::Expression& init); @@ -141,7 +140,7 @@ class CodeGen { hilti::Unit* hiltiUnit() const; // will abort if not compiling a module. private: - const codegen::CxxTypeInfo& _getOrCreateTypeInfo(const hilti::QualifiedTypePtr& t); + const codegen::CxxTypeInfo& _getOrCreateTypeInfo(const QualifiedTypePtr& t); // Adapt expression so that it can be used as a LHS. If expr is already a // LHS, it's returned directly. Otherwise it assigns it over into a @@ -155,7 +154,7 @@ class CodeGen { std::vector _cxx_blocks; std::vector _tmps; std::map _tmp_counters; - std::vector _need_decls; + std::vector _need_decls; hilti::util::Cache _cache_types_storage; hilti::util::Cache _cache_type_info; hilti::util::Cache _cache_types_declarations; diff --git a/hilti/toolchain/include/compiler/detail/parser/driver.h b/hilti/toolchain/include/compiler/detail/parser/driver.h index 65377ecc5..6cca5b869 100644 --- a/hilti/toolchain/include/compiler/detail/parser/driver.h +++ b/hilti/toolchain/include/compiler/detail/parser/driver.h @@ -47,7 +47,7 @@ class Scanner; /** Driver for flex/bison. */ class Driver { public: - Result parse(Builder* builder, std::istream& in, const std::string& filename); + Result parse(Builder* builder, std::istream& in, const std::string& filename); Scanner* scanner() const { return _scanner; } Parser* parser() const { return _parser; } @@ -64,7 +64,7 @@ class Driver { void enableDottedIDMode(); void disableDottedIDMode(); - void setDestinationModule(NodePtr m) { _module = std::move(m); } + void setDestinationModule(ModulePtr m) { _module = std::move(m); } private: Builder* _builder = nullptr; @@ -72,7 +72,7 @@ class Driver { Parser* _parser = nullptr; Scanner* _scanner = nullptr; - NodePtr _module; + ModulePtr _module; int _expression_mode = 0; }; diff --git a/hilti/toolchain/include/compiler/detail/visitors.h b/hilti/toolchain/include/compiler/detail/visitors.h index 23c2273da..765219066 100644 --- a/hilti/toolchain/include/compiler/detail/visitors.h +++ b/hilti/toolchain/include/compiler/detail/visitors.h @@ -29,9 +29,6 @@ class Stream; namespace detail { -/** Performs imports for an AST. */ -std::set importModules(const Node& root, Unit* unit); - /** * Prints an AST as HILTI source code. This consults any installed plugin * `print_ast` hooks. @@ -45,20 +42,16 @@ void printAST(const NodePtr& root, std::ostream& out, bool compact = false); void printAST(const NodePtr& root, printer::Stream& stream); // NOLINT /** Returns a string with the prototype for an operator for display. */ -std::string renderOperatorPrototype(const std::shared_ptr& o); +std::string renderOperatorPrototype(const NodeDerivedPtr& o); -#if 0 /** Returns a string with the prototype for an operator for display. */ -std::string renderOperatorPrototype(const std::shared_ptr& o); -#endif +std::string renderOperatorPrototype(const NodeDerivedPtr& o); /** Returns a string with an instantiated operator for display. */ -std::string renderOperatorInstance(const std::shared_ptr& o); +std::string renderOperatorInstance(const NodeDerivedPtr& o); -#if 0 /** Returns a string with an instantiated operator for display. */ -std::string renderOperatorInstance(const std::shared_ptr& o); -#endif +std::string renderOperatorInstance(const NodeDerivedPtr& o); /** Prints a debug dump of a node, including its childrens. */ void renderNode(const NodePtr& n, std::ostream& out, bool include_scopes = false); @@ -71,21 +64,21 @@ void renderNode(const NodePtr& n, logging::DebugStream stream, bool include_scop * expression is not represeneting a constant value, but only that we aren't * able to compute it. */ -Result> foldConstant(const Node& expr); +Result foldConstant(const ExpressionPtr& expr); namespace ast { /** Implements the corresponding functionality for the default HILTI compiler plugin. */ -void buildScopes(const std::shared_ptr& ctx, NodePtr root, Unit* unit); +void buildScopes(Builder* builder, const ASTRootPtr& root); /** Implements the corresponding functionality for the default HILTI compiler plugin. */ -bool normalize(NodePtr root, Unit* unit); +bool normalize(Builder* builder, const ASTRootPtr& root); /** Implements the corresponding functionality for the default HILTI compiler plugin. */ -bool coerce(NodePtr root, Unit* unit); +bool coerce(Builder* builder, const ASTRootPtr& root); /** Implements the corresponding functionality for the default HILTI compiler plugin. */ -bool resolve(const std::shared_ptr& ctx, NodePtr root, Unit* unit); +bool resolve(Builder* builder, const ASTRootPtr& root); /** Implements the corresponding functionality for the default HILTI compiler plugin. */ -void validate_pre(NodePtr root); +void validate_pre(ASTContext* ctx, const ASTRootPtr& root); /** Implements the corresponding functionality for the default HILTI compiler plugin. */ -void validate_post(NodePtr root); +void validate_post(ASTContext* ctx, const ASTRootPtr& root); } // namespace ast } // namespace detail } // namespace hilti diff --git a/hilti/toolchain/include/compiler/driver.h b/hilti/toolchain/include/compiler/driver.h index b62fed10e..2cc476edc 100644 --- a/hilti/toolchain/include/compiler/driver.h +++ b/hilti/toolchain/include/compiler/driver.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -118,18 +119,20 @@ class Driver { */ Result parseOptions(int argc, char** argv); - /** - * Schedules a unit for compilation. The driver will compile the unit once - * `compile()` is called. If a module of the same ID or path has been added - * previously, this will have no further effect. - * - * The `hookAddInput()` and `hookNewASTPreCompilation()` hooks will be - * called immediately for the new module. - * - * @param u unit to schedule for compilation - * @return set if successful; otherwise the result provides an error message + /* + * #<{(|* + * * Schedules a unit for compilation. The driver will compile the unit once + * * `compile()` is called. If a module of the same ID or path has been added + * * previously, this will have no further effect. + * * + * * The `hookAddInput()` and `hookNewASTPreCompilation()` hooks will be + * * called immediately for the new module. + * * + * * @param u unit to schedule for compilation + * * @return set if successful; otherwise the result provides an error message + * |)}># + * Result addInput(const std::shared_ptr& u); */ - Result addInput(const std::shared_ptr& u); /** * Schedules a source file for compilation. The file will be parsed @@ -146,10 +149,7 @@ class Driver { Result addInput(const hilti::rt::filesystem::path& path); /** Returns true if at least one input file has been added. */ - bool hasInputs() const { - return ! (_pending_units.empty() && _processed_units.empty() && _processed_paths.empty() && - _libraries.empty() && _external_cxxs.empty()); - } + bool hasInputs() const { return ! (_units.empty() && _libraries.empty() && _external_cxxs.empty()); } /** Returns the driver options currently in effect. */ const auto& driverOptions() const { return _driver_options; } @@ -478,19 +478,17 @@ class Driver { driver::Options _driver_options; hilti::Options _compiler_options; - std::vector> _pending_units; - std::set _processed_units; - std::set _processed_paths; - std::shared_ptr _ctx; // driver's compiler context std::unique_ptr _jit; // driver's JIT instance std::shared_ptr _library; // Compiled code + std::unordered_map> _units; std::vector _generated_cxxs; std::unordered_map _libraries; std::vector _external_cxxs; std::vector _mds; - std::vector> _hlts; + + std::unordered_set _processed_paths; bool _runtime_initialized = false; // true once initRuntime() has succeeded std::set _tmp_files; // all tmp files created, so that we can clean them up. diff --git a/hilti/toolchain/include/compiler/plugin.h b/hilti/toolchain/include/compiler/plugin.h index f735653b0..6741ba40c 100644 --- a/hilti/toolchain/include/compiler/plugin.h +++ b/hilti/toolchain/include/compiler/plugin.h @@ -24,8 +24,9 @@ namespace hilti { -class Unit; +class ASTContext; class Context; +class Unit; namespace printer { class Stream; @@ -76,10 +77,10 @@ struct Plugin { * Hook called to retrieve paths to search when importing modules that * this plugin handles. * - * @param arg1 compiler context that's in use + * @param arg1 AST context that's in use * @return directories to search */ - Hook, std::shared_ptr> library_paths; + Hook, Context*> library_paths; /** * Hook called to parse input file that this plugin handles. @@ -89,7 +90,7 @@ struct Plugin { * @param arg3 file associated with the input stream * @return module AST if parsing succeeded */ - Hook, Builder*, std::istream&, hilti::rt::filesystem::path> parse; + Hook, Builder*, std::istream&, hilti::rt::filesystem::path> parse; /** * Hook called to perform coercion of a `Ctor` into another of a given target type. @@ -97,13 +98,13 @@ struct Plugin { * If the plugin knows how to handle the coercion, the hook returns a new * `Ctor` that's now of the target type. * - * @param arg1 compiler context that's in use + * @param arg1 builder to use * @param arg2 ctor that needs coercion * @param arg3 target type for ctor * @param arg4 coercion style to use * @return new ctor if plugin performed coercion, or nullptr otherwise */ - Hook> coerce_ctor; + Hook> coerce_ctor; /** * Hook called to approved coercion of an expression into a different @@ -114,74 +115,71 @@ struct Plugin { * `apply_coercions` hook that will later be called to perform the actual * coercion during code generation. * - * @param arg1 compiler context that's in use + * @param arg1 builder to use * @param arg2 type that needs coercion * @param arg3 target type for coercion * @param arg4 coercion style to use * @return new type if plugin can handle this coercion */ - Hook> coerce_type; + Hook> + coerce_type; /** * Hook called to build the scopes in a module's AST. * - * @param arg1 compiler context that's in use + * @param arg1 AST context that's in use * @param arg2 root node of AST; the hook may modify the AST - * @param arg3 current unit being compiled * @return true if the hook modified the AST in a substantial way */ - Hook, NodePtr, Unit*> ast_build_scopes; + Hook ast_build_scopes; /** * Hook called to prepare an AST before any further stages execute. * - * @param arg1 compiler context that's in use + * @param arg1 AST context that's in use * @param arg2 root node of AST; the hook may modify the AST - * @param arg3 current unit being compiled * @return true if the hook modified the AST in a substantial way */ - Hook, NodePtr, Unit*> ast_normalize; + Hook ast_normalize; /** * Hook called to apply type coercions to the AST. * - * @param arg1 compiler context that's in use + * @param arg1 AST context that's in use * @param arg2 root node of AST; the hook may modify the AST - * @param arg3 current unit being compiled * @return true if the hook modified the AST in a substantial way */ - Hook, NodePtr, Unit*> ast_coerce; + Hook ast_coerce; /** * Hook called to resolve unknown types and other entities. * - * @param arg1 compiler context that's in use + * @param arg1 AST context that's in use * @param arg2 root node of AST; the hook may modify the AST - * @param arg3 current unit being compiled * @return true if the hook modified the AST in a substantial way */ - Hook, NodePtr, Unit*> ast_resolve; + Hook ast_resolve; /** * Hook called to validate correctness of an AST before resolving starts * (to the degree it can at that time). Any errors must be reported by * setting the nodes' error information. * - * @param arg1 compiler context that's in use + * @param arg1 AST context that's in use * @param arg2 root node of AST; the hook may not modify the AST * @param arg3 current unit being compiled */ - Hook, NodePtr, Unit*> ast_validate_pre; + Hook ast_validate_pre; /** * Hook called to validate correctness of an AST once fully resolved. Any * errors must be reported by setting the nodes' error information. * - * @param arg1 compiler context that's in use + * @param arg1 AST context that's in use * @param arg2 root node of AST; the hook may not modify the AST * @param arg3 current unit being compiled */ - Hook, NodePtr, Unit*> ast_validate_post; + Hook ast_validate_post; /** * Hook called to print an AST back as source code. The hook gets to choose @@ -198,12 +196,12 @@ struct Plugin { * Hook called to replace AST nodes of one language (plugin) with nodes * of another coming further down in the pipeline. * - * @param arg1 compiler context that's in use + * @param arg1 AST context that's in use * @param arg2 root node of AST; the hook may modify the AST * @param arg3 current unit being compiled * @return true if the hook modified the AST in a substantial way */ - Hook, NodePtr, Unit*> ast_transform; + Hook ast_transform; }; class PluginRegistry; diff --git a/hilti/toolchain/include/compiler/unit.h b/hilti/toolchain/include/compiler/unit.h index 927f171c4..69bd889a5 100644 --- a/hilti/toolchain/include/compiler/unit.h +++ b/hilti/toolchain/include/compiler/unit.h @@ -51,49 +51,14 @@ class Unit { /** Destructor. */ ~Unit(); - /** Returns the root node of the module AST's. */ - ModulePtr module() const { return _module->as(); } - /** - * Returns the root node of the module AST's. Must only be called if + * Returns the root node of the module's AST. Must only be called if * `isCompiledHilti()` returns true. */ - ModulePtr module() { - assert(_module); - return _module->as(); - } - - /** - * Returns the index to use when storing the unit inside the context's - * unit cache. - */ - const auto& cacheIndex() const { return _index; } - - /** Returns the ID of the unit's module. */ - const auto& id() const { return _index.id; } - - /** - * Returns an ID for the unit's module that's globally unique across all - * units processed within the current context. This ID will often be the - * same as returned by `id()`, but it may include an additional string for - * unification, in particular if there are more than one module with the - * same ID. - * - * @returns globally unique ID for the module; the method guarantees that - * the ID represents a valid C++ identifier - */ - ID uniqueID() const { return _unique_id; } + ModulePtr module() const; - /** Returns the path associated with the unit's module. */ - const auto& path() const { return _index.path; } - - /** - * Returns the file extension associated with the unit's code. This - * extension determines which plugin the unit's AST will be processed with. - * Note that this does not necessarily match the extension of module's - * source path. - */ - const auto& extension() const { return _extension; } + /** Returns the unique ID of the unit's module. */ + const auto& uid() const { return _uid; } /** * Set a file extension associated with the unit's code. By default, the @@ -103,53 +68,7 @@ class Unit { * * @param ext new extension */ - void setExtension(const hilti::rt::filesystem::path& ext) { _extension = ext; } - - enum ASTState { Modified, NotModified }; - - /** Clears any scopes and errors accumulated inside the unit's AST. */ - void resetAST(); - - /** - * Runs a plugin's scope-building phase on the unit's AST. - * - * @param plugin plugin to execute - * @returns success if no error occurred, and an appropriate error otherwise - */ - Result buildASTScopes(const Plugin& plugin); - - /** - * Runs a plugin's resolver phase on the unit's AST. - * - * @param plugin plugin to execute - * @returns flag indicating whether the AST was modified or not; or an - * appropriate error if a failure occurred - */ - Result resolveAST(const Plugin& plugin); - - /** - * Runs a plugin's validation phase on the unit's AST before resolving. - * - * @param plugin plugin to execute - * @returns true if the AST does not contain any errors - */ - bool validateASTPre(const Plugin& plugin); - - /** - * Runs a plugin's validation phase on the unit's AST after resolving. - * - * @param plugin plugin to execute - * @returns true if the AST does not contain any errors - */ - bool validateASTPost(const Plugin& plugin); - - /** - * Runs a plugin's transformation phase on the unit's AST. - * - * @param plugin plugin to execute - * @returns success if no error occurred, and an appropriate error otherwise - */ - Result transformAST(const Plugin& plugin); + void setExtension(const hilti::rt::filesystem::path& ext); /** * Triggers generation of C++ code from the compiled AST. @@ -192,18 +111,7 @@ class Unit { * @param recursive if true, return the transitive closure of all * dependent units, vs just direct dependencies of the current unit */ - std::vector> dependencies(bool recursive = false) const; - - /** Removes any dependencies registered for the unit so far. */ - void clearDependencies() { _dependencies.clear(); }; - - /** - * Register a dependency on another unit for the current one. - * - * @param unit the unit this one depends on - * @returns true if this is a new dependency that had not been previously added - */ - bool addDependency(const std::shared_ptr& unit); + std::vector dependencies(bool recursive = false) const; /** * Returns the unit's meta data for the internal HILTI linker. @@ -218,11 +126,11 @@ class Unit { } /** - * Returns true if this unit has been compiled from HILTI source. This is - * usually the case, but we also represent HILTI's linker output as a - * unit and there's no corresponding HILTI source code for that. + * Returns true if this unit has HILTI source code available. This is + * usually the case, but we also represent HILTI's linker output as a unit + * and there's no corresponding HILTI source code for that. */ - bool isCompiledHILTI() const { return _module != nullptr; } + bool isCompiledHILTI() const; /** * Returns true if the AST has been determined to contain code that needs @@ -232,24 +140,11 @@ class Unit { bool requiresCompilation(); /** - * Explicily marks the unit as requiring compilation down to C++, overriding + * Explicitly marks the unit as requiring compilation down to C++, overriding * any automatic determination. */ void setRequiresCompilation() { _requires_compilation = true; } - /** - * Returns true if the unit has been marked as fully resolved, so that no further AST processing is needed. - */ - bool isResolved() { return _resolved; } - - /** - * Sets the resolver state for the unit. - * - * @param resolved true to mark the module as fully resolved, so that no - * further AST processing is needed - */ - void setResolved(bool resolved) { _resolved = resolved; } - /** Returns the compiler context in use. */ std::shared_ptr context() const { return _context.lock(); } @@ -260,70 +155,12 @@ class Unit { * Factory method that instantiates a unit from an existing source file * that it will parse. * - * This method also caches the module with the global context. Note that - * the module's ID or path should usually not exist with the context yet. - * * @param context global compiler context * @param path path to parse the module from - * @param scope import-from scope associated with the import operation, if any - * @param ast_extension extension indicating which plugin to use for - * processing the AST; if not given, this will be taken from the filename * @return instantiated unit, or an appropriate error result if operation failed */ static Result> fromSource(const std::shared_ptr& context, - const hilti::rt::filesystem::path& path, - const std::optional& scope, - std::optional process_extension = {}); - - /** - * Factory method that instantiates a unit from an existing HILTI AST. - * - * This method also caches the module with the global context. Note that - * the module's ID or path should usually not exist with the context yet. - * If it does, this one will replace the existing version. - * - * @param context global compiler context - * @param module AST of the module - * @param extension extension indicating which plugin to use for - * processing the AST - * @return instantiated unit, or an appropriate error result if operation failed - */ - static std::shared_ptr fromModule(const std::shared_ptr& context, const ModulePtr& module, - hilti::rt::filesystem::path extension); - - /** - * Factory method that instantiates a unit for an `import` statement, - * performing the search for the right source file first. - * - * This method also caches the module with the global context. Note that - * the module's ID or path should usually not exist with the context yet. - * - * @param context global compiler context - * @param id ID of the module to be imported - * @param parse_extension file extension indicating how to parse the module's source file - * @param ast_extension extension indicating which plugin to use for - * processing the AST; this will usually match `parse_extension`, but - * doesn't need to. - * @return instantiated unit, or an appropriate error result if operation failed - */ - static Result> fromImport(const std::shared_ptr& context, const ID& id, - const hilti::rt::filesystem::path& parse_extension, - const hilti::rt::filesystem::path& process_extension, - std::optional scope, - std::vector search_dirs); - - /** - * Factory method that instantiates a unit from an existing HILTI module - * already cached by the global context. - * - * @param context global compiler context - * @param path path of the cached module - * @param scope import-from scope associated with the existing module - * @return instantiated unit, or an appropriate error result if operation failed - */ - static Result> fromCache(const std::shared_ptr& context, - const hilti::rt::filesystem::path& path, - const std::optional& scope); + const hilti::rt::filesystem::path& path); /** * Factory method that instantiates a unit from existing C++ source code @@ -333,7 +170,7 @@ class Unit { * @param path path associated with the C++ code, if any * @return instantiated unit, or an appropriate error result if operation failed */ - static Result> fromCXX(const std::shared_ptr& context, detail::cxx::Unit cxx, + static Result> fromCXX(const std::shared_ptr& context, const detail::cxx::Unit& cxx, const hilti::rt::filesystem::path& path = ""); /** @@ -371,54 +208,16 @@ class Unit { private: // Private constructor initializing the unit's meta data. Use the public // `from*()` factory functions instead to instantiate a unit. - Unit(const std::shared_ptr& context, const ID& id, const std::optional& scope, - const hilti::rt::filesystem::path& path, hilti::rt::filesystem::path extension, NodePtr module) - : _index(id, scope, util::normalizePath(path)), - _unique_id(_makeUniqueID(id)), - _extension(std::move(std::move(extension))), - _module(std::move(module)), - _context(context) {} - - Unit(const std::shared_ptr& context, const ID& id, const std::optional& scope, - const hilti::rt::filesystem::path& path, hilti::rt::filesystem::path extension, - std::optional cxx_unit = {}) - : _index(id, scope, util::normalizePath(path)), - _unique_id(_makeUniqueID(id)), - _extension(std::move(std::move(extension))), - _context(context), - _cxx_unit(std::move(cxx_unit)) {} - - // Make a given ID globally unique. - ID _makeUniqueID(const ID& id); - - // Backend for the public import() methods. - Result _import(const hilti::rt::filesystem::path& path, std::optional expected_name); - - // Reports any errors recorded in the AST to stderr. - // - // @returns false if there were errors, true if the AST is all good - bool _collectErrors(); - - // Recursively destroys the module's AST. - void _destroyModule(); - - // Helper for dependencies() to recurse. - void _recursiveDependencies(std::vector>* dst, std::unordered_set* seen) const; - - // Parses a source file with the appropriate plugin. - static Result _parse(const std::shared_ptr& context, const hilti::rt::filesystem::path& path); - - context::CacheIndex _index; // index for the context's module cache - ID _unique_id; // globally unique ID for this module - hilti::rt::filesystem::path _extension; // AST extension, which may differ from source file - NodePtr _module; // root node for AST, if available; must be a Module - std::vector> _dependencies; // recorded dependencies - std::weak_ptr _context; // global context - std::optional _cxx_unit; // compiled C++ code for this unit, once available - bool _resolved = false; // state of resolving the AST - bool _requires_compilation = false; // mark explicitly as requiring compilation to C++ - - static std::unordered_map _uid_cache; // cache storing state for generating globally unique IDs + Unit(const std::shared_ptr& context, module::UID uid) : _context(context), _uid(std::move(uid)) {} + Unit(const std::shared_ptr& context, module::UID uid, const detail::cxx::Unit& cxx_unit) + : _context(context), _uid(std::move(uid)), _cxx_unit(std::move(cxx_unit)) {} + + Result _codegenModule(const module::UID& uid); + + std::weak_ptr _context; // global context + module::UID _uid; // module's globally unique ID + std::optional _cxx_unit; // compiled C++ code for this unit, once available + bool _requires_compilation = false; // mark explicitly as requiring compilation to C++ }; } // namespace hilti diff --git a/hilti/toolchain/include/global.h b/hilti/toolchain/include/global.h index db2092043..bd0fc34e4 100644 --- a/hilti/toolchain/include/global.h +++ b/hilti/toolchain/include/global.h @@ -22,7 +22,7 @@ namespace hilti { * * Returns: The parsed AST, or a corresponding error if parsing failed. */ -Result parseSource(Builder* builder, std::istream& in, const std::string& filename); +Result parseSource(Builder* builder, std::istream& in, const std::string& filename); /** * Prints out a debug representation of an AST node to a debug stream. The diff --git a/hilti/toolchain/src/ast/ast-context.cc b/hilti/toolchain/src/ast/ast-context.cc new file mode 100644 index 000000000..cccb693a7 --- /dev/null +++ b/hilti/toolchain/src/ast/ast-context.cc @@ -0,0 +1,470 @@ +// Copyright (c) 2020-2023 by the Zeek Project. See LICENSE for details. + +#include "ast/ast-context.h" + +#include + +#include +#include +#include +#include + +#include "ast/builder/builder.h" +#include "compiler/detail/visitors.h" + +using namespace hilti; +using namespace hilti::util; + +namespace hilti::logging::debug { +inline const DebugStream AstCache("ast-cache"); +inline const DebugStream AstCodegen("ast-codegen"); +inline const DebugStream AstDeclarations("ast-declarations"); +inline const DebugStream AstDumpIterations("ast-dump-iterations"); +inline const DebugStream AstFinal("ast-final"); +inline const DebugStream AstOrig("ast-orig"); +inline const DebugStream AstPrintTransformed("ast-print-transformed"); +inline const DebugStream AstResolved("ast-resolved"); +inline const DebugStream AstTransformed("ast-transformed"); +inline const DebugStream Compiler("compiler"); +} // namespace hilti::logging::debug + +ASTRoot::~ASTRoot() = default; + +std::string ASTRoot::_render() const { return ""; } + +ASTContext::ASTContext(Context* context) : _context(context) { + _root = ASTRoot::create(this); + _root->getOrCreateScope(); // create the global scope +} + +Result ASTContext::parseSource(const hilti::rt::filesystem::path& path, + std::optional process_extension) { + return _parseSource(path, {}, std::move(process_extension)); +} + +Result ASTContext::importModule(const ID& id, const std::optional& scope, + const hilti::rt::filesystem::path& parse_extension, + const std::optional& process_extension, + std::vector search_dirs) { + auto parse_plugin = plugin::registry().pluginForExtension(parse_extension); + + if ( ! (parse_plugin && parse_plugin->get().parse) ) + return result::Error(fmt("no plugin provides support for importing *%s files", parse_extension.native())); + + auto filename = fmt("%s%s", util::tolower(id), parse_extension.native()); + + if ( scope ) + filename = fmt("%s/%s", util::replace(scope->str(), ".", "/"), filename); + + std::vector library_paths = std::move(search_dirs); + + if ( parse_plugin->get().library_paths ) + library_paths = util::concat(std::move(library_paths), (*parse_plugin->get().library_paths)(_context)); + + library_paths = util::concat(_context->options().library_paths, library_paths); + + auto path = util::findInPaths(filename, library_paths); + if ( ! path ) { + HILTI_DEBUG(logging::debug::Compiler, fmt("Failed to find module '%s' in search paths:", filename)); + for ( const auto& p : library_paths ) + HILTI_DEBUG(logging::debug::Compiler, fmt(" %s", p)); + + return result::Error(fmt("cannot find file")); + } + + auto uid = _parseSource(*path, scope, process_extension); + if ( ! uid ) + return uid; + + if ( uid->id != id ) + return result::Error( + util::fmt("file %s does not contain expected module %s (but %s)", path->native(), id, uid->id)); + + return uid; +} + +Result ASTContext::_parseSource(const hilti::rt::filesystem::path& path, const std::optional& scope, + std::optional process_extension) { + util::timing::Collector _("hilti/compiler/parser"); + + std::ifstream in; + in.open(path); + + if ( ! in ) + return result::Error(fmt("cannot open source file %s", path)); + + auto plugin = plugin::registry().pluginForExtension(path.extension()); + + if ( ! (plugin && plugin->get().parse) ) + return result::Error(fmt("no plugin provides support for importing *%s files", path.extension().native())); + + auto dbg_message = fmt("parsing file %s as %s code", path, plugin->get().component); + + if ( plugin->get().component != "HILTI" ) + dbg_message += fmt(" (%s)", plugin->get().component); + + HILTI_DEBUG(logging::debug::Compiler, dbg_message); + + auto builder = Builder(this); + auto module = (*plugin->get().parse)(&builder, in, path); + if ( ! module ) + return module.error(); + + if ( module && ! (*module)->id() ) + return result::Error(fmt("module in %s does not have an ID", path.native())); + + if ( scope ) + (*module)->setScopePath(*scope); + + if ( process_extension ) + (*module)->setProcessExtension(*process_extension); + + return _addModuleToAST(std::move(*module)); +} + +module::UID ASTContext::_addModuleToAST(ModulePtr module) { + auto uid = module->uid(); + + _modules_by_uid[uid] = module; + _modules_by_path[uid.path.native()] = module; + _modules_by_id_and_scope[std::make_pair(uid.id, module->scopePath())] = module; + + _root->addChild(std::move(module)); + return uid; +} + +template +Result _runHook(bool* modified, const Plugin& plugin, PluginMember hook, const std::string& description, + const Args&... args) { + if ( ! (plugin.*hook) ) + return Nothing(); + + auto msg = fmt("[%s] %s", plugin.component, description); + + HILTI_DEBUG(logging::debug::Compiler, msg); + if ( (*(plugin.*hook))(args...) ) { + *modified = true; + HILTI_DEBUG(logging::debug::Compiler, " -> modified"); + } + + if ( logger().errors() ) + return result::Error("aborting due to errors during " + description); + + return Nothing(); +} + +Result ASTContext::processAST() { + if ( _resolved ) + return Nothing(); + + _rebuild_scopes = true; + + for ( const auto& plugin : plugin::registry().plugins() ) { + if ( auto rc = _validate(plugin, true); ! rc ) + return rc; + + if ( auto rc = _resolve(plugin) ) + return rc; + + if ( ! _context->options().skip_validation ) { + if ( auto rc = _validate(plugin, false); ! rc ) + return rc; + } + + if ( auto rc = _optimize(plugin); ! rc ) + return rc; + + if ( ! _context->options().skip_validation ) { + if ( auto rc = _validate(plugin, false); ! rc ) + return rc; + } + + // TODO: Transform + } + + return Nothing(); +} + +Result ASTContext::_resolve(const Plugin& plugin) { + HILTI_DEBUG(logging::debug::Compiler, fmt("resolving units with plugin %s", plugin.component)) + + logging::DebugPushIndent _(logging::debug::Compiler); + + _dumpAST(logging::debug::AstOrig, plugin, "Original AST", 0); + _saveIterationAST(plugin, "AST before first iteration", 0); + + int round = 1; + bool modified = true; + + auto builder = Builder(this); + + while ( modified ) { + HILTI_DEBUG(logging::debug::Compiler, fmt("processing ASTs, round %d", round)); + logging::DebugPushIndent _(logging::debug::Compiler); + + for ( const auto& i : hilti::visitor::PreOrder().walk(_root) ) { + assert(i); // walk() should not give us null pointer children. + i->clearErrors(); + } + + if ( _rebuild_scopes ) { + HILTI_DEBUG(logging::debug::Compiler, "building scopes"); + + for ( const auto& i : hilti::visitor::PreOrder().walk(_root) ) + i->clearScope(); + + if ( auto rc = _runHook(&modified, plugin, &Plugin::ast_build_scopes, "building scopes", &builder, _root); + ! rc ) + return rc.error(); + } + + modified = false; + + if ( auto rc = _runHook(&modified, plugin, &Plugin::ast_normalize, "normalizing", &builder, _root); ! rc ) + return rc.error(); + + if ( auto rc = _runHook(&modified, plugin, &Plugin::ast_coerce, "coercing", &builder, _root); ! rc ) + return rc.error(); + + if ( auto rc = _runHook(&modified, plugin, &Plugin::ast_resolve, "resolving", &builder, _root); ! rc ) + return rc.error(); + + _dumpAST(logging::debug::AstResolved, plugin, "AST after resolving", round); + _saveIterationAST(plugin, "AST after resolving", round); + + if ( ++round >= 50 ) + logger().internalError("hilti::Unit::compile() didn't terminate, AST keeps changing"); + } + + _dumpAST(logging::debug::AstFinal, plugin, "Final AST", round); + _dumpDeclarations(plugin); + _saveIterationAST(plugin, "Final AST", round); + +#if 0 + // TODO + if ( _driver_options.dump_code ) + dumpUnit(*u); // may be overwritten again later after optimization +#endif + + HILTI_DEBUG(logging::debug::Compiler, "finalized AST"); + _resolved = true; + +#if 0 + // TODO: Where to call these now? + hookNewASTPostCompilation(); + + if ( auto rc = hookCompilationFinished(plugin); ! rc ) + return augmentError(rc.error()); +#endif + + return Nothing(); +} + +Result ASTContext::_optimize(const Plugin& plugin) { return Nothing(); } + +Result ASTContext::_validate(const Plugin& plugin, bool pre_resolve) { return Nothing(); } + +void ASTContext::_dumpAST(const logging::DebugStream& stream, const Plugin& plugin, const std::string& prefix, + int round) { + if ( ! logger().isEnabled(stream) ) + return; + + std::string r; + + if ( round > 0 ) + r = fmt(" (round %d)", round); + + HILTI_DEBUG(stream, fmt("# [%s] %s%s", plugin.component, prefix, r)); + detail::renderNode(root(), stream, true); +} + +void ASTContext::_dumpAST(std::ostream& stream, const Plugin& plugin, const std::string& prefix, int round) { + std::string r; + + if ( round > 0 ) + r = fmt(" (round %d)", round); + + stream << fmt("# [%s] %s%s\n", plugin.component, prefix, r); + detail::renderNode(root(), stream, true); +} + +void ASTContext::dumpAST(const logging::DebugStream& stream, const std::string& prefix) { + if ( ! logger().isEnabled(stream) ) + return; + + HILTI_DEBUG(stream, fmt("# %s\n", prefix)); + detail::renderNode(root(), stream, true); +} + +void ASTContext::_dumpDeclarations(const Plugin& plugin) { + if ( ! logger().isEnabled(logging::debug::AstDeclarations) ) + return; + + logger().debugSetIndent(logging::debug::AstDeclarations, 0); + HILTI_DEBUG(logging::debug::AstDeclarations, fmt("# [%s]", plugin.component)); + + auto nodes = visitor::RangePreOrder(root()); + for ( auto i = nodes.begin(); i != nodes.end(); ++i ) { + auto decl = (*i)->tryAs(); + if ( ! decl ) + continue; + + logger().debugSetIndent(logging::debug::AstDeclarations, i.depth() - 1); + HILTI_DEBUG(logging::debug::AstDeclarations, + fmt("- %s \"%s\" (%s)", ID((*i)->typename_()).local(), decl->id(), decl->canonicalID())); + } +} + +void ASTContext::_saveIterationAST(const Plugin& plugin, const std::string& prefix, int round) { + if ( ! logger().isEnabled(logging::debug::AstDumpIterations) ) + return; + + std::ofstream out(fmt("ast-%s-%d.tmp", plugin.component, round)); + _dumpAST(out, plugin, prefix, round); +} + +void ASTContext::_saveIterationAST(const Plugin& plugin, const std::string& prefix, const std::string& tag) { + if ( ! logger().isEnabled(logging::debug::AstDumpIterations) ) + return; + + std::ofstream out(fmt("ast-%s-%s.tmp", plugin.component, tag)); + _dumpAST(out, plugin, prefix, 0); +} + +std::vector ASTContext::dependencies(const module::UID& uid, bool recursive) const { + // TODO + assert(false && "not implemented yet"); + return {}; +} + +#if 0 + + +Result Unit::transformAST(const Plugin& plugin) { + if ( ! _module ) + return Nothing(); + + bool modified = false; + runHook(&modified, plugin, _module->as(), _extension, &Plugin::ast_transform, + fmt("transforming module %s", uniqueID()), context(), _module, this); + + return Nothing(); +} + +static node::ErrorPriority _recursiveValidateAST(const NodePtr& n, Location closest_location, node::ErrorPriority prio, + int level, std::vector* errors) { + if ( n->location() ) + closest_location = n->location(); + + if ( ! n->pruneWalk() ) { + auto oprio = prio; + for ( const auto& c : n->children() ) { + if ( c ) + prio = std::max(prio, _recursiveValidateAST(c, closest_location, oprio, level + 1, errors)); + } + } + + auto errs = n->errors(); + auto nprio = prio; + for ( auto& err : errs ) { + if ( ! err.location && closest_location ) + err.location = closest_location; + + if ( err.priority > prio ) + errors->push_back(err); + + nprio = std::max(nprio, err.priority); + } + + return nprio; +} + +static void _reportErrors(const std::vector& errors) { + // We only report the highest priority error category. + std::set reported; + + auto prios = {node::ErrorPriority::High, node::ErrorPriority::Normal, node::ErrorPriority::Low}; + + for ( auto p : prios ) { + for ( const auto& e : errors ) { + if ( e.priority != p ) + continue; + + if ( reported.find(e) == reported.end() ) { + logger().error(e.message, e.context, e.location); + reported.insert(e); + } + } + + if ( reported.size() ) + break; + } +} + +bool Unit::_collectErrors() { + std::vector errors; + _recursiveValidateAST(_module, Location(), node::ErrorPriority::NoError, 0, &errors); + + if ( errors.size() ) { + _reportErrors(errors); + return false; + } + + return true; +} + +void Unit::_destroyModule() { + if ( ! _module ) + return; + + _module->destroyChildren(); + _module = {}; +} + +Result Driver::_transformUnitsWithPlugin(const Plugin& plugin, + const std::vector>& units) { + if ( ! plugin.ast_transform ) + return Nothing(); + + HILTI_DEBUG(logging::debug::Compiler, + fmt("transforming units with plugin %s: %s", plugin.component, + util::join(util::transform(units, [](const auto& u) { return u->uniqueID(); }), ", "))); + + logging::DebugPushIndent _(logging::debug::Compiler); + + for ( const auto& unit : units ) { + if ( auto rc = unit->transformAST(plugin); ! rc ) + return rc; + + unit->setResolved(false); + context()->cacheUnit(unit); + + _dumpAST(unit, logging::debug::AstTransformed, plugin, "Transformed AST", 0); + _saveIterationAST(unit, plugin, "Transformed AST", 0); + + if ( logger().isEnabled(logging::debug::AstPrintTransformed) ) + hilti::print(std::cout, unit->module()); + + if ( logger().errors() ) + return result::Error("aborting after errors"); + } + + return Nothing(); +} + +Result Driver::_optimizeUnits() { + return Nothing(); +#if 0 // TODO + if ( ! _driver_options.global_optimizations ) + return Nothing(); + + HILTI_DEBUG(logging::debug::Driver, "performing global transformations"); + + Optimizer opt(_hlts); + opt.run(); + + return Nothing(); +#endif +} + +#endif diff --git a/hilti/toolchain/src/ast/dispatcher.cc b/hilti/toolchain/src/ast/dispatcher.cc index 40cc17f4f..b3a5e28d3 100644 --- a/hilti/toolchain/src/ast/dispatcher.cc +++ b/hilti/toolchain/src/ast/dispatcher.cc @@ -5,6 +5,7 @@ using namespace hilti; +HILTI_NODE_IMPLEMENTATION_0(ASTRoot); HILTI_NODE_IMPLEMENTATION_0(Attribute); HILTI_NODE_IMPLEMENTATION_0(AttributeSet); HILTI_NODE_IMPLEMENTATION_0(Ctor); diff --git a/hilti/toolchain/src/ast/expressions/id.cc b/hilti/toolchain/src/ast/expressions/name.cc similarity index 90% rename from hilti/toolchain/src/ast/expressions/id.cc rename to hilti/toolchain/src/ast/expressions/name.cc index fffcee047..454e26e97 100644 --- a/hilti/toolchain/src/ast/expressions/id.cc +++ b/hilti/toolchain/src/ast/expressions/name.cc @@ -3,12 +3,14 @@ #include #include -#include +#include #include using namespace hilti; -const TypePtr& expression::ResolvedID::type() const { +#if 0 +const QualifiedTypePtr& expression::Name::type() const { + assert(false && "TODO"); struct Visitor : hilti::visitor::PreOrder, Visitor> { result_t operator()(const declaration::Constant& c) { return c.type(); } result_t operator()(const declaration::Expression& e) { return e.expression().type(); } @@ -20,16 +22,14 @@ const TypePtr& expression::ResolvedID::type() const { result_t operator()(const declaration::Type& t) { return t.type(); } }; - if ( ! declarationRef() ) - return type::auto_; - if ( const auto& x = Visitor().dispatch(*declarationRef()) ) return *x; logger().internalError(util::fmt("unsupported declaration type %s", declaration().typename_()), *this); } -bool expression::ResolvedID::isConstant() const { +bool expression::Name::isConstant() const { + assert(false && "TODO"); struct Visitor : hilti::visitor::PreOrder { result_t operator()(const declaration::Constant& c) { return true; } // NOLINT result_t operator()(const declaration::Expression& e) { return e.expression().isConstant(); } @@ -48,3 +48,4 @@ bool expression::ResolvedID::isConstant() const { logger().internalError(util::fmt("unsupported declaration type %s", declaration().typename_()), *this); } +#endif diff --git a/hilti/toolchain/src/ast/module.cc b/hilti/toolchain/src/ast/module.cc index ee483e220..c2247ac4f 100644 --- a/hilti/toolchain/src/ast/module.cc +++ b/hilti/toolchain/src/ast/module.cc @@ -11,23 +11,23 @@ Module::~Module() = default; std::string Module::_render() const { return ""; } -#if 0 -// TODO void Module::clear() { - auto v = visitor::PostOrder<>(); + assert(false && "Do we need this?? Need reimplementation"); +#if 0 + auto v = visitor::PostOrder(); // We fully walk the AST here in order to break any reference cycles it may // contain. Start at child 1 to leave ID in place. for ( size_t i = 0; i < children().size(); i++ ) { - for ( auto j : v.walk(&children()[i]) ) - j.node.reset(); + for ( auto j : v.walk(children()[i]) ) + j->reset(); } clearChildren(); addChild(statement::Block::create({}, meta())); clearDocumentation(); -} #endif +} std::shared_ptr Module::moduleProperty(const ID& id) const { for ( const auto& d : declarations() ) { diff --git a/hilti/toolchain/src/ast/node.cc b/hilti/toolchain/src/ast/node.cc index 86706186e..e11eceb75 100644 --- a/hilti/toolchain/src/ast/node.cc +++ b/hilti/toolchain/src/ast/node.cc @@ -3,12 +3,21 @@ #include #include +#include +#include +#include +#include +#include +#include #include #include #include #include #include +#include "ast/declarations/constant.h" +#include "ast/expressions/ctor.h" + using namespace hilti; Node::~Node() = default; @@ -29,7 +38,7 @@ std::string Node::render(bool include_location) const { sprops = util::fmt(" <%s>", util::join(props, " ")); // Prettify the name a bit. - auto name = typename_(); + auto name = util::demangle(typename_()); name = util::replace(name, "hilti::", ""); if ( util::startsWith(name, "detail::") ) @@ -37,7 +46,7 @@ std::string Node::render(bool include_location) const { auto location = (include_location && meta().location()) ? util::fmt(" (%s)", meta().location().render(true)) : ""; auto prune = (pruneWalk() ? " (prune)" : ""); - auto no_inherit_scope = (inheritScope() ? " (no-inherit-scope)" : ""); + auto no_inherit_scope = (inheritScope() ? "" : " (no-inherit-scope)"); #if 0 // TODO: Move into the corresponding sub-classes. @@ -100,7 +109,6 @@ void Node::destroyChildren() { _destroyChildrenRecursively(this); } NodePtr node::clone(ASTContext* ctx, const NodePtr& n) { auto clone = n->_clone(ctx); - clone->clearParent(); return clone; } @@ -114,3 +122,75 @@ NodePtr node::deepClone(ASTContext* ctx, const NodePtr& n) { return clone_; } + +// Helper looking up an ID inside a node's direct scope, applying visibility rules. +static std::pair>> _lookupID(const ID& id, const Node* n) { + assert(n->scope()); + auto resolved = n->scope()->lookupAll(id); + + if ( resolved.empty() ) { + auto err = result::Error(util::fmt("unknown ID '%s'", id)); + return std::make_pair(false, std::move(err)); + } + + if ( resolved.size() > 1 ) { + auto err = result::Error(util::fmt("ID '%s' is ambiguous", id)); + return std::make_pair(true, std::move(err)); + } + + const auto& r = resolved.front(); + assert(r.node); + const auto& d = r.node; + + if ( d->isA() || d->isA() ) { + auto err = result::Error(util::fmt("cannot refer to module '%s' through an ID in this context", id)); + return std::make_pair(true, std::move(err)); + } + + if ( r.external && d->linkage() != declaration::Linkage::Public ) { + bool ok = false; + + // We allow access to types (and type-derived constants) to + // make it less cumbersome to define external hooks. + + if ( d->isA() ) + ok = true; + + if ( auto c = d->tryAs() ) { + if ( auto ctor = c->value()->tryAs(); ctor && ctor->ctor()->isA() ) + ok = true; + } + + if ( ! ok ) { + auto err = result::Error(util::fmt("'%s' has not been declared public", id)); + return std::make_pair(true, std::move(err)); + } + } + + auto x = std::make_pair(resolved.front().node, ID(resolved.front().qualified)); + return std::make_pair(true, std::move(x)); +} + +Result> Node::lookupID(const ID& id, const std::string_view& what) const { + for ( const auto* n = this; n; n = n->parent() ) { + if ( ! n->scope() ) + continue; + + auto [stop, resolved] = _lookupID(id, n); + if ( resolved ) + // Found it. + return resolved; + + if ( stop ) + // Pass back error. + return std::move(resolved); + + if ( ! n->inheritScope() ) { + // Advance to module scope directly. + while ( n->parent() && (! n->parent()->isA()) ) + n = n->parent(); + } + } + + return result::Error(util::fmt("unknown ID '%s'", id)); +} diff --git a/hilti/toolchain/src/ast/scope-lookup.cc b/hilti/toolchain/src/ast/scope-lookup.cc deleted file mode 100644 index 37036228a..000000000 --- a/hilti/toolchain/src/ast/scope-lookup.cc +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2020-2023 by the Zeek Project. See LICENSE for details. - -#include -#include -#include -#include -#include -#include - -using namespace hilti; - -std::pair>> hilti::scope::detail::lookupID(const ID& id, const Node& n) { - auto resolved = n.scope()->lookupAll(id); - - if ( resolved.empty() ) { - auto err = result::Error(util::fmt("unknown ID '%s'", id)); - return std::make_pair(false, std::move(err)); - } - - if ( resolved.size() == 1 ) { - auto& r = resolved.front(); - - if ( ! r.node ) { - auto err = result::Error(util::fmt("internal error: scope's entry for ID '%s' is no longer valid", id)); - return std::make_pair(false, std::move(err)); - } - - if ( r.node->isA() ) { - // Explicit stop of scope traversal. - auto err = result::Error(util::fmt("unknown ID '%s'", id)); - return std::make_pair(true, std::move(err)); - } - - if ( auto d = r.node->template tryAs() ) { - if ( d->isA() || d->isA() ) { - auto err = result::Error(util::fmt("cannot use module '%s' as an ID", id)); - return std::make_pair(true, std::move(err)); - } - - if ( r.external && d->linkage() != declaration::Linkage::Public ) { - bool ok = false; - - // We allow access to types (and type-derived constants) to - // make it less cumbersome to define external hooks. - - if ( d->isA() ) - ok = true; - - if ( auto c = d->tryAs() ) { - if ( auto ctor = c->value().tryAs(); ctor && ctor->ctor().isA() ) - ok = true; - } - - if ( ! ok ) { - auto err = result::Error(util::fmt("'%s' has not been declared public", id)); - return std::make_pair(true, std::move(err)); - } - } - - auto x = std::make_pair(resolved.front().node, ID(resolved.front().qualified)); - return std::make_pair(true, std::move(x)); - } - - if ( r.node->isA() ) { - // Likely a node delete through assignment. - auto err = result::Error("node has been deleted"); - return std::make_pair(false, std::move(err)); - } - - logger().internalError(util::fmt("ID '%s' resolved to something else than a declaration (%s)", id, - resolved.front().node->typename_()), - resolved.front().node->meta().location()); - } - - auto err = result::Error(util::fmt("ID '%s' is ambiguous", id)); - return std::make_pair(true, std::move(err)); -} diff --git a/hilti/toolchain/src/ast/scope.cc b/hilti/toolchain/src/ast/scope.cc index f12655bfe..feb9e1736 100644 --- a/hilti/toolchain/src/ast/scope.cc +++ b/hilti/toolchain/src/ast/scope.cc @@ -13,16 +13,10 @@ #include - using namespace hilti; -void Scope::insert(const ID& id, NodePtr&& n) { _items[id].insert(std::move(n)); } - -void Scope::insert(NodePtr&& n) { - assert(n && n->isA()); - const auto& d = n->as(); - insert(d->id(), std::move(n)); -} +void Scope::insert(const ID& id, DeclarationPtr&& d) { _items[id].insert(std::move(d)); } +void Scope::insert(DeclarationPtr&& d) { _items[d->id()].insert(d); } void Scope::insertNotFound(const ID& id) { _items[std::string(id)] = {}; } @@ -44,9 +38,8 @@ static auto createRefs(const NodeSet& refs, const std::string& id, bool external std::vector result; result.reserve(refs.size()); - std::transform(refs.begin(), refs.end(), std::back_inserter(result), [&](const auto& n) { - return Scope::Referee{.node = n, .qualified = id, .external = external}; - }); + std::transform(refs.begin(), refs.end(), std::back_inserter(result), + [&](const auto& n) { return Scope::Referee{.node = n, .qualified = id, .external = external}; }); return result; } @@ -70,10 +63,10 @@ std::vector Scope::_findID(const Scope* scope, const ID& id, boo return createRefs(i->second, h, external); for ( const auto& v : (*i).second ) { - Scope* scope_ = v->scope().get(); + Scope* scope_ = v->scope(); if ( auto m = v->tryAs() ) - scope_ = m->module_()->scope().get(); + scope_ = m->module_()->scope(); auto e = v->isA(); diff --git a/hilti/toolchain/src/compiler/codegen/codegen.cc b/hilti/toolchain/src/compiler/codegen/codegen.cc index 820720126..c25226627 100644 --- a/hilti/toolchain/src/compiler/codegen/codegen.cc +++ b/hilti/toolchain/src/compiler/codegen/codegen.cc @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -16,6 +15,8 @@ #include #include +#include "base/timing.h" + using namespace hilti; using util::fmt; @@ -26,7 +27,7 @@ namespace { // This visitor will only receive AST nodes of the first two levels (i.e., // the module and its declarations). -struct GlobalsVisitor : hilti::visitor::PreOrder { +struct GlobalsVisitor : hilti::visitor::PreOrder { explicit GlobalsVisitor(CodeGen* cg, bool include_implementation) : cg(cg), include_implementation(include_implementation) {} @@ -36,14 +37,20 @@ struct GlobalsVisitor : hilti::visitor::PreOrder { CodeGen* cg; bool include_implementation; +#if 0 std::vector globals; std::vector constants; +#endif - // Helper creating function to access dynamically allocated globals, if needed. - void createGlobalsAccessorFunction(const Node& module, const ID& module_id, cxx::Unit* unit) { - if ( ! cg->options().cxx_enable_dynamic_globals ) - // Access to globals is direct, no need for function. - return; + static void addDeclarations(CodeGen* cg, const ModulePtr& module, const ID& module_id, cxx::Unit* unit, + bool include_implementation) { +#if 0 + auto v = GlobalsVisitor(cg, include_implementation); + + v.dispatch(module); + + for ( const auto& i : module.children() ) + v.dispatch(i); auto ns = cxx::ID(cg->options().cxx_namespace_intern, module_id); auto t = cxx::declaration::Type{{ns, "__globals_t"}, cxxGlobalsType()}; @@ -135,58 +142,10 @@ struct GlobalsVisitor : hilti::visitor::PreOrder { unit->add(body_decl); unit->add(body_impl); } - - // Helpers creating function destroying the module's globals, if needed. - void createDestroyGlobals(const Node& module, const ID& module_id, cxx::Unit* unit) { - if ( cg->options().cxx_enable_dynamic_globals ) - // Will be implicitly destroyed at termination by the runtime. - return; - - auto ns = cxx::ID(cg->options().cxx_namespace_intern, module_id); - auto id = cxx::ID{ns, "__destroy_globals"}; - - auto body_decl = - cxx::declaration::Function{.result = cg->compile(type::void_, codegen::TypeUsage::FunctionResult), - .id = id, - .args = {{.id = "ctx", .type = "::hilti::rt::Context*"}}, - .linkage = "extern"}; - - auto body = cxx::Block(); - cg->pushCxxBlock(&body); - - for ( const auto& g : globals ) - body.addStatement(fmt("::%s::%s.reset();", ns, g.id.local())); - - auto body_impl = cxx::Function{.declaration = body_decl, .body = std::move(body)}; - unit->add(body_decl); - unit->add(body_impl); - } - - static void addDeclarations(CodeGen* cg, const Node& module, const ID& module_id, cxx::Unit* unit, - bool include_implementation) { - auto v = GlobalsVisitor(cg, include_implementation); - - v.dispatch(module); - - for ( const auto& i : module.children() ) - v.dispatch(i); - - for ( const auto& c : v.constants ) - unit->add(c); - - if ( ! v.globals.empty() ) { - v.createGlobalsAccessorFunction(module, module_id, unit); - - if ( include_implementation ) { - unit->setUsesGlobals(); - v.createInitGlobals(module, module_id, unit); - v.createDestroyGlobals(module, module_id, unit); - } - else - v.createGlobalsDeclarations(module, module_id, unit); - } +#endif } +#if 0 cxx::type::Struct cxxGlobalsType() const { std::vector fields; @@ -228,19 +187,22 @@ struct GlobalsVisitor : hilti::visitor::PreOrder { cg->unit()->prioritizeType(dt->id); } } + +#endif }; // This visitor will only receive AST nodes of the first two levels (i.e., // the module and its declarations). -struct Visitor : hilti::visitor::PreOrder { - Visitor(CodeGen* cg, const Scope& module_scope, cxx::Unit* unit, hilti::Unit* hilti_unit) +struct Visitor : hilti::visitor::PreOrder { + Visitor(CodeGen* cg, const Scope* module_scope, cxx::Unit* unit, hilti::Unit* hilti_unit) : cg(cg), unit(unit), hilti_unit(hilti_unit), module_scope(module_scope) {} CodeGen* cg; cxx::Unit* unit; hilti::Unit* hilti_unit; + const Scope* module_scope; +#if 0 std::optional module; - const Scope& module_scope; // Top-level nodes. @@ -626,6 +588,7 @@ struct Visitor : hilti::visitor::PreOrder { cg->unit()->addPreInitialization(call_preinit_func); } } +#endif }; } // anonymous namespace @@ -655,13 +618,13 @@ codegen::TypeUsage CodeGen::parameterKindToTypeUsage(declaration::parameter::Kin util::cannot_be_reached(); } -cxx::declaration::Function CodeGen::compile(const ID& id, type::Function ft, declaration::Linkage linkage, - function::CallingConvention cc, const std::optional& fattrs, - std::optional namespace_) { +cxx::declaration::Function CodeGen::compile(const ID& id, const NodeDerivedPtr& ft, + declaration::Linkage linkage, function::CallingConvention cc, + const AttributeSetPtr& fattrs, std::optional namespace_) { auto result_ = [&]() { - auto rt = compile(ft.result().type(), codegen::TypeUsage::FunctionResult); + auto rt = compile(ft->result(), codegen::TypeUsage::FunctionResult); - switch ( ft.flavor() ) { + switch ( ft->flavor() ) { case hilti::type::function::Flavor::Hook: case hilti::type::function::Flavor::Method: case hilti::type::function::Flavor::Standard: return rt; @@ -669,9 +632,9 @@ cxx::declaration::Function CodeGen::compile(const ID& id, type::Function ft, dec } }; - auto param_ = [&](auto p) { - auto t = compile(p.type(), parameterKindToTypeUsage(p.kind())); - return cxx::declaration::Argument{.id = cxx::ID(p.id()), .type = std::move(t)}; + auto param_ = [&](const auto& p) { + auto t = compile(p->type(), parameterKindToTypeUsage(p->kind())); + return cxx::declaration::Argument{.id = cxx::ID(p->id()), .type = std::move(t)}; }; auto linkage_ = [&]() { @@ -693,7 +656,7 @@ cxx::declaration::Function CodeGen::compile(const ID& id, type::Function ft, dec if ( linkage == declaration::Linkage::Struct ) { // For method implementations, check if the ID is fully scoped with // the module name; if so, remove. - if ( id.sub(0) == _hilti_unit->id() ) + if ( id.sub(0) == _hilti_unit->uid() ) cxx_id = id.sub(1, -1); } @@ -702,38 +665,40 @@ cxx::declaration::Function CodeGen::compile(const ID& id, type::Function ft, dec if ( namespace_ && *namespace_ ) ns += *namespace_; else - ns += _hilti_unit->id(); + ns += _hilti_unit->uid(); return cxx::declaration::Function{.result = result_(), .id = {ns, cxx_id}, - .args = node::transform(ft.parameters(), param_), + .args = node::transform(ft->parameters(), param_), .linkage = linkage_()}; } -std::vector CodeGen::compileCallArguments(const node::Range& args, - const node::Set& params) { - auto kinds = node::transform(params, [](auto& x) { return x.kind(); }); - - std::vector x; - x.reserve(args.size()); - for ( auto i = 0U; i < params.size(); i++ ) { - Expression arg = (i < args.size() ? args[i] : *params[i].default_()); - x.emplace_back(compile(arg, params[i].kind() == declaration::parameter::Kind::InOut)); - } - - return x; -} - +// std::vector CodeGen::compileCallArguments(const node::Range& args, +// const node::Set& params) { +// auto kinds = node::transform(params, [](const auto& x) { return x->kind(); }); +// +// std::vector x; +// x.reserve(args.size()); +// for ( auto i = 0U; i < params.size(); i++ ) { +// ExpressionPtr arg = (i < args.size() ? args[i] : params[i]->default_()); +// x.emplace_back(compile(arg, params[i]->kind() == declaration::parameter::Kind::InOut)); +// } +// +// return x; +// } +// std::vector CodeGen::compileCallArguments(const node::Range& args, const node::Range& params) { assert(args.size() == params.size()); - auto kinds = node::transform(params, [](auto& x) { return x.kind(); }); + auto kinds = node::transform(params, [](auto& x) { return x->kind(); }); std::vector x; x.reserve(args.size()); - for ( auto i = 0U; i < args.size(); i++ ) - x.emplace_back(compile(args[i], params[i].kind() == declaration::parameter::Kind::InOut)); + for ( auto i = 0U; i < args.size(); i++ ) { + ExpressionPtr arg = (i < args.size() ? args[i] : params[i]->default_()); + x.emplace_back(compile(args[i], params[i]->kind() == declaration::parameter::Kind::InOut)); + } return x; } @@ -743,11 +708,11 @@ Result CodeGen::compileModule(const ModulePtr& root, hilti::Unit* hil _cxx_unit = std::make_unique(context()); _hilti_unit = hilti_unit; - auto v = Visitor(this, *root.scope(), _cxx_unit.get(), hilti_unit); + auto v = Visitor(this, root->scope(), _cxx_unit.get(), hilti_unit); v.dispatch(root); - for ( const auto& i : root.children() ) + for ( const auto& i : root->children() ) v.dispatch(i); GlobalsVisitor::addDeclarations(this, root, ID(std::string(_cxx_unit->moduleID())), _cxx_unit.get(), diff --git a/hilti/toolchain/src/compiler/codegen/coercions.cc b/hilti/toolchain/src/compiler/codegen/coercions.cc index 5e105056c..f00019cdc 100644 --- a/hilti/toolchain/src/compiler/codegen/coercions.cc +++ b/hilti/toolchain/src/compiler/codegen/coercions.cc @@ -4,6 +4,9 @@ #include #include +#include +#include +#include #include #include #include @@ -15,13 +18,15 @@ using namespace hilti::detail; namespace { -struct Visitor : public hilti::visitor::PreOrder, type::Visitor { - Visitor(CodeGen* cg, const cxx::Expression& expr, const TypePtr& dst) : cg(cg), expr(expr), dst(dst) {} +struct Visitor : public hilti::visitor::PreOrder { + Visitor(CodeGen* cg, const cxx::Expression& expr, const QualifiedTypePtr& dst) : cg(cg), expr(expr), dst(dst) {} + CodeGen* cg; const cxx::Expression& expr; - const TypePtr& dst; + const QualifiedTypePtr& dst; - std::optional _result; + std::optional result; +#if 0 result_t operator()(const type::Bytes& src, type::Visitor::position_t&) override { if ( auto t = dst.tryAs() ) @@ -246,28 +251,28 @@ struct Visitor : public hilti::visitor::PreOrder, type::Visitor { logger().internalError( fmt("codegen: unexpected type coercion from value reference to %s", dst.typename_())); } +#endif }; } // anonymous namespace -cxx::Expression CodeGen::coerce(const cxx::Expression& e, const TypePtr& src, const TypePtr& dst) { - if ( type::sameExceptForConstness(src, dst) ) +cxx::Expression CodeGen::coerce(const cxx::Expression& e, const QualifiedTypePtr& src, const QualifiedTypePtr& dst) { + if ( src->type() == dst->type() ) // If only difference is constness, nothing to do. return e; - if ( auto t = dst.tryAs(); t && ! src.isA() ) + if ( auto t = dst->tryAs(); t && ! src->isA() ) return fmt("%s(%s)", compile(dst, codegen::TypeUsage::Storage), e); - if ( auto t = dst.tryAs() ) + if ( auto t = dst->tryAs() ) return fmt("%s(%s)", compile(dst, codegen::TypeUsage::Storage), e); - if ( dst.tryAs() && ! type::isReferenceType(src) ) + if ( dst->tryAs() && ! type::isReferenceType(src) ) return e; auto v = Visitor(this, e, dst); - v.dispatch(src); - if ( auto nt = v._result ) + if ( auto nt = hilti::visitor::dispatch(v, src, [](const auto& v) { return v.result; }) ) return *nt; - logger().internalError(fmt("codegen: type %s unhandled for coercion", src.typename_())); + logger().internalError(fmt("codegen: type %s unhandled for coercion", src->typename_())); } diff --git a/hilti/toolchain/src/compiler/codegen/ctors.cc b/hilti/toolchain/src/compiler/codegen/ctors.cc index abba3ac03..317a1df60 100644 --- a/hilti/toolchain/src/compiler/codegen/ctors.cc +++ b/hilti/toolchain/src/compiler/codegen/ctors.cc @@ -16,10 +16,14 @@ using hilti::rt::fmt; namespace { -struct Visitor : hilti::visitor::PreOrder { +struct Visitor : hilti::visitor::PreOrder { explicit Visitor(CodeGen* cg) : cg(cg) {} + CodeGen* cg; + std::optional result; + +#if 0 result_t operator()(const ctor::Address& n) { return fmt("::hilti::rt::Address(\"%s\")", n.value()); } result_t operator()(const ctor::Bool& n) { return fmt("::hilti::rt::Bool(%s)", n.value() ? "true" : "false"); } @@ -224,13 +228,15 @@ struct Visitor : hilti::visitor::PreOrder { result_t operator()(const ctor::WeakReference& n) { return fmt("::hilti::rt::WeakReference<%s>()", cg->compile(*n.dereferencedType(), codegen::TypeUsage::Ctor)); } +#endif }; } // anonymous namespace -cxx::Expression CodeGen::compile(const hilti::Ctor& c, bool lhs) { - if ( auto x = Visitor(this).dispatch(c) ) - return lhs ? _makeLhs(*x, c.type()) : *x; +cxx::Expression CodeGen::compile(const CtorPtr& c, bool lhs) { + auto v = Visitor(this); + if ( auto x = hilti::visitor::dispatch(v, c, [](const auto& v) { return v.result; }) ) + return lhs ? _makeLhs(*x, c->type()) : *x; - logger().internalError(fmt("ctor %s failed to compile", to_node(c).typename_()), c); + logger().internalError(fmt("ctor %s failed to compile", c->typename_()), c); } diff --git a/hilti/toolchain/src/compiler/codegen/expressions.cc b/hilti/toolchain/src/compiler/codegen/expressions.cc index 2024e081c..0f7164fe8 100644 --- a/hilti/toolchain/src/compiler/codegen/expressions.cc +++ b/hilti/toolchain/src/compiler/codegen/expressions.cc @@ -15,11 +15,15 @@ using namespace hilti::detail; namespace { -struct Visitor : hilti::visitor::PreOrder { +struct Visitor : hilti::visitor::PreOrder { Visitor(CodeGen* cg, bool lhs) : cg(cg), lhs(lhs) {} + CodeGen* cg; bool lhs; + std::optional result; + +#if 0 result_t operator()(const expression::Assign& n) { return {fmt("%s = %s", cg->compile(n.target(), true), cg->compile(n.source())), cxx::Side::LHS}; } @@ -209,13 +213,15 @@ struct Visitor : hilti::visitor::PreOrder { } result_t operator()(const expression::Void& n) { return ""; } +#endif }; } // anonymous namespace -cxx::Expression CodeGen::compile(const hilti::Expression& e, bool lhs) { - if ( auto x = Visitor(this, lhs).dispatch(e) ) - return lhs ? _makeLhs(*x, e.type()) : *x; +cxx::Expression CodeGen::compile(const ExpressionPtr& e, bool lhs) { + auto v = Visitor(this, lhs); + if ( auto x = hilti::visitor::dispatch(v, e, [](const auto& v) { return v.result; }) ) + return lhs ? _makeLhs(*x, e->type()) : *x; - logger().internalError(fmt("expression failed to compile ('%s' / %s)", e, e.typename_()), e); + logger().internalError(fmt("expression failed to compile ('%s' / %s)", e, e->typename_()), e); } diff --git a/hilti/toolchain/src/compiler/codegen/operators.cc b/hilti/toolchain/src/compiler/codegen/operators.cc index cdcc06771..452f8b902 100644 --- a/hilti/toolchain/src/compiler/codegen/operators.cc +++ b/hilti/toolchain/src/compiler/codegen/operators.cc @@ -1,6 +1,7 @@ // Copyright (c) 2020-2023 by the Zeek Project. See LICENSE for details. #include +#include #include #include #include @@ -8,6 +9,8 @@ #include #include +#include "global.h" + using namespace hilti; using util::fmt; @@ -15,11 +18,15 @@ using namespace hilti::detail; namespace { -struct Visitor : hilti::visitor::PreOrder { +struct Visitor : hilti::visitor::PreOrder { Visitor(CodeGen* cg, bool lhs) : cg(cg), lhs(lhs) {} + CodeGen* cg; bool lhs; + std::optional result; + +#if 0 // Helpers result_t op0(const expression::ResolvedOperatorBase& o, bool lhs = false) { return cg->compile(o.op0(), lhs); } @@ -1184,14 +1191,17 @@ struct Visitor : hilti::visitor::PreOrder { result_t operator()(const operator_::value_reference::Deref& n) { return {fmt("(*%s)", op0(n)), cxx::Side::LHS}; } result_t operator()(const operator_::value_reference::Equal& n) { return fmt("%s == %s", op0(n), op1(n)); } result_t operator()(const operator_::value_reference::Unequal& n) { return fmt("%s != %s", op0(n), op1(n)); } + +#endif }; } // anonymous namespace -cxx::Expression CodeGen::compile(const expression::ResolvedOperator& o, bool lhs) { - if ( auto x = Visitor(this, lhs).dispatch(Expression(o)) ) - return lhs ? _makeLhs(*x, o.type()) : *x; +cxx::Expression CodeGen::compile(const NodeDerivedPtr& o, bool lhs) { + auto v = Visitor(this, lhs); + if ( auto x = hilti::visitor::dispatch(v, o, [](const auto& v) { return v.result; }) ) + return lhs ? _makeLhs(*x, o->type()) : *x; - hilti::render(std::cerr, Expression(o)); + hilti::render(std::cerr, o); logger().internalError(fmt("operator failed to compile: %s", detail::renderOperatorPrototype(o))); } diff --git a/hilti/toolchain/src/compiler/codegen/statements.cc b/hilti/toolchain/src/compiler/codegen/statements.cc index dde312c56..07e191d0a 100644 --- a/hilti/toolchain/src/compiler/codegen/statements.cc +++ b/hilti/toolchain/src/compiler/codegen/statements.cc @@ -11,26 +11,28 @@ using util::fmt; using namespace hilti::detail; -inline auto traceStatement(CodeGen* cg, cxx::Block* b, const Statement& s, bool skip_location = false) { - if ( s.isA() ) +inline auto traceStatement(CodeGen* cg, cxx::Block* b, const StatementPtr& s) { + if ( s->isA() ) return; - if ( cg->options().track_location && s.meta().location() && ! skip_location ) - b->addStatement(fmt(" __location__(\"%s\")", s.meta().location())); + if ( cg->options().track_location && s->meta().location() ) + b->addStatement(fmt(" __location__(\"%s\")", s->meta().location())); if ( cg->options().debug_trace ) - b->addStatement(fmt(R"(HILTI_RT_DEBUG("hilti-trace", "%s: %s"))", s.meta().location(), + b->addStatement(fmt(R"(HILTI_RT_DEBUG("hilti-trace", "%s: %s"))", s->meta().location(), util::escapeUTF8(fmt("%s", s), true))); } namespace { -struct Visitor : hilti::visitor::PreOrder { +struct Visitor : hilti::visitor::PreOrder { Visitor(CodeGen* cg, cxx::Block* b) : cg(cg), block(b) {} + CodeGen* cg; + cxx::Block* block; +#if 0 int level = 0; - cxx::Block* block; void operator()(const statement::Assert& n) { std::string throw_; @@ -378,11 +380,12 @@ struct Visitor : hilti::visitor::PreOrder { block->addStatement("::hilti::rt::detail::yield()"); } +#endif }; } // anonymous namespace -cxx::Block CodeGen::compile(const hilti::Statement& s, cxx::Block* b) { +cxx::Block CodeGen::compile(const StatementPtr& s, cxx::Block* b) { if ( b ) { pushCxxBlock(b); traceStatement(this, b, s); diff --git a/hilti/toolchain/src/compiler/codegen/types.cc b/hilti/toolchain/src/compiler/codegen/types.cc index bcc3ace2b..28d43ba86 100644 --- a/hilti/toolchain/src/compiler/codegen/types.cc +++ b/hilti/toolchain/src/compiler/codegen/types.cc @@ -10,6 +10,8 @@ #include #include +#include "global.h" + using namespace hilti; using namespace hilti::detail; using namespace hilti::detail::codegen; @@ -18,15 +20,16 @@ using util::fmt; namespace { -struct VisitorDeclaration : hilti::visitor::PreOrder, type::Visitor { +struct VisitorDeclaration : hilti::visitor::PreOrder { VisitorDeclaration(CodeGen* cg, util::Cache* cache) : cg(cg), cache(cache) {} CodeGen* cg; util::Cache* cache; std::list dependencies; - std::optional _result; + std::optional result; +#if 0 void addDependency(const TypePtr& t) { for ( auto&& t : cg->typeDependencies(t) ) dependencies.push_back(std::move(t)); @@ -361,9 +364,10 @@ struct VisitorDeclaration : hilti::visitor::PreOrder, _result = cxx::declaration::Type(id, fmt("HILTI_EXCEPTION_NS(%s, %s, %s)", id.local(), base_ns, base_cls), {}, false, false, true); } +#endif }; -struct VisitorStorage : hilti::visitor::PreOrder, type::Visitor { +struct VisitorStorage : hilti::visitor::PreOrder { VisitorStorage(CodeGen* cg, util::Cache* cache, codegen::TypeUsage usage) : cg(cg), cache(cache), usage(usage) {} @@ -371,9 +375,9 @@ struct VisitorStorage : hilti::visitor::PreOrder, type::Vi util::Cache* cache; codegen::TypeUsage usage; - std::optional _result; - + std::optional result; +#if 0 auto typeID(const Node& n) { return n.as().typeID(); } auto cxxID(const Node& n) { return n.as().cxxID(); } @@ -808,16 +812,18 @@ struct VisitorStorage : hilti::visitor::PreOrder, type::Vi else _result = CxxTypes{.base_type = "*"}; } +#endif }; // Visitor returning the ID of static, predefined type information instances for types that provide it. -struct VisitorTypeInfoPredefined : hilti::visitor::PreOrder, type::Visitor { +struct VisitorTypeInfoPredefined : hilti::visitor::PreOrder { VisitorTypeInfoPredefined(CodeGen* cg) : cg(cg) {} CodeGen* cg; - std::optional _result; + std::optional result; +#if 0 result_t operator()(const type::Address& n, type::Visitor::position_t&) override { _result = "::hilti::rt::type_info::address"; } @@ -883,15 +889,18 @@ struct VisitorTypeInfoPredefined : hilti::visitor::PreOrder, type::Visitor { +struct VisitorTypeInfoDynamic : hilti::visitor::PreOrder { VisitorTypeInfoDynamic(CodeGen* cg) : cg(cg) {} + CodeGen* cg; - std::optional _result; + std::optional result; +#if 0 auto typeID(const Node& n) { return n.as().typeID(); } auto cxxID(const Node& n) { return n.as().cxxID(); } @@ -1061,18 +1070,17 @@ struct VisitorTypeInfoDynamic : hilti::visitor::PreOrder base_type; @@ -1087,7 +1095,7 @@ cxx::Type CodeGen::compile(const hilti::TypePtrPtr& t, codegen::TypeUsage usage) if ( base_type ) return std::move(*base_type); - logger().internalError(fmt("codegen: type %s does not support use as storage", to_node(t).render()), t); + logger().internalError(fmt("codegen: type %s does not support use as storage", t->render()), t); break; case codegen::TypeUsage::CopyParameter: @@ -1097,8 +1105,7 @@ cxx::Type CodeGen::compile(const hilti::TypePtrPtr& t, codegen::TypeUsage usage) if ( base_type ) return fmt("%s", *base_type); - logger().internalError(fmt("codegen: type %s does not support use as copy-parameter ", to_node(t).render()), - t); + logger().internalError(fmt("codegen: type %s does not support use as copy-parameter ", t->render()), t); break; case codegen::TypeUsage::InParameter: @@ -1108,8 +1115,7 @@ cxx::Type CodeGen::compile(const hilti::TypePtrPtr& t, codegen::TypeUsage usage) if ( base_type ) return fmt("const %s&", *base_type); - logger().internalError(fmt("codegen: type %s does not support use as in-parameter ", to_node(t).render()), - t); + logger().internalError(fmt("codegen: type %s does not support use as in-parameter ", t->render()), t); break; case codegen::TypeUsage::InOutParameter: @@ -1119,9 +1125,7 @@ cxx::Type CodeGen::compile(const hilti::TypePtrPtr& t, codegen::TypeUsage usage) if ( base_type ) return fmt("%s&", *base_type); - logger().internalError(fmt("codegen: type %s does not support use as inout-parameter ", - to_node(t).render()), - t); + logger().internalError(fmt("codegen: type %s does not support use as inout-parameter ", t->render()), t); break; case codegen::TypeUsage::FunctionResult: @@ -1131,8 +1135,7 @@ cxx::Type CodeGen::compile(const hilti::TypePtrPtr& t, codegen::TypeUsage usage) if ( base_type ) return std::move(*base_type); - logger().internalError(fmt("codegen: type %s does not support use as function result", to_node(t).render()), - t); + logger().internalError(fmt("codegen: type %s does not support use as function result", t->render()), t); break; case codegen::TypeUsage::Ctor: @@ -1142,54 +1145,50 @@ cxx::Type CodeGen::compile(const hilti::TypePtrPtr& t, codegen::TypeUsage usage) if ( x->base_type ) return std::move(*x->base_type); - logger().internalError(fmt("codegen: type %s does not support use as storage", to_node(t).render()), t); + logger().internalError(fmt("codegen: type %s does not support use as storage", t->render()), t); break; case codegen::TypeUsage::None: - logger().internalError(fmt("codegen: type compilation with 'None' usage", to_node(t).render()), t); + logger().internalError(fmt("codegen: type compilation with 'None' usage", t->render()), t); break; default: util::cannot_be_reached(); } } -std::optional CodeGen::typeDefaultValue(const hilti::TypePtrPtr& t) { +std::optional CodeGen::typeDefaultValue(const hilti::QualifiedTypePtr& t) { auto v = VisitorStorage(this, &_cache_types_storage, codegen::TypeUsage::None); - v.dispatch(t); - auto& x = v._result; - + auto x = hilti::visitor::dispatch(v, t, [](const auto& v) { return v.result; }); if ( ! x ) { hilti::render(std::cerr, t); - logger().internalError(fmt("codegen: type %s does not have a visitor", t), t); + logger().internalError(fmt("codegen: type %s does not have a visitor", *t), t); } - return std::move(x->default_); -}; +} -std::list CodeGen::typeDependencies(const hilti::TypePtrPtr& t) { +std::list CodeGen::typeDependencies(const QualifiedTypePtr& t) { VisitorDeclaration v(this, &_cache_types_declarations); v.dispatch(t); return v.dependencies; }; -std::optional CodeGen::typeDeclaration(const hilti::TypePtrPtr& t) { +std::optional CodeGen::typeDeclaration(const QualifiedTypePtr& t) { auto v = VisitorDeclaration(this, &_cache_types_declarations); - v.dispatch(t); - return v._result; + return hilti::visitor::dispatch(v, t, [](const auto& v) { return v.result; }); }; -const CxxTypeInfo& CodeGen::_getOrCreateTypeInfo(const hilti::TypePtrPtr& t) { +const CxxTypeInfo& CodeGen::_getOrCreateTypeInfo(const QualifiedTypePtr& t) { std::stringstream display; - if ( t.typeID() ) + if ( t->type()->typeID() ) // Prefer the bare type name as the display value. - display << *t.typeID(); + display << *t->type()->typeID(); else hilti::print(display, t); if ( display.str().empty() ) logger().internalError(fmt("codegen: type %s does not have a display rendering for type information", - t.typename_()), + t->type()->typename_()), t); // Each module contains all the type information it needs. We put the @@ -1201,8 +1200,8 @@ const CxxTypeInfo& CodeGen::_getOrCreateTypeInfo(const hilti::TypePtrPtr& t) { tid, [&]() { auto v = VisitorTypeInfoPredefined(this); - v.dispatch(t); - if ( auto x = v._result; x && *x ) + + if ( auto x = hilti::visitor::dispatch(v, t, [](const auto& v) { return v.result; }); x && *x ) return CxxTypeInfo{.predefined = true, .reference = fmt("&%s", *x)}; auto forward = cxx::declaration::Constant{.id = tid, @@ -1219,13 +1218,11 @@ const CxxTypeInfo& CodeGen::_getOrCreateTypeInfo(const hilti::TypePtrPtr& t) { return ti; auto v = VisitorTypeInfoDynamic(this); - v.dispatch(t); - auto& x = v._result; - + auto x = hilti::visitor::dispatch(v, t, [](const auto& v) { return v.result; }); if ( ! x ) - logger().internalError(fmt("codegen: type %s does not have a dynamic type info visitor", t), t); + logger().internalError(fmt("codegen: type %s does not have a dynamic type info visitor", *t), t); - auto id_init = (t.typeID() ? fmt("\"%s\"", *t.typeID()) : std::string("{}")); + auto id_init = (t->type()->typeID() ? fmt("\"%s\"", *t->type()->typeID()) : std::string("{}")); auto init = fmt("{ %s, \"%s\", new %s }", id_init, display.str(), *x); ti.declaration = @@ -1237,25 +1234,25 @@ const CxxTypeInfo& CodeGen::_getOrCreateTypeInfo(const hilti::TypePtrPtr& t) { }); } -cxx::Expression CodeGen::_makeLhs(cxx::Expression expr, const TypePtr& type) { +cxx::Expression CodeGen::_makeLhs(cxx::Expression expr, const QualifiedTypePtr& type) { if ( expr.isLhs() ) return expr; auto tmp = addTmp("lhs", compile(type, TypeUsage::Storage)); cxx::Expression result; - if ( type.isA() ) + if ( type->isA() ) result = cxx::Expression{fmt("(%s=(%s).asSharedPtr())", tmp, expr), cxx::Side::LHS}; // avoid copy else result = cxx::Expression{fmt("(%s=(%s))", tmp, expr), cxx::Side::LHS}; // This can help show where LHS conversions happen unexpectedly; they // should be very rare. - HILTI_DEBUG(logging::debug::CodeGen, fmt("RHS -> LHS: %s -> %s [%s]", expr, result, type.typename_())); + HILTI_DEBUG(logging::debug::CodeGen, fmt("RHS -> LHS: %s -> %s [%s]", expr, result, type->typename_())); return result; } -cxx::Expression CodeGen::typeInfo(const hilti::TypePtrPtr& t) { return _getOrCreateTypeInfo(t).reference; }; +cxx::Expression CodeGen::typeInfo(const QualifiedTypePtr& t) { return _getOrCreateTypeInfo(t).reference; }; -void CodeGen::addTypeInfoDefinition(const hilti::TypePtrPtr& t) { _getOrCreateTypeInfo(t); } +void CodeGen::addTypeInfoDefinition(const QualifiedTypePtr& t) { _getOrCreateTypeInfo(t); } diff --git a/hilti/toolchain/src/compiler/codegen/unpack.cc b/hilti/toolchain/src/compiler/codegen/unpack.cc index da2c57bbb..5a1e0aefc 100644 --- a/hilti/toolchain/src/compiler/codegen/unpack.cc +++ b/hilti/toolchain/src/compiler/codegen/unpack.cc @@ -17,16 +17,20 @@ using namespace hilti::detail; namespace { -struct Visitor : hilti::visitor::PreOrder { +struct Visitor : hilti::visitor::PreOrder { enum class Kind { Pack, Unpack }; Visitor(CodeGen* cg, Kind kind, cxx::Expression data, const std::vector& args) : cg(cg), kind(kind), data(std::move(data)), args(args) {} + CodeGen* cg; Kind kind; cxx::Expression data; const std::vector& args; + std::optional result; + +#if 0 auto kindToString() const { switch ( kind ) { case Kind::Pack: return "pack"; @@ -56,46 +60,51 @@ struct Visitor : hilti::visitor::PreOrder { result_t operator()(const type::Real& n) { return fmt("::hilti::rt::real::%s(%s, %s, %s)", kindToString(), data, args[0], args[1]); } +#endif }; } // anonymous namespace -cxx::Expression CodeGen::pack(const Expression& data, const std::vector& args) { +cxx::Expression CodeGen::pack(const ExpressionPtr& data, const Expressions& args) { auto cxx_args = util::transform(args, [&](const auto& e) { return compile(e, false); }); - if ( auto x = Visitor(this, Visitor::Kind::Pack, compile(data), cxx_args).dispatch(data.type()) ) - return cxx::Expression(*x); + auto v = Visitor(this, Visitor::Kind::Pack, compile(data), cxx_args); + if ( auto result = hilti::visitor::dispatch(v, data->type(), [](const auto& v) { return v.result; }) ) + return cxx::Expression(*result); - logger().internalError("pack failed to compile", data.type()); + logger().internalError("pack failed to compile", data->type()); } -cxx::Expression CodeGen::pack(const hilti::TypePtrPtr& t, const cxx::Expression& data, +cxx::Expression CodeGen::pack(const QualifiedTypePtr& t, const cxx::Expression& data, const std::vector& args) { - if ( auto x = Visitor(this, Visitor::Kind::Pack, data, args).dispatch(t) ) - return cxx::Expression(*x); + auto v = Visitor(this, Visitor::Kind::Pack, data, args); + if ( auto result = hilti::visitor::dispatch(v, t, [](const auto& v) { return v.result; }) ) + return cxx::Expression(*result); logger().internalError("pack failed to compile", t); } -cxx::Expression CodeGen::unpack(const hilti::TypePtrPtr& t, const Expression& data, const std::vector& args, +cxx::Expression CodeGen::unpack(const QualifiedTypePtr& t, const ExpressionPtr& data, const Expressions& args, bool throw_on_error) { auto cxx_args = util::transform(args, [&](const auto& e) { return compile(e, false); }); - if ( auto x = Visitor(this, Visitor::Kind::Unpack, compile(data), cxx_args).dispatch(t) ) { + auto v = Visitor(this, Visitor::Kind::Unpack, compile(data), cxx_args); + if ( auto result = hilti::visitor::dispatch(v, t, [](const auto& v) { return v.result; }) ) { if ( throw_on_error ) - return cxx::Expression(util::fmt("%s.valueOrThrow()", *x)); + return cxx::Expression(util::fmt("%s.valueOrThrow()", *result)); else - return cxx::Expression(*x); + return cxx::Expression(*result); } logger().internalError("unpack failed to compile", t); } -cxx::Expression CodeGen::unpack(const hilti::TypePtrPtr& t, const cxx::Expression& data, +cxx::Expression CodeGen::unpack(const QualifiedTypePtr& t, const cxx::Expression& data, const std::vector& args, bool throw_on_error) { - if ( auto x = Visitor(this, Visitor::Kind::Unpack, data, args).dispatch(t) ) { + auto v = Visitor(this, Visitor::Kind::Unpack, data, args); + if ( auto result = hilti::visitor::dispatch(v, t, [](const auto& v) { return v.result; }) ) { if ( throw_on_error ) - return cxx::Expression(util::fmt("%s.valueOrThrow<::hilti::rt::InvalidValue>()", *x)); + return cxx::Expression(util::fmt("%s.valueOrThrow<::hilti::rt::InvalidValue>()", *result)); else - return cxx::Expression(*x); + return cxx::Expression(*result); } logger().internalError("unpack failed to compile", t); diff --git a/hilti/toolchain/src/compiler/coercion.cc b/hilti/toolchain/src/compiler/coercion.cc index 9aa4fbe45..59df0442a 100644 --- a/hilti/toolchain/src/compiler/coercion.cc +++ b/hilti/toolchain/src/compiler/coercion.cc @@ -1,6 +1,7 @@ // Copyright (c) 2020-2023 by the Zeek Project. See LICENSE for details. -#include +#include + #include #include #include @@ -20,6 +21,9 @@ #include #include +#include "ast/builder/builder.h" +#include "base/timing.h" + using namespace hilti; using namespace util; @@ -30,20 +34,23 @@ inline const DebugStream Operator("operator"); namespace { -struct VisitorCtor : public visitor::PreOrder, VisitorCtor> { - VisitorCtor(const TypePtr& dst, bitmask style) : dst(dst), style(style) {} +struct VisitorCtor : visitor::PreOrder { + explicit VisitorCtor(QualifiedTypePtr dst, bitmask style) : dst(std::move(dst)), style(style) {} - const TypePtr& dst; + QualifiedTypePtr dst; bitmask style; - result_t operator()(const ctor::Enum& c) { + CtorPtr result = nullptr; + +#if 0 + void operator()(const ctor::Enum& c) { if ( dst.isA() && (style & CoercionStyle::ContextualConversion) ) return ctor::Bool(c.value().id() != ID("Undef"), c.meta()); return {}; } - result_t operator()(const ctor::Map& c) { + void operator()(const ctor::Map& c) { if ( auto t = dst.tryAs() ) { std::vector nelemns; for ( const auto& e : c.value() ) { @@ -62,7 +69,7 @@ struct VisitorCtor : public visitor::PreOrder, VisitorCtor> return {}; } - result_t operator()(const ctor::Null& c) { + void operator()(const ctor::Null& c) { if ( auto t = dst.tryAs() ) return ctor::Optional(*t->dereferencedType()); @@ -75,7 +82,7 @@ struct VisitorCtor : public visitor::PreOrder, VisitorCtor> return {}; } - result_t operator()(const ctor::List& c) { + void operator()(const ctor::List& c) { if ( auto t = dst.tryAs() ) { std::vector nexprs; for ( const auto& e : c.value() ) { @@ -116,7 +123,7 @@ struct VisitorCtor : public visitor::PreOrder, VisitorCtor> return {}; } - result_t operator()(const ctor::Real& c) { + void operator()(const ctor::Real& c) { // Note: double->Integral constant conversions check 'non-narrowing' via // double->Int->double roundtrip - the generated code looks good. @@ -173,7 +180,7 @@ struct VisitorCtor : public visitor::PreOrder, VisitorCtor> return {}; } - result_t operator()(const ctor::Set& c) { + void operator()(const ctor::Set& c) { if ( auto t = dst.tryAs() ) { std::vector nexprs; for ( const auto& e : c.value() ) { @@ -188,7 +195,7 @@ struct VisitorCtor : public visitor::PreOrder, VisitorCtor> return {}; } - result_t operator()(const ctor::SignedInteger& c) { + void operator()(const ctor::SignedInteger& c) { if ( auto t = dst.tryAs() ) { if ( t->width() == 64 ) return c; @@ -223,7 +230,7 @@ struct VisitorCtor : public visitor::PreOrder, VisitorCtor> return {}; } - result_t operator()(const ctor::Vector& c) { + void operator()(const ctor::Vector& c) { if ( auto t = dst.tryAs() ) { std::vector nexprs; for ( const auto& e : c.value() ) { @@ -238,7 +245,7 @@ struct VisitorCtor : public visitor::PreOrder, VisitorCtor> return {}; } - result_t operator()(const ctor::UnsignedInteger& c) { + void operator()(const ctor::UnsignedInteger& c) { if ( auto t = dst.tryAs() ) { if ( t->width() == 64 ) return c; @@ -273,7 +280,7 @@ struct VisitorCtor : public visitor::PreOrder, VisitorCtor> return {}; } - result_t operator()(const ctor::Tuple& c) { + void operator()(const ctor::Tuple& c) { if ( auto t = dst.tryAs() ) { auto vc = c.value(); auto ve = t.value().elements(); @@ -299,7 +306,7 @@ struct VisitorCtor : public visitor::PreOrder, VisitorCtor> return {}; } - result_t operator()(const ctor::Struct& c) { + void operator()(const ctor::Struct& c) { auto dst_ = dst; if ( (dst.isA() || dst.isA()) && ! type::isReferenceType(dst) ) @@ -363,27 +370,29 @@ struct VisitorCtor : public visitor::PreOrder, VisitorCtor> return {}; } +#endif }; -struct VisitorType : public visitor::PreOrder, type::Visitor { - VisitorType(const TypePtr& dst, bitmask style) : dst(dst), style(style) {} +struct VisitorType : visitor::PreOrder { + explicit VisitorType(QualifiedTypePtr dst, bitmask style) : dst(std::move(dst)), style(style) {} - const TypePtr& dst; + QualifiedTypePtr dst; bitmask style; - TypePtr _result; + QualifiedTypePtr result = nullptr; - result_t operator()(const type::Enum& c, type::Visitor::position_t&) override { +#if 0 + void operator()(const type::Enum& c, type::Visitor::position_t&) override { if ( auto t = dst.tryAs(); t && (style & CoercionStyle::ContextualConversion) ) _result = dst; } - result_t operator()(const type::Interval& c, type::Visitor::position_t&) override { + void operator()(const type::Interval& c, type::Visitor::position_t&) override { if ( auto t = dst.tryAs(); t && (style & CoercionStyle::ContextualConversion) ) _result = dst; } - result_t operator()(const type::Null& c, type::Visitor::position_t&) override { + void operator()(const type::Null& c, type::Visitor::position_t&) override { if ( auto t = dst.tryAs() ) _result = dst; else if ( auto t = dst.tryAs() ) @@ -392,17 +401,17 @@ struct VisitorType : public visitor::PreOrder, type::Visitor _result = dst; } - result_t operator()(const type::Bytes& c, type::Visitor::position_t&) override { + void operator()(const type::Bytes& c, type::Visitor::position_t&) override { if ( dst.tryAs() && (style & (CoercionStyle::Assignment | CoercionStyle::FunctionCall)) ) _result = dst; } - result_t operator()(const type::Error& e, type::Visitor::position_t&) override { + void operator()(const type::Error& e, type::Visitor::position_t&) override { if ( auto t = dst.tryAs() ) _result = dst; } - result_t operator()(const type::List& e, type::Visitor::position_t&) override { + void operator()(const type::List& e, type::Visitor::position_t&) override { if ( auto t = dst.tryAs(); t && t->elementType() == e.elementType() ) _result = dst; @@ -410,7 +419,7 @@ struct VisitorType : public visitor::PreOrder, type::Visitor _result = dst; } - result_t operator()(const type::Optional& r, type::Visitor::position_t&) override { + void operator()(const type::Optional& r, type::Visitor::position_t&) override { if ( auto t = dst.tryAs() ) { const auto& s = *r.dereferencedType(); const auto& d = *t->dereferencedType(); @@ -427,7 +436,7 @@ struct VisitorType : public visitor::PreOrder, type::Visitor _result = dst; } - result_t operator()(const type::StrongReference& r, type::Visitor::position_t&) override { + void operator()(const type::StrongReference& r, type::Visitor::position_t&) override { if ( auto t = dst.tryAs(); (style & CoercionStyle::ContextualConversion) && t ) { _result = dst; return; @@ -447,12 +456,12 @@ struct VisitorType : public visitor::PreOrder, type::Visitor } } - result_t operator()(const type::Time& c, type::Visitor::position_t&) override { + void operator()(const type::Time& c, type::Visitor::position_t&) override { if ( auto t = dst.tryAs(); t && (style & CoercionStyle::ContextualConversion) ) _result = dst; } - result_t operator()(const type::Result& r, type::Visitor::position_t&) override { + void operator()(const type::Result& r, type::Visitor::position_t&) override { if ( auto t = dst.tryAs(); (style & CoercionStyle::ContextualConversion) && t ) _result = dst; @@ -460,7 +469,7 @@ struct VisitorType : public visitor::PreOrder, type::Visitor _result = dst; } - result_t operator()(const type::SignedInteger& src, type::Visitor::position_t&) override { + void operator()(const type::SignedInteger& src, type::Visitor::position_t&) override { if ( dst.isA() && (style & CoercionStyle::ContextualConversion) ) _result = dst; @@ -470,17 +479,17 @@ struct VisitorType : public visitor::PreOrder, type::Visitor } } - result_t operator()(const type::Stream& c, type::Visitor::position_t&) override { + void operator()(const type::Stream& c, type::Visitor::position_t&) override { if ( auto t = dst.tryAs() ) _result = dst; } - result_t operator()(const type::stream::View& c, type::Visitor::position_t&) override { + void operator()(const type::stream::View& c, type::Visitor::position_t&) override { if ( dst.tryAs() && (style & (CoercionStyle::Assignment | CoercionStyle::FunctionCall)) ) _result = dst; } - result_t operator()(const type::Type_& src, type::Visitor::position_t&) override { + void operator()(const type::Type_& src, type::Visitor::position_t&) override { if ( auto t = dst.tryAs() ) { // We don't allow arbitrary coercions here, just (more or less) direct matches. if ( auto x = hilti::coerceType(src.typeValue(), t->typeValue(), CoercionStyle::TryDirectForMatching) ) @@ -488,12 +497,12 @@ struct VisitorType : public visitor::PreOrder, type::Visitor } } - result_t operator()(const type::Union& c, type::Visitor::position_t&) override { + void operator()(const type::Union& c, type::Visitor::position_t&) override { if ( auto t = dst.tryAs(); t && (style & CoercionStyle::ContextualConversion) ) _result = dst; } - result_t operator()(const type::UnsignedInteger& src, type::Visitor::position_t&) override { + void operator()(const type::UnsignedInteger& src, type::Visitor::position_t&) override { if ( dst.isA() && (style & CoercionStyle::ContextualConversion) ) { _result = dst; return; @@ -515,7 +524,7 @@ struct VisitorType : public visitor::PreOrder, type::Visitor } } - result_t operator()(const type::Tuple& src, type::Visitor::position_t&) override { + void operator()(const type::Tuple& src, type::Visitor::position_t&) override { if ( auto t = dst.tryAs() ) { auto vc = src.elements(); auto ve = t->elements(); @@ -532,7 +541,7 @@ struct VisitorType : public visitor::PreOrder, type::Visitor } } - result_t operator()(const type::ValueReference& r, type::Visitor::position_t&) override { + void operator()(const type::ValueReference& r, type::Visitor::position_t&) override { if ( auto t = dst.tryAs(); (style & CoercionStyle::ContextualConversion) && t ) { _result = hilti::coerceType(*r.dereferencedType(), dst, style); return; @@ -551,7 +560,7 @@ struct VisitorType : public visitor::PreOrder, type::Visitor } } - result_t operator()(const type::WeakReference& r, type::Visitor::position_t&) override { + void operator()(const type::WeakReference& r, type::Visitor::position_t&) override { if ( auto t = dst.tryAs(); (style & CoercionStyle::ContextualConversion) && t ) { _result = dst; return; @@ -571,38 +580,41 @@ struct VisitorType : public visitor::PreOrder, type::Visitor } } } +#endif }; } // anonymous namespace // Public version going through all plugins. -Result hilti::coerceCtor(Ctor c, const TypePtr& dst, bitmask style) { - if ( c.type() == dst ) +Result hilti::coerceCtor(Builder* builder, CtorPtr c, const QualifiedTypePtr& dst, + bitmask style) { + if ( c->type() == dst ) return std::move(c); for ( auto p : plugin::registry().plugins() ) { if ( ! (p.coerce_ctor) ) continue; - if ( auto nc = (*p.coerce_ctor)(c, dst, style) ) - return *nc; + if ( auto nc = (*p.coerce_ctor)(builder, c, dst, style) ) + return nc; } - return result::Error("could not coeerce type for constructor"); + return result::Error("could not coerce type for constructor"); } -static Result _coerceParameterizedType(const TypePtr& src, const TypePtr& dst, bitmask style) { - if ( src == dst ) +static Result _coerceParameterizedType(Builder* builder, const QualifiedTypePtr& src, + const QualifiedTypePtr& dst, bitmask style) { + if ( src && dst && *src == *dst ) return dst; - if ( src.typename_() != dst.typename_() ) + if ( src->type()->typename_() != dst->type()->typename_() ) return {}; - if ( dst.isWildcard() ) + if ( dst->type()->isWildcard() ) return src; - auto params1 = src.typeParameters(); - auto params2 = dst.typeParameters(); + auto params1 = src->type()->typeParameters(); + auto params2 = dst->type()->typeParameters(); if ( params1.size() != params2.size() ) return {}; @@ -610,23 +622,23 @@ static Result _coerceParameterizedType(const TypePtr& src, const TypePtr& bool have_wildcard = false; for ( auto&& [p1, p2] : util::zip2(params1, params2) ) { - // If we cannot get both parametres as types, we don't have a generic + // If we cannot get both parameters as types, we don't have a generic // node comparison for the individual parameters, so just stop here and // decline. (Note that the case of src == dst has been handled already, // that usually does it.) - auto t1 = p1.tryAs(); + auto t1 = p1->tryAs(); if ( ! t1 ) return {}; - auto t2 = p2.tryAs(); + auto t2 = p2->tryAs(); if ( ! t2 ) return {}; - if ( ! coerceType(*t1, *t2, style) ) + if ( ! coerceType(builder, t1, t2, style) ) return {}; - if ( t2->isWildcard() ) + if ( t2->type()->isWildcard() ) have_wildcard = true; } @@ -639,14 +651,15 @@ static Result _coerceParameterizedType(const TypePtr& src, const TypePtr& return have_wildcard ? src : dst; } -static Result _coerceType(const TypePtr& src, const TypePtr& dst, bitmask style) { +static Result _coerceType(Builder* builder, const QualifiedTypePtr& src, const QualifiedTypePtr& dst, + bitmask style) { // TODO(robin): Not sure if this should/must replicate all the type coercion // login in coerceExpression(). If so, we should factor that out. // Update: I believe the answer is yes ... Added a few more cases, but this will // likely need more work. - if ( src.typeID() && dst.typeID() ) { - if ( *src.typeID() == *dst.typeID() ) + if ( src->type()->typeID() && dst->type()->typeID() ) { + if ( *src->type()->typeID() == *dst->type()->typeID() ) return dst; else return result::Error("type IDs do not match"); @@ -656,33 +669,33 @@ static Result _coerceType(const TypePtr& src, const TypePtr& dst, bitmask< return src; if ( style & (CoercionStyle::Assignment | CoercionStyle::FunctionCall) ) { - if ( auto opt = dst.tryAs() ) { - if ( dst.isWildcard() ) + if ( auto opt = dst->tryAs() ) { + if ( dst->type()->isWildcard() ) return dst; // All types converts into a corresponding optional. - if ( auto x = coerceType(src, *opt->dereferencedType(), style) ) - return {type::Optional(*x, src.meta())}; + if ( auto x = coerceType(builder, src, opt->dereferencedType(), style) ) + return builder->qualifiedType(builder->typeOptional(*x, src->meta()), false); } - if ( auto opt = dst.tryAs() ) { - if ( dst.isWildcard() ) + if ( auto opt = dst->type()->tryAs() ) { + if ( dst->type()->isWildcard() ) return dst; // All types converts into a corresponding result. - if ( auto x = coerceType(src, *opt->dereferencedType(), style) ) - return {type::Result(*x, src.meta())}; + if ( auto x = coerceType(builder, src, opt->dereferencedType(), style) ) + return builder->qualifiedType(builder->typeResult(*x, src->meta()), false); } - if ( auto x = dst.tryAs(); x && ! type::isReferenceType(src) ) { + if ( auto x = dst->type()->tryAs(); x && ! type::isReferenceType(src) ) { // All types converts into a corresponding value_ref. - if ( auto y = coerceType(src, *x->dereferencedType(), style) ) - return {type::ValueReference(*x, src.meta())}; + if ( auto y = coerceType(builder, src, x->dereferencedType(), style) ) + return builder->qualifiedType(builder->typeValueReference(dst, src->meta()), false); } } if ( type::isParameterized(src) && type::isParameterized(dst) ) { - if ( auto x = _coerceParameterizedType(src, dst, style) ) + if ( auto x = _coerceParameterizedType(builder, src, dst, style) ) return *x; } @@ -690,16 +703,17 @@ static Result _coerceType(const TypePtr& src, const TypePtr& dst, bitmask< if ( ! (p.coerce_type) ) continue; - if ( auto nt = (*p.coerce_type)(type::nonConstant(src), type::nonConstant(dst), style) ) - return type::nonConstant(*nt); + if ( auto nt = (*p.coerce_type)(builder, src, dst, style) ) + return nt; } return result::Error("cannot coerce types"); } // Public version going through all plugins. -Result hilti::coerceType(const TypePtr& src, const TypePtr& dst, bitmask style) { - return _coerceType(src, dst, style); +Result hilti::coerceType(Builder* builder, const QualifiedTypePtr& src, const QualifiedTypePtr& dst, + bitmask style) { + return _coerceType(builder, src, dst, style); } std::string hilti::to_string(bitmask style) { @@ -732,12 +746,12 @@ std::string hilti::to_string(bitmask style) { return util::join(labels, ","); }; -Result>> hilti::coerceOperands(const node::Range& exprs, - const std::vector& operands, +Result> hilti::coerceOperands(Builder* builder, const node::Range& exprs, + const operator_::Operands& operands, bitmask style) { int num_type_changes = 0; bool changed = false; - std::vector transformed; + Expressions transformed; if ( exprs.size() > operands.size() ) return result::Error("more expressions than operands"); @@ -746,7 +760,7 @@ Result>> hilti::coerceOperands(const nod if ( i >= exprs.size() ) { // Running out of operands, must have a default or be optional. if ( op.default_ ) { - transformed.emplace_back(*op.default_); + transformed.emplace_back(op.default_); changed = true; } else if ( op.optional ) { @@ -766,17 +780,17 @@ Result>> hilti::coerceOperands(const nod return result::Error("could not look up operand type"); } - auto result = coerceExpression(exprs[i], *oat, style); + auto result = coerceExpression(builder, exprs[i], oat, style); if ( ! result ) { HILTI_DEBUG(logging::debug::Operator, - util::fmt(" [param %d] matching %s against %s -> failure", i, exprs[i].type(), *oat)); + util::fmt(" [param %d] matching %s against %s -> failure", i, exprs[i]->type(), *oat)); return result::Error("could not match coercion operands"); } HILTI_DEBUG(logging::debug::Operator, util::fmt(" [param %d] matching %s against %s -> success: %s (coerced expression is %s) (%s)", i, - exprs[i].type(), *oat, result.coerced->type(), - (result.coerced->isConstant() ? "const" : "non-const"), + exprs[i]->type(), *oat, (*result.coerced)->type(), + ((*result.coerced)->type()->isConstant() ? "const" : "non-const"), (result.consider_type_changed ? "type changed" : "type not changed"))); // We check if the primary type of the alternative has changed. Only @@ -792,20 +806,20 @@ Result>> hilti::coerceOperands(const nod changed = true; } - std::vector x; + Expressions x; x.reserve(transformed.size()); for ( const auto& n : transformed ) - x.push_back(n.as()); + x.push_back(n->as()); return std::make_pair(changed, std::move(x)); } -static CoercedExpression _coerceExpression(const Expression& e, const TypePtr& src, const TypePtr& dst, - bitmask style, bool lhs) { +static CoercedExpression _coerceExpression(Builder* builder, const ExpressionPtr& e, const QualifiedTypePtr& src, + const QualifiedTypePtr& dst, bitmask style, bool lhs) { if ( ! (style & CoercionStyle::_Recursing) ) style |= CoercionStyle::_Recursing; - const auto no_change = CoercedExpression(e); + const auto& no_change = e; CoercedExpression _result; int _line = 0; @@ -816,22 +830,22 @@ static CoercedExpression _coerceExpression(const Expression& e, const TypePtr& s goto exit; \ } - const bool dst_is_const = type::isConstant(dst); + const bool dst_is_const = dst->isConstant(); const bool dst_is_mut = type::isMutable(dst); - const bool e_is_const = e.isConstant(); + const bool e_is_const = e->isConstant(); - if ( dst.isA() ) + if ( dst->isA() ) // Always accept, we're going to update the auto type later. RETURN(no_change); - if ( src.cxxID() && dst.cxxID() ) { - if ( *src.cxxID() == *dst.cxxID() ) { + if ( src->type()->cxxID() && dst->type()->cxxID() ) { + if ( *src->type()->cxxID() == *dst->type()->cxxID() ) { RETURN(no_change); } } - if ( src.typeID() && dst.typeID() ) { - if ( *src.typeID() == *dst.typeID() ) { + if ( src->type()->typeID() && dst->type()->typeID() ) { + if ( *src->type()->typeID() == *dst->type()->typeID() ) { RETURN(no_change); } else { @@ -849,7 +863,7 @@ static CoercedExpression _coerceExpression(const Expression& e, const TypePtr& s } if ( e_is_const == dst_is_const && type::isParameterized(src) && type::isParameterized(dst) && - _coerceParameterizedType(src, dst, CoercionStyle::TryExactMatch) ) + _coerceParameterizedType(builder, src, dst, CoercionStyle::TryExactMatch) ) RETURN(no_change); // can say no_change because we're in the ExactMatch case } @@ -859,13 +873,13 @@ static CoercedExpression _coerceExpression(const Expression& e, const TypePtr& s if ( e_is_const && (! dst_is_const) && dst_is_mut ) RETURN(result::Error()); - if ( dst.isWildcard() && src.typename_() == dst.typename_() ) + if ( dst->type()->isWildcard() && src->type()->typename_() == dst->type()->typename_() ) RETURN(no_change); if ( src == dst ) RETURN(no_change); - if ( type::sameExceptForConstness(src, dst) ) { + if ( src->type() == dst->type() ) { RETURN(no_change); } } @@ -874,10 +888,10 @@ static CoercedExpression _coerceExpression(const Expression& e, const TypePtr& s if ( src == dst ) RETURN(no_change); - if ( type::sameExceptForConstness(src, dst) ) + if ( src->type() == dst->type() ) RETURN(no_change); - if ( dst.isWildcard() && src.typename_() == dst.typename_() ) + if ( dst->type()->isWildcard() && src->type()->typename_() == dst->type()->typename_() ) RETURN(no_change); } } @@ -896,39 +910,39 @@ static CoercedExpression _coerceExpression(const Expression& e, const TypePtr& s } } - if ( dst.isA() ) + if ( dst->type()->isA() ) // type::Any accepts anything without actual coercion. RETURN(no_change); - if ( auto x = e.tryAs() ) { + if ( auto x = e->tryAs() ) { // Make sure the expression remains a member expression, as we will // be expecting to cast it to that. - if ( auto t = hilti::coerceType(x->type(), dst, style) ) { - RETURN(CoercedExpression(src, expression::Member(x->id(), *t, x->meta()))); + if ( auto t = hilti::coerceType(builder, x->type(), dst, style) ) { + RETURN(CoercedExpression(src, builder->expressionMember(*t, x->id(), x->meta()))); } else RETURN(result::Error()); } - if ( auto o = dst.template tryAs() ) { + if ( auto o = dst->template tryAs() ) { // Match tuple against operands according to function call rules. HILTI_DEBUG(logging::debug::Operator, util::fmt("matching against call parameters")); logging::DebugPushIndent _(logging::debug::Operator); - auto c = e.template tryAs(); + auto c = e->template tryAs(); if ( ! c ) RETURN(CoercedExpression()); // TODO(robin): Why do we need this block? We do a separate operand // matching afterwards, too. - if ( auto t = c->ctor().template tryAs() ) { + if ( auto t = c->ctor()->template tryAs() ) { CoercionStyle function_style = (style & CoercionStyle::TryCoercion ? CoercionStyle::TryAllForFunctionCall : CoercionStyle::TryDirectMatchForFunctionCall); - if ( auto result = coerceOperands(t->value(), o->operands(), function_style) ) { + if ( auto result = coerceOperands(builder, t->value(), o->operands(), function_style) ) { if ( result->first ) { - RETURN(CoercedExpression(e.type(), expression::Ctor(hilti::ctor::Tuple(result->second)))); + RETURN(CoercedExpression(e->type(), builder->expressionCtor(builder->ctorTuple(result->second)))); } else { RETURN(no_change); @@ -940,42 +954,43 @@ static CoercedExpression _coerceExpression(const Expression& e, const TypePtr& s } if ( style & (CoercionStyle::Assignment | CoercionStyle::FunctionCall) ) { - if ( auto opt = dst.tryAs() ) { + if ( auto opt = dst->tryAs() ) { if ( opt->isWildcard() ) RETURN(no_change); // All types converts into a corresponding optional. - if ( auto x = coerceExpression(e, *opt->dereferencedType(), style) ) - RETURN(CoercedExpression(src, expression::Coerced(*x.coerced, dst, e.meta()))); + if ( auto x = coerceExpression(builder, e, opt->dereferencedType(), style) ) + RETURN(CoercedExpression(src, builder->expressionCoerced(*x.coerced, dst, e->meta()))); } - if ( auto result = dst.tryAs() ) { + if ( auto result = dst->tryAs() ) { if ( result->isWildcard() ) RETURN(no_change); // All types convert into a corresponding result. - if ( auto x = coerceExpression(e, *result->dereferencedType(), style) ) - RETURN(CoercedExpression(src, expression::Coerced(*x.coerced, dst, e.meta()))); + if ( auto x = coerceExpression(builder, e, result->dereferencedType(), style) ) + RETURN(CoercedExpression(src, builder->expressionCoerced(*x.coerced, dst, e->meta()))); } - if ( auto x = dst.tryAs(); x && ! type::isReferenceType(src) ) { + if ( auto x = dst->tryAs(); x && ! type::isReferenceType(src) ) { // All types converts into a corresponding value_ref. - if ( auto y = coerceExpression(e, *x->dereferencedType(), style) ) - RETURN(CoercedExpression(src, expression::Coerced(*y.coerced, dst, e.meta()))); + if ( auto y = coerceExpression(builder, e, x->dereferencedType(), style) ) + RETURN(CoercedExpression(src, builder->expressionCoerced(*y.coerced, dst, e->meta()))); } } if ( style & CoercionStyle::TryCoercion ) { - if ( auto c = e.tryAs() ) { - if ( auto nc = hilti::coerceCtor(c->ctor(), dst, style) ) - RETURN(CoercedExpression(src, expression::Ctor(ctor::Coerced(c->ctor(), *nc, c->meta()), e.meta()))); + if ( auto c = e->tryAs() ) { + if ( auto nc = hilti::coerceCtor(builder, c->ctor(), dst, style) ) + RETURN(CoercedExpression(src, builder->expressionCtor(builder->ctorCoerced(c->ctor(), *nc, c->meta()), + e->meta()))); } - if ( auto t = hilti::coerceType(src, dst, style) ) + if ( auto t = hilti::coerceType(builder, src, dst, style) ) // We wrap the expression into a coercion even if the new type is // the same as *dst*. That way the overloader has a way to // recognize that the types aren't identical. - RETURN(CoercedExpression(src, expression::Coerced(e, *t, e.meta()))); + RETURN(CoercedExpression(src, builder->expressionCoerced(e, *t, e->meta()))); } _result = result::Error(); @@ -984,15 +999,16 @@ static CoercedExpression _coerceExpression(const Expression& e, const TypePtr& s if ( logger().isEnabled(logging::debug::Operator) ) HILTI_DEBUG(logging::debug::Operator, util::fmt("coercing %s %s (%s) to %s%s (%s) -> %s [%s] (%s) (#%d)", - (e_is_const ? "const" : "non-const"), to_node(src), - util::replace(src.typename_(), "hilti::type::", ""), (dst_is_const ? "" : "non-const "), - to_node(dst), util::replace(dst.typename_(), "hilti::type::", ""), + (e_is_const ? "const" : "non-const"), *src, + util::replace(src->typename_(), "hilti::type::", ""), (dst_is_const ? "" : "non-const "), + *dst, util::replace(dst->typename_(), "hilti::type::", ""), (_result ? - util::fmt("%s %s (%s)", (_result.coerced->isConstant() ? "const" : "non-const"), - _result.coerced->type(), - util::replace(_result.coerced->type().typename_(), "hilti::type::", "")) : + util::fmt("%s %s (%s)", ((*_result.coerced)->isConstant() ? "const" : "non-const"), + (*_result.coerced)->type(), + util::replace((*_result.coerced)->type()->typename_(), + "hilti::type::", "")) : "fail"), - to_string(style), e.meta().location(), _line)); + to_string(style), e->meta().location(), _line)); #undef RETURN @@ -1000,35 +1016,37 @@ static CoercedExpression _coerceExpression(const Expression& e, const TypePtr& s } // Public version going through all plugins. -CoercedExpression hilti::coerceExpression(const Expression& e, const TypePtr& src, const TypePtr& dst, - bitmask style, bool lhs) { - return _coerceExpression(e, src, dst, style, lhs); +CoercedExpression hilti::coerceExpression(Builder* builder, const ExpressionPtr& e, const QualifiedTypePtr& src, + const QualifiedTypePtr& dst, bitmask style, bool lhs) { + return _coerceExpression(builder, e, src, dst, style, lhs); } // Public version going through all plugins. -CoercedExpression hilti::coerceExpression(const Expression& e, const TypePtr& dst, bitmask style, - bool lhs) { - return coerceExpression(e, e.type(), dst, style, lhs); +CoercedExpression hilti::coerceExpression(Builder* builder, const ExpressionPtr& e, const QualifiedTypePtr& dst, + bitmask style, bool lhs) { + return coerceExpression(builder, e, e->type(), dst, style, lhs); } - // Plugin-specific version just kicking off the local visitor. -std::optional hilti::detail::coerceCtor(Ctor c, const TypePtr& dst, bitmask style) { - if ( ! (type::isResolved(c.type()) && type::isResolved(dst)) ) - return {}; +CtorPtr hilti::detail::coerceCtor(Builder* builder, const CtorPtr& c, const QualifiedTypePtr& dst, + bitmask style) { + util::timing::Collector _("hilti/compiler/ast/coerce"); - if ( auto nc = VisitorCtor(dst, style).dispatch(std::move(c)) ) - return *nc; + if ( ! (type::isResolved(c->type()) && type::isResolved(dst)) ) + return {}; - return {}; + auto v = VisitorCtor(dst, style); + return hilti::visitor::visit(std::move(v), c, [](const auto& v) { return v.result; }); } // Plugin-specific version just kicking off the local visitor. -TypePtr hilti::detail::coerceType(Type t, const TypePtr& dst, bitmask style) { +QualifiedTypePtr hilti::detail::coerceType(Builder* builder, const QualifiedTypePtr& t, const QualifiedTypePtr& dst, + bitmask style) { + util::timing::Collector _("hilti/compiler/ast/coerce"); + if ( ! (type::isResolved(t) && type::isResolved(dst)) ) return {}; auto v = VisitorType(dst, style); - v.dispatch(std::move(t)); - return v._result; + return hilti::visitor::visit(std::move(v), t, [](const auto& v) { return v.result; }); } diff --git a/hilti/toolchain/src/compiler/context.cc b/hilti/toolchain/src/compiler/context.cc index cbfad7cce..23bbd1048 100644 --- a/hilti/toolchain/src/compiler/context.cc +++ b/hilti/toolchain/src/compiler/context.cc @@ -62,143 +62,9 @@ void Options::print(std::ostream& out) const { } Context::Context(Options options) - : _options(std::move(std::move(options))), _ast_context(std::make_shared()) { + : _options(std::move(std::move(options))), _ast_context(std::make_shared(this)) { operator_::Registry::singleton().printDebug(); } Context::~Context() { - // We explicitly clear out the modules to break any reference cycles they - // may contain. - for ( auto& u : _unit_cache_by_id ) - u.second->unit = nullptr; - - for ( auto& u : _unit_cache_by_path ) - u.second->unit = nullptr; -} - -void Context::cacheUnit(const std::shared_ptr& unit) { - auto entry = std::make_shared(unit); - auto idx = unit->cacheIndex(); - - auto i = _unit_cache_by_id.find(idx.scopedID()); - if ( i == _unit_cache_by_id.end() ) { - HILTI_DEBUG(logging::debug::Compiler, - util::fmt("registering %s AST for module %s (%s)", unit->extension(), idx.id, idx.path)); - - _unit_cache_by_id.insert({idx.scopedID(), entry}); - - if ( ! idx.path.empty() ) - _unit_cache_by_path.insert({idx.path, entry}); - } - else { - HILTI_DEBUG(logging::debug::Compiler, util::fmt("updating cached AST for module %s", unit->uniqueID())); - i->second->unit = unit; - } -} - -std::optional Context::lookupUnit(const context::CacheIndex& idx, - const std::optional& extension) { - if ( auto x = _unit_cache_by_id.find(idx.scopedID()); x != _unit_cache_by_id.end() ) { - if ( x->second->unit->extension() == extension ) - return *x->second; - } - - if ( ! idx.path.empty() ) - return lookupUnit(idx.path, idx.scope, extension); - else - return {}; -} - -std::optional Context::lookupUnit(const ID& id, const std::optional& scope, - const hilti::rt::filesystem::path& extension) { - ID idx = scope ? (*scope + id) : id; - if ( auto x = _unit_cache_by_id.find(idx); x != _unit_cache_by_id.end() ) { - if ( x->second->unit->extension() == extension ) - return *x->second; - } - - return {}; -} - -std::optional Context::lookupUnit(const hilti::rt::filesystem::path& path, const std::optional& scope, - std::optional ast_extension) { - if ( ! ast_extension ) - ast_extension = path.extension(); - - if ( auto x = _unit_cache_by_path.find(util::normalizePath(path)); x != _unit_cache_by_path.end() ) { - if ( x->second->unit->extension() == *ast_extension ) - return *x->second; - } - - return {}; -} - - -static void _dependencies(const std::weak_ptr& u, std::vector>* seen) { - auto unit = u.lock(); - - for ( const auto& d : *seen ) { - if ( d.lock().get() == unit.get() ) - return; - } - - seen->push_back(u); - - for ( const auto& x : unit->dependencies() ) - _dependencies(x, seen); -} - -std::vector> Context::lookupDependenciesForUnit(const context::CacheIndex& idx, - const hilti::rt::filesystem::path& extension) { - auto m = lookupUnit(idx, extension); - if ( ! m ) - return {}; - - std::vector> seen; - _dependencies(m->unit, &seen); - seen.erase(seen.begin()); // don't report entry point - return seen; -} - -void Context::dumpUnitCache(const hilti::logging::DebugStream& stream) { - if ( ! logger().isEnabled(stream) ) - return; - - HILTI_DEBUG(stream, "### Unit cache"); - HILTI_DEBUG(stream, ""); - - for ( const auto& x : _unit_cache_by_id ) { - auto idx = x.first; - auto unit = x.second->unit; - HILTI_DEBUG(stream, util::fmt("- %s -> %s %s [%p] [%p]", idx, unit->uniqueID(), unit->extension(), - unit->module()->renderedRid(), unit.get())); - } - - HILTI_DEBUG(stream, ""); - - for ( const auto& x : _unit_cache_by_path ) { - auto idx = x.first; - auto unit = x.second->unit; - HILTI_DEBUG(stream, util::fmt("- %s -> %s %s [%p] [%p]", idx, unit->uniqueID(), unit->extension(), - unit->module()->renderedRid(), unit.get())); - } - - HILTI_DEBUG(stream, ""); - - for ( const auto& x : _unit_cache_by_id ) { - auto unit = x.second->unit; - HILTI_DEBUG(stream, util::fmt("### %s %s [%p] [%p]", unit->uniqueID(), unit->extension(), - unit->module()->renderedRid(), unit.get())); - - for ( const auto& d_ : unit->dependencies() ) { - auto d = d_.lock(); - HILTI_DEBUG(stream, util::fmt("### Dependency: %s %s [%p] [%p]", d->uniqueID(), d->extension(), - d->module()->renderedRid(), d.get())); - } - - hilti::render(stream, unit->module(), true); - HILTI_DEBUG(stream, ""); - } - - HILTI_DEBUG(stream, ""); } diff --git a/hilti/toolchain/src/compiler/cxx/unit.cc b/hilti/toolchain/src/compiler/cxx/unit.cc index c60a97ba8..4035595b1 100644 --- a/hilti/toolchain/src/compiler/cxx/unit.cc +++ b/hilti/toolchain/src/compiler/cxx/unit.cc @@ -25,7 +25,7 @@ Unit::Unit(const std::shared_ptr& context, cxx::ID module_id) : _context(context), _module_id(std::move(module_id)), _no_linker_meta_data(true) {} void Unit::setModule(const hilti::Module& m, const hilti::Unit& hilti_unit) { - _module_id = hilti_unit.uniqueID(); + _module_id = cxx::ID(hilti_unit.uid().str()); _module_path = m.meta().location().file(); } diff --git a/hilti/toolchain/src/compiler/driver.cc b/hilti/toolchain/src/compiler/driver.cc index 0bde9959c..0f9305f06 100644 --- a/hilti/toolchain/src/compiler/driver.cc +++ b/hilti/toolchain/src/compiler/driver.cc @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -22,15 +23,7 @@ using namespace hilti; using util::fmt; namespace hilti::logging::debug { -inline const DebugStream AstCache("ast-cache"); inline const DebugStream AstCodegen("ast-codegen"); -inline const DebugStream AstDeclarations("ast-declarations"); -inline const DebugStream AstDumpIterations("ast-dump-iterations"); -inline const DebugStream AstFinal("ast-final"); -inline const DebugStream AstOrig("ast-orig"); -inline const DebugStream AstPrintTransformed("ast-print-transformed"); -inline const DebugStream AstResolved("ast-resolved"); -inline const DebugStream AstTransformed("ast-transformed"); inline const DebugStream Compiler("compiler"); inline const DebugStream Driver("driver"); } // namespace hilti::logging::debug @@ -67,14 +60,6 @@ static struct option long_driver_options[] = {{"abort-on-exceptions", required_a {"version", no_argument, nullptr, 'v'}, {nullptr, 0, nullptr, 0}}; -static auto pluginForUnit(const std::shared_ptr& u) { - auto p = plugin::registry().pluginForExtension(u->extension()); - if ( ! p ) - logger().internalError(util::fmt("no plugin for unit extension %s: %s", u->extension(), p.error())); - - return *p; -} - Driver::Driver(std::string name) : _name(std::move(name)) { configuration().initLocation(false); } Driver::Driver(std::string name, const hilti::rt::filesystem::path& argv0) : _name(std::move(name)) { @@ -231,23 +216,23 @@ Result Driver::writeToTemp(std::ifstream& in, const void Driver::dumpUnit(const Unit& unit) { if ( auto module = unit.module() ) { - auto output_path = util::fmt("dbg.%s%s.ast", unit.uniqueID(), unit.extension().native()); + auto output_path = util::fmt("dbg.%s%s.ast", unit.uid().str(), unit.uid().process_extension.native()); if ( auto out = openOutput(output_path) ) { - HILTI_DEBUG(logging::debug::Driver, fmt("saving AST for module %s to %s", unit.id(), output_path)); + HILTI_DEBUG(logging::debug::Driver, fmt("saving AST for module %s to %s", unit.uid().str(), output_path)); render(*out, module, true); } } if ( unit.isCompiledHILTI() ) { - auto output_path = util::fmt("dbg.%s%s", unit.uniqueID(), unit.extension().native()); + auto output_path = util::fmt("dbg.%s%s", unit.uid().str(), unit.uid().process_extension.native()); if ( auto out = openOutput(output_path) ) { - HILTI_DEBUG(logging::debug::Driver, fmt("saving code for module %s to %s", unit.id(), output_path)); + HILTI_DEBUG(logging::debug::Driver, fmt("saving code for module %s to %s", unit.uid().str(), output_path)); unit.print(*out); } } if ( auto cxx = unit.cxxCode() ) { - ID id = (unit.isCompiledHILTI() ? unit.uniqueID() : ID(unit.cxxCode()->id())); + ID id = (unit.isCompiledHILTI() ? ID(unit.uid().str()) : ID(unit.cxxCode()->id())); auto output_path = util::fmt("dbg.%s.cc", id); if ( auto out = openOutput(util::fmt("dbg.%s.cc", id)) ) { HILTI_DEBUG(logging::debug::Driver, fmt("saving C++ code for module %s to %s", id, output_path)); @@ -481,19 +466,18 @@ void Driver::setDriverOptions(driver::Options options) { } void Driver::_addUnit(const std::shared_ptr& unit) { - if ( _processed_units.find(unit->uniqueID()) != _processed_units.end() ) + if ( _units.find(unit->uid()) != _units.end() ) return; - if ( ! unit->path().empty() && _processed_paths.find(unit->path()) != _processed_paths.end() ) + // TODO: This can go if we don't need the 2nd addInput() anymore. + if ( ! unit->uid().path.empty() && _processed_paths.find(unit->uid().path.native()) != _processed_paths.end() ) return; - _processed_units.insert(unit->uniqueID()); + _units.emplace(unit->uid(), unit); - if ( ! unit->path().empty() ) - _processed_paths.insert(unit->path()); - - if ( std::find(_pending_units.begin(), _pending_units.end(), unit) == _pending_units.end() ) - _pending_units.push_back(unit); + // TODO: This can go if we don't need the 2nd addInput() anymore. + if ( ! unit->uid().path.empty() ) + _processed_paths.insert(unit->uid().path.native()); hookNewASTPreCompilation(unit); } @@ -518,7 +502,7 @@ Result Driver::_symbol(const std::string& symbol) { } Result Driver::addInput(const hilti::rt::filesystem::path& path) { - if ( _processed_paths.find(path) != _processed_paths.end() ) + if ( _processed_paths.find(path.native()) != _processed_paths.end() ) return Nothing(); // Calling hook before stage check so that it can execute initialize() @@ -532,22 +516,13 @@ Result Driver::addInput(const hilti::rt::filesystem::path& path) { logger().internalError("no further inputs can be added after compilation has finished already"); if ( plugin::registry().supportsExtension(path.extension()) ) { - HILTI_DEBUG(logging::debug::Driver, fmt("adding source file %s", path)); - - if ( auto unit = Unit::fromCache(_ctx, path, {}) ) { - HILTI_DEBUG(logging::debug::Driver, fmt("reusing previously cached module %s", (*unit)->uniqueID())); - (*unit)->setRequiresCompilation(); - _addUnit(*unit); - } - else { HILTI_DEBUG(logging::debug::Driver, fmt("parsing input file %s", path)); - unit = Unit::fromSource(context(), path, {}); + auto unit = Unit::fromSource(context(), path); if ( ! unit ) return augmentError(unit.error()); (*unit)->setRequiresCompilation(); _addUnit(*unit); - } return Nothing(); } @@ -591,283 +566,46 @@ Result Driver::addInput(const hilti::rt::filesystem::path& path) { return error("unsupported file type", path); } -Result Driver::addInput(const std::shared_ptr& u) { - if ( _processed_units.find(u->uniqueID()) != _processed_units.end() ) - return Nothing(); - - if ( ! u->path().empty() && _processed_paths.find(u->path()) != _processed_paths.end() ) - return Nothing(); - - // Calling hook before stage check so that it can execute initialize() - // just in time if it so desires. - hookAddInput(u); - - if ( _stage == Stage::UNINITIALIZED ) - logger().internalError(" driver must be initialized before inputs can be added"); - - if ( _stage != Stage::INITIALIZED ) - logger().internalError("no further inputs can be added after compilation has finished already"); - - _addUnit(u); - return Nothing(); -} - -Result Driver::_resolveUnitsWithPlugin(const Plugin& plugin, std::vector> units, - int& round) { - HILTI_DEBUG(logging::debug::Compiler, - fmt("resolving units with plugin %s: %s", plugin.component, - util::join(util::transform(units, [](const auto& u) { return u->uniqueID(); }), ", "))); - - logging::DebugPushIndent _(logging::debug::Compiler); - - for ( const auto& u : units ) { - // Double-check that we only get units for the provided plugin. - assert(u->extension() == plugin.extension); - _dumpAST(u, logging::debug::AstOrig, plugin, "Original AST", 0); - _saveIterationAST(u, plugin, "AST before first iteration", 0); - } - - if ( ! options().skip_validation ) { - bool have_errors = false; - for ( const auto& u : units ) { - if ( ! u->validateASTPre(plugin) ) - have_errors = true; - } - - if ( have_errors || logger().errors() ) - return result::Error("aborting after errors"); - } - - int extra_rounds = 0; // set to >0 for debugging - - while ( true ) { - HILTI_DEBUG(logging::debug::Compiler, fmt("processing ASTs, round %d", round)); - logging::DebugPushIndent _(logging::debug::Compiler); - - bool modified = false; - std::vector> dependencies; - - for ( auto&& u : units ) - u->resetAST(); - - for ( auto&& u : units ) { - auto rc = u->buildASTScopes(plugin); - if ( ! rc ) - return rc.error(); - } - - for ( auto&& u : units ) { - auto rc = u->resolveAST(plugin); - if ( ! rc ) - return rc.error(); - - for ( const auto& d : u->dependencies() ) { - if ( std::find(dependencies.begin(), dependencies.end(), d.lock()) == dependencies.end() ) - dependencies.push_back(d.lock()); - } - - _dumpAST(u, logging::debug::AstResolved, plugin, "AST after resolving", round); - _saveIterationAST(u, plugin, "AST after resolving", round); - modified = modified || (*rc == Unit::Modified); - } - - // Check for newly encountered dependencies that we need to compile as well. - for ( const auto& d : dependencies ) { - if ( d->isResolved() ) - continue; - - if ( std::find(units.begin(), units.end(), d) == units.end() ) { - HILTI_DEBUG(logging::debug::Compiler, - fmt("new dependency to process: %s (%s)", d->uniqueID(), d->extension())); - units.push_back(d); - modified = true; - } - } - - if ( ! modified && extra_rounds-- == 0 ) - break; - - if ( ++round >= 50 ) - logger().internalError("hilti::Unit::compile() didn't terminate, AST keeps changing"); - } - - for ( const auto& u : units ) { - _dumpAST(u, logging::debug::AstFinal, plugin, "Final AST", round); - _dumpDeclarations(u, plugin); - _saveIterationAST(u, plugin, "Final AST", round); - - if ( _driver_options.dump_code ) - dumpUnit(*u); // may be overwritten again later after optimization - } - - if ( ! options().skip_validation ) { - bool have_errors = false; - for ( const auto& u : units ) { - if ( ! u->validateASTPost(plugin) ) - have_errors = true; - } - - if ( have_errors || logger().errors() ) - return result::Error("aborting after errors"); - } - - for ( const auto& u : units ) { - HILTI_DEBUG(logging::debug::Compiler, fmt("finalized module %s", u->uniqueID())); - u->setResolved(true); - - if ( u->dependencies().size() ) { - logging::DebugPushIndent _(logging::debug::Compiler); - HILTI_DEBUG(logging::debug::Compiler, - fmt("dependencies: %s", - util::join(util::transform(u->dependencies(), - [](const auto& u) { return u.lock()->uniqueID(); }), - ", "))); - } - - hookNewASTPostCompilation(u); - } - - if ( auto rc = hookCompilationFinished(plugin); ! rc ) - return augmentError(rc.error()); - - if ( _driver_options.execute_code && ! _driver_options.skip_dependencies ) { - // Compile any implicit dependencies as well. - for ( const auto& unit : units ) { - for ( const auto& d : unit->dependencies() ) { - if ( auto rc = addInput(d.lock()); ! rc ) - return rc.error(); - } - } - } - - return Nothing(); -} - -Result Driver::_transformUnitsWithPlugin(const Plugin& plugin, - const std::vector>& units) { - if ( ! plugin.ast_transform ) - return Nothing(); - - HILTI_DEBUG(logging::debug::Compiler, - fmt("transforming units with plugin %s: %s", plugin.component, - util::join(util::transform(units, [](const auto& u) { return u->uniqueID(); }), ", "))); - - logging::DebugPushIndent _(logging::debug::Compiler); - - for ( const auto& unit : units ) { - if ( auto rc = unit->transformAST(plugin); ! rc ) - return rc; - - unit->setResolved(false); - context()->cacheUnit(unit); - - _dumpAST(unit, logging::debug::AstTransformed, plugin, "Transformed AST", 0); - _saveIterationAST(unit, plugin, "Transformed AST", 0); - - if ( logger().isEnabled(logging::debug::AstPrintTransformed) ) - hilti::print(std::cout, unit->module()); - - if ( logger().errors() ) - return result::Error("aborting after errors"); - } - - return Nothing(); -} - -/** - * Filters a set of units for those associated with a specified plugin - * extension. For those matching, includes all their dependencies for the same - * extension as well. One can choose to have only fully resolved units - * considered. +/* + * Result Driver::addInput(const std::shared_ptr& u) { + * if ( _processed_units.find(u->uniqueID()) != _processed_units.end() ) + * return Nothing(); + * + * if ( ! u->path().empty() && _processed_paths.find(u->path()) != _processed_paths.end() ) + * return Nothing(); + * + * // Calling hook before stage check so that it can execute initialize() + * // just in time if it so desires. + * hookAddInput(u); + * + * if ( _stage == Stage::UNINITIALIZED ) + * logger().internalError(" driver must be initialized before inputs can be added"); + * + * if ( _stage != Stage::INITIALIZED ) + * logger().internalError("no further inputs can be added after compilation has finished already"); + * + * _addUnit(u); + * return Nothing(); + * } */ -static auto _unitsForPlugin(const std::vector>& units, const std::string& extension, - bool include_resolved) { - auto cmp = [](const auto& u1, const auto& u2) { return u1->uniqueID() < u2->uniqueID(); }; - std::set, decltype(cmp)> nunits(cmp); - - for ( auto&& u : units ) { - if ( u->extension() == extension && (! u->isResolved() || include_resolved) ) { - nunits.insert(u); - - for ( const auto& d_ : u->dependencies(true) ) { - auto d = d_.lock(); - assert(d); - if ( d->extension() == extension && (! d->isResolved() || include_resolved) ) - nunits.insert(d); - } - } - } - - std::vector> nunits_vec; - nunits_vec.reserve(nunits.size()); - for ( auto&& u : nunits ) - nunits_vec.push_back(u); - - return nunits_vec; -} - -Result Driver::_resolveUnits() { - if ( _stage != Stage::INITIALIZED ) - logger().internalError("unexpected driver stage in compileUnits()"); - - int round = 0; - - auto plugin = plugin::registry().plugins().begin(); - while ( plugin != plugin::registry().plugins().end() ) { - // Get remaining units that are relevant for the current plugin. Note - // that the list of pending units may change during this loop if more - // input files get added. If that happens, we will process any new ones - // that are not associated with plugins that we have already finish - // with. - if ( auto units = _unitsForPlugin(_pending_units, plugin->extension, false); units.size() ) { - if ( auto rc = _resolveUnitsWithPlugin(*plugin, units, round); ! rc ) - return rc; - } - else { - // All done, switch to next plugin, but first perform any pending - // transformations. - auto all_units = _unitsForPlugin(_pending_units, plugin->extension, true); - if ( auto rc = _transformUnitsWithPlugin(*plugin, all_units); ! rc ) - return rc; - - ++plugin; - - context()->dumpUnitCache(logging::debug::AstCache); - } - } - - for ( const auto& unit : _pending_units ) { - // We should have only fully resolved HILTI modules now. - if ( unit->extension() != ".hlt" ) - return result::Error(fmt("module %s was not compiled down to HILTI", unit->id())); - - if ( ! unit->isResolved() ) - return result::Error(fmt("module %s was not marked as resolved", unit->id())); - - if ( unit->requiresCompilation() ) - _hlts.push_back(unit); - } - - _stage = Stage::COMPILED; - return Nothing(); -} Result Driver::_codegenUnits() { if ( _stage != Stage::COMPILED ) logger().internalError("unexpected driver stage in codegenUnits()"); - for ( auto& unit : _hlts ) - _dumpAST(unit, logging::debug::AstCodegen, "Before C++ codegen"); + context()->astContext()->dumpAST(logging::debug::AstCodegen, "Before C++ codegen"); - if ( _driver_options.output_hilti && ! _driver_options.include_linker ) { + if ( _driver_options.output_hilti && ! _driver_options.include_linker ) // No need to kick off code generation. return Nothing(); - } logging::DebugPushIndent _(logging::debug::Compiler); - for ( auto& unit : _hlts ) { - HILTI_DEBUG(logging::debug::Driver, fmt("codegen for input unit %s", unit->uniqueID())); + for ( auto& [uid, unit] : _units ) { + if ( ! unit->isCompiledHILTI() ) + continue; + + HILTI_DEBUG(logging::debug::Driver, fmt("codegen for input unit %s", unit->uid().str())); if ( auto rc = unit->codegen(); ! rc ) return augmentError(rc.error()); @@ -883,27 +621,11 @@ Result Driver::_codegenUnits() { return Nothing(); } -Result Driver::_optimizeUnits() { - return Nothing(); -#if 0 // TODO - if ( ! _driver_options.global_optimizations ) - return Nothing(); - - HILTI_DEBUG(logging::debug::Driver, "performing global transformations"); - - Optimizer opt(_hlts); - opt.run(); - - return Nothing(); -#endif -} - Result Driver::compileUnits() { - if ( auto rc = _resolveUnits(); ! rc ) + if ( auto rc = context()->astContext()->processAST(); ! rc ) return error(rc.error()); - if ( auto rc = _optimizeUnits(); ! rc ) - return rc; + _stage = COMPILED; if ( _driver_options.output_hilti ) { std::string output_path = (_driver_options.output_path.empty() ? "/dev/stdout" : _driver_options.output_path); @@ -911,18 +633,19 @@ Result Driver::compileUnits() { if ( ! output ) return error(output.error()); - for ( auto& unit : _hlts ) { + for ( auto& [uid, unit] : _units ) { if ( ! unit->isCompiledHILTI() ) continue; - HILTI_DEBUG(logging::debug::Driver, util::fmt("saving HILTI code for module %s", unit->uniqueID())); + HILTI_DEBUG(logging::debug::Driver, util::fmt("saving HILTI code for module %s", unit->uid().str())); if ( ! unit->print(*output) ) - return error(fmt("error print HILTI code for module %s", unit->id())); + return error(fmt("error print HILTI code for module %s", unit->uid().str())); } } - + else { if ( auto rc = _codegenUnits(); ! rc ) return error(rc.error()); + } return Nothing(); } @@ -1004,9 +727,6 @@ Result Driver::compile() { } } - _pending_units.clear(); - _hlts.clear(); - return Nothing(); } @@ -1060,7 +780,7 @@ Result Driver::linkUnits() { dumpUnit(**linker_unit); if ( (*linker_unit)->cxxCode()->code() && (*linker_unit)->cxxCode()->code()->size() ) - _hlts.push_back(*linker_unit); + _units.emplace((*linker_unit)->uid(), *linker_unit); return Nothing(); } @@ -1072,7 +792,10 @@ Result Driver::outputUnits() { std::string output_path = (_driver_options.output_path.empty() ? "/dev/stdout" : _driver_options.output_path); bool append = false; - for ( auto& unit : _hlts ) { + for ( auto& [uid, unit] : _units ) { + if ( ! unit->isCompiledHILTI() ) + continue; + if ( auto cxx = unit->cxxCode() ) { if ( _driver_options.output_cxx ) { auto cxx_path = output_path; @@ -1093,7 +816,7 @@ Result Driver::outputUnits() { return output.error(); HILTI_DEBUG(logging::debug::Driver, - fmt("saving C++ code for module %s to %s", unit->uniqueID(), cxx_path)); + fmt("saving C++ code for module %s to %s", unit->uid().str(), cxx_path)); cxx->save(*output); } @@ -1103,17 +826,22 @@ Result Driver::outputUnits() { return output.error(); HILTI_DEBUG(logging::debug::Driver, - fmt("saving C++ prototypes for module %s to %s", unit->uniqueID(), output_path)); + fmt("saving C++ prototypes for module %s to %s", unit->uid().str(), output_path)); unit->createPrototypes(*output); } if ( _driver_options.output_dependencies != driver::Dependencies::None ) { const bool code_only = (_driver_options.output_dependencies == driver::Dependencies::Code); - for ( const auto& unit_ : - context()->lookupDependenciesForUnit(unit->cacheIndex(), unit->extension()) ) { - auto unit = unit_.lock(); - if ( code_only && ! unit->requiresCompilation() ) + auto deps = context()->astContext()->dependencies(unit->uid()); + + // TODO: I don't think the following does anything??? + for ( const auto& d : deps ) { + auto dependent_unit = _units.find(d); + if ( dependent_unit == _units.end() ) + continue; + + if ( code_only && ! dependent_unit->second->requiresCompilation() ) continue; } } @@ -1125,7 +853,7 @@ Result Driver::outputUnits() { append = _driver_options.output_cxx_prefix.empty(); } else - return error(fmt("error retrieving C++ code for module %s", unit->id())); + return error(fmt("error retrieving C++ code for module %s", unit->uid().str())); } return Nothing(); @@ -1255,75 +983,3 @@ Result Driver::finishRuntime() { return Nothing(); } - -void Driver::_dumpAST(const std::shared_ptr& unit, const logging::DebugStream& stream, const Plugin& plugin, - const std::string& prefix, int round) { - if ( ! logger().isEnabled(stream) ) - return; - - std::string r; - - if ( round > 0 ) - r = fmt(" (round %d)", round); - - HILTI_DEBUG(stream, fmt("# [%s] %s: %s%s", pluginForUnit(unit).get().component, unit->uniqueID(), prefix, r)); - detail::renderNode(unit->module(), stream, true); -} - -void Driver::_dumpAST(const std::shared_ptr& unit, std::ostream& stream, const Plugin& plugin, - const std::string& prefix, int round) { - std::string r; - - if ( round > 0 ) - r = fmt(" (round %d)", round); - - stream << fmt("# [%s] %s: %s%s\n", pluginForUnit(unit).get().component, unit->uniqueID(), prefix, r); - detail::renderNode(unit->module(), stream, true); -} - -void Driver::_dumpAST(const std::shared_ptr& unit, const logging::DebugStream& stream, - const std::string& prefix) { - if ( ! logger().isEnabled(stream) ) - return; - - HILTI_DEBUG(stream, fmt("# %s: %s\n", unit->uniqueID(), prefix)); - detail::renderNode(unit->module(), stream, true); -} - -void Driver::_dumpDeclarations(const std::shared_ptr& unit, const Plugin& plugin) { - if ( ! logger().isEnabled(logging::debug::AstDeclarations) ) - return; - - logger().debugSetIndent(logging::debug::AstDeclarations, 0); - HILTI_DEBUG(logging::debug::AstDeclarations, - fmt("# [%s] %s", pluginForUnit(unit).get().component, unit->uniqueID())); - - auto nodes = visitor::RangePreOrder(unit->module()); - for ( auto i = nodes.begin(); i != nodes.end(); ++i ) { - auto decl = (*i)->tryAs(); - if ( ! decl ) - continue; - - logger().debugSetIndent(logging::debug::AstDeclarations, i.depth() - 1); - HILTI_DEBUG(logging::debug::AstDeclarations, - fmt("- %s \"%s\" (%s)", ID((*i)->typename_()).local(), decl->id(), decl->canonicalID())); - } -} - -void Driver::_saveIterationAST(const std::shared_ptr& unit, const Plugin& plugin, const std::string& prefix, - int round = 0) { - if ( ! logger().isEnabled(logging::debug::AstDumpIterations) ) - return; - - std::ofstream out(fmt("ast-%s-%s-%d.tmp", plugin.component, unit->id(), round)); - _dumpAST(unit, out, plugin, prefix, round); -} - -void Driver::_saveIterationAST(const std::shared_ptr& unit, const Plugin& plugin, const std::string& prefix, - const std::string& tag) { - if ( ! logger().isEnabled(logging::debug::AstDumpIterations) ) - return; - - std::ofstream out(fmt("ast-%s-%s.tmp", plugin.component, tag)); - _dumpAST(unit, out, plugin, prefix, 0); -} diff --git a/hilti/toolchain/src/compiler/parser/driver.cc b/hilti/toolchain/src/compiler/parser/driver.cc index bafa16f0f..e625d1c3c 100644 --- a/hilti/toolchain/src/compiler/parser/driver.cc +++ b/hilti/toolchain/src/compiler/parser/driver.cc @@ -15,11 +15,11 @@ using namespace hilti; using namespace hilti::detail::parser; -Result hilti::parseSource(Builder* builder, std::istream& in, const std::string& filename) { +Result hilti::parseSource(Builder* builder, std::istream& in, const std::string& filename) { return Driver().parse(builder, in, filename); } -Result Driver::parse(Builder* builder, std::istream& in, const std::string& filename) { +Result Driver::parse(Builder* builder, std::istream& in, const std::string& filename) { _builder = builder; auto old_errors = logger().errors(); diff --git a/hilti/toolchain/src/compiler/parser/parser.yy b/hilti/toolchain/src/compiler/parser/parser.yy index 13eacdf9a..822e4b0e9 100644 --- a/hilti/toolchain/src/compiler/parser/parser.yy +++ b/hilti/toolchain/src/compiler/parser/parser.yy @@ -262,7 +262,8 @@ static hilti::UnqualifiedTypePtr viewForType(hilti::Builder* builder, hilti::Qua %start module; module : MODULE local_id '{' - global_scope_items '}' { auto m = builder->module($2, std::move($4.first), std::move($4.second), __loc__); + global_scope_items '}' { auto uid = module::UID($2, hilti::rt::filesystem::path(*driver->currentFile())); + auto m = builder->module(uid, {}, std::move($4.first), std::move($4.second), __loc__); driver->setDestinationModule(std::move(m)); } ; @@ -595,7 +596,7 @@ base_type : base_type_no_attrs { $$ = $1; } type : base_type { $$ = std::move($1); } | function_type { $$ = std::move($1); } - | scoped_id { $$ = builder->typeUnresolvedID(std::move($1)); } + | scoped_id { $$ = builder->typeName(std::move($1)); } ; qtype : type { $$ = builder->qualifiedType(std::move($1), false, __loc__); } diff --git a/hilti/toolchain/src/compiler/plugin.cc b/hilti/toolchain/src/compiler/plugin.cc index 9ebe94f57..c70d22653 100644 --- a/hilti/toolchain/src/compiler/plugin.cc +++ b/hilti/toolchain/src/compiler/plugin.cc @@ -52,45 +52,41 @@ Plugin hilti::detail::create_hilti_plugin() { .extension = ".hlt", .cxx_includes = {"hilti/rt/libhilti.h"}, - .library_paths = - [](const std::shared_ptr& ctx) { return hilti::configuration().hilti_library_paths; }, + .library_paths = [](hilti::Context* ctx) { return hilti::configuration().hilti_library_paths; }, - .parse = [](Builder* builder, std::istream& in, const hilti::rt::filesystem::path& path) { return parseSource(builder, in, path); }, + .parse = [](Builder* builder, std::istream& in, + const hilti::rt::filesystem::path& path) { return parseSource(builder, in, path); }, -#if 0 - .coerce_ctor = [](Ctor c, const TypePtr& dst, - bitmask style) { return detail::coerceCtor(std::move(c), dst, style); }, + .coerce_ctor = [](Builder* builder, const CtorPtr& c, const QualifiedTypePtr& dst, + bitmask style) { return detail::coerceCtor(builder, c, dst, style); }, - .coerce_type = [](Type t, const TypePtr& dst, - bitmask style) { return detail::coerceType(std::move(t), dst, style); }, + .coerce_type = [](Builder* builder, const QualifiedTypePtr& t, const QualifiedTypePtr& dst, + bitmask style) { return detail::coerceType(builder, t, dst, style); }, .ast_build_scopes = - [](const std::shared_ptr& ctx, Node* m, Unit* u) { - ast::buildScopes(ctx, m, u); + [](Builder* ctx, const ASTRootPtr& root) { + ast::buildScopes(ctx, root); return false; }, - .ast_normalize = [](const std::shared_ptr& ctx, Node* m, - Unit* u) { return ast::normalize(m, u); }, + .ast_normalize = [](Builder* ctx, const ASTRootPtr& root) { return ast::normalize(ctx, root); }, - .ast_coerce = [](const std::shared_ptr& ctx, Node* m, Unit* u) { return ast::coerce(m, u); }, + .ast_coerce = [](Builder* ctx, const ASTRootPtr& root) { return ast::coerce(ctx, root); }, - .ast_resolve = [](const std::shared_ptr& ctx, Node* m, - Unit* u) { return ast::resolve(ctx, m, u); }, + .ast_resolve = [](Builder* ctx, const ASTRootPtr& root) { return ast::resolve(ctx, root); }, .ast_validate_pre = - [](const std::shared_ptr& ctx, Node* m, Unit* u) { - ast::validate_pre(m); + [](ASTContext* ctx, const ASTRootPtr& m) { + ast::validate_pre(ctx, m); return false; }, .ast_validate_post = - [](const std::shared_ptr& ctx, Node* m, Unit* u) { - ast::validate_post(m); + [](ASTContext* ctx, const ASTRootPtr& root) { + ast::validate_post(ctx, root); return false; }, .ast_transform = {}, -#endif }; } diff --git a/hilti/toolchain/src/compiler/unit.cc b/hilti/toolchain/src/compiler/unit.cc index 5a9a6b909..f369ba826 100644 --- a/hilti/toolchain/src/compiler/unit.cc +++ b/hilti/toolchain/src/compiler/unit.cc @@ -3,18 +3,15 @@ #include #include -#include +#include #include -#include -#include +#include #include +#include #include #include -#include #include -#include "base/timing.h" - using namespace hilti; using namespace hilti::context; using util::fmt; @@ -24,276 +21,92 @@ inline const DebugStream AstCodegen("ast-codegen"); inline const DebugStream Compiler("compiler"); } // namespace hilti::logging::debug -std::unordered_map Unit::_uid_cache; - -template -bool runHook(bool* modified, const Plugin& plugin, const ModulePtr& module, const std::string& extension, - PluginMember hook, const std::string& debug_msg, const Args&... args) { - if ( ! (plugin.*hook) ) - return true; - - auto p = plugin::registry().pluginForExtension(extension); - if ( ! p ) - logger().internalError(util::fmt("no plugin for unit extension %s: %s", extension, p.error())); - - if ( p->get().component != plugin.component ) - return true; - - auto msg = fmt("[%s] %s", plugin.component, debug_msg); - - HILTI_DEBUG(logging::debug::Compiler, msg); - if ( (*(plugin.*hook))(args...) ) { - *modified = true; - HILTI_DEBUG(logging::debug::Compiler, " -> modified"); - } - - return logger().errors() == 0; -} - -ID Unit::_makeUniqueID(const ID& id) { - if ( auto i = _uid_cache.find(id); i != _uid_cache.end() ) - return ID(util::fmt("%s_%s", id, ++(i->second))); - else { - _uid_cache[id] = 1; - return id; - } -} - -Unit::~Unit() { _destroyModule(); } - -Result> Unit::fromCache(const std::shared_ptr& context, - const hilti::rt::filesystem::path& path, const std::optional& scope) { - if ( auto cached = context->lookupUnit(path, scope) ) - return cached->unit; - else - return result::Error(fmt("unknown module %s", path)); -} Result> Unit::fromSource(const std::shared_ptr& context, - const hilti::rt::filesystem::path& path, const std::optional& scope, - std::optional process_extension) { - if ( auto cached = context->lookupUnit(path, scope, process_extension) ) - return cached->unit; - - auto module = _parse(context, path); - if ( ! module ) - return module.error(); - - if ( ! process_extension ) - process_extension = path.extension(); - - auto id = (*module)->as()->id(); - auto unit = std::shared_ptr( - new Unit(context, id, scope, path, *process_extension, *module)); // no make_shared, ctor is private - - context->cacheUnit(unit); - - return unit; -} - -std::shared_ptr Unit::fromModule(const std::shared_ptr& context, const ModulePtr& module, - hilti::rt::filesystem::path extension) { - auto unit = std::shared_ptr(new Unit(context, module->id(), {}, {}, std::move(extension), module)); - context->cacheUnit(unit); - return unit; -} - -Result> Unit::fromImport(const std::shared_ptr& context, const ID& id, - const hilti::rt::filesystem::path& parse_extension, - const hilti::rt::filesystem::path& process_extension, - std::optional scope, - std::vector search_dirs) { - if ( auto cached = context->lookupUnit(id, scope, process_extension) ) - return cached->unit; - - auto parse_plugin = plugin::registry().pluginForExtension(parse_extension); - - if ( ! (parse_plugin && parse_plugin->get().parse) ) - return result::Error(fmt("no plugin provides support for importing *%s files", parse_extension.native())); - - auto name = fmt("%s%s", util::tolower(id), parse_extension.native()); - - if ( scope ) - name = fmt("%s/%s", util::replace(scope->str(), ".", "/"), name); - - std::vector library_paths = std::move(search_dirs); - - if ( parse_plugin->get().library_paths ) - library_paths = util::concat(std::move(library_paths), (*parse_plugin->get().library_paths)(context)); - - library_paths = util::concat(context->options().library_paths, library_paths); - - auto path = util::findInPaths(name, library_paths); - if ( ! path ) { - HILTI_DEBUG(logging::debug::Compiler, fmt("Failed to find module '%s' in search paths:", name)); - for ( const auto& p : library_paths ) - HILTI_DEBUG(logging::debug::Compiler, fmt(" %s", p)); - - return result::Error(fmt("cannot find file")); - } - - auto unit = fromSource(context, *path, scope, process_extension); - if ( ! unit ) - return unit; - - if ( (*unit)->id() != id ) - return result::Error( - util::fmt("file %s does not contain expected module %s (but %s)", path->native(), id, (*unit)->id())); + const hilti::rt::filesystem::path& path) { + auto uid = context->astContext()->parseSource(path); + if ( ! uid ) + return uid.error(); - return unit; + return std::shared_ptr(new Unit(context, *uid)); } -Result> Unit::fromCXX(const std::shared_ptr& context, detail::cxx::Unit cxx, +Result> Unit::fromCXX(const std::shared_ptr& context, const detail::cxx::Unit& cxx, const hilti::rt::filesystem::path& path) { - return std::shared_ptr( - new Unit(context, ID(fmt("", path.native())), {}, ".cxx", path, std::move(cxx))); + auto uid = module::UID({}, path.native()); + return std::shared_ptr(new Unit(context, uid, cxx)); } -Result Unit::_parse(const std::shared_ptr& context, const hilti::rt::filesystem::path& path) { - util::timing::Collector _("hilti/compiler/parser"); - - std::ifstream in; - in.open(path); - - if ( ! in ) - return result::Error(fmt("cannot open source file %s", path)); - - auto plugin = plugin::registry().pluginForExtension(path.extension()); - - if ( ! (plugin && plugin->get().parse) ) - return result::Error(fmt("no plugin provides support for importing *%s files", path.extension().native())); +Unit::~Unit() {} - auto dbg_message = fmt("parsing file %s as %s code", path, plugin->get().component); - - if ( plugin->get().component != "HILTI" ) - dbg_message += fmt(" (%s)", plugin->get().component); - - HILTI_DEBUG(logging::debug::Compiler, dbg_message); - - auto builder = std::make_unique(context->astContext()); - auto module = (*plugin->get().parse)(builder.get(), in, path); - - if ( module && ! (*module)->as()->id() ) - return result::Error(fmt("module in %s does not have an ID", path.native())); - - return module; -} - -Result Unit::buildASTScopes(const Plugin& plugin) { - if ( ! _module ) - return Nothing(); +ModulePtr Unit::module() const { return context()->astContext()->getModule(_uid); } - bool modified = false; // not used +bool Unit::isCompiledHILTI() const { return _uid.id && context()->astContext()->getModule(_uid) != nullptr; } - if ( ! runHook(&modified, plugin, _module->as(), _extension, &Plugin::ast_build_scopes, - fmt("building scopes for module %s", uniqueID()), context(), _module, this) ) - return result::Error("errors encountered during scope building"); +Result Unit::print(std::ostream& out) const { + if ( auto m = module() ) + detail::printAST(module(), out); return Nothing(); } -Result Unit::resolveAST(const Plugin& plugin) { - bool modified = false; - - if ( ! runHook(&modified, plugin, _module->as(), _extension, &Plugin::ast_normalize, - fmt("normalizing nodes in module %s", uniqueID()), context(), _module, this) ) - return result::Error("errors encountered during normalizing"); - - if ( ! runHook(&modified, plugin, _module->as(), _extension, &Plugin::ast_coerce, - fmt("coercing nodes in module %s", uniqueID()), context(), _module, this) ) - return result::Error("errors encountered during coercing"); - - if ( ! runHook(&modified, plugin, _module->as(), _extension, &Plugin::ast_resolve, - fmt("resolving nodes in module %s", uniqueID()), context(), _module, this) ) - return result::Error("errors encountered during resolving"); - - return modified ? Modified : NotModified; -} - -bool Unit::validateASTPre(const Plugin& plugin) { - if ( ! _module ) - return true; - - bool modified = false; // not used - runHook(&modified, plugin, _module->as(), _extension, &Plugin::ast_validate_pre, - fmt("validating module %s (pre)", uniqueID()), context(), _module, this); +Result Unit::createPrototypes(std::ostream& out) { + if ( ! _cxx_unit ) + return result::Error("no C++ code available for unit"); - return _collectErrors(); + return _cxx_unit->createPrototypes(out); } -bool Unit::validateASTPost(const Plugin& plugin) { - if ( ! _module ) - return true; +Result Unit::_codegenModule(const module::UID& uid) { + auto module = context()->astContext()->getModule(_uid); + assert(module); - bool modified = false; // not used - runHook(&modified, plugin, _module->as(), _extension, &Plugin::ast_validate_post, - fmt("validating module %s (post)", uniqueID()), context(), _module, this); + HILTI_DEBUG(logging::debug::Compiler, fmt("compiling module %s to C++", _uid)); + logging::DebugPushIndent _(logging::debug::Compiler); - return _collectErrors(); -} + auto cxx = detail::CodeGen(context()).compileModule(module, this, true); -Result Unit::transformAST(const Plugin& plugin) { - if ( ! _module ) - return Nothing(); + if ( logger().errors() ) + return result::Error("errors encountered during code generation"); - bool modified = false; - runHook(&modified, plugin, _module->as(), _extension, &Plugin::ast_transform, - fmt("transforming module %s", uniqueID()), context(), _module, this); + if ( ! cxx ) + logger().internalError( + fmt("code generation for module %s failed, but did not log error (%s)", _uid, cxx.error().description())); - return Nothing(); + return cxx; } Result Unit::codegen() { - assert(false && "TODO"); -#if 0 - if ( ! _module ) + if ( ! _uid ) return Nothing(); - HILTI_DEBUG(logging::debug::Compiler, fmt("compiling module %s to C++", uniqueID())); - logging::DebugPushIndent _(logging::debug::Compiler); - - // Compile to C++. - auto c = detail::CodeGen(context()).compileModule(_module, this, true); - - if ( logger().errors() ) - return result::Error("errors encountered during code generation"); - - if ( ! c ) - logger().internalError(fmt("code generation for module %s failed, but did not log error (%s)", uniqueID(), - c.error().description())); + auto cxx = _codegenModule(_uid); + if ( ! cxx ) + return cxx.error(); // Import declarations from our dependencies. They will have been compiled // at this point. // // TODO(robin): Would be nice if we had a "cheap" compilation mode that // only generated declarations. - for ( const auto& unit : dependencies(true) ) { - HILTI_DEBUG(logging::debug::Compiler, fmt("importing declarations from module %s", unit.lock()->uniqueID())); - auto other = detail::CodeGen(context()).compileModule(module(), unit.lock().get(), false); - c->importDeclarations(*other); + for ( const auto& d : dependencies(true) ) { + HILTI_DEBUG(logging::debug::Compiler, fmt("importing declarations from module %s", d)); + if ( auto other_cxx = _codegenModule(d) ) + cxx->importDeclarations(*other_cxx); + else + return other_cxx.error(); } - HILTI_DEBUG(logging::debug::Compiler, fmt("finalizing module %s", uniqueID())); - if ( auto x = c->finalize(); ! x ) + HILTI_DEBUG(logging::debug::Compiler, fmt("finalizing module %s", _uid)); + if ( auto x = cxx->finalize(); ! x ) return x.error(); - _cxx_unit = *c; - return Nothing(); -#endif -} - -Result Unit::print(std::ostream& out) const { - if ( _module ) - detail::printAST(_module, out); - + _cxx_unit = *cxx; return Nothing(); } -Result Unit::createPrototypes(std::ostream& out) { - if ( ! _cxx_unit ) - return result::Error("no C++ code available for unit"); - - return _cxx_unit->createPrototypes(out); +std::vector Unit::dependencies(bool recursive) const { + return context()->astContext()->dependencies(_uid, recursive); } Result Unit::cxxCode() const { @@ -309,137 +122,31 @@ Result Unit::cxxCode() const { return CxxCode{_cxx_unit->moduleID(), cxx}; } -void Unit::_recursiveDependencies(std::vector>* dst, std::unordered_set* seen) const { - // This uses two vectors because the weak_ptr are a bit tough to work with, - // in particular they can't be compared through std::find(). - - for ( const auto& d : _dependencies ) { - auto dptr = d.lock().get(); - - if ( seen->find(dptr) != seen->end() ) - continue; - - dst->push_back(d); - seen->insert(dptr); - dptr->_recursiveDependencies(dst, seen); - } -} - -std::vector> Unit::dependencies(bool recursive) const { - if ( ! recursive ) - return _dependencies; - - std::vector> deps; - std::unordered_set seen; - _recursiveDependencies(&deps, &seen); - return deps; -} - -bool Unit::addDependency(const std::shared_ptr& unit) { - for ( const auto& d : _dependencies ) { - if ( d.lock().get() == unit.get() ) - return false; - } - - _dependencies.push_back(unit); - return true; -} - bool Unit::requiresCompilation() { if ( _requires_compilation ) return true; + auto m = module(); + if ( ! m ) + return false; + // Visitor that goes over an AST and flags whether any node provides // code that needs compilation. struct Visitor : hilti::visitor::PreOrder { bool result = false; - void operator()(const declaration::GlobalVariable& n) final { result = true; } - void operator()(const declaration::Function& n) final { - if ( n.function()->body() ) + void operator()(declaration::GlobalVariable* n) final { result = true; } + void operator()(declaration::Function* n) final { + if ( n->function()->body() ) result = true; } }; - auto v = Visitor(); - v.visitAll(_module); - return v.result; - } - -static node::ErrorPriority _recursiveValidateAST(const NodePtr& n, Location closest_location, node::ErrorPriority prio, - int level, std::vector* errors) { - if ( n->location() ) - closest_location = n->location(); - - if ( ! n->pruneWalk() ) { - auto oprio = prio; - for ( const auto& c : n->children() ) { - if ( c ) - prio = std::max(prio, _recursiveValidateAST(c, closest_location, oprio, level + 1, errors)); - } - } - - auto errs = n->errors(); - auto nprio = prio; - for ( auto& err : errs ) { - if ( ! err.location && closest_location ) - err.location = closest_location; - - if ( err.priority > prio ) - errors->push_back(err); - - nprio = std::max(nprio, err.priority); - } - - return nprio; -} - -static void _reportErrors(const std::vector& errors) { - // We only report the highest priority error category. - std::set reported; - - auto prios = {node::ErrorPriority::High, node::ErrorPriority::Normal, node::ErrorPriority::Low}; - - for ( auto p : prios ) { - for ( const auto& e : errors ) { - if ( e.priority != p ) - continue; - - if ( reported.find(e) == reported.end() ) { - logger().error(e.message, e.context, e.location); - reported.insert(e); - } - } - - if ( reported.size() ) - break; - } -} - -bool Unit::_collectErrors() { - std::vector errors; - _recursiveValidateAST(_module, Location(), node::ErrorPriority::NoError, 0, &errors); - - if ( errors.size() ) { - _reportErrors(errors); - return false; - } - - return true; -} - -void Unit::_destroyModule() { - if ( ! _module ) - return; - - _module->destroyChildren(); - _module = {}; + return visitor::visit(Visitor(), m, [](const auto& v) { return v.result; }); } Result> Unit::link(const std::shared_ptr& context, const std::vector& mds) { - assert(false && "TODO"); -#if 0 HILTI_DEBUG(logging::debug::Compiler, fmt("linking %u modules", mds.size())); auto cxx_unit = detail::CodeGen(context).linkUnits(mds); @@ -447,7 +154,6 @@ Result> Unit::link(const std::shared_ptr& context return result::Error("no C++ code available for unit"); return fromCXX(context, *cxx_unit, ""); -#endif } std::pair> Unit::readLinkerMetaData(std::istream& input, @@ -455,16 +161,3 @@ std::pair> Unit::readLinkerMetaData(std::i HILTI_DEBUG(logging::debug::Compiler, fmt("reading linker data from %s", path)); return detail::cxx::Unit::readLinkerMetaData(input); } - -void Unit::resetAST() { - if ( ! _module ) - return; - - HILTI_DEBUG(logging::debug::Compiler, fmt("resetting nodes for module %s", uniqueID())); - - for ( auto&& i : hilti::visitor::PreOrder().walk(_module) ) { - assert(i); // walk() should not give us null pointer children. - i->clearScope(); - i->clearErrors(); - } -} diff --git a/hilti/toolchain/src/compiler/visitors/coercer.cc b/hilti/toolchain/src/compiler/visitors/coercer.cc index 989ad6132..c60dd68bf 100644 --- a/hilti/toolchain/src/compiler/visitors/coercer.cc +++ b/hilti/toolchain/src/compiler/visitors/coercer.cc @@ -3,16 +3,15 @@ #include #include -#include -#include #include #include #include #include #include +#include "base/timing.h" + using namespace hilti; -using util::fmt; namespace hilti::logging::debug { inline const hilti::logging::DebugStream Coercer("coercer"); @@ -20,11 +19,13 @@ inline const hilti::logging::DebugStream Coercer("coercer"); namespace { -struct Visitor : public visitor::PreOrder { - Visitor(Unit* unit) : unit(unit) {} - Unit* unit; +struct Visitor : visitor::PreOrder { + explicit Visitor(Builder* builder) : builder(builder) {} + + Builder* builder; bool modified = false; +#if 0 // Log debug message recording updating attributes. void logChange(const Node& old, const Node& new_, const char* desc) { HILTI_DEBUG(logging::debug::Coercer, @@ -607,16 +608,14 @@ struct Visitor : public visitor::PreOrder { modified = true; } } +#endif }; } // anonymous namespace -bool hilti::detail::ast::coerce(Node* root, Unit* unit) { +bool hilti::detail::ast::coerce(Builder* builder, const ASTRootPtr& root) { util::timing::Collector _("hilti/compiler/ast/coerce"); - auto v = Visitor(unit); - for ( auto i : v.walk(root) ) - v.dispatch(i); - - return v.modified; + auto v = Visitor(builder); + return hilti::visitor::visit(std::move(v), root, [](const auto& v) { return v.modified; }); } diff --git a/hilti/toolchain/src/compiler/visitors/constant-folder.cc b/hilti/toolchain/src/compiler/visitors/constant-folder.cc index be5cc4e19..2e5e4c56f 100644 --- a/hilti/toolchain/src/compiler/visitors/constant-folder.cc +++ b/hilti/toolchain/src/compiler/visitors/constant-folder.cc @@ -4,15 +4,15 @@ #include -#include #include #include #include #include -#include -#include +// #include +// #include #include #include +#include #include #include @@ -20,25 +20,21 @@ using namespace hilti; namespace { -// Internal version of _foldConstant() that passes expceptions through to caller. -static Result _foldConstant(const Node& expr); +// Internal version of _foldConstant() that passes exceptions through to caller. +static Result _foldConstant(const ExpressionPtr& expr); template -Result foldConstant(const Expression& expr) { - auto ctor = _foldConstant(expr); - if ( ! ctor ) - return ctor.error(); - - if ( auto ctor_ = ctor->tryAs() ) - return *ctor_; - else - return result::Error("unexpected type"); +Result foldConstant(const ExpressionPtr& expr) { + return _foldConstant(expr); } // For now, this is only a very basic constant folder that only covers cases we // need to turn type constructor expressions coming with a single argument into // ctor expressions. -struct VisitorConstantFolder : public visitor::PreOrder, VisitorConstantFolder> { +struct VisitorConstantFolder : public visitor::PreOrder { + CtorPtr result = nullptr; + +#if 0 // Helper to replace an type constructor expression that receives a // constant argument with a corresponding ctor expression. template @@ -329,32 +325,25 @@ struct VisitorConstantFolder : public visitor::PreOrder, Vis return ctor::UnsignedInteger(ctor.value(), 64); }); } +#endif }; -Result _foldConstant(const Node& expr) { - auto v = VisitorConstantFolder(); - - if ( auto ctor = v.dispatch(expr); ctor && *ctor ) - return **ctor; +Result _foldConstant(const NodePtr& expr) { + if ( auto result = hilti::visitor::dispatch(VisitorConstantFolder(), expr, [](const auto& v) { return v.result; }) ) + return result; else return result::Error("not a foldable constant expression"); } } // anonymous namespace -Result> detail::foldConstant(const Node& expr) { - // Don't fold away direct, top-level references to constant IDs. It's - // likely as least as efficient to leave them as is, and potentially more. - if ( expr.isA() ) - return {std::nullopt}; - +Result detail::foldConstant(const ExpressionPtr& expr) { try { - auto v = VisitorConstantFolder(); - - if ( auto ctor = v.dispatch(expr) ) - return *ctor; + if ( auto result = + hilti::visitor::dispatch(VisitorConstantFolder(), expr, [](const auto& v) { return v.result; }) ) + return result; else - return {std::nullopt}; + return {nullptr}; } catch ( const hilti::rt::RuntimeError& e ) { return result::Error(e.what()); } diff --git a/hilti/toolchain/src/compiler/visitors/normalizer.cc b/hilti/toolchain/src/compiler/visitors/normalizer.cc index 702b94546..999df3153 100644 --- a/hilti/toolchain/src/compiler/visitors/normalizer.cc +++ b/hilti/toolchain/src/compiler/visitors/normalizer.cc @@ -1,17 +1,19 @@ // Copyright (c) 2020-2023 by the Zeek Project. See LICENSE for details. +#include #include #include #include #include #include #include -#include #include #include #include #include +#include "base/timing.h" + using namespace hilti; namespace hilti::logging::debug { @@ -19,9 +21,10 @@ inline const hilti::logging::DebugStream Normalizer("normalizer"); } // namespace hilti::logging::debug namespace { -struct VisitorConstants : public visitor::PreOrder { +struct VisitorConstants : visitor::PreOrder { bool modified = false; +#if 0 // Log debug message recording resolving a expression. void logChange(const Node& old, const Ctor& ctor) { HILTI_DEBUG(logging::debug::Normalizer, @@ -49,13 +52,13 @@ struct VisitorConstants : public visitor::PreOrder { p.node = nexpr; modified = true; } +#endif }; -struct VisitorNormalizer : visitor::PreOrder, type::Visitor { - using position_t = visitor::PreOrder::position_t; - +struct VisitorNormalizer : visitor::PreOrder { bool modified = false; +#if 0 // Log debug message recording resolving a expression. void logChange(const Node& old, const Expression& nexpr) { HILTI_DEBUG(logging::debug::Normalizer, @@ -229,29 +232,30 @@ struct VisitorNormalizer : visitor::PreOrder, type::Vis } } } +#endif }; -} // anonymous namespace - -struct VisitorComputeCanonicalIDs; -static void _computeCanonicalIDs(VisitorComputeCanonicalIDs* v, Node* node, ID current); - // Visitor to unset all canonical IDs in preparation for their recalculation. -struct VisitorClearCanonicalIDs : public visitor::PreOrder { +struct VisitorClearCanonicalIDs : visitor::PreOrder { +#if 0 result_t operator()(const Declaration& d, position_t p) { p.node.as().setCanonicalID(ID()); }; +#endif }; // Visitor computing canonical IDs. -struct VisitorComputeCanonicalIDs : public visitor::PreOrder { +struct VisitorComputeCanonicalIDs : visitor::PreOrder { // This visitor runs twice, with slightly different behaviour by pass. VisitorComputeCanonicalIDs(int pass) : pass(pass) { assert(pass == 1 || pass == 2); } int pass; + ID result; + ID parent_id; ID module_id; int ctor_struct_count = 0; Scope* module_scope = nullptr; +#if 0 result_t operator()(const Module& m, position_t p) { module_id = m.id(); module_scope = p.node.scope().get(); @@ -312,22 +316,27 @@ struct VisitorComputeCanonicalIDs : public visitor::PreOrder(d.children().data()), std::move(id)); return {}; } +#endif }; // Visitor double-checking that all declarations have their canonical IDs set. -struct VisitorCheckCanonicalIDs : public visitor::PreOrder { +struct VisitorCheckCanonicalIDs : visitor::PreOrder { +#if 0 result_t operator()(const Declaration& d, position_t p) { if ( ! d.canonicalID() ) hilti::render(std::cerr, p.node); assert(d.canonicalID()); }; +#endif }; -static void _computeCanonicalIDs(VisitorComputeCanonicalIDs* v, Node* node, ID current) { +void _computeCanonicalIDs(VisitorComputeCanonicalIDs* v, const NodePtr& node, ID current) { v->parent_id = current; - if ( auto x = v->dispatch(node) ) - current = *x; + v->result = {}; + v->dispatch(node); + if ( v->result ) + current = v->result; if ( node->pruneWalk() ) return; @@ -338,22 +347,22 @@ static void _computeCanonicalIDs(VisitorComputeCanonicalIDs* v, Node* node, ID c return; for ( auto& c : node->children() ) - _computeCanonicalIDs(v, &c, current); + _computeCanonicalIDs(v, c, current); } -bool hilti::detail::ast::normalize(Node* root, Unit* unit) { +} // namespace + +bool hilti::detail::ast::normalize(Builder* builder, const ASTRootPtr& root) { util::timing::Collector _("hilti/compiler/ast/normalizer"); auto v0 = VisitorConstants(); - for ( auto i : v0.walk(root) ) - v0.dispatch(i); + ::hilti::visitor::visit(v0, root); if ( logger().errors() ) return v0.modified; auto v1 = VisitorNormalizer(); - for ( auto i : v1.walk(root) ) - v1.dispatch(i); + ::hilti::visitor::visit(v1, root); auto v2 = VisitorComputeCanonicalIDs(1); _computeCanonicalIDs(&v2, root, ID()); @@ -363,8 +372,7 @@ bool hilti::detail::ast::normalize(Node* root, Unit* unit) { #ifndef NDEBUG auto v4 = VisitorCheckCanonicalIDs(); - for ( auto i : v4.walk(root) ) - v4.dispatch(i); + ::hilti::visitor::visit(v4, root); #endif return v0.modified || v1.modified; diff --git a/hilti/toolchain/src/compiler/visitors/printer.cc b/hilti/toolchain/src/compiler/visitors/printer.cc index 80fbe165d..964e2a3cc 100644 --- a/hilti/toolchain/src/compiler/visitors/printer.cc +++ b/hilti/toolchain/src/compiler/visitors/printer.cc @@ -144,20 +144,20 @@ struct Printer : visitor::PreOrder { } } - auto const_(const QualifiedType& t) { - return (out.isCompact() && t.isConstant() && type::isMutable(t)) ? "const " : ""; + auto const_(const QualifiedType* t) { + return (out.isCompact() && t->isConstant() && type::isMutable(*t)) ? "const " : ""; } - void operator()(const Attribute& n) final { - out << n.tag(); + void operator()(Attribute* n) final { + out << n->tag(); - if ( n.hasValue() ) - out << "=" << n.value(); + if ( n->hasValue() ) + out << "=" << n->value(); } - void operator()(const AttributeSet& n) final { + void operator()(AttributeSet* n) final { bool first = true; - for ( const auto& a : n.attributes() ) { + for ( const auto& a : n->attributes() ) { if ( ! first ) out << ' '; else @@ -167,35 +167,35 @@ struct Printer : visitor::PreOrder { } } - void operator()(const Function& n) final { - if ( n.callingConvention() != function::CallingConvention::Standard ) - out << to_string(n.callingConvention()) << ' '; + void operator()(Function* n) final { + if ( n->callingConvention() != function::CallingConvention::Standard ) + out << to_string(n->callingConvention()) << ' '; - printFunctionType(*n.ftype(), n.id()); + printFunctionType(*n->ftype(), n->id()); - if ( n.attributes() ) - out << ' ' << std::make_pair(n.attributes()->attributes(), " "); + if ( n->attributes() ) + out << ' ' << std::make_pair(n->attributes()->attributes(), " "); - if ( n.body() ) - out << ' ' << n.body(); + if ( n->body() ) + out << ' ' << n->body(); else out << ';' << out.newline(); } - void operator()(const ID& n) { - if ( n.namespace_() == _currentScope() ) - out << std::string(n.local()); + void operator()(ID* n) { + if ( n->namespace_() == _currentScope() ) + out << std::string(n->local()); else - out << std::string(n); + out << std::string(*n); } - void operator()(const Module& n) final { - printDoc(n.documentation()); + void operator()(Module* n) final { + printDoc(n->documentation()); out.beginLine(); - out << "module " << n.id() << " {" << out.newline(); + out << "module " << n->id() << " {" << out.newline(); out.endLine(); - _pushScope(n.id()); + _pushScope(n->id()); auto printDecls = [&](const auto& decls) { for ( const auto& d : decls ) @@ -205,20 +205,20 @@ struct Printer : visitor::PreOrder { out.emptyLine(); }; - printDecls(util::filter(n.declarations(), + printDecls(util::filter(n->declarations(), [](const auto& d) { return d->template isA(); })); - printDecls(util::filter(n.declarations(), [](const auto& d) { return d->template isA(); })); + printDecls(util::filter(n->declarations(), [](const auto& d) { return d->template isA(); })); printDecls( - util::filter(n.declarations(), [](const auto& d) { return d->template isA(); })); - printDecls(util::filter(n.declarations(), + util::filter(n->declarations(), [](const auto& d) { return d->template isA(); })); + printDecls(util::filter(n->declarations(), [](const auto& d) { return d->template isA(); })); printDecls( - util::filter(n.declarations(), [](const auto& d) { return d->template isA(); })); + util::filter(n->declarations(), [](const auto& d) { return d->template isA(); })); - for ( const auto& s : n.statements()->statements() ) + for ( const auto& s : n->statements()->statements() ) out << s; - if ( ! n.statements()->statements().empty() ) + if ( ! n->statements()->statements().empty() ) out.emptyLine(); _popScope(); @@ -230,89 +230,89 @@ struct Printer : visitor::PreOrder { ////// Ctors - void operator()(const ctor::Address& n) override { out << n.value(); } + void operator()(ctor::Address* n) override { out << n->value(); } - void operator()(const ctor::Bool& n) final { out << (n.value() ? "True" : "False"); } + void operator()(ctor::Bool* n) final { out << (n->value() ? "True" : "False"); } - void operator()(const ctor::Bytes& n) final { out << "b\"" << util::escapeUTF8(n.value(), true) << '"'; } + void operator()(ctor::Bytes* n) final { out << "b\"" << util::escapeUTF8(n->value(), true) << '"'; } - void operator()(const ctor::Coerced& n) final { out << n.originalCtor(); } + void operator()(ctor::Coerced* n) final { out << n->originalCtor(); } - void operator()(const ctor::Default& n) final { - out << "default<" << n.type() << ">(" << std::make_pair(n.typeArguments(), ", ") << ")"; + void operator()(ctor::Default* n) final { + out << "default<" << n->type() << ">(" << std::make_pair(n->typeArguments(), ", ") << ")"; } - void operator()(const ctor::Enum& n) final { - assert(n.type()->type()->typeID()); - out << *n.type()->type()->typeID() << "::" << n.label()->id(); + void operator()(ctor::Enum* n) final { + assert(n->type()->type()->typeID()); + out << *n->type()->type()->typeID() << "::" << n->label()->id(); } - void operator()(const ctor::Error& n) final { out << "error(\"" << n.value() << "\")"; } + void operator()(ctor::Error* n) final { out << "error(\"" << n->value() << "\")"; } - void operator()(const ctor::Interval& n) final { out << "interval_ns(" << n.value().nanoseconds() << ")"; } + void operator()(ctor::Interval* n) final { out << "interval_ns(" << n->value().nanoseconds() << ")"; } - void operator()(const ctor::List& n) final { out << '[' << std::make_pair(n.value(), ", ") << ']'; } + void operator()(ctor::List* n) final { out << '[' << std::make_pair(n->value(), ", ") << ']'; } - void operator()(const ctor::Map& n) final { - auto elems = node::transform(n.value(), + void operator()(ctor::Map* n) final { + auto elems = node::transform(n->value(), [](const auto& e) -> std::string { return fmt("%s: %s", e->key(), e->value()); }); out << "map(" << std::make_pair(elems, ", ") << ')'; } - void operator()(const ctor::Network& n) final { out << n.value(); } + void operator()(ctor::Network* n) final { out << n->value(); } - void operator()(const ctor::Null& n) final { out << "Null"; } + void operator()(ctor::Null* n) final { out << "Null"; } - void operator()(const ctor::Optional& n) final { - if ( n.value() ) - out << n.value(); + void operator()(ctor::Optional* n) final { + if ( n->value() ) + out << n->value(); else out << "Null"; } - void operator()(const ctor::Port& n) final { out << n.value(); } + void operator()(ctor::Port* n) final { out << n->value(); } - void operator()(const ctor::Real& n) final { - // We use hexformat for lossless serialization. Older platforms like + void operator()(ctor::Real* n) final { + // We use hexformat for lossless serialization-> Older platforms like // centos7 have inconsistent support for that in iostreams so we use // C99 snprintf instead. constexpr size_t size = 256; char buf[size]; - assert(std::snprintf(buf, size, "%a", n.value()) >= 0); + assert(std::snprintf(buf, size, "%a", n->value()) >= 0); out << buf; } - void operator()(const ctor::StrongReference& n) final { out << "Null"; } + void operator()(ctor::StrongReference* n) final { out << "Null"; } - void operator()(const ctor::RegExp& n) final { - out << std::make_pair(util::transform(n.value(), [](auto p) { return fmt("/%s/", p); }), " |"); + void operator()(ctor::RegExp* n) final { + out << std::make_pair(util::transform(n->value(), [](auto p) { return fmt("/%s/", p); }), " |"); } - void operator()(const ctor::Result& n) final { - if ( n.value() ) - out << n.value(); + void operator()(ctor::Result* n) final { + if ( n->value() ) + out << n->value(); else - out << n.error(); + out << n->error(); } - void operator()(const ctor::Set& n) final { out << "set(" << std::make_pair(n.value(), ", ") << ')'; } + void operator()(ctor::Set* n) final { out << "set(" << std::make_pair(n->value(), ", ") << ')'; } - void operator()(const ctor::SignedInteger& n) final { - if ( n.width() < 64 ) - out << fmt("int%d(%" PRId64 ")", n.width(), n.value()); + void operator()(ctor::SignedInteger* n) final { + if ( n->width() < 64 ) + out << fmt("int%d(%" PRId64 ")", n->width(), n->value()); else - out << n.value(); + out << n->value(); } - void operator()(const ctor::Stream& n) final { out << "stream(" << util::escapeUTF8(n.value(), true) << ')'; } + void operator()(ctor::Stream* n) final { out << "stream(" << util::escapeUTF8(n->value(), true) << ')'; } - void operator()(const ctor::String& n) final { out << '"' << util::escapeUTF8(n.value(), true) << '"'; } + void operator()(ctor::String* n) final { out << '"' << util::escapeUTF8(n->value(), true) << '"'; } - void operator()(const ctor::Struct& n) final { + void operator()(ctor::Struct* n) final { out << "["; bool first = true; - for ( const auto& f : n.fields() ) { + for ( const auto& f : n->fields() ) { if ( ! first ) out << ", "; else @@ -324,55 +324,55 @@ struct Printer : visitor::PreOrder { out << "]"; } - void operator()(const ctor::Time& n) final { out << "time_ns(" << n.value().nanoseconds() << ")"; } + void operator()(ctor::Time* n) final { out << "time_ns(" << n->value().nanoseconds() << ")"; } - void operator()(const ctor::Tuple& n) final { out << '(' << std::make_pair(n.value(), ", ") << ')'; } + void operator()(ctor::Tuple* n) final { out << '(' << std::make_pair(n->value(), ", ") << ')'; } - void operator()(const ctor::UnsignedInteger& n) final { - if ( n.width() < 64 ) - out << fmt("uint%d(%" PRId64 ")", n.width(), n.value()); + void operator()(ctor::UnsignedInteger* n) final { + if ( n->width() < 64 ) + out << fmt("uint%d(%" PRId64 ")", n->width(), n->value()); else - out << n.value(); + out << n->value(); } - void operator()(const ctor::Vector& n) final { out << "vector(" << std::make_pair(n.value(), ", ") << ')'; } + void operator()(ctor::Vector* n) final { out << "vector(" << std::make_pair(n->value(), ", ") << ')'; } - void operator()(const ctor::WeakReference& n) final { out << "Null"; } + void operator()(ctor::WeakReference* n) final { out << "Null"; } - void operator()(const ctor::ValueReference& n) final { out << "value_ref(" << n.expression() << ')'; } + void operator()(ctor::ValueReference* n) final { out << "value_ref(" << n->expression() << ')'; } ////// Declarations - void operator()(const declaration::Constant& n) final { - printDoc(n.documentation()); + void operator()(declaration::Constant* n) final { + printDoc(n->documentation()); out.beginLine(); - out << linkage(n.linkage()) << "const "; - out << n.type(); - out << ' ' << n.id() << " = " << n.value() << ';'; + out << linkage(n->linkage()) << "const "; + out << n->type(); + out << ' ' << n->id() << " = " << n->value() << ';'; out.endLine(); } - void operator()(const declaration::Expression& n) final { out << n.expression(); } + void operator()(declaration::Expression* n) final { out << n->expression(); } - void operator()(const declaration::Field& n) final { + void operator()(declaration::Field* n) final { out << " "; - if ( auto ft = n.type()->tryAs() ) { + if ( auto ft = n->type()->tryAs() ) { out << to_string(ft->flavor()) << " "; - if ( auto cc = n.callingConvention(); cc && *cc != function::CallingConvention::Standard ) + if ( auto cc = n->callingConvention(); cc && *cc != function::CallingConvention::Standard ) out << to_string(*cc) << ' '; - out << ft->result()->type() << " " << n.id() << "(" << std::make_pair(ft->parameters(), ", ") << ")"; + out << ft->result()->type() << " " << n->id() << "(" << std::make_pair(ft->parameters(), ", ") << ")"; } else - out << n.type() << ' ' << n.id(); + out << n->type() << ' ' << n->id(); - if ( n.attributes() ) - out << ' ' << n.attributes(); + if ( n->attributes() ) + out << ' ' << n->attributes(); - if ( auto f = n.inlineFunction(); f && f->body() ) { + if ( auto f = n->inlineFunction(); f && f->body() ) { const auto& block = f->body()->tryAs(); if ( block && block->statements().empty() ) { out << " {}"; @@ -394,7 +394,7 @@ struct Printer : visitor::PreOrder { out << ";" << out.newline(); } - void operator()(const declaration::Parameter& n) final { + void operator()(declaration::Parameter* n) final { auto kind = [&](auto k) { switch ( k ) { case declaration::parameter::Kind::Copy: return "copy "; @@ -406,94 +406,94 @@ struct Printer : visitor::PreOrder { util::cannot_be_reached(); }; - out << kind(n.kind()) << n.type() << ' ' << n.id(); + out << kind(n->kind()) << n->type() << ' ' << n->id(); - if ( n.default_() ) - out << " = " << n.default_(); + if ( n->default_() ) + out << " = " << n->default_(); - if ( const auto attrs = n.attributes(); attrs && ! attrs->attributes().empty() ) + if ( const auto attrs = n->attributes(); attrs && ! attrs->attributes().empty() ) out << ' ' << attrs; } - void operator()(const declaration::Function& n) final { - const auto& func = n.function(); + void operator()(declaration::Function* n) final { + const auto& func = n->function(); if ( ! func->body() ) { - printDoc(n.documentation()); + printDoc(n->documentation()); out.beginLine(); out << "declare "; } else { out.emptyLine(); - printDoc(n.documentation()); + printDoc(n->documentation()); out.beginLine(); } - out << linkage(n.linkage()); + out << linkage(n->linkage()); - if ( n.linkage() != declaration::Linkage::Struct ) + if ( n->linkage() != declaration::Linkage::Struct ) out << "function "; - out << n.function(); + out << n->function(); } - void operator()(const declaration::ImportedModule& n) final { + void operator()(declaration::ImportedModule* n) final { out.beginLine(); - if ( n.scope() ) - out << "import " << n.id() << " from " << *n.scope() << ';'; + if ( n->scope() ) + out << "import " << n->id() << " from " << *n->scope() << ';'; else - out << "import " << n.id() << ';'; + out << "import " << n->id() << ';'; out.endLine(); } - void operator()(const declaration::Type& n) final { - printDoc(n.documentation()); + void operator()(declaration::Type* n) final { + printDoc(n->documentation()); out.beginLine(); - for ( const auto& comment : n.meta().comments() ) + for ( const auto& comment : n->meta().comments() ) out << "# " << comment << '\n'; - out << linkage(n.linkage()) << "type " << n.id() << " = "; + out << linkage(n->linkage()) << "type " << n->id() << " = "; out.setExpandSubsequentType(true); - out << n.type(); + out << n->type(); - if ( n.attributes() ) - out << ' ' << n.attributes(); + if ( n->attributes() ) + out << ' ' << n->attributes(); out << ';'; out.endLine(); } - void operator()(const declaration::LocalVariable& n) final { + void operator()(declaration::LocalVariable* n) final { // Will be printed through a statement, hence no outer formatting. out << "local "; - if ( n.type() ) - out << n.type() << ' '; + if ( n->type() ) + out << n->type() << ' '; - out << n.id(); + out << n->id(); - if ( n.typeArguments().size() ) - out << '(' << std::make_pair(n.typeArguments(), ", ") << ')'; + if ( n->typeArguments().size() ) + out << '(' << std::make_pair(n->typeArguments(), ", ") << ')'; - if ( n.init() ) - out << " = " << n.init(); + if ( n->init() ) + out << " = " << n->init(); } - void operator()(const declaration::GlobalVariable& n) final { - printDoc(n.documentation()); + void operator()(declaration::GlobalVariable* n) final { + printDoc(n->documentation()); out.beginLine(); - out << linkage(n.linkage()) << "global "; + out << linkage(n->linkage()) << "global "; - if ( n.type() ) - out << n.type() << ' '; + if ( n->type() ) + out << n->type() << ' '; - out << n.id(); + out << n->id(); - if ( n.typeArguments().size() ) - out << '(' << std::make_pair(n.typeArguments(), ", ") << ')'; + if ( n->typeArguments().size() ) + out << '(' << std::make_pair(n->typeArguments(), ", ") << ')'; - if ( n.init() ) - out << " = " << n.init(); + if ( n->init() ) + out << " = " << n->init(); out << ';'; out.endLine(); @@ -501,21 +501,21 @@ struct Printer : visitor::PreOrder { ////// Expressions - void operator()(const expression::Assign& n) final { out << n.target() << " = " << n.source(); } + void operator()(expression::Assign* n) final { out << n->target() << " = " << n->source(); } - void operator()(const expression::BuiltInFunction& n) final { - out << n.name() << "(" << util::join(node::transform(n.arguments(), [](auto& p) { return fmt("%s", p); }), ", ") - << ")"; + void operator()(expression::BuiltInFunction* n) final { + out << n->name() << "(" + << util::join(node::transform(n->arguments(), [](auto& p) { return fmt("%s", p); }), ", ") << ")"; } - void operator()(const expression::Coerced& n) final { out << n.expression(); } + void operator()(expression::Coerced* n) final { out << n->expression(); } - void operator()(const expression::Ctor& n) final { out << n.ctor(); } + void operator()(expression::Ctor* n) final { out << n->ctor(); } - void operator()(const expression::Grouping& n) final { out << '(' << n.expression() << ')'; } + void operator()(expression::Grouping* n) final { out << '(' << n->expression() << ')'; } - void operator()(const expression::Keyword& n) final { - switch ( n.kind() ) { + void operator()(expression::Keyword* n) final { + switch ( n->kind() ) { case expression::keyword::Kind::Self: out << "self"; break; case expression::keyword::Kind::DollarDollar: out << "$$"; break; case expression::keyword::Kind::Captures: @@ -525,71 +525,71 @@ struct Printer : visitor::PreOrder { } } - void operator()(const expression::ListComprehension& n) final { - out << '[' << n.output() << " for " << n.local() << " in " << n.input(); + void operator()(expression::ListComprehension* n) final { + out << '[' << n->output() << " for " << n->local() << " in " << n->input(); - if ( n.condition() ) - out << " if " << n.condition(); + if ( n->condition() ) + out << " if " << n->condition(); out << ']'; } - void operator()(const expression::LogicalAnd& n) final { out << n.op0() << " && " << n.op1(); } + void operator()(expression::LogicalAnd* n) final { out << n->op0() << " &* " << n->op1(); } - void operator()(const expression::LogicalNot& n) final { out << "! " << n.expression(); } + void operator()(expression::LogicalNot* n) final { out << "! " << n->expression(); } - void operator()(const expression::LogicalOr& n) final { out << n.op0() << " || " << n.op1(); } + void operator()(expression::LogicalOr* n) final { out << n->op0() << " || " << n->op1(); } - void operator()(const expression::Member& n) final { out << n.id(); } + void operator()(expression::Member* n) final { out << n->id(); } - void operator()(const expression::Move& n) final { out << "move(" << n.expression() << ")"; } + void operator()(expression::Move* n) final { out << "move(" << n->expression() << ")"; } - void operator()(const expression::Name& n) final { out << n.id(); } + void operator()(expression::Name* n) final { out << n->id(); } - void operator()(const expression::Ternary& n) final { - out << n.condition() << " ? " << n.true_() << " : " << n.false_(); + void operator()(expression::Ternary* n) final { + out << n->condition() << " ? " << n->true_() << " : " << n->false_(); } - void operator()(const expression::Type_& n) final { - if ( auto id = n.typeValue()->type()->typeID() ) + void operator()(expression::Type_* n) final { + if ( auto id = n->typeValue()->type()->typeID() ) out << *id; else - out << n.typeValue(); + out << n->typeValue(); } - void operator()(const expression::TypeInfo& n) final { out << "typeinfo(" << n.expression() << ")"; } + void operator()(expression::TypeInfo* n) final { out << "typeinfo(" << n->expression() << ")"; } - void operator()(const expression::TypeWrapped& n) final { out << n.expression(); } + void operator()(expression::TypeWrapped* n) final { out << n->expression(); } - void operator()(const expression::Void& n) final { - out << ""; // Shouldn't really happen. + void operator()(expression::Void* n) final { + out << ""; // Shouldn't really happen-> } ////// Statements - void operator()(const statement::Assert& n) final { + void operator()(statement::Assert* n) final { out.beginLine(); - if ( n.expectsException() ) + if ( n->expectsException() ) out << "assert-exception "; else out << "assert "; - out << n.expression(); - if ( n.message() ) - out << " : " << n.message(); + out << n->expression(); + if ( n->message() ) + out << " : " << n->message(); out << ";"; out.endLine(); } - void operator()(const statement::Block& n) final { - if ( out.indent() == 0 || n.statements().size() > 1 ) + void operator()(statement::Block* n) final { + if ( out.indent() == 0 || n->statements().size() > 1 ) out << "{"; out.endLine(); out.incrementIndent(); - const auto& stmts = n.statements(); + const auto& stmts = n->statements(); for ( const auto&& [i, s] : util::enumerate(stmts) ) { out.setPositionInBlock(i == 0, i == (stmts.size() - 1)); @@ -604,104 +604,104 @@ struct Printer : visitor::PreOrder { out.decrementIndent(); - if ( out.indent() == 0 || n.statements().size() > 1 ) { + if ( out.indent() == 0 || n->statements().size() > 1 ) { out.beginLine(); out << "}"; out.endLine(); } } - void operator()(const statement::Break& n) final { + void operator()(statement::Break* n) final { out.beginLine(); out << "break;"; out.endLine(); } - void operator()(const statement::Continue& n) final { + void operator()(statement::Continue* n) final { out.beginLine(); out << "continue;"; out.endLine(); } - void operator()(const statement::Comment& n) final { - if ( (n.separator() == hilti::statement::comment::Separator::Before || - n.separator() == hilti::statement::comment::Separator::BeforeAndAfter) && + void operator()(statement::Comment* n) final { + if ( (n->separator() == hilti::statement::comment::Separator::Before || + n->separator() == hilti::statement::comment::Separator::BeforeAndAfter) && ! out.isFirstInBlock() ) out.emptyLine(); out.beginLine(); - out << "# " << n.comment(); + out << "# " << n->comment(); out.endLine(); - if ( (n.separator() == hilti::statement::comment::Separator::After || - n.separator() == hilti::statement::comment::Separator::BeforeAndAfter) && + if ( (n->separator() == hilti::statement::comment::Separator::After || + n->separator() == hilti::statement::comment::Separator::BeforeAndAfter) && ! out.isLastInBlock() ) out.emptyLine(); } - void operator()(const statement::Declaration& n) final { + void operator()(statement::Declaration* n) final { out.beginLine(); - out << n.declaration() << ';'; + out << n->declaration() << ';'; out.endLine(); } - void operator()(const statement::Expression& n) final { + void operator()(statement::Expression* n) final { out.beginLine(); - out << n.expression() << ';'; + out << n->expression() << ';'; out.endLine(); } - void operator()(const statement::For& n) final { + void operator()(statement::For* n) final { out.emptyLine(); out.beginLine(); - out << "for ( " << n.local()->id() << " in " << n.sequence() << " ) " << n.body(); + out << "for ( " << n->local()->id() << " in " << n->sequence() << " ) " << n->body(); out.endLine(); } - void operator()(const statement::If& n) final { + void operator()(statement::If* n) final { out.emptyLine(); out.beginLine(); out << "if ( "; - if ( auto e = n.init() ) + if ( auto e = n->init() ) out << e << "; "; - if ( auto e = n.condition() ) + if ( auto e = n->condition() ) out << e; - out << " ) " << n.true_(); + out << " ) " << n->true_(); - if ( n.false_() ) { + if ( n->false_() ) { out.beginLine(); - out << "else " << n.false_(); + out << "else " << n->false_(); } out.endLine(); } - void operator()(const statement::SetLocation& n) final { + void operator()(statement::SetLocation* n) final { out.beginLine(); - out << "# " << n.expression(); + out << "# " << n->expression(); out.endLine(); } - void operator()(const statement::Return& n) final { + void operator()(statement::Return* n) final { out.beginLine(); out << "return"; - if ( auto e = n.expression() ) + if ( auto e = n->expression() ) out << ' ' << e; out << ';'; out.endLine(); } - void operator()(const statement::Switch& n) final { + void operator()(statement::Switch* n) final { out.emptyLine(); out.beginLine(); out << "switch ( "; - if ( const auto& cond = n.condition(); cond->id().str() != "__x" ) + if ( const auto& cond = n->condition(); cond->id().str() != "__x" ) out << cond; else out << cond->init(); @@ -710,7 +710,7 @@ struct Printer : visitor::PreOrder { out.incrementIndent(); out.endLine(); - for ( const auto& c : n.cases() ) { + for ( const auto& c : n->cases() ) { out.beginLine(); if ( ! c->isDefault() ) @@ -728,95 +728,95 @@ struct Printer : visitor::PreOrder { out.endLine(); } - void operator()(const statement::Throw& n) final { + void operator()(statement::Throw* n) final { out.beginLine(); out << "throw"; - if ( auto e = n.expression() ) + if ( auto e = n->expression() ) out << fmt(" %s", *e); out << ";"; out.endLine(); } - void operator()(const statement::try_::Catch& n) final { + void operator()(statement::try_::Catch* n) final { out.beginLine(); out << "catch "; - if ( auto p = n.parameter() ) + if ( auto p = n->parameter() ) out << "( " << p << " ) "; - out << n.body(); + out << n->body(); } - void operator()(const statement::Try& n) final { + void operator()(statement::Try* n) final { out.beginLine(); - out << "try " << n.body(); + out << "try " << n->body(); - for ( const auto& c : n.catches() ) + for ( const auto& c : n->catches() ) out << c; out.endLine(); } - void operator()(const statement::While& n) final { + void operator()(statement::While* n) final { out.emptyLine(); out.beginLine(); out << "while ( "; - if ( auto e = n.init() ) + if ( auto e = n->init() ) out << e << "; "; - if ( auto e = n.condition() ) + if ( auto e = n->condition() ) out << e; - out << " ) " << n.body(); + out << " ) " << n->body(); - if ( n.else_() ) { + if ( n->else_() ) { out.beginLine(); - out << "else " << n.else_(); + out << "else " << n->else_(); } out.endLine(); } - void operator()(const statement::Yield& n) final { + void operator()(statement::Yield* n) final { out.beginLine(); out << "yield"; out.endLine(); } #if 0 - void operator()(const expression::ResolvedOperator& n) final { - out << renderOperator(n.operator_().kind(), node::transform(n.operands(), [](auto o) { return fmt("%s", o); })); + void operator()(expression::ResolvedOperator* n) final { + out << renderOperator(n->operator_().kind(), node::transform(n->operands(), [](auto o) { return fmt("%s", o); })); } #endif - void operator()(const expression::UnresolvedOperator& n) final { - out << renderOperator(n.kind(), - node::transform(n.operands(), [](auto& o) -> std::string { return fmt("%s", *o); })); + void operator()(expression::UnresolvedOperator* n) final { + out << renderOperator(n->kind(), + node::transform(n->operands(), [](auto& o) -> std::string { return fmt("%s", *o); })); } ////// Types - void operator()(const QualifiedType& n) final { out << const_(n) << n.type(); } + void operator()(QualifiedType* n) final { out << const_(n) << n->type(); } - void operator()(const type::Any& n) final { out << "any"; } + void operator()(type::Any* n) final { out << "any"; } - void operator()(const type::Address& n) final { out << "addr"; } + void operator()(type::Address* n) final { out << "addr"; } - void operator()(const type::Auto& n) final { out << "auto"; } + void operator()(type::Auto* n) final { out << "auto"; } - void operator()(const type::Bool& n) final { out << "bool"; } + void operator()(type::Bool* n) final { out << "bool"; } - void operator()(const type::Bytes& n) final { out << "bytes"; } + void operator()(type::Bytes* n) final { out << "bytes"; } - void operator()(const type::enum_::Label& n) final { out << n.id() << " = " << n.value(); } + void operator()(type::enum_::Label* n) final { out << n->id() << " = " << n->value(); } - void operator()(const type::Enum& n) final { + void operator()(type::Enum* n) final { if ( ! out.isExpandSubsequentType() ) { out.setExpandSubsequentType(false); - if ( auto id = n.typeID() ) { + if ( auto id = n->typeID() ) { out << *id; return; } @@ -824,18 +824,18 @@ struct Printer : visitor::PreOrder { out.setExpandSubsequentType(false); - auto x = util::transform(util::filter(n.labels(), [](const auto& l) { return l.get()->id() != ID("Undef"); }), + auto x = util::transform(util::filter(n->labels(), [](const auto& l) { return l.get()->id() != ID("Undef"); }), [](const auto& l) { return l.get(); }); out << "enum { " << std::make_pair(std::move(x), ", ") << " }"; } - void operator()(const type::Error& n) final { out << "error"; } + void operator()(type::Error* n) final { out << "error"; } - void operator()(const type::Exception& n) final { + void operator()(type::Exception* n) final { out << "exception"; - if ( auto t = n.baseType() ) { + if ( auto t = n->baseType() ) { out << " : "; if ( auto id = t->typeID() ) out << *id; @@ -844,135 +844,135 @@ struct Printer : visitor::PreOrder { } } - void operator()(const type::Function& n) final { + void operator()(type::Function* n) final { out << "function "; - printFunctionType(n, {}); + printFunctionType(*n, {}); } - void operator()(const type::Interval& n) final { out << "interval"; } + void operator()(type::Interval* n) final { out << "interval"; } - void operator()(const type::Member& n) final { out << n.id(); } + void operator()(type::Member* n) final { out << n->id(); } - void operator()(const type::Name& n) final { out << n.id(); } + void operator()(type::Name* n) final { out << n->id(); } - void operator()(const type::Network& n) final { out << "net"; } + void operator()(type::Network* n) final { out << "net"; } - void operator()(const type::Null& n) final { out << ""; } + void operator()(type::Null* n) final { out << ""; } - void operator()(const type::OperandList& n) final { out << ""; } + void operator()(type::OperandList* n) final { out << ""; } - void operator()(const type::Optional& n) final { - if ( n.isWildcard() ) + void operator()(type::Optional* n) final { + if ( n->isWildcard() ) out << "optional<*>"; else { - out << "optional<" << n.dereferencedType() << ">"; + out << "optional<" << n->dereferencedType() << ">"; } } - void operator()(const type::Port& n) final { out << "port"; } + void operator()(type::Port* n) final { out << "port"; } - void operator()(const type::Real& n) final { out << "real"; } + void operator()(type::Real* n) final { out << "real"; } - void operator()(const type::StrongReference& n) final { - if ( n.isWildcard() ) + void operator()(type::StrongReference* n) final { + if ( n->isWildcard() ) out << "strong_ref<*>"; else - out << "strong_ref<" << n.dereferencedType() << ">"; + out << "strong_ref<" << n->dereferencedType() << ">"; } - void operator()(const type::Stream& n) final { out << "stream"; } + void operator()(type::Stream* n) final { out << "stream"; } - void operator()(const type::bytes::Iterator& n) final { out << "iterator"; } + void operator()(type::bytes::Iterator* n) final { out << "iterator"; } - void operator()(const type::list::Iterator& n) final { - if ( n.isWildcard() ) + void operator()(type::list::Iterator* n) final { + if ( n->isWildcard() ) out << "iterator>"; else - out << fmt("iterator>", *n.dereferencedType()); + out << fmt("iterator>", *n->dereferencedType()); } - void operator()(const type::stream::Iterator& n) final { out << "iterator"; } + void operator()(type::stream::Iterator* n) final { out << "iterator"; } - void operator()(const type::vector::Iterator& n) final { - if ( n.isWildcard() ) + void operator()(type::vector::Iterator* n) final { + if ( n->isWildcard() ) out << "iterator>"; else - out << fmt("iterator>", *n.dereferencedType()); + out << fmt("iterator>", *n->dereferencedType()); } - void operator()(const type::stream::View& n) final { out << "view"; } + void operator()(type::stream::View* n) final { out << "view"; } - void operator()(const type::Library& n) final { - if ( auto id = n.typeID() ) + void operator()(type::Library* n) final { + if ( auto id = n->typeID() ) out << *id; else - out << fmt("__library_type(\"%s\")", n.cxxName()); + out << fmt("__library_type(\"%s\")", n->cxxName()); } - void operator()(const type::List& n) final { - if ( n.isWildcard() ) + void operator()(type::List* n) final { + if ( n->isWildcard() ) out << "list<*>"; else { - out << "list<" << n.elementType() << ">"; + out << "list<" << n->elementType() << ">"; } } - void operator()(const type::map::Iterator& n) final { - if ( n.isWildcard() ) + void operator()(type::map::Iterator* n) final { + if ( n->isWildcard() ) out << "iterator>"; else - out << fmt("iterator>", *n.dereferencedType()); + out << fmt("iterator>", *n->dereferencedType()); } - void operator()(const type::Map& n) final { - if ( n.isWildcard() ) + void operator()(type::Map* n) final { + if ( n->isWildcard() ) out << "map<*>"; else { - out << "map<" << n.keyType() << ", " << n.valueType() << ">"; + out << "map<" << n->keyType() << ", " << n->valueType() << ">"; } } - void operator()(const type::RegExp& n) final { out << "regexp"; } + void operator()(type::RegExp* n) final { out << "regexp"; } - void operator()(const type::Result& n) final { - if ( n.isWildcard() ) + void operator()(type::Result* n) final { + if ( n->isWildcard() ) out << "result<*>"; else { - out << "result<" << n.dereferencedType() << ">"; + out << "result<" << n->dereferencedType() << ">"; } } - void operator()(const type::set::Iterator& n) final { - if ( n.isWildcard() ) + void operator()(type::set::Iterator* n) final { + if ( n->isWildcard() ) out << "iterator>"; else - out << fmt("iterator>", *n.dereferencedType()); + out << fmt("iterator>", *n->dereferencedType()); } - void operator()(const type::Set& n) final { - if ( n.isWildcard() ) + void operator()(type::Set* n) final { + if ( n->isWildcard() ) out << "set<*>"; else { - out << "set<" << n.elementType() << ">"; + out << "set<" << n->elementType() << ">"; } } - void operator()(const type::SignedInteger& n) final { - if ( n.isWildcard() ) + void operator()(type::SignedInteger* n) final { + if ( n->isWildcard() ) out << "int<*>"; else - out << fmt("int<%d>", n.width()); + out << fmt("int<%d>", n->width()); } - void operator()(const type::String& n) final { out << "string"; } + void operator()(type::String* n) final { out << "string"; } - void operator()(const type::Struct& n) final { + void operator()(type::Struct* n) final { if ( ! out.isExpandSubsequentType() ) { - if ( auto id = n.typeID() ) { + if ( auto id = n->typeID() ) { out << *id; - if ( n.parameters().size() ) - out << '(' << std::make_pair(n.parameters(), ", ") << ')'; + if ( n->parameters().size() ) + out << '(' << std::make_pair(n->parameters(), ", ") << ')'; return; } @@ -982,8 +982,8 @@ struct Printer : visitor::PreOrder { out << "struct"; - if ( n.parameters().size() ) - out << " (" << std::make_pair(n.parameters(), ", ") << ')'; + if ( n->parameters().size() ) + out << " (" << std::make_pair(n->parameters(), ", ") << ')'; auto printFields = [&](const auto& fields) { for ( const auto& f : fields ) @@ -992,18 +992,18 @@ struct Printer : visitor::PreOrder { out << " {" << out.newline(); printFields( - util::filter(n.fields(), [](const auto& f) { return ! f->type()->template isA(); })); - printFields(util::filter(n.fields(), [](const auto& f) { return f->type()->template isA(); })); + util::filter(n->fields(), [](const auto& f) { return ! f->type()->template isA(); })); + printFields(util::filter(n->fields(), [](const auto& f) { return f->type()->template isA(); })); out << "}"; } - void operator()(const type::Time& n) final { out << "time"; } + void operator()(type::Time* n) final { out << "time"; } - void operator()(const type::Type_& n) final { out << fmt("type<%s>", n.typeValue()); } + void operator()(type::Type_* n) final { out << fmt("type<%s>", n->typeValue()); } - void operator()(const type::Union& n) final { + void operator()(type::Union* n) final { if ( ! out.isExpandSubsequentType() ) { - if ( auto id = n.typeID() ) { + if ( auto id = n->typeID() ) { out << *id; return; } @@ -1013,28 +1013,28 @@ struct Printer : visitor::PreOrder { out << "union {" << out.newline(); - for ( const auto& f : n.fields() ) + for ( const auto& f : n->fields() ) out << f; out << "}"; } - void operator()(const type::Unknown& n) final { out << ""; } + void operator()(type::Unknown* n) final { out << ""; } - void operator()(const type::UnsignedInteger& n) final { - if ( n.isWildcard() ) + void operator()(type::UnsignedInteger* n) final { + if ( n->isWildcard() ) out << "uint<*>"; else - out << fmt("uint<%d>", n.width()); + out << fmt("uint<%d>", n->width()); } - void operator()(const type::Tuple& n) final { - if ( n.isWildcard() ) + void operator()(type::Tuple* n) final { + if ( n->isWildcard() ) out << "tuple<*>"; else { out << "tuple<"; - auto types = node::transform(n.elements(), [](const auto& x) -> std::string { + auto types = node::transform(n->elements(), [](const auto& x) -> std::string { return x->id() ? fmt("%s: %s", *x->id(), *x->type()) : fmt("%s", *x->type()); }); @@ -1042,28 +1042,28 @@ struct Printer : visitor::PreOrder { } } - void operator()(const type::Vector& n) final { - if ( n.isWildcard() ) + void operator()(type::Vector* n) final { + if ( n->isWildcard() ) out << "vector<*>"; else { - out << "vector<" << n.elementType() << ">"; + out << "vector<" << n->elementType() << ">"; } } - void operator()(const type::Void& n) final { out << "void"; } + void operator()(type::Void* n) final { out << "void"; } - void operator()(const type::WeakReference& n) final { - if ( n.isWildcard() ) + void operator()(type::WeakReference* n) final { + if ( n->isWildcard() ) out << "weak_ref<*>"; else - out << "weak_ref<" << n.dereferencedType() << ">"; + out << "weak_ref<" << n->dereferencedType() << ">"; } - void operator()(const type::ValueReference& n) final { - if ( n.isWildcard() ) + void operator()(type::ValueReference* n) final { + if ( n->isWildcard() ) out << "value_ref<*>"; else - out << "value_ref<" << n.dereferencedType() << ">"; + out << "value_ref<" << n->dereferencedType() << ">"; } private: @@ -1109,17 +1109,16 @@ void hilti::detail::printAST(const NodePtr& root, printer::Stream& stream) { Printer(stream).dispatch(root); } -#if 0 -std::string hilti::detail::renderOperatorPrototype(const expression::ResolvedOperator& o) { - const auto& op = o.operator_(); - const auto& exprs = o.operands(); +std::string hilti::detail::renderOperatorPrototype(const NodeDerivedPtr& o) { + const auto& op = o->operator_(); + const auto& exprs = o->operands(); switch ( op.kind() ) { case operator_::Kind::Call: { assert(exprs.size() == 2); auto id = exprs[0]; auto ops = - operator_::type(o.operator_().operands()[1].type, exprs, exprs)->as().operands(); + operator_::type(o->operator_().operands()[1].type, exprs, exprs)->as()->operands(); auto args = util::join(util::transform(ops, [&](auto x) { return fmt("<%s>", renderOperand(x, exprs)); }), ", "); return fmt("%s(%s)", id, args); @@ -1130,7 +1129,7 @@ std::string hilti::detail::renderOperatorPrototype(const expression::ResolvedOpe auto self = exprs[0]; auto id = exprs[1]; auto ops = - operator_::type(o.operator_().operands()[2].type, exprs, exprs)->as().operands(); + operator_::type(o->operator_().operands()[2].type, exprs, exprs)->as()->operands(); auto args = util::join(util::transform(ops, [&](auto x) { return fmt("<%s>", renderOperand(x, exprs)); }), ", "); return fmt("<%s>.%s(%s)", renderExpressionType(self), id, args); @@ -1148,7 +1147,7 @@ static std::string _renderOperatorInstance(operator_::Kind kind, const node::Ran case operator_::Kind::Call: { assert(exprs.size() == 2); const auto& id = exprs[0]; - auto ops = exprs[1].as().ctor().as().value(); + auto ops = exprs[1]->as()->ctor()->as()->value(); auto args = util::join(node::transform(ops, [&](auto x) { return fmt("<%s>", renderExpressionType(x)); }), ", "); return fmt("%s(%s)", id, args); @@ -1158,7 +1157,7 @@ static std::string _renderOperatorInstance(operator_::Kind kind, const node::Ran assert(exprs.size() == 3); const auto& self = exprs[0]; const auto& id = exprs[1]; - auto ops = exprs[2].as().ctor().as().value(); + auto ops = exprs[2]->as()->ctor()->as()->value(); auto args = util::join(node::transform(ops, [&](auto x) { return fmt("<%s>", renderExpressionType(x)); }), ", "); return fmt("<%s>.%s(%s)", renderExpressionType(self), id, args); @@ -1170,12 +1169,10 @@ static std::string _renderOperatorInstance(operator_::Kind kind, const node::Ran } } -std::string hilti::detail::renderOperatorInstance(const expression::ResolvedOperator& o) { - return _renderOperatorInstance(o.operator_().kind(), o.operands()); +std::string hilti::detail::renderOperatorInstance(const NodeDerivedPtr& o) { + return _renderOperatorInstance(o->operator_().kind(), o->operands()); } -std::string hilti::detail::renderOperatorInstance(const expression::UnresolvedOperator& o) { - return _renderOperatorInstance(o.kind(), o.operands()); +std::string hilti::detail::renderOperatorInstance(const NodeDerivedPtr& o) { + return _renderOperatorInstance(o->kind(), o->operands()); } - -#endif diff --git a/hilti/toolchain/src/compiler/visitors/renderer.cc b/hilti/toolchain/src/compiler/visitors/renderer.cc index f9f0f0dd8..af460b560 100644 --- a/hilti/toolchain/src/compiler/visitors/renderer.cc +++ b/hilti/toolchain/src/compiler/visitors/renderer.cc @@ -1,6 +1,6 @@ // Copyright (c) 2020-2023 by the Zeek Project. See LICENSE for details. -#include +#include #include #include #include @@ -34,7 +34,7 @@ static void render(const NodePtr& n, std::ostream* out, std::optionalscope() ) { std::stringstream buffer; (*i)->scope()->render(buffer, " | "); diff --git a/hilti/toolchain/src/compiler/visitors/resolver.cc b/hilti/toolchain/src/compiler/visitors/resolver.cc index 1a6c91cdf..2b5adb8d6 100644 --- a/hilti/toolchain/src/compiler/visitors/resolver.cc +++ b/hilti/toolchain/src/compiler/visitors/resolver.cc @@ -5,21 +5,21 @@ #include #include +#include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include -#include -#include +//#include #include #include #include @@ -32,6 +32,8 @@ #include #include +#include "base/timing.h" + using namespace hilti; namespace hilti::logging::debug { @@ -41,18 +43,14 @@ inline const hilti::logging::DebugStream Operator("operator"); namespace { -struct Visitor : visitor::PostOrder, type::Visitor { - using position_t = visitor::PostOrder::position_t; - - Visitor(std::shared_ptr ctx, Node* module, Unit* unit) - : _context(std::move(ctx)), unit(unit), _module(module->as()) {} +struct Visitor : visitor::PostOrder { + explicit Visitor(Builder* builder, const ASTRootPtr& root) : root(root), builder(builder) {} - std::shared_ptr _context; - Unit* unit; - Module& _module; + const ASTRootPtr& root; + Builder* builder; bool modified = false; - std::map auto_params; // miapping of `auto` parameters inferred, indexed by canonical ID + std::map auto_params; // miapping of `auto` parameters inferred, indexed by canonical ID #if 0 std::set seen; @@ -71,6 +69,7 @@ struct Visitor : visitor::PostOrder, type::Visitor { }; #endif +#if 0 // Log debug message recording resolving a epxxression. void logChange(const Node& old, const Expression& nexpr) { HILTI_DEBUG(logging::debug::Resolver, @@ -519,15 +518,17 @@ struct Visitor : visitor::PostOrder, type::Visitor { p.node = hilti::type::pruneWalk(std::move(t)); // alias to avoid visitor cycles modified = true; } +#endif }; // Visitor to resolve any auto parameters that we inferred during the main resolver pass. -struct VisitorApplyAutoParameters : public visitor::PreOrder { +struct VisitorApplyAutoParameters : visitor::PreOrder { VisitorApplyAutoParameters(const ::Visitor& v) : visitor(v) {} const ::Visitor& visitor; bool modified = false; +#if 0 void operator()(const declaration::Parameter& u, position_t p) { if ( ! u.type().isA() ) return; @@ -543,8 +544,10 @@ struct VisitorApplyAutoParameters : public visitor::PreOrder().setType(i->second); modified = true; } +#endif }; +#if 0 bool Visitor::resolveOperator(const expression::UnresolvedOperator& u, position_t p) { if ( ! u.areOperandsResolved() ) return false; @@ -1009,18 +1012,19 @@ std::vector Visitor::matchOverloads(const std::vector& candidate return resolved; } +#endif + } // anonymous namespace -bool hilti::detail::ast::resolve(const std::shared_ptr& ctx, Node* root, Unit* unit) { + +bool hilti::detail::ast::resolve(Builder* builder, const ASTRootPtr& root) { util::timing::Collector _("hilti/compiler/ast/resolver"); - auto v1 = Visitor(ctx, root, unit); - for ( auto i : v1.walk(root) ) - v1.dispatch(i); + auto v1 = Visitor(builder, root); + hilti::visitor::visit(v1, root); auto v2 = VisitorApplyAutoParameters(v1); - for ( auto i : v2.walk(root) ) - v2.dispatch(i); + hilti::visitor::visit(v2, root); return v1.modified || v2.modified; } diff --git a/hilti/toolchain/src/compiler/visitors/scope-builder.cc b/hilti/toolchain/src/compiler/visitors/scope-builder.cc index 0c3f0467d..8203d49a3 100644 --- a/hilti/toolchain/src/compiler/visitors/scope-builder.cc +++ b/hilti/toolchain/src/compiler/visitors/scope-builder.cc @@ -1,5 +1,7 @@ // Copyright (c) 2020-2023 by the Zeek Project. See LICENSE for details. +#include + #include #include #include @@ -16,49 +18,49 @@ #include #include +#include "ast/builder/builder.h" +#include "ast/declarations/type.h" +#include "base/timing.h" + using namespace hilti; namespace { -struct Visitor : visitor::PostOrder, type::Visitor { - using position_t = visitor::PostOrder::position_t; - - explicit Visitor(std::shared_ptr ctx, Unit* unit) : context(std::move(ctx)), unit(unit) {} +struct Visitor : visitor::PostOrder { + explicit Visitor(Builder* builder, const ASTRootPtr& root) : root(root), builder(builder) {} - std::shared_ptr context; - Unit* unit; + const ASTRootPtr& root; + Builder* builder; - void operator()(const Module& m, position_t p) { - auto scope = p.node.scope(); - - // Insert module name into top-level scope. - Declaration d = declaration::Module(NodeRef(p.node), m.meta()); - d.setCanonicalID(m.id()); - auto n = p.node.as().preserve(d); - scope->insert(std::move(n)); + void operator()(Module* m) final { + // Insert module name into global scope. + auto d = builder->declarationModule(m->as(), m->meta()); + d->setCanonicalID(m->id()); + root->getOrCreateScope()->insert(std::move(d)); } - void operator()(const declaration::GlobalVariable& d, position_t p) { - if ( p.parent().isA() ) - p.parent().scope()->insert(NodeRef(p.node)); + void operator()(declaration::GlobalVariable* d) final { + if ( d->parent()->isA() ) + d->parent()->getOrCreateScope()->insert(d->as()); } - void operator()(const declaration::Type& d, position_t p) { - if ( p.parent().isA() ) - p.parent().scope()->insert(NodeRef(p.node)); + void operator()(declaration::Type* d) final { + if ( d->parent()->isA() ) + d->parent()->getOrCreateScope()->insert(d->as()); } - void operator()(const declaration::Constant& d, position_t p) { - if ( p.parent().isA() ) - p.parent().scope()->insert(NodeRef(p.node)); + void operator()(declaration::Constant* d) final { + if ( d->parent()->isA() ) + d->parent()->getOrCreateScope()->insert(d->as()); } - void operator()(const declaration::Expression& d, position_t p) { - if ( p.parent().isA() ) - p.parent().scope()->insert(NodeRef(p.node)); + void operator()(declaration::Expression* d) final { + if ( d->parent()->isA() ) + d->parent()->getOrCreateScope()->insert(d->as()); } - void operator()(const declaration::Field& f, position_t p) { +#if 0 + void operator()(declaration::Field* f) final { if ( auto func = f.inlineFunction() ) { for ( auto&& x : func->ftype().parameterRefs() ) p.node.scope()->insert(std::move(x)); @@ -73,9 +75,9 @@ struct Visitor : visitor::PostOrder, type::Visitor { p.parent(2).scope()->insert(NodeRef(p.node)); } - void operator()(const declaration::Function& f, position_t p) { - if ( p.parent().isA() ) - p.parent().scope()->insert(NodeRef(p.node)); + void operator()(declaration::Function* f) final { + if ( d.parent()->isA() ) + d.parent()->scope()->insert(NodeRef(p.node)); for ( auto&& x : f.function().ftype().parameterRefs() ) p.node.scope()->insert(std::move(x)); @@ -123,41 +125,41 @@ struct Visitor : visitor::PostOrder, type::Visitor { } } - void operator()(const declaration::ImportedModule& m, position_t p) { + void operator()(declaration::ImportedModule* m) final { if ( const auto& cached = context->lookupUnit(m.id(), m.scope(), unit->extension()) ) { auto other = cached->unit->moduleRef(); p.node.setScope(other->scope()); auto n = unit->module().as().preserve(p.node); const_cast(*n).setScope(other->scope()); - p.parent().scope()->insert(std::move(n)); + d.parent()->scope()->insert(std::move(n)); } } - void operator()(const expression::ListComprehension& e, position_t p) { p.node.scope()->insert(e.localRef()); } + void operator()(expression::ListComprehension* e) final { p.node.scope()->insert(e.localRef()); } - void operator()(const statement::Declaration& d, position_t p) { p.parent().scope()->insert(d.declarationRef()); } + void operator()(statement::Declaration* d) final { d.parent()->scope()->insert(d.declarationRef()); } - void operator()(const statement::For& s, position_t p) { p.node.scope()->insert(s.localRef()); } + void operator()(statement::For* s) final { p.node.scope()->insert(s.localRef()); } - void operator()(const statement::If& s, position_t p) { + void operator()(statement::If* s) final { if ( s.initRef() ) p.node.scope()->insert(s.initRef()); } - void operator()(const statement::Switch& s, position_t p) { p.node.scope()->insert(s.conditionRef()); } + void operator()(statement::Switch* s) final { p.node.scope()->insert(s.conditionRef()); } - void operator()(const statement::try_::Catch& s, position_t p) { + void operator()(statement::try_::Catch* s) final { if ( auto x = s.parameterRef() ) p.node.scope()->insert(std::move(x)); } - void operator()(const statement::While& s, position_t p) { + void operator()(statement::While* s) final { if ( auto x = s.initRef() ) p.node.scope()->insert(std::move(x)); } - void operator()(const type::Enum& m, type::Visitor::position_t& p) override { - if ( ! p.parent().isA() ) + void operator()(type::Enum* m) final { + if ( ! d.parent()->isA() ) return; if ( ! p.node.as().typeID() ) @@ -166,13 +168,10 @@ struct Visitor : visitor::PostOrder, type::Visitor { return; for ( auto&& d : p.node.as().labelDeclarationRefs() ) - p.parent().scope()->insert(std::move(d)); + d.parent()->scope()->insert(std::move(d)); } - void operator()(const type::Struct& t, position_t p) { - for ( auto&& x : t.parameterRefs() ) - p.parent().scope()->insert(std::move(x)); - + void operator()(type::Struct* t) final { if ( ! p.node.as().typeID() ) // We need to associate the type ID with the declaration we're // creating, so wait for that to have been set by the resolver. @@ -180,15 +179,16 @@ struct Visitor : visitor::PostOrder, type::Visitor { if ( t.selfRef() ) p.node.scope()->insert(t.selfRef()); + + for ( auto&& x : t.parameterRefs() ) + d.parent()->scope()->insert(std::move(x)); } +#endif }; } // anonymous namespace -void hilti::detail::ast::buildScopes(const std::shared_ptr& ctx, Node* root, Unit* unit) { +void hilti::detail::ast::buildScopes(Builder* builder, const ASTRootPtr& root) { util::timing::Collector _("hilti/compiler/ast/scope-builder"); - - auto v = Visitor(ctx, unit); - for ( auto i : v.walk(root) ) - v.dispatch(i); + ::hilti::visitor::visit(Visitor(builder, root), root); } diff --git a/hilti/toolchain/src/compiler/visitors/validator.cc b/hilti/toolchain/src/compiler/visitors/validator.cc index 523ee505c..ded47a470 100644 --- a/hilti/toolchain/src/compiler/visitors/validator.cc +++ b/hilti/toolchain/src/compiler/visitors/validator.cc @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -13,6 +14,8 @@ #include #include +#include "base/timing.h" + using namespace hilti; using util::fmt; @@ -20,34 +23,32 @@ namespace { struct VisitorBase { // Record error at location of current node. - void error(std::string msg, const visitor::Position& p, - node::ErrorPriority priority = node::ErrorPriority::Normal) { - p.node.addError(std::move(msg), p.node.location(), priority); + void error(std::string msg, const NodePtr& n, node::ErrorPriority priority = node::ErrorPriority::Normal) { + n->addError(std::move(msg), n->location(), priority); ++errors; } // Record error with current node, but report with another node's location. - void error(std::string msg, const visitor::Position& p, const Node& n, + void error(std::string msg, const NodePtr& n, const NodePtr& other, node::ErrorPriority priority = node::ErrorPriority::Normal) { - p.node.addError(std::move(msg), n.location(), priority); + n->addError(std::move(msg), other->location(), priority); ++errors; } // Record error with current node, but report with a custom location. - void error(std::string msg, const visitor::Position& p, Location l, + void error(std::string msg, const NodePtr& n, Location l, node::ErrorPriority priority = node::ErrorPriority::Normal) { - p.node.addError(std::move(msg), std::move(l), priority); + n->addError(std::move(msg), std::move(l), priority); ++errors; } int errors = 0; }; -struct VisitorPre : public hilti::visitor::PreOrder, public VisitorBase {}; - -struct VisitorPost : public hilti::visitor::PreOrder, public VisitorBase, type::Visitor { - using position_t = hilti::visitor::PreOrder::position_t; +struct VisitorPre : visitor::PreOrder, VisitorBase {}; +struct VisitorPost : visitor::PreOrder, VisitorBase { +#if 0 void preDispatch(const Node& n, int level) override { // Validate that identifier names are not reused. for ( const auto& [id, nodes] : n.scope()->items() ) { @@ -618,22 +619,17 @@ struct VisitorPost : public hilti::visitor::PreOrder, public error(fmt("type parameter %u is missing (%s)", i + 1, want[i].id()), p); } } +#endif }; } // anonymous namespace -void hilti::detail::ast::validate_pre(Node* root) { +void hilti::detail::ast::validate_pre(ASTContext* ctx, const ASTRootPtr& root) { util::timing::Collector _("hilti/compiler/ast/validator"); - - auto v = VisitorPre(); - for ( auto i : v.walk(root) ) - v.dispatch(i); + ::hilti::visitor::visit(VisitorPre(), root); } -void hilti::detail::ast::validate_post(Node* root) { +void hilti::detail::ast::validate_post(ASTContext* ctx, const ASTRootPtr& root) { util::timing::Collector _("hilti/compiler/ast/validator"); - - auto v = VisitorPost(); - for ( auto i : v.walk(root) ) - v.dispatch(i); + ::hilti::visitor::visit(VisitorPost(), root); }