action_stack.h 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #ifndef CARBON_EXPLORER_INTERPRETER_ACTION_STACK_H_
  5. #define CARBON_EXPLORER_INTERPRETER_ACTION_STACK_H_
  6. #include <memory>
  7. #include <optional>
  8. #include <stack>
  9. #include "common/ostream.h"
  10. #include "explorer/ast/statement.h"
  11. #include "explorer/ast/value.h"
  12. #include "explorer/base/trace_stream.h"
  13. #include "explorer/interpreter/action.h"
  14. namespace Carbon {
  15. // Selects between compile-time and run-time behavior.
  16. enum class Phase { CompileTime, RunTime };
  17. // The stack of Actions currently being executed by the interpreter.
  18. class ActionStack : public Printable<ActionStack> {
  19. public:
  20. // Constructs an empty compile-time ActionStack.
  21. explicit ActionStack(Nonnull<TraceStream*> trace_stream)
  22. : phase_(Phase::CompileTime), trace_stream_(trace_stream) {}
  23. // Constructs an empty run-time ActionStack that allocates global variables
  24. // on `heap`.
  25. explicit ActionStack(Nonnull<TraceStream*> trace_stream,
  26. Nonnull<HeapAllocationInterface*> heap)
  27. : globals_(RuntimeScope(heap)),
  28. phase_(Phase::RunTime),
  29. trace_stream_(trace_stream) {}
  30. void Print(llvm::raw_ostream& out) const;
  31. // Starts execution with `action` at the top of the stack. Cannot be called
  32. // when IsEmpty() is false.
  33. void Start(std::unique_ptr<Action> action);
  34. // True if the stack is empty.
  35. auto empty() const -> bool { return todo_.empty(); }
  36. // The Action currently at the top of the stack. This will never be a
  37. // ScopeAction.
  38. auto CurrentAction() -> Action& { return *todo_.Top(); }
  39. // Allocates storage for `value_node`, and initializes it to `value`.
  40. void Initialize(ValueNodeView value_node, Nonnull<const Value*> value);
  41. // Returns the value bound to `value_node`. If `value_node` is a local
  42. // variable, this will be an LocationValue.
  43. auto ValueOfNode(ValueNodeView value_node, SourceLocation source_loc) const
  44. -> ErrorOr<Nonnull<const Value*>>;
  45. // Merges `scope` into the innermost scope currently on the stack.
  46. void MergeScope(RuntimeScope scope);
  47. // The result produced by the `action` argument of the most recent
  48. // Start call. Cannot be called if IsEmpty() is false, or if `action`
  49. // was an action that doesn't produce results.
  50. auto result() const -> Nonnull<const Value*> { return *result_; }
  51. // The following methods, called "transition methods", update the state of
  52. // the ActionStack and/or the current Action to reflect the effects of
  53. // executing a step of that Action. Execution of an Action step should always
  54. // invoke exactly one transition method, as the very last operation. This is a
  55. // matter of safety as well as convention: most transition methods modify the
  56. // state of the current action, and some of them destroy it. To help enforce
  57. // this requirement, we have a convention of making these methods return an
  58. // ErrorOr<Success> even when a method can't actually fail, and calling the
  59. // methods as part of return statements, e.g. `return todo_.FinishAction()`.
  60. // Finishes execution of the current Action. If `result` is specified, it
  61. // represents the result of that Action.
  62. auto FinishAction() -> ErrorOr<Success>;
  63. auto FinishAction(Nonnull<const Value*> result) -> ErrorOr<Success>;
  64. // Advances the current action one step, and push `child` onto the stack.
  65. // If `scope` is specified, `child` will be executed in that scope.
  66. auto Spawn(std::unique_ptr<Action> child) -> ErrorOr<Success>;
  67. auto Spawn(std::unique_ptr<Action> child, RuntimeScope scope)
  68. -> ErrorOr<Success>;
  69. // Replace the current action with another action that produces the same kind
  70. // of result and run it next.
  71. auto ReplaceWith(std::unique_ptr<Action> replacement) -> ErrorOr<Success>;
  72. // Start a new recursive action.
  73. auto BeginRecursiveAction() {
  74. todo_.Push(std::make_unique<RecursiveAction>());
  75. }
  76. // Advances the current action one step.
  77. auto RunAgain() -> ErrorOr<Success>;
  78. // Unwinds Actions from the stack until the StatementAction associated with
  79. // `ast_node` is at the top of the stack.
  80. auto UnwindTo(Nonnull<const Statement*> ast_node) -> ErrorOr<Success>;
  81. // Unwinds Actions from the stack until the StatementAction associated with
  82. // `ast_node` has been removed from the stack. If `result` is specified,
  83. // it represents the result of that Action (StatementActions normally cannot
  84. // produce results, but the body of a function can).
  85. auto UnwindPast(Nonnull<const Statement*> ast_node) -> ErrorOr<Success>;
  86. auto UnwindPast(Nonnull<const Statement*> ast_node,
  87. Nonnull<const Value*> result) -> ErrorOr<Success>;
  88. auto Pop() -> std::unique_ptr<Action> {
  89. auto popped_action = todo_.Pop();
  90. if (trace_stream_->is_enabled()) {
  91. trace_stream_->Pop() << "stack-pop: " << *popped_action << " ("
  92. << popped_action->source_loc() << ")\n";
  93. }
  94. return popped_action;
  95. }
  96. void Push(std::unique_ptr<Action> action) {
  97. if (trace_stream_->is_enabled()) {
  98. trace_stream_->Push()
  99. << "stack-push: " << *action << " (" << action->source_loc() << ")\n";
  100. }
  101. todo_.Push(std::move(action));
  102. }
  103. auto size() const -> int { return todo_.size(); }
  104. private:
  105. // Pop any ScopeActions from the top of the stack, propagating results as
  106. // needed, to restore the invariant that todo_.Top() is not a ScopeAction.
  107. // Store the popped scope action into cleanup_stack, so that the destructor
  108. // can be called for the variables
  109. void PopScopes(std::stack<std::unique_ptr<Action>>& cleanup_stack);
  110. // Set `result` as the result of the Action most recently removed from the
  111. // stack.
  112. void SetResult(Nonnull<const Value*> result);
  113. auto UnwindToWithCaptureScopesToDestroy(Nonnull<const Statement*> ast_node)
  114. -> std::stack<std::unique_ptr<Action>>;
  115. auto UnwindPastWithCaptureScopesToDestroy(Nonnull<const Statement*> ast_node)
  116. -> std::stack<std::unique_ptr<Action>>;
  117. // Create CleanUpActions for all actions
  118. void PushCleanUpActions(std::stack<std::unique_ptr<Action>> actions);
  119. // Create and push a CleanUpAction on the stack
  120. void PushCleanUpAction(std::unique_ptr<Action> act);
  121. // TODO: consider defining a non-nullable unique_ptr-like type to use here.
  122. Stack<std::unique_ptr<Action>> todo_;
  123. std::optional<Nonnull<const Value*>> result_;
  124. std::optional<RuntimeScope> globals_;
  125. Phase phase_;
  126. Nonnull<TraceStream*> trace_stream_;
  127. };
  128. } // namespace Carbon
  129. #endif // CARBON_EXPLORER_INTERPRETER_ACTION_STACK_H_