Joos1W Compiler Framework
All Classes Functions Typedefs Pages
ExprResolver.h
1 #pragma once
2 
3 #include <functional>
4 #include <memory_resource>
5 #include <variant>
6 
7 #include "ast/AstNode.h"
8 #include "ast/DeclContext.h"
9 #include "ast/ExprEvaluator.h"
10 #include "ast/ExprNode.h"
11 #include "diagnostics/Diagnostics.h"
12 #include "diagnostics/Location.h"
13 #include "semantic/ExprTypeResolver.h"
14 #include "semantic/HierarchyChecker.h"
15 #include "semantic/Semantic.h"
16 #include "utils/BumpAllocator.h"
17 #include "utils/EnumMacros.h"
18 
19 namespace semantic {
20 
21 /* ===--------------------------------------------------------------------=== */
22 // internal::
23 /* ===--------------------------------------------------------------------=== */
24 
25 namespace internal {
26 
27 class ExprNameWrapper;
28 
29 /**
30  * @brief The ExprResolverTy struct is a variant that represents the different
31  * things an expression can resolve to. They are as follows:
32  * 1. A name wrapper is a chain of names that are being resolved
33  * 2. An expression node is a single unresolved expression
34  * 3. An expression node list is a list of resolved expressions
35  */
36 using ExprResolverTy =
37  std::variant<ExprNameWrapper*, ast::ExprNode*, ast::ExprNodeList>;
38 
39 /**
40  * @brief Represents a wrapper around a name that is being resolved. This
41  * is a list of either ExprNameWrapper or ast::ExprNodeList.
42  */
44 public:
46 
47 public:
48 #define NAME_TYPE_LIST(F)
49  F(PackageName)
50  F(TypeName)
51  F(ExpressionName)
52  F(MethodName)
53  F(SingleAmbiguousName)
54 
55  DECLARE_ENUM(Type, NAME_TYPE_LIST)
56 
57 private:
58  DECLARE_STRING_TABLE(Type, type_strings, NAME_TYPE_LIST)
59 #undef NAME_TYPE_LIST
60 
61 public:
62  /**
63  * @brief Build an unresolved wrapper of Type given a name node,
64  * represents a name particle represented by "node" of type "type".
65  *
66  * @param type The Java name type of the current particle
67  * @param node The expression node representing the particle
68  * @param op The operator joining the particle to the previous particle.
69  * If this is a single name, then op is nullptr.
70  */
71  ExprNameWrapper(Type type, ast::exprnode::MemberName* node,
72  ast::exprnode::MemberAccess* op)
73  : node{node},
74  op{op},
75  type_{type},
76  prev_{std::nullopt},
78  // Reclassifies the name as a type name based on JLS 6.5.2
79  void reclassify(Type type, ast::Decl const* resolution,
80  ast::Type const* typeResolution) {
81  this->resolution_ = resolution;
82  this->type_ = type;
83  this->typeResolution_ = typeResolution;
84  }
85  // Reclassifies the name as a package name based on JLS 6.5.2
86  void reclassify(Type type, NameResolver::Pkg const* resolution) {
87  this->resolution_ = resolution;
88  this->type_ = type;
89  this->typeResolution_ = nullptr;
90  }
91  // Function to verify invariants of the wrapper
92  void verifyInvariants(Type expectedTy) const;
93  // Get the previous value of the wrapper as a wrapper
94  ExprNameWrapper* prevAsWrapper() const {
95  assert(prev_.has_value() && "No previous value");
96  assert(std::holds_alternative<ExprNameWrapper*>(prev_.value()) &&
97  "Previous value is not a wrapper");
98  return std::get<ExprNameWrapper*>(prev_.value());
99  }
100  // Get the previous value of the wrapper as a list of expressions
101  ExprNameWrapper* prevIfWrapper() const {
102  assert(prev_.has_value() && "No previous value");
103  if(auto* list = std::get_if<ExprNameWrapper*>(&prev_.value())) return *list;
104  return nullptr;
105  }
106  /**
107  * @brief If the previous value is a wrapper, unwrap the declaration.
108  * However, if the previous value is a list of expressions, return the
109  * the class representation of the type of the expression.
110  *
111  * @param TR The expression type resolver
112  * @param NR The name resolver
113  * @return ast::Decl const* Either a decl or a type represented as decl
114  */
116  SourceRange loc) const;
117  // Gets the "type of name" the current particle has been resolved to
118  Type type() const { return type_; }
119  // Gets the resolution of the name particle (must exist). The resolution
120  // will be either a package or a declaration.
121  auto resolution() const { return resolution_.value(); }
122  // Gets the type of the resolution of the name particle.
123  auto typeResolution() const { return typeResolution_; }
124  // Sets the previous type of the wrapper
125  void setPrev(std::optional<PrevTy> prev) {
126  // Make sure if prev is a wrapper, it is not nullptr
127  if(prev.has_value())
128  if(auto wrapper = std::get_if<ExprNameWrapper*>(&prev.value()))
129  assert(*wrapper && "Previous value is nullptr");
130  prev_ = prev;
131  }
132  std::optional<PrevTy> prev() const { return prev_; }
133  void dump() const;
134  void dump(int indent) const;
135  std::string_view type_string() const {
136  return Type_to_string(type_, "Unknown Type");
137  }
138 
139 public:
140  ast::exprnode::MemberName* node;
141  ast::exprnode::MemberAccess* op;
142 
143  // Private means the semantics of the wrapper must NOT be changed!
144 private:
145  Type type_;
146  std::optional<PrevTy> prev_;
147  NameResolver::ConstImportOpt resolution_;
148  ast::Type const* typeResolution_;
149 };
150 
151 } // namespace internal
152 
153 /* ===--------------------------------------------------------------------=== */
154 // ExprResolver
155 /* ===--------------------------------------------------------------------=== */
156 
157 class ExprResolver final : public ast::ExprEvaluator<internal::ExprResolverTy> {
158  using ETy = internal::ExprResolverTy;
159  using Heap = std::pmr::memory_resource;
160 
161 public:
162  ExprResolver(diagnostics::DiagnosticEngine& diag, Heap* heap)
163  : diag{diag}, heap{heap}, alloc{heap} {}
164  void Init(ExprTypeResolver* TR, NameResolver* NR, ast::Semantic* Sema,
165  semantic::HierarchyChecker* HC) {
166  // FIXME(kevin): This API is ugly but its low priority to remove
167  this->TR = TR;
168  this->NR = NR;
169  this->Sema = Sema;
170  this->HC = HC;
171  }
172  void BeginCU(ast::CompilationUnit const* cu) { cu_ = cu; }
173  void BeginContext(ast::DeclContext const* ctx) { lctx_ = ctx; }
174  ast::ExprNodeList Evaluate(ast::Expr* expr) {
175  loc_ = expr->location();
176  lscope_ = expr->scope();
177  auto ret = EvaluateList(expr->list());
178  return resolveExprNode(ret);
179  }
180 
181 private:
182  ETy EvaluateList(ast::ExprNodeList subexpr) override final {
183  // Clear the heap
184  if(auto h = dyn_cast<utils::CustomBufferResource*>(heap)) h->reset();
185  // Call the base class implementation
186  return ast::ExprEvaluator<internal::ExprResolverTy>::EvaluateList(subexpr);
187  }
188 
189 private: // Overriden methods
190  using Type = ast::Type;
191  using BinaryOp = ast::exprnode::BinaryOp;
192  using UnaryOp = ast::exprnode::UnaryOp;
193  using DotOp = ast::exprnode::MemberAccess;
194  using MethodOp = ast::exprnode::MethodInvocation;
195  using NewOp = ast::exprnode::ClassInstanceCreation;
196  using NewArrayOp = ast::exprnode::ArrayInstanceCreation;
197  using ArrayAccessOp = ast::exprnode::ArrayAccess;
198  using CastOp = ast::exprnode::Cast;
199  using ExprValue = ast::exprnode::ExprValue;
200 
201  ETy mapValue(ExprValue& node) const override;
202  ETy evalBinaryOp(BinaryOp& op, const ETy lhs, const ETy rhs) const override;
203  ETy evalUnaryOp(UnaryOp& op, const ETy rhs) const override;
204  ETy evalMemberAccess(DotOp& op, const ETy lhs, const ETy field) const override;
205  ETy evalMethodCall(MethodOp& op, const ETy method,
206  const op_array& args) const override;
207  ETy evalNewObject(NewOp& op, const ETy object,
208  const op_array& args) const override;
209  ETy evalNewArray(NewArrayOp& op, const ETy type, const ETy size) const override;
210  ETy evalArrayAccess(ArrayAccessOp& op, const ETy array,
211  const ETy index) const override;
212  ETy evalCast(CastOp& op, const ETy type, const ETy value) const override;
213  bool validate(ETy const& value) const override;
214 
215 private:
216  using ty_array = std::pmr::vector<ast::Type const*>;
217 
218  // Resolve an expression node into a list (i.e., removes expr wrapper)
219  ast::ExprNodeList resolveExprNode(const ETy node) const;
220  // Given a single ambiguous name, reclassify it into a package or type name
221  internal::ExprNameWrapper* reclassifySingleAmbiguousName(
222  internal::ExprNameWrapper* data) const;
223  // Try to reclassify "data" into a declaration against "ctx"
224  bool tryReclassifyDecl(internal::ExprNameWrapper& data,
225  ast::DeclContext const* ctx) const;
226  // Try to reclassify "data" against an imported object/pkg "import"
227  bool tryReclassifyImport(internal::ExprNameWrapper& data,
228  NameResolver::ConstImportOpt import) const;
229  // Resolve access into a context (i.e., field member access)
230  void resolveFieldAccess(internal::ExprNameWrapper* access) const;
231  // Resolve access into a type (i.e., static member access)
232  void resolveTypeAccess(internal::ExprNameWrapper* access) const;
233  // Resolve access into a package -> either package or type is resolved
234  void resolvePackageAccess(internal::ExprNameWrapper* access) const;
235  // Resolves a single name node into a wrapped name. This is just a light
236  // function over reclassifySingleAmbiguousName that allocates the wrapper.
237  internal::ExprNameWrapper* resolveSingleName(
238  ast::exprnode::MemberName* node) const {
239  if(auto method = dyn_cast<ast::exprnode::MethodName*>(node)) {
240  return alloc.new_object<internal::ExprNameWrapper>(
241  internal::ExprNameWrapper::Type::MethodName, method, nullptr);
242  }
243  return reclassifySingleAmbiguousName(
244  alloc.new_object<internal::ExprNameWrapper>(
245  internal::ExprNameWrapper::Type::SingleAmbiguousName,
246  node,
247  nullptr));
248  }
249  // Recursively reduces the wrapped node into an expression list
250  ast::ExprNodeList recursiveReduce(internal::ExprNameWrapper* node) const;
251  // Gets the parent context the method is declared under
252  ast::DeclContext const* getMethodParent(internal::ExprNameWrapper* node) const;
253  // Resolves a method overload given a context and a list of argument types
254  ast::MethodDecl const* resolveMethodOverload(ast::DeclContext const* ctx,
255  std::string_view name,
256  const ty_array& argtys,
257  bool isCtor) const;
258  // Checks if a method is more specific than another: returns a > b
259  bool isMethodMoreSpecific(ast::MethodDecl const* a,
260  ast::MethodDecl const* b) const;
261  // Checks if the parameter types are applicable
262  bool areParameterTypesApplicable(ast::MethodDecl const* method,
263  const ty_array& argtys) const;
264  // Checks if the method is accessible in the given context
265  bool isAccessible(ast::Modifiers, ast::DeclContext const*) const;
266  // Gets the inherited methods of a context
267  utils::Generator<ast::MethodDecl const*> getInheritedMethods(
268  ast::DeclContext const* ctx) const;
269  /**
270  * @brief If a unique declaration exists with the given name in the
271  * immediate context, then it is returned. Otherwise, nullptr is returned.
272  *
273  * @param name The name of the declaration to look up.
274  * @return Decl const* The declaration with the given name or nullptr.
275  */
276  const ast::Decl* lookupDecl(ast::DeclContext const* ctx,
277  std::function<bool(ast::Decl const*)> cond) const;
278  /**
279  * @brief Finds the unique declaration with the given name that is visible
280  * in the context given. The scope of the declaration is considered IFF
281  * the ctx is exactly equal to the lctx_ state. Then, only the in-scope
282  * declarations are returned, the scope given by lscope_.
283  *
284  * @param ctx The context to look up the declaration in.
285  * @param name The name of the declaration to look up.
286  * @return const ast::Decl*
287  */
288  const ast::Decl* lookupNamedDecl(ast::DeclContext const* ctx,
289  std::string_view name) const;
290 
291 private:
292  diagnostics::DiagnosticEngine& diag;
293  ast::CompilationUnit const* cu_;
294  ast::DeclContext const* lctx_;
295  ast::ScopeID const* lscope_;
296  semantic::NameResolver* NR;
297  semantic::ExprTypeResolver* TR;
298  semantic::HierarchyChecker* HC;
299  ast::Semantic* Sema;
300  mutable Heap* heap;
301  mutable BumpAllocator alloc;
302  SourceRange loc_;
303 };
304 
305 } // namespace semantic