Spicy
ast-context.h
1 // Copyright (c) 2020-now by the Zeek Project. See LICENSE for details.
2 
3 #pragma once
4 
5 #include <map>
6 #include <memory>
7 #include <set>
8 #include <string>
9 #include <unordered_map>
10 #include <utility>
11 #include <vector>
12 
13 #include <hilti/rt/types/integer.h>
14 
15 #include <hilti/ast/declarations/module-uid.h>
16 #include <hilti/ast/forward.h>
17 #include <hilti/ast/node.h>
18 #include <hilti/base/logger.h>
19 #include <hilti/base/uniquer.h>
20 
21 namespace hilti {
22 
23 class ASTContext;
24 class Context;
25 class Driver;
26 struct Plugin;
27 
28 namespace type {
29 struct Wildcard;
30 }
31 
32 namespace detail::cfg {
33 class Cache;
34 }
35 
45 Result<declaration::Module*> parseSource(Builder* builder, std::istream& in, const std::string& filename);
46 
47 namespace ast {
48 
49 namespace detail {
50 
51 class DependencyTracker;
52 
57 template<char Prefix>
58 class ContextIndex {
59 public:
66  explicit ContextIndex(size_t index = 0) : _value(index) {}
67 
69  auto value() const { return _value; }
70 
75  auto str() const { return _value > 0 ? std::string(1, Prefix) + rt::to_string(_value) : std::string("-"); }
76 
78  explicit operator bool() const { return *this != None; }
79 
80  bool operator==(const ContextIndex& other) const { return _value == other._value; }
81  bool operator!=(const ContextIndex& other) const { return _value != other._value; }
82 
83  ContextIndex(const ContextIndex& other) = default;
84  ContextIndex(ContextIndex&& other) noexcept = default;
85  ContextIndex& operator=(const ContextIndex& other) = default;
86  ContextIndex& operator=(ContextIndex&& other) noexcept = default;
87 
88  static const ContextIndex None;
90 private:
91  rt::integer::safe<uint32_t> _value = 0; // safe integer to catch any uint32_t overflows
92 };
93 
94 template<char Prefix>
95 inline std::string to_string(const hilti::ast::detail::ContextIndex<Prefix>& index) {
96  return index.str();
97 }
98 
99 template<char Prefix>
100 inline const ContextIndex<Prefix> ContextIndex<Prefix>::None{0};
101 
102 template<char Prefix>
103 inline std::ostream& operator<<(std::ostream& out, const hilti::ast::detail::ContextIndex<Prefix>& index) {
104  return out << index.str();
105 }
106 
107 // Helper to sort declaration pointers by their canonical IDs.
109  bool operator()(const Declaration* a, const Declaration* b) const;
110 };
111 
112 } // namespace detail
113 
116 
119 
120 } // namespace ast
121 
128 class ASTContext : public std::enable_shared_from_this<ASTContext> {
129 public:
130  // Set of declarations ordered by their canonical IDs.
131  using DeclarationSet = std::set<Declaration*, ast::detail::DeclarationPtrCmp>;
132 
138  ASTContext(Context* context);
139 
141  ~ASTContext();
142 
144  auto* compilerContext() const { return _context; }
145 
147  auto root() const { return _root.get(); }
148 
165  const hilti::rt::filesystem::path& path,
166  std::optional<hilti::rt::filesystem::path> process_extension = {});
167 
187  Result<declaration::module::UID> importModule(Builder* builder,
188  const ID& id,
189  const ID& scope,
190  const hilti::rt::filesystem::path& parse_extension,
191  const std::optional<hilti::rt::filesystem::path>& process_extension,
192  std::vector<hilti::rt::filesystem::path> search_dirs);
193 
195  declaration::Module* newModule(Builder* builder, ID id, const hilti::rt::filesystem::path& process_extension);
196 
204  if ( auto m = _modules_by_uid.find(uid); m != _modules_by_uid.end() )
205  return m->second;
206  else
207  return nullptr;
208  }
209 
220 
225  Driver* driver() const { return _driver; }
226 
237  std::set<declaration::module::UID> dependencies(const declaration::module::UID& uid, bool recursive = false) const;
238 
257  const DeclarationSet& dependentDeclarations(Declaration* n);
258 
271  void updateModuleUID(const declaration::module::UID& old_uid, const declaration::module::UID& new_uid);
272 
290 
298  Declaration* lookup(ast::DeclarationIndex index); // must exists, otherwise internal error -> result is not null
299 
315  void replace(Declaration* old, Declaration* new_);
316 
330 
339 
351  void replace(UnqualifiedType* old, UnqualifiedType* new_);
352 
357  ID uniqueCanononicalID(const ID& id) { return _canon_id_uniquer.get(id, false); }
358 
365  void dump(const hilti::logging::DebugStream& stream, const std::string& prefix) const;
366 
373  void dump(std::ostream& out, bool include_state) const;
374 
381  template<typename T, typename... Args>
382  T* make(Args&&... args) {
383  auto t = new T(std::forward<Args>(args)...);
384  _nodes.emplace_back(std::unique_ptr<Node>(t));
385  return t;
386  }
387 
399  template<typename T, typename... Args>
400  T* make(ASTContext* ctx, std::initializer_list<Node*> children, Args&&... args) {
401  assert(ctx == this);
402  auto t = new T(ctx, children, std::forward<Args>(args)...);
403  _nodes.emplace_back(std::unique_ptr<Node>(t));
404  return t;
405  }
406 
419  template<typename T, typename... Args>
420  T* make(ASTContext* ctx, type::Wildcard&& wildcard, std::initializer_list<Node*> children, Args&&... args) {
421  assert(ctx == this);
422  auto t = new T(ctx, std::forward<type::Wildcard>(wildcard), children, std::forward<Args>(args)...);
423  _nodes.emplace_back(std::unique_ptr<Node>(t));
424  return t;
425  }
426 
433  void clearErrors(Node* node = nullptr);
434 
441  void clearScopes(Node* node = nullptr);
442 
449 
450 #ifndef NDEBUG
460  void checkAST(bool finished = true) const;
461 #endif
462 
464  void garbageCollect();
465 
467  void clear();
468 
475  static constexpr unsigned int MaxASTIterationRounds = 100;
476 
477 private:
478  // The following methods implement the corresponding phases of AST processing.
479 
480  Result<declaration::module::UID> _parseSource(Builder* builder,
481  const hilti::rt::filesystem::path& path,
482  const ID& scope,
483  std::optional<hilti::rt::filesystem::path> process_extension = {});
484  Result<Nothing> _init(Builder* builder, const Plugin& plugin);
485  Result<Nothing> _buildScopes(Builder* builder, const Plugin& plugin);
486  Result<Nothing> _resolve(Builder* builder, const Plugin& plugin);
487  Result<Nothing> _resolveUnresolvedNodes(bool* modified, Builder* builder, const Plugin& plugin);
488  Result<Nothing> _resolveRoot(bool* modified, Builder* builder, const Plugin& plugin);
489  Result<Nothing> _validate(Builder* builder, const Plugin& plugin, bool pre_resolver);
490  Result<Nothing> _transform(Builder* builder, const Plugin& plugin);
491  Result<Nothing> _optimize(Builder* builder, detail::cfg::Cache* cfg_cache);
492  Result<Nothing> _computeDependencies();
493 
494  // Adds a module to the AST. The module must not be part of any AST yet
495  // (including the current one).
497 
498  // Dumps the AST to disk during AST processing, for debugging..
499  void _saveIterationAST(const Plugin& plugin, const std::string& prefix, unsigned int round = 0);
500 
501  // Dumps the AST to disk during AST processing, for debugging..
502  void _saveIterationAST(const Plugin& plugin, const std::string& prefix, const std::string& tag);
503 
504  // Dumps the AST to a debugging stream.
505  void _dumpAST(const hilti::logging::DebugStream& stream,
506  const Plugin& plugin,
507  const std::string& prefix,
508  std::optional<unsigned int> round);
509 
510  // Dumps the AST to a debugging stream.
511  void _dumpAST(std::ostream& stream,
512  const Plugin& plugin,
513  const std::string& prefix,
514  std::optional<unsigned int> round);
515 
516  // Dumps the accumulated state tables of the context to a debugging stream.
517  void _dumpState(const logging::DebugStream& stream) const;
518 
519  // Dumps the accumulated state tables of the context to an output stream.
520  void _dumpState(std::ostream& out) const;
521 
522  // Dump statistics about the AST to a debugging stream.
523  void _dumpStats(const logging::DebugStream& stream, std::string_view tag);
524 
525  // Dumps the accumulated state tables of the context to a debugging stream.
526  void _dumpDeclarations(const logging::DebugStream& stream, const Plugin& plugin);
527 
528  Context* _context = nullptr; // compiler context
529  std::vector<std::unique_ptr<Node>> _nodes; // all nodes allocated through the context; used by garbage collection
530 
531  node::RetainedPtr<ASTRoot> _root = nullptr; // root node of the AST
532  bool _resolved = false; // true if `processAST()` has finished successfully
533  Driver* _driver = nullptr; // pointer to compiler drive during `processAST()`, null outside of that
534  util::Uniquer<ID> _canon_id_uniquer; // Produces unique canonified IDs
535  std::unique_ptr<ast::detail::DependencyTracker> _dependency_tracker; // records dependencies between declarations
536 
537  unsigned int _total_rounds = 0; // total number of rounds of AST processing
538 
539  std::unordered_map<declaration::module::UID, node::RetainedPtr<declaration::Module>>
540  _modules_by_uid; // all known modules indexed by UID
541 
542  std::unordered_map<std::string, declaration::Module*>
543  _modules_by_path; // all known modules indexed by path (retained by _by_uid)
544  std::map<std::pair<ID, ID>, declaration::Module*>
545  _modules_by_id_and_scope; // all known modules indexed by their ID and search scope (retained by _by_uid)
546 
547  std::vector<node::RetainedPtr<Declaration>>
548  _declarations_by_index; // all registered declarations; vector position corresponds to their index
549  std::vector<node::RetainedPtr<UnqualifiedType>>
550  _types_by_index; // all registered types; vector position corresponds to their index
551 };
552 
557 class ASTRoot : public Node {
558 public:
559  static auto create(ASTContext* ctx) { return ctx->make<ASTRoot>(ctx); }
560 
561 protected:
562  ASTRoot(ASTContext* ctx) : Node(ctx, NodeTags, {}, Meta(Location("<root>"))) {}
563 
564  std::string _dump() const final;
565 
566  HILTI_NODE_0(ASTRoot, final);
567 };
568 
569 } // namespace hilti
570 
571 namespace std {
572 template<char Prefix>
573 struct hash<hilti::ast::detail::ContextIndex<Prefix>> {
574  size_t operator()(hilti::ast::detail::ContextIndex<Prefix> x) const { return x.value().Ref(); }
575 };
576 } // namespace std
Definition: ast-context.h:128
ASTContext(Context *context)
Definition: ast-context.cc:225
~ASTContext()
Definition: ast-context.cc:232
auto root() const
Definition: ast-context.h:147
T * make(ASTContext *ctx, std::initializer_list< Node * > children, Args &&... args)
Definition: ast-context.h:400
void updateModuleUID(const declaration::module::UID &old_uid, const declaration::module::UID &new_uid)
Definition: ast-context.cc:417
std::set< declaration::module::UID > dependencies(const declaration::module::UID &uid, bool recursive=false) const
void checkAST(bool finished=true) const
Definition: ast-context.cc:715
Driver * driver() const
Definition: ast-context.h:225
ast::DeclarationIndex register_(Declaration *decl)
Definition: ast-context.cc:433
void dump(const hilti::logging::DebugStream &stream, const std::string &prefix) const
Definition: ast-context.cc:923
void clearScopes(Node *node=nullptr)
Definition: ast-context.cc:756
ID uniqueCanononicalID(const ID &id)
Definition: ast-context.h:357
static constexpr unsigned int MaxASTIterationRounds
Definition: ast-context.h:475
void clear()
Definition: ast-context.cc:245
auto * compilerContext() const
Definition: ast-context.h:144
T * make(Args &&... args)
Definition: ast-context.h:382
declaration::Module * newModule(Builder *builder, ID id, const hilti::rt::filesystem::path &process_extension)
Definition: ast-context.cc:322
Result< declaration::module::UID > importModule(Builder *builder, const ID &id, const ID &scope, const hilti::rt::filesystem::path &parse_extension, const std::optional< hilti::rt::filesystem::path > &process_extension, std::vector< hilti::rt::filesystem::path > search_dirs)
Definition: ast-context.cc:269
void replace(Declaration *old, Declaration *new_)
Definition: ast-context.cc:464
Declaration * lookup(ast::DeclarationIndex index)
Definition: ast-context.cc:497
Result< declaration::module::UID > parseSource(Builder *builder, const hilti::rt::filesystem::path &path, std::optional< hilti::rt::filesystem::path > process_extension={})
Definition: ast-context.cc:263
T * make(ASTContext *ctx, type::Wildcard &&wildcard, std::initializer_list< Node * > children, Args &&... args)
Definition: ast-context.h:420
void clearErrors(Node *node=nullptr)
Definition: ast-context.cc:747
void garbageCollect()
Definition: ast-context.cc:331
const DeclarationSet & dependentDeclarations(Declaration *n)
Definition: ast-context.cc:1073
Result< Nothing > processAST(Builder *builder, Driver *driver)
Definition: ast-context.cc:622
Result< Nothing > collectErrors()
Definition: ast-context.cc:1131
declaration::Module * module(const declaration::module::UID &uid) const
Definition: ast-context.h:203
Definition: ast-context.h:557
std::string _dump() const final
Definition: ast-context.cc:223
Definition: builder.h:36
Definition: context.h:120
Definition: declaration.h:53
Definition: driver.h:85
Definition: id.h:15
Definition: location.h:17
Definition: meta.h:30
Definition: node.h:243
Node(ASTContext *ctx, node::Tags node_tags, Nodes children, Meta meta)
Definition: node.h:984
Definition: type.h:147
Definition: ast-context.h:58
static const ContextIndex None
Definition: ast-context.h:88
auto str() const
Definition: ast-context.h:75
ContextIndex(size_t index=0)
Definition: ast-context.h:66
auto value() const
Definition: ast-context.h:69
Definition: module.h:29
Definition: cfg.h:286
Definition: logger.h:29
Definition: node.h:179
Definition: result.h:73
Definition: uniquer.h:15
std::string to_string(T &&x)
Definition: extension-points.h:26
Definition: plugin.h:47
Definition: ast-context.h:108
Definition: module-uid.h:20
Definition: type.h:38