Joos1W Compiler Framework
All Classes Functions Typedefs Pages
ExprResolver.cc
1 #include "semantic/ExprResolver.h"
2 
3 #include <functional>
4 #include <string_view>
5 #include <utility>
6 #include <variant>
7 
8 #include "ast/AST.h"
9 #include "ast/AstNode.h"
10 #include "ast/Decl.h"
11 #include "ast/DeclContext.h"
12 #include "ast/ExprNode.h"
13 #include "ast/Type.h"
14 #include "diagnostics/Location.h"
15 #include "semantic/HierarchyChecker.h"
16 #include "semantic/NameResolver.h"
17 #include "utils/Generator.h"
18 #include "utils/Utils.h"
19 
20 namespace semantic {
21 
22 namespace ex = ast::exprnode;
23 using ETy = internal::ExprResolverTy;
24 using PrevTy = internal::ExprNameWrapper::PrevTy;
25 using ER = ExprResolver;
26 using internal::ExprNameWrapper;
27 using Pkg = semantic::NameResolver::Pkg;
28 
29 /* ===--------------------------------------------------------------------=== */
30 // Static helper functions
31 /* ===--------------------------------------------------------------------=== */
32 
33 static ast::DeclContext const* GetTypeAsDecl(ast::Type const* type,
34  NameResolver const& NR) {
35  if(auto refty = dyn_cast<ast::ReferenceType>(type)) {
36  return cast<ast::DeclContext>(refty->decl());
37  } else if(type->isString()) {
38  return NR.GetJavaLang().String;
39  } else if(type->isArray()) {
40  return NR.GetArrayPrototype();
41  }
42  return nullptr;
43 }
44 
45 /* ===--------------------------------------------------------------------=== */
46 // Functions to resolve names and chains of names
47 /* ===--------------------------------------------------------------------=== */
48 
49 const ast::Decl* ER::lookupDecl(ast::DeclContext const* ctx,
50  std::function<bool(ast::Decl const*)> cond) const {
51  const ast::Decl* ret = nullptr;
52  auto classDecl = dyn_cast<ast::ClassDecl>(ctx);
53  if(classDecl && ctx != NR->GetArrayPrototype()) {
54  for(auto decl : HC->getInheritedMembers(classDecl)) {
55  if(cond(decl)) {
56  if(ret) return nullptr; // Ambiguous, cannot resolve
57  ret = decl;
58  }
59  }
60  return ret;
61  } else {
62  // Search for the unique local variable
63  for(auto decl : ctx->decls())
64  if(cond(decl)) return decl;
65  return nullptr;
66  }
67  // Context is probably CU
68  return nullptr;
69 }
70 
71 const ast::Decl* ER::lookupNamedDecl(ast::DeclContext const* ctx,
72  std::string_view name) const {
73  auto cond = [name, this](ast::Decl const* d) {
74  auto td = dyn_cast<ast::TypedDecl>(d);
75  if(!td) return false;
76  bool scopeVisible = true;
77  bool sameName = d->name() == name;
78  bool sameContext = d->parent() == this->lctx_;
79  // Ignore scoping rules for fields
80  bool checkScope = dyn_cast<ast::VarDecl>(d) && this->lscope_;
81  // Scoping is only meaningful inside methods
82  if(sameContext && checkScope)
83  scopeVisible = this->lscope_->canView(td->scope());
84  // Check access modifiers for fields
85  bool canAccess = true;
86  if(auto fieldDecl = dyn_cast<ast::FieldDecl>(d)) {
87  canAccess = isAccessible(fieldDecl->modifiers(), fieldDecl->parent());
88  }
89  return sameName && scopeVisible && canAccess;
90  };
91  return lookupDecl(ctx, cond);
92 }
93 
94 bool ER::tryReclassifyDecl(ExprNameWrapper& data,
95  ast::DeclContext const* ctx) const {
96  // Search in this context
97  if(auto decl = lookupNamedDecl(ctx, data.node->name())) {
98  if(auto varDecl = dynamic_cast<ast::VarDecl const*>(decl)) {
99  data.reclassify(
100  ExprNameWrapper::Type::ExpressionName, varDecl, varDecl->type());
101  return true;
102  } else if(auto fieldDecl = dynamic_cast<ast::FieldDecl const*>(decl)) {
103  data.reclassify(ExprNameWrapper::Type::ExpressionName,
104  fieldDecl,
105  fieldDecl->type());
106  return true;
107  }
108  }
109  // If not found in this context, search in parent context
110  // If the parent context does not exist, return the original data
111  auto ctxAsDecl = dynamic_cast<ast::Decl const*>(ctx);
112  if(!ctxAsDecl) return false;
113  auto parentCtx = dynamic_cast<ast::DeclContext const*>(ctxAsDecl->parent());
114  if(!parentCtx) return false;
115  return tryReclassifyDecl(data, parentCtx);
116 }
117 
118 bool ER::tryReclassifyImport(ExprNameWrapper& data,
119  NameResolver::ConstImportOpt import) const {
120  if(!import.has_value()) {
121  throw diag.ReportError(loc_) << "cannot resolve name: " << data.node->name();
122  }
123  if(std::holds_alternative<const ast::Decl*>(import.value())) {
124  auto decl = std::get<const ast::Decl*>(import.value());
125  // If declaration is null, then there is an import-on-demand conflict
126  if(!decl) {
127  throw diag.ReportError(loc_) << "ambiguous import-on-demand conflict";
128  }
129  data.reclassify(
130  ExprNameWrapper::Type::TypeName, decl, Sema->BuildReferenceType(decl));
131  return true;
132  } else if(std::holds_alternative<const Pkg*>(import.value())) {
133  auto pkg = std::get<const Pkg*>(import.value());
134  assert(pkg && "expected non-null package here");
135  data.reclassify(ExprNameWrapper::Type::PackageName, pkg);
136  return true;
137  }
138  return false;
139 }
140 
141 ExprNameWrapper* ER::reclassifySingleAmbiguousName(ExprNameWrapper* data) const {
142  // JLS 6.5.2 Reclassification of Contextually Ambiguous Names
143 
144  assert(data->type() == ExprNameWrapper::Type::SingleAmbiguousName &&
145  "Expected SingleAmbiguousName here");
146  auto copy = alloc.new_object<ExprNameWrapper>(*data);
147 
148  /**
149  * 1. If the Identifier appears within the scope (§6.3) of a local variable
150  * declaration (§14.4) or parameter declaration (§8.4.1, §8.8.1, §14.19)
151  * or field declaration (§8.3) with that name, then the AmbiguousName is
152  * reclassified as an ExpressionName.
153  * 2. Otherwise, if a type of that name is declared in the compilation unit
154  * (§7.3) containing the Identifier, either by a single-type-import
155  * declaration (§7.5.1) or by a top level class (§8) or interface type
156  * declaration (§9), then the AmbiguousName is reclassified as a TypeName.
157  * 3. Otherwise, if a type of that name is declared in another compilation unit
158  * (§7.3) of the package (§7.1) of the compilation unit containing the
159  * Identifier, then the AmbiguousName is reclassified as a TypeName.
160  * 4. Otherwise, if a type of that name is declared by exactly one
161  * type-import-on-demand declaration (§7.5.2) of the compilation unit
162  * containing the Identifier, then the AmbiguousName is reclassified as a
163  * TypeName.
164  * 5. Otherwise, if a type of that name is declared by more than one
165  * type-import-on-demand declaration of the compilation unit containing the
166  * Identifier, then a compile-time error results.
167  * 6. Otherwise, the AmbiguousName is reclassified as a PackageName. A later
168  * step determines whether or not a package of that name actually exists.
169  */
170 
171  // Criteria 1 and part of 2 (checks body of CU)
172  if(tryReclassifyDecl(*copy, lctx_)) return copy;
173 
174  // Criteria 2 through 6 are handled by the NameResolver
175  if(tryReclassifyImport(*copy, NR->GetImport(cu_, data->node->name())))
176  return copy;
177 
178  // If all else fails, we probably hit criteria 6
179  throw diag.ReportError(loc_)
180  << "Unknown error when attempting to resolve import type";
181 }
182 
183 void ER::resolveFieldAccess(ExprNameWrapper* access) const {
184  // First, verify invariants if access is a field access
185  access->verifyInvariants(ExprNameWrapper::Type::SingleAmbiguousName);
186  if(auto p = access->prevIfWrapper())
187  p->verifyInvariants(ExprNameWrapper::Type::ExpressionName);
188  // Next, fetch the type or declaration
189  auto name = access->node->name();
190  auto typeOrDecl = access->prevAsDecl(*TR, *NR, loc_);
191  ast::DeclContext const* refTy = nullptr;
192  if(access->prevIfWrapper()) {
193  // If the previous node is a wrapper, then we resolve the type
194  auto decl = typeOrDecl;
195  auto typeddecl = dyn_cast<ast::TypedDecl>(decl);
196  if(!typeddecl) {
197  throw diag.ReportError(loc_)
198  << "field access \"" << name
199  << "\" to non-typed declaration: " << decl->name();
200  }
201  auto type = typeddecl->type();
202  if(!type) {
203  throw diag.ReportError(loc_)
204  << "field access \"" << name
205  << "\" to void-typed declaration: " << decl->name();
206  }
207  refTy = GetTypeAsDecl(type, *NR);
208  if(!refTy) {
209  throw diag.ReportError(loc_)
210  << "field access \"" << name << "\" to non-class type: "
212  : decl->name());
213  }
214  } else {
215  refTy = typeOrDecl->asDeclContext();
216  assert(refTy && "Expected non-null type here");
217  }
218  // Now we check if "name" is a field of "decl"
219  auto field = lookupNamedDecl(refTy, name);
220  if(!field) {
221  throw diag.ReportError(loc_)
222  << "cannot access field: " << name << access->node->location()
223  << "inaccessible member";
224  }
225  // Field must be either a FieldDecl or a MethodDecl
226  assert(dyn_cast<ast::FieldDecl>(field) || dyn_cast<ast::MethodDecl>(field));
227  // Now we can reclassify the access node
228  ast::Type const* fieldty = nullptr;
229  if(auto x = dyn_cast<ast::FieldDecl>(field)) fieldty = x->type();
230  access->reclassify(ExprNameWrapper::Type::ExpressionName, field, fieldty);
231 }
232 
233 void ER::resolveTypeAccess(internal::ExprNameWrapper* access) const {
234  // First, verify invariants if access is a type access
235  access->verifyInvariants(ExprNameWrapper::Type::SingleAmbiguousName);
236  if(auto p = access->prevIfWrapper())
237  p->verifyInvariants(ExprNameWrapper::Type::TypeName);
238  // Next, fetch the type or declaration
239  auto name = access->node->name();
240  auto typeOrDecl = access->prevAsDecl(*TR, *NR, loc_);
241  // We note this must be a class type or we have a type error
242  auto type = dynamic_cast<ast::ClassDecl const*>(typeOrDecl);
243  if(!type) {
244  throw diag.ReportError(loc_)
245  << "static member access \"" << name
246  << "\" to non-class type: " << typeOrDecl->name();
247  }
248  // Now we check if "name" is a field of "decl".
249  auto field = lookupNamedDecl(type, name);
250  if(!field) {
251  throw diag.ReportError(loc_)
252  << "static member access to undeclared field: " << name;
253  }
254  // With the additional constraint that the field must be static
255  ast::Modifiers mods;
256  if(auto fieldDecl = dyn_cast<ast::FieldDecl>(field)) {
257  mods = fieldDecl->modifiers();
258  } else if(auto methodDecl = dyn_cast<ast::MethodDecl>(field)) {
259  mods = methodDecl->modifiers();
260  } else {
261  assert(false && "Field must be either a FieldDecl or a MethodDecl");
262  }
263  if(!mods.isStatic()) {
264  throw diag.ReportError(loc_)
265  << "attempted to access non-static member: "
266  << (field->hasCanonicalName() ? field->getCanonicalName()
267  : field->name());
268  }
269  // We've reached here which means the field access is valid
270  ast::Type const* fieldty = nullptr;
271  if(auto x = dyn_cast<ast::FieldDecl>(field)) fieldty = x->type();
272  access->reclassify(ExprNameWrapper::Type::ExpressionName, field, fieldty);
273  access->setPrev(std::nullopt);
274 }
275 
276 void ER::resolvePackageAccess(internal::ExprNameWrapper* access) const {
277  // First, verify invariants if access is a package access
278  auto prev = access->prevAsWrapper();
279  assert(prev && "Expected non-null previous node");
280  access->verifyInvariants(ExprNameWrapper::Type::SingleAmbiguousName);
281  prev->verifyInvariants(ExprNameWrapper::Type::PackageName);
282  // Now we can get the "pkg" and "name" to resolve against
283  auto pkg = std::get<Pkg const*>(prev->resolution());
284  auto name = access->node->name();
285  assert(pkg && "Expected non-null package here");
286  // Now we check if "name" is a package of "pkg"
287  auto subpkg = pkg->lookup(name, alloc);
288  if(!subpkg.has_value()) {
289  throw diag.ReportError(loc_)
290  << "package access to undeclared member: " << name;
291  }
292  // Now we can reclassify the access node depending on what we found
293  if(std::holds_alternative<ast::Decl const*>(subpkg.value())) {
294  auto pkgdecl = std::get<ast::Decl const*>(subpkg.value());
295  access->reclassify(ExprNameWrapper::Type::TypeName,
296  pkgdecl,
297  Sema->BuildReferenceType(pkgdecl));
298  } else {
299  access->reclassify(ExprNameWrapper::Type::PackageName,
300  std::get<Pkg const*>(subpkg.value()));
301  }
302  access->setPrev(std::nullopt);
303 }
304 
305 /* ===--------------------------------------------------------------------=== */
306 // ExprNameWrapper methods and ResolveExprNode reduction functions
307 /* ===--------------------------------------------------------------------=== */
308 
309 void ExprNameWrapper::dump() const {
310  std::cerr << "\n**** ExprNameWrapper reverse list dump ****\n";
311  dump(0);
312 }
313 
314 void ExprNameWrapper::dump(int indent) const {
315  // Print the indent
316  for(int i = 0; i < indent; i++) std::cerr << " ";
317  // Print the current type and node
318  std::cerr << "{ " << Type_to_string(type_, "??") << ", ";
319  node->print(std::cerr) << " }\n";
320  // If the previous is nullopt
321  if(!prev_.has_value()) {
322  for(int i = 0; i < indent; i++) std::cerr << " ";
323  std::cerr << " - prev: nullopt\n";
324  }
325  // If the previous node is a wrapper, print it
326  else if(auto p = prevIfWrapper()) {
327  p->dump(indent + 1);
328  }
329  // If the previous node is a list, print it
330  else {
331  std::get<ast::ExprNodeList>(prev_.value()).print(std::cerr);
332  }
333 }
334 
335 void ExprNameWrapper::verifyInvariants(ExprNameWrapper::Type expectedTy) const {
336  assert(type_ == expectedTy && "Expected type does not match actual type");
337  // TODO(kevin): Add more invariants here
338 }
339 
340 ast::Decl const* ExprNameWrapper::prevAsDecl(ExprTypeResolver& TR,
341  NameResolver& NR,
342  SourceRange loc) const {
343  // Simple case, the previous node is wrapped so we know the decl
344  if(auto p = prevIfWrapper()) return std::get<ast::Decl const*>(p->resolution());
345  // Complex case, the previous node is an expression list
346  ast::ExprNodeList prevList = std::get<ast::ExprNodeList>(prev_.value());
347  auto type = TR.EvalList(prevList, loc);
348  return cast<ast::Decl>(GetTypeAsDecl(type, NR));
349 }
350 
351 ast::ExprNodeList ER::recursiveReduce(ExprNameWrapper* node) const {
352  if(node->type() != ExprNameWrapper::Type::ExpressionName) {
353  throw diag.ReportError(loc_) << "expected an expression name here, got: \""
354  << node->type_string() << "\" instead";
355  }
356  node->verifyInvariants(ExprNameWrapper::Type::ExpressionName);
357  auto decl = std::get<ast::Decl const*>(node->resolution());
358  auto* expr = node->node;
359 
360  // Base case, no more nodes to reduce. Return singleton list.
361  if(!node->prev().has_value() /* Either no more previous */ ||
362  (node->prevIfWrapper() /* Or the previous node is irreducible */ &&
363  node->prevAsWrapper()->type() != ExprNameWrapper::Type::ExpressionName)) {
364  expr->resolveDeclAndType(decl, node->typeResolution());
365  return ast::ExprNodeList{expr};
366  }
367 
368  // Otherwise, chain the previous node to the current node
369  ast::ExprNodeList list{};
370  if(auto prev = node->prevIfWrapper()) {
371  list = recursiveReduce(prev);
372  } else {
373  list = std::get<ast::ExprNodeList>(node->prev().value());
374  }
375  expr->resolveDeclAndType(decl, node->typeResolution());
376  list.push_back(expr);
377  assert(node->op && "Expected non-null operator here");
378  list.push_back(node->op);
379  return list;
380 }
381 
382 ast::ExprNodeList ER::resolveExprNode(const ETy node) const {
383  ExprNameWrapper* Q = nullptr;
384  // 1. If node is an ExprNode, then resolution is only needed for names
385  // 2. If the node is a list, then no resolution is needed
386  // 3. Otherwise, get the wrapped node and build resolution
387  if(std::holds_alternative<ast::ExprNode*>(node)) {
388  auto expr = std::get<ast::ExprNode*>(node);
389  auto thisNode = dyn_cast<ex::ThisNode*>(expr);
390  auto name = dynamic_cast<ex::MemberName*>(expr);
391  if(thisNode) {
392  thisNode->resolveDeclAndType(
393  cast<ast::Decl>(cu_->body()),
394  Sema->BuildReferenceType(cast<ast::Decl>(cu_->body())));
395  }
396  if(!name) return ast::ExprNodeList{expr};
397  Q = resolveSingleName(name);
398  } else if(std::holds_alternative<ast::ExprNodeList>(node)) {
399  return std::get<ast::ExprNodeList>(node);
400  } else {
401  Q = std::get<ExprNameWrapper*>(node);
402  }
403  // Now we can reduce the expression to a list of nodes
404  return recursiveReduce(Q);
405 }
406 
407 bool ER::isMethodMoreSpecific(ast::MethodDecl const* a,
408  ast::MethodDecl const* b) const {
409  // Let a be declared in T with parameter types Ti
410  // Let b be declared in U with parameter types Ui
411  // Then a > b when for all i, Ti > Ui and T > U
412  // Where two types A > B when A converts to B
413  auto T = alloc.new_object<ast::ReferenceType>(cast<ast::Decl>(a->parent()),
414  SourceRange{});
415  auto U = alloc.new_object<ast::ReferenceType>(cast<ast::Decl>(b->parent()),
416  SourceRange{});
417  if(!TR->isAssignableTo(T, U)) return false;
418  assert(a->parameters().size() == b->parameters().size());
419  for(size_t i = 0; i < a->parameters().size(); i++) {
420  auto Ti = a->parameters()[i]->type();
421  auto Ui = b->parameters()[i]->type();
422  if(!TR->isAssignableTo(Ti, Ui)) return false;
423  }
424  return true;
425 }
426 
427 bool ER::areParameterTypesApplicable(ast::MethodDecl const* decl,
428  const ty_array& argtys) const {
429  bool valid = true;
430  for(size_t i = 0; i < argtys.size(); i++) {
431  auto ty1 = argtys[i];
432  auto ty2 = decl->parameters()[i]->type();
433  valid &= TR->isAssignableTo(ty1, ty2);
434  }
435  return valid;
436 }
437 
438 utils::Generator<ast::MethodDecl const*> ER::getInheritedMethods(
439  ast::DeclContext const* ctx) const {
440  bool isArrayType = ctx == NR->GetArrayPrototype();
441  if(isArrayType) {
442  for(auto method : ctx->decls())
443  if(auto decl = dyn_cast<ast::MethodDecl>(method)) co_yield decl;
444  } else {
445  for(auto method : HC->getInheritedMethods(cast<ast::Decl>(ctx)))
446  co_yield method;
447  }
448  // JLS 9.2: Interfaces should also inherit java.lang.Object methods
449  if(dyn_cast<ast::InterfaceDecl>(ctx)) {
450  for(auto method : NR->GetJavaLang().Object->decls())
451  if(auto decl = dyn_cast<ast::MethodDecl>(method)) co_yield decl;
452  }
453 }
454 
455 ast::MethodDecl const* ER::resolveMethodOverload(ast::DeclContext const* ctx,
456  std::string_view name,
457  const ty_array& argtys,
458  bool isCtor) const {
459  // Set the name to the constructor name if isCtor is true
460  if(isCtor) name = ctx->asDecl()->name();
461  // 15.12.2.1 Find Methods that are Applicable and Accessible
462  std::pmr::vector<ast::MethodDecl const*> candidates{alloc};
463  if(isCtor) {
464  auto cu = cast<ast::CompilationUnit>(cast<ast::Decl>(ctx)->parent());
465  // Only grab the constructors of this type
466  for(auto decl : ctx->decls()) {
467  auto ctor = dynamic_cast<ast::MethodDecl const*>(decl);
468  if(!ctor) continue;
469  if(!ctor->isConstructor()) continue;
470  if(ctor->parameters().size() != argtys.size()) continue;
471  if(!isAccessible(ctor->modifiers(), ctor->parent())) continue;
472  // If the ctor is private, it must be in the same PACKAGE
473  if(ctor->modifiers().isProtected()) {
474  if(cu->getPackageName() != cu_->getPackageName()) {
475  throw diag.ReportError(loc_)
476  << "attempted to access protected constructor from different "
477  "package";
478  }
479  }
480  if(areParameterTypesApplicable(ctor, argtys)) candidates.push_back(ctor);
481  }
482  } else {
483  // Search the current class and all superclasses
484  // 1. Parameters number match
485  // 2. Parameters are convertible
486  // 3. Method is accessible
487  for(auto decl : getInheritedMethods(ctx)) {
488  if(!decl) continue;
489  if(decl->parameters().size() != argtys.size()) continue;
490  if(decl->name() != name) continue;
491  if(!areParameterTypesApplicable(decl, argtys)) continue;
492  if(!isAccessible(decl->modifiers(), decl->parent())) continue;
493  candidates.push_back(decl);
494  }
495  }
496  if(candidates.size() == 0) {
497  if(isCtor) {
498  throw diag.ReportError(loc_) << "no method found for " << name;
499  } else {
500  throw diag.ReportError(loc_) << "no constructor found for " << name;
501  }
502  }
503  if(candidates.size() == 1) return candidates[0];
504 
505  // 15.12.2.2 Choose the Most Specific Method
506  ast::MethodDecl const* mostSpecific = nullptr;
507  std::pmr::vector<ast::MethodDecl const*> maxSpecific{alloc};
508  // Grab the (not necessarily unique) minimum
509  for(auto cur : candidates) {
510  if(!mostSpecific) {
511  mostSpecific = cur;
512  continue;
513  }
514  // cur < minimum?
515  if(isMethodMoreSpecific(cur, mostSpecific)) {
516  mostSpecific = cur;
517  }
518  }
519  // Now grab all the maximally specific methods
520  for(auto cur : candidates) {
521  if(cur == mostSpecific) {
522  maxSpecific.push_back(cur);
523  } else if(isMethodMoreSpecific(cur, mostSpecific) &&
524  isMethodMoreSpecific(mostSpecific, cur)) {
525  maxSpecific.push_back(cur);
526  }
527  }
528  // If there's only one maximally specific method, return it
529  if(maxSpecific.size() == 1) return maxSpecific[0];
530  // FIXME(kevin): There are more conditions i.e., abstract...
531  // Otherwise, we have an ambiguity error
532  for(auto cur : maxSpecific) cur->printSignature(std::cerr) << "\n";
533  throw diag.ReportError(loc_) << "ambiguous method found for name: " << name;
534 }
535 
536 /* ===--------------------------------------------------------------------=== */
537 // Begin the evaluation of the expression nodes
538 /* ===--------------------------------------------------------------------=== */
539 
540 ETy ER::mapValue(ExprValue& node) const { return &node; }
541 
542 ETy ER::evalMemberAccess(DotOp& op, const ETy lhs, const ETy id) const {
543  // Resolves expr of form: Q . Id
544  // Where Q is either a simple identifier or a qualified
545  // identifier. If the LHS is an ExprNode, then it is simple and we need to
546  // reclassify it. Otherwise, we can continue to resolve recursively.
547  PrevTy Q = nullptr;
548  if(std::holds_alternative<ast::ExprNode*>(lhs)) {
549  auto node = std::get<ast::ExprNode*>(lhs);
550  if(auto simpleName = dyn_cast<ex::MemberName>(node)) {
551  Q = resolveSingleName(simpleName);
552  } else if(auto thisNode = dyn_cast<ex::ThisNode>(node)) {
553  Q = resolveExprNode(thisNode);
554  } else if(auto lit = dyn_cast<ex::LiteralNode>(node)) {
555  if(!lit->builtinType()->isString()) {
556  throw diag.ReportError(loc_)
557  << "attempted to access field on non-string literal"
558  << node->location() << "is a non-string literal";
559  }
560  Q = ast::ExprNodeList{lit};
561  } else {
562  assert(false && "Malformed node. Expected MemberName here.");
563  }
564  } else if(std::holds_alternative<ExprNameWrapper*>(lhs)) {
565  Q = std::get<ExprNameWrapper*>(lhs);
566  } else {
567  Q = std::get<ast::ExprNodeList>(lhs);
568  }
569  // The LHS must be an ExpressionName, TypeName or PackageName
570  auto* P = std::get_if<ExprNameWrapper*>(&Q);
571  if(P) {
572  assert(((*P)->type() == ExprNameWrapper::Type::ExpressionName ||
573  (*P)->type() == ExprNameWrapper::Type::TypeName ||
574  (*P)->type() == ExprNameWrapper::Type::PackageName) &&
575  "Malformed node. Expected ExpressionName, TypeName or PackageName "
576  "here.");
577  }
578  // Grab the Id as an expr node
579  auto exprNode = std::get<ast::ExprNode*>(id);
580  // Special case: If "Id" in Q . Id is a method name, then defer resolution
581  if(auto methodNode = dyn_cast<ex::MethodName*>(exprNode)) {
582  auto newQ = alloc.new_object<ExprNameWrapper>(
583  ExprNameWrapper::Type::MethodName, methodNode, &op);
584  newQ->setPrev(Q);
585  return newQ;
586  }
587  // Now grab the id and cast it to the appropriate type
588  auto fieldNode = dyn_cast<ex::MemberName*>(exprNode);
589  assert(fieldNode && "Malformed node. Expected MemberName here.");
590  // Allocate a new node as the member access to represent "Id" in Lhs . Id
591  auto newQ = alloc.new_object<ExprNameWrapper>(
592  ExprNameWrapper::Type::SingleAmbiguousName, fieldNode, &op);
593  newQ->setPrev(Q);
594  // And we can build the reduced expression now
595  // 1. If the previous node is a wrapper, then newQ can be anything
596  // 2. If the previous is a list, then newQ must be ExpressionName
597  // FIXME: Is this true? What about (Class).Field?
598  if(P) {
599  switch((*P)->type()) {
600  case ExprNameWrapper::Type::ExpressionName:
601  resolveFieldAccess(newQ);
602  break;
603  case ExprNameWrapper::Type::TypeName:
604  resolveTypeAccess(newQ);
605  break;
606  case ExprNameWrapper::Type::PackageName:
607  resolvePackageAccess(newQ);
608  break;
609  default:
610  std::unreachable();
611  }
612  } else {
613  resolveFieldAccess(newQ);
614  }
615  return newQ;
616 }
617 
618 ETy ER::evalBinaryOp(BinaryOp& op, const ETy lhs, const ETy rhs) const {
619  ast::ExprNodeList list{};
620  list.concat(resolveExprNode(lhs));
621  list.concat(resolveExprNode(rhs));
622  list.push_back(&op);
623  return list;
624 }
625 
626 ETy ER::evalUnaryOp(UnaryOp& op, const ETy rhs) const {
627  ast::ExprNodeList list{};
628  list.concat(resolveExprNode(rhs));
629  list.push_back(&op);
630  return list;
631 }
632 
633 ast::DeclContext const* ER::getMethodParent(ExprNameWrapper* Q) const {
634  Q->verifyInvariants(ExprNameWrapper::Type::MethodName);
635  // If there's no previous, use the current context
636  if(!Q->prev().has_value()) return cu_->body();
637  auto declOrType = Q->prevAsDecl(*TR, *NR, loc_);
638  auto ty = dynamic_cast<ast::DeclContext const*>(declOrType);
639  if(Q->prevIfWrapper() && !ty) {
640  auto typedDecl = dynamic_cast<ast::TypedDecl const*>(declOrType);
641  if(!typedDecl) {
642  throw diag.ReportError(loc_)
643  << "method call to non-typed declaration: " << declOrType->name();
644  }
645  auto type = typedDecl->type();
646  if(!type) {
647  throw diag.ReportError(loc_)
648  << "method call to void-typed declaration: " << declOrType->name();
649  }
650  ty = GetTypeAsDecl(type, *NR);
651  if(!ty) {
652  throw diag.ReportError(loc_)
653  << "method call to non-reference type: " << declOrType->name();
654  }
655  }
656  assert(ty && "Expected non-null type here");
657  return ty;
658 }
659 
660 bool ER::isAccessible(ast::Modifiers mod, ast::DeclContext const* parent) const {
661  // 6.6.1 Determining Accessibility
662  if(mod.isPublic()) return true;
663  // 6.6.2 Details on protected Access
664  // If current is a child of parent, then it is accessible
665  auto* targetClass = cast<ast::ClassDecl>(parent);
666  if(auto* curClass = dyn_cast<ast::ClassDecl>(cu_->bodyAsDecl())) {
667  if(HC->isSuperClass(targetClass, curClass)) return true;
668  }
669  // If they are in the same package, access is also allowed
670  if(auto other_cu = dyn_cast<ast::CompilationUnit>(targetClass->parent())) {
671  if(cu_->getPackageName() == other_cu->getPackageName()) return true;
672  }
673  return false;
674 }
675 
676 ETy ER::evalMethodCall(MethodOp& op, const ETy method,
677  const op_array& args) const {
678  // Q is the incompletely resolved method name
679  ExprNameWrapper* Q = nullptr;
680  // Single name method can never be static, and static methods can
681  // never be single name methods!
682  bool isSingleNameMethod = false;
683 
684  // 1. It could be a raw ExprNode, in which case is something like Fun()
685  // 2. Or it could be a wrapped ExprNameWrapper, in which case is something
686  // like org.pkg.Class.Fun()
687  // 3. Or it could be a list of nodes, in which case is something like
688  // (1 + 2 + 3)()
689  if(std::holds_alternative<ast::ExprNode*>(method)) {
690  auto expr = std::get<ast::ExprNode*>(method);
691  auto name = dynamic_cast<ex::MethodName*>(expr);
692  assert(name && "Malformed node. Expected MethodName here.");
693  Q = resolveSingleName(name);
694  isSingleNameMethod = true;
695  } else if(std::holds_alternative<ExprNameWrapper*>(method)) {
696  Q = std::get<ExprNameWrapper*>(method);
697  } else {
698  throw diag.ReportError(loc_) << "malformed method call expression";
699  }
700 
701  // Resolve the array of arguments
702  ty_array argtys{alloc};
703  ast::ExprNodeList arglist{};
704  for(auto& arg : args | std::views::reverse) {
705  auto tmplist = resolveExprNode(arg);
706  argtys.push_back(TR->EvalList(tmplist, loc_));
707  arglist.concat(tmplist);
708  }
709 
710  // Begin resolution of the method call
711  auto ctx = getMethodParent(Q);
712  auto methodDecl = resolveMethodOverload(ctx, Q->node->name(), argtys, false);
713 
714  // Check if the method call is legal
715  if(!isAccessible(methodDecl->modifiers(), methodDecl->parent())) {
716  throw diag.ReportError(loc_)
717  << "method call to non-accessible method: " << methodDecl->name();
718  }
719 
720  // Check static method call
721  if(isSingleNameMethod && methodDecl->modifiers().isStatic()) {
722  throw diag.ReportError(loc_)
723  << "attempted to call static method using single name: "
724  << methodDecl->name();
725  }
726 
727  // Is the previous a type name?
728  if(Q->prev().has_value() && Q->prevIfWrapper() &&
729  Q->prevAsWrapper()->type() == ExprNameWrapper::Type::TypeName) {
730  // If so, then we are calling a static method, so we should check
731  if(!methodDecl->modifiers().isStatic()) {
732  throw diag.ReportError(loc_)
733  << "attempted to call non-static method: " << methodDecl->name();
734  }
735  }
736 
737  // Reclassify the Q node to be an ExpressionName
738  Q->reclassify(ExprNameWrapper::Type::ExpressionName, methodDecl, nullptr);
739 
740  // Once Q has been resolved, we can build the expression list
741  ast::ExprNodeList list{};
742  list.concat(recursiveReduce(Q));
743  list.concat(arglist);
744  list.push_back(&op);
745  return list;
746 }
747 
748 ETy ER::evalNewObject(NewOp& op, const ETy object, const op_array& args) const {
749  // Get the type we are instantiating
750  ex::TypeNode* expr = nullptr;
751  if(std::holds_alternative<ast::ExprNode*>(object)) {
752  expr = cast<ex::TypeNode>(std::get<ast::ExprNode*>(object));
753  } else {
754  assert(false && "Grammar wrong, object must be an atomic ExprNode*");
755  }
756 
757  // Resolve the array of arguments
758  ty_array argtys{alloc};
759  ast::ExprNodeList arglist{};
760  for(auto& arg : args | std::views::reverse) {
761  auto tmplist = resolveExprNode(arg);
762  argtys.push_back(TR->EvalList(tmplist, loc_));
763  arglist.concat(tmplist);
764  }
765 
766  // Check if the type is abstract
767  auto ctx = cast<ast::DeclContext>(expr->type()->getAsDecl());
768  if(auto classDecl = dyn_cast<ast::ClassDecl>(ctx)) {
769  if(classDecl->modifiers().isAbstract()) {
770  throw diag.ReportError(loc_)
771  << "attempted to instantiate abstract class: "
772  << (classDecl->hasCanonicalName() ? classDecl->getCanonicalName()
773  : classDecl->name());
774  }
775  }
776 
777  // Begin resolution of the method call
778  auto methodDecl = resolveMethodOverload(ctx, "", argtys, true);
779  expr->overrideDecl(methodDecl);
780 
781  // Once op has been resolved, we can build the expression list
782  ast::ExprNodeList list{};
783  list.push_back(expr);
784  list.concat(arglist);
785  list.push_back(&op);
786  return list;
787 }
788 
789 ETy ER::evalNewArray(NewArrayOp& op, const ETy type, const ETy size) const {
790  ast::ExprNodeList list{};
791  if(std::holds_alternative<ast::ExprNode*>(type)) {
792  auto expr = cast<ex::TypeNode>(std::get<ast::ExprNode*>(type));
793  list.push_back(expr);
794  } else {
795  assert(false && "Grammar wrong, type must be an atomic ExprNode*");
796  }
797  list.concat(resolveExprNode(size));
798  list.push_back(&op);
799  return list;
800 }
801 
802 ETy ER::evalArrayAccess(ArrayAccessOp& op, const ETy array,
803  const ETy index) const {
804  ast::ExprNodeList list{};
805  list.concat(resolveExprNode(array));
806  list.concat(resolveExprNode(index));
807  list.push_back(&op);
808  return list;
809 }
810 
811 ETy ER::evalCast(CastOp& op, const ETy type, const ETy value) const {
812  ast::ExprNodeList list{};
813  if(std::holds_alternative<ast::ExprNode*>(type)) {
814  auto expr = cast<ex::TypeNode>(std::get<ast::ExprNode*>(type));
815  list.push_back(expr);
816  } else {
817  assert(false && "Grammar wrong, type must be an atomic ExprNode*");
818  }
819  list.concat(resolveExprNode(value));
820  list.push_back(&op);
821  return list;
822 }
823 
824 bool ER::validate(ETy const& value) const {
825  if(auto wrapper = std::get_if<ExprNameWrapper*>(&value)) {
826  // TODO(kevin)
827  } else if(auto list = std::get_if<ast::ExprNodeList>(&value)) {
828  if(list->size() == 0) return false;
829  if(list->size() == 1) return true;
830  return dyn_cast<ex::ExprOp>(list->tail()) != nullptr;
831  } else if(auto expr = std::get_if<ast::ExprNode*>(&value)) {
832  // TODO(kevin)
833  }
834  return true;
835 }
836 
837 } // namespace semantic