1 //===-- StatementContext.h --------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef FORTRAN_LOWER_STATEMENTCONTEXT_H
14 #define FORTRAN_LOWER_STATEMENTCONTEXT_H
15 
16 #include "llvm/ADT/Optional.h"
17 #include "llvm/ADT/SmallVector.h"
18 #include <functional>
19 
20 namespace Fortran::lower {
21 
22 /// When lowering a statement, temporaries for intermediate results may be
23 /// allocated on the heap.  A StatementContext enables their deallocation
24 /// either explicitly with finalize() calls, or implicitly at the end of
25 /// the context.  A context may prohibit temporary allocation.  Otherwise,
26 /// an initial "outer" context scope may have nested context scopes, which
27 /// must make explicit subscope finalize() calls.
28 class StatementContext {
29 public:
30   explicit StatementContext(bool cleanupProhibited = false) {
31     if (cleanupProhibited)
32       return;
33     cufs.push_back({});
34   }
35 
~StatementContext()36   ~StatementContext() {
37     if (!cufs.empty())
38       finalizeAndPop();
39     assert(cufs.empty() && "invalid StatementContext destructor call");
40   }
41 
42   using CleanupFunction = std::function<void()>;
43 
44   /// Push a context subscope.
pushScope()45   void pushScope() {
46     assert(!cufs.empty() && "invalid pushScope statement context");
47     cufs.push_back({});
48   }
49 
50   /// Append a cleanup function to the "list" of cleanup functions.
attachCleanup(CleanupFunction cuf)51   void attachCleanup(CleanupFunction cuf) {
52     assert(!cufs.empty() && "invalid attachCleanup statement context");
53     if (cufs.back()) {
54       CleanupFunction oldCleanup = *cufs.back();
55       cufs.back() = [=]() {
56         cuf();
57         oldCleanup();
58       };
59     } else {
60       cufs.back() = cuf;
61     }
62   }
63 
64   /// Make cleanup calls.  Retain the stack top list for a repeat call.
finalizeAndKeep()65   void finalizeAndKeep() {
66     assert(!cufs.empty() && "invalid finalize statement context");
67     if (cufs.back())
68       (*cufs.back())();
69   }
70 
71   /// Make cleanup calls.  Pop the stack top list.
finalizeAndPop()72   void finalizeAndPop() {
73     finalizeAndKeep();
74     cufs.pop_back();
75   }
76 
77   /// Make cleanup calls.  Clear the stack top list.
finalize()78   void finalize() {
79     finalizeAndKeep();
80     cufs.back().reset();
81   }
82 
workListIsEmpty()83   bool workListIsEmpty() const {
84     return cufs.empty() || llvm::all_of(cufs, [](auto &opt) -> bool {
85              return !opt.has_value();
86            });
87   }
88 
89 private:
90   // A statement context should never be copied or moved.
91   StatementContext(const StatementContext &) = delete;
92   StatementContext &operator=(const StatementContext &) = delete;
93   StatementContext(StatementContext &&) = delete;
94 
95   // Stack of cleanup function "lists" (nested cleanup function calls).
96   llvm::SmallVector<llvm::Optional<CleanupFunction>> cufs;
97 };
98 
99 } // namespace Fortran::lower
100 
101 #endif // FORTRAN_LOWER_STATEMENTCONTEXT_H
102