1 #include "semantic/ExprResolver.h"
9 #include "ast/AstNode.h"
11 #include "ast/DeclContext.h"
12 #include "ast/ExprNode.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"
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;
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()) {
39 }
else if(type->isArray()) {
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)) {
56 if(ret)
return nullptr;
63 for(
auto decl : ctx->decls())
64 if(cond(decl))
return decl;
72 std::string_view name)
const {
73 auto cond = [name,
this](ast::
Decl const* d) {
76 bool scopeVisible =
true;
80 bool checkScope = dyn_cast<ast::
VarDecl>(d) &&
this->lscope_;
82 if(sameContext && checkScope)
83 scopeVisible =
this->lscope_->canView(td->scope());
85 bool canAccess =
true;
86 if(
auto fieldDecl = dyn_cast<ast::FieldDecl>(d)) {
87 canAccess = isAccessible(fieldDecl->modifiers(), fieldDecl->parent());
89 return sameName && scopeVisible && canAccess;
91 return lookupDecl(ctx, cond);
94 bool ER::tryReclassifyDecl(ExprNameWrapper& data,
97 if(
auto decl = lookupNamedDecl(ctx, data.node->name())) {
98 if(
auto varDecl =
dynamic_cast<ast::
VarDecl const*>(decl)) {
100 ExprNameWrapper::Type::ExpressionName, varDecl, varDecl->type());
102 }
else if(
auto fieldDecl =
dynamic_cast<ast::FieldDecl
const*>(decl)) {
103 data.reclassify(ExprNameWrapper::Type::ExpressionName,
111 auto ctxAsDecl =
dynamic_cast<ast::
Decl const*>(ctx);
112 if(!ctxAsDecl)
return false;
114 if(!parentCtx)
return false;
115 return tryReclassifyDecl(data, parentCtx);
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();
123 if(std::holds_alternative<
const ast::Decl*>(import.value())) {
124 auto decl = std::get<
const ast::Decl*>(import.value());
127 throw diag.ReportError(loc_) <<
"ambiguous import-on-demand conflict";
130 ExprNameWrapper::Type::TypeName, decl, Sema->BuildReferenceType(decl));
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);
141 ExprNameWrapper* ER::reclassifySingleAmbiguousName(ExprNameWrapper* data)
const {
144 assert(data->type() == ExprNameWrapper::Type::SingleAmbiguousName &&
145 "Expected SingleAmbiguousName here");
146 auto copy = alloc.new_object<ExprNameWrapper>(*data);
172 if(tryReclassifyDecl(*copy, lctx_))
return copy;
175 if(tryReclassifyImport(*copy, NR->GetImport(cu_, data->node->name())))
179 throw diag.ReportError(loc_)
180 <<
"Unknown error when attempting to resolve import type";
183 void ER::resolveFieldAccess(ExprNameWrapper* access)
const {
185 access->verifyInvariants(ExprNameWrapper::Type::SingleAmbiguousName);
186 if(
auto p = access->prevIfWrapper())
187 p->verifyInvariants(ExprNameWrapper::Type::ExpressionName);
189 auto name = access->node->name();
190 auto typeOrDecl = access->prevAsDecl(*TR, *NR, loc_);
192 if(access->prevIfWrapper()) {
194 auto decl = typeOrDecl;
195 auto typeddecl = dyn_cast<ast::
TypedDecl>(decl);
197 throw diag.ReportError(loc_)
198 <<
"field access \"" << name
199 <<
"\" to non-typed declaration: " << decl
->name();
201 auto type = typeddecl->type();
203 throw diag.ReportError(loc_)
204 <<
"field access \"" << name
205 <<
"\" to void-typed declaration: " << decl
->name();
207 refTy = GetTypeAsDecl(type, *NR);
209 throw diag.ReportError(loc_)
210 <<
"field access \"" << name <<
"\" to non-class type: "
215 refTy = typeOrDecl->asDeclContext();
216 assert(refTy &&
"Expected non-null type here");
219 auto field = lookupNamedDecl(refTy, name);
221 throw diag.ReportError(loc_)
222 <<
"cannot access field: " << name << access->node->location()
223 <<
"inaccessible member";
226 assert(dyn_cast<ast::FieldDecl>(field) || dyn_cast<ast::MethodDecl>(field));
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);
233 void ER::resolveTypeAccess(internal::ExprNameWrapper* access)
const {
235 access->verifyInvariants(ExprNameWrapper::Type::SingleAmbiguousName);
236 if(
auto p = access->prevIfWrapper())
237 p->verifyInvariants(ExprNameWrapper::Type::TypeName);
239 auto name = access->node->name();
240 auto typeOrDecl = access->prevAsDecl(*TR, *NR, loc_);
242 auto type =
dynamic_cast<ast::ClassDecl
const*>(typeOrDecl);
244 throw diag.ReportError(loc_)
245 <<
"static member access \"" << name
246 <<
"\" to non-class type: " << typeOrDecl
->name();
249 auto field = lookupNamedDecl(type, name);
251 throw diag.ReportError(loc_)
252 <<
"static member access to undeclared field: " << name;
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();
261 assert(
false &&
"Field must be either a FieldDecl or a MethodDecl");
263 if(!mods.isStatic()) {
264 throw diag.ReportError(loc_)
265 <<
"attempted to access non-static member: "
266 << (field->hasCanonicalName() ? field->getCanonicalName()
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);
276 void ER::resolvePackageAccess(internal::ExprNameWrapper* access)
const {
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);
283 auto pkg = std::get<Pkg
const*>(prev->resolution());
284 auto name = access->node->name();
285 assert(pkg &&
"Expected non-null package here");
287 auto subpkg = pkg->lookup(name, alloc);
288 if(!subpkg.has_value()) {
289 throw diag.ReportError(loc_)
290 <<
"package access to undeclared member: " << name;
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,
297 Sema->BuildReferenceType(pkgdecl));
299 access->reclassify(ExprNameWrapper::Type::PackageName,
300 std::get<Pkg
const*>(subpkg.value()));
302 access->setPrev(std::nullopt);
309 void ExprNameWrapper::dump()
const {
310 std::cerr <<
"\n**** ExprNameWrapper reverse list dump ****\n";
314 void ExprNameWrapper::dump(
int indent)
const {
316 for(
int i = 0; i < indent; i++) std::cerr <<
" ";
318 std::cerr <<
"{ " << Type_to_string(type_,
"??") <<
", ";
319 node->print(std::cerr) <<
" }\n";
321 if(!prev_.has_value()) {
322 for(
int i = 0; i < indent; i++) std::cerr <<
" ";
323 std::cerr <<
" - prev: nullopt\n";
326 else if(
auto p = prevIfWrapper()) {
331 std::get<ast::ExprNodeList>(prev_.value()).print(std::cerr);
335 void ExprNameWrapper::verifyInvariants(ExprNameWrapper::Type expectedTy)
const {
336 assert(type_ == expectedTy &&
"Expected type does not match actual type");
340 ast::
Decl const* ExprNameWrapper::prevAsDecl(ExprTypeResolver& TR,
344 if(
auto p = prevIfWrapper())
return std::get<ast::
Decl const*>(p->resolution());
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));
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";
356 node->verifyInvariants(ExprNameWrapper::Type::ExpressionName);
357 auto decl = std::get<ast::
Decl const*>(node->resolution());
358 auto* expr = node->node;
361 if(!node->prev().has_value() ||
362 (node->prevIfWrapper() &&
363 node->prevAsWrapper()->type() != ExprNameWrapper::Type::ExpressionName)) {
364 expr->resolveDeclAndType(decl, node->typeResolution());
365 return ast::ExprNodeList{expr};
369 ast::ExprNodeList list{};
370 if(
auto prev = node->prevIfWrapper()) {
371 list = recursiveReduce(prev);
373 list = std::get<ast::ExprNodeList>(node->prev().value());
375 expr->resolveDeclAndType(decl, node->typeResolution());
377 assert(node->op &&
"Expected non-null operator here");
382 ast::ExprNodeList ER::resolveExprNode(
const ETy node)
const {
383 ExprNameWrapper* Q =
nullptr;
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);
392 thisNode->resolveDeclAndType(
393 cast<ast::Decl>(cu_->body()),
394 Sema->BuildReferenceType(cast<ast::Decl>(cu_->body())));
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);
401 Q = std::get<ExprNameWrapper*>(node);
404 return recursiveReduce(Q);
407 bool ER::isMethodMoreSpecific(ast::MethodDecl
const* a,
408 ast::MethodDecl
const* b)
const {
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;
427 bool ER::areParameterTypesApplicable(ast::MethodDecl
const* decl,
428 const ty_array& argtys)
const {
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);
438 utils::Generator<ast::MethodDecl
const*> ER::getInheritedMethods(
439 ast::DeclContext
const* ctx)
const {
442 for(
auto method : ctx->decls())
443 if(
auto decl = dyn_cast<ast::MethodDecl>(method))
co_yield decl;
445 for(
auto method : HC->getInheritedMethods(cast<ast::Decl>(ctx)))
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;
455 ast::MethodDecl
const* ER::resolveMethodOverload(ast::
DeclContext const* ctx,
456 std::string_view name,
457 const ty_array& argtys,
460 if(isCtor) name = ctx->asDecl()
->name();
462 std::pmr::vector<ast::MethodDecl
const*> candidates{alloc};
464 auto cu = cast<ast::CompilationUnit>(cast<ast::
Decl>(ctx)->parent());
466 for(
auto decl : ctx->decls()) {
467 auto ctor =
dynamic_cast<ast::MethodDecl
const*>(decl);
469 if(!ctor->isConstructor())
continue;
470 if(ctor->parameters().size() != argtys.size())
continue;
471 if(!isAccessible(ctor->modifiers(), ctor->parent()))
continue;
473 if(ctor->modifiers().isProtected()) {
474 if(cu->getPackageName() != cu_->getPackageName()) {
475 throw diag.ReportError(loc_)
476 <<
"attempted to access protected constructor from different "
480 if(areParameterTypesApplicable(ctor, argtys)) candidates.push_back(ctor);
487 for(
auto decl : getInheritedMethods(ctx)) {
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);
496 if(candidates.size() == 0) {
498 throw diag.ReportError(loc_) <<
"no method found for " << name;
500 throw diag.ReportError(loc_) <<
"no constructor found for " << name;
503 if(candidates.size() == 1)
return candidates[0];
506 ast::MethodDecl
const* mostSpecific =
nullptr;
507 std::pmr::vector<ast::MethodDecl
const*> maxSpecific{alloc};
509 for(
auto cur : candidates) {
515 if(isMethodMoreSpecific(cur, mostSpecific)) {
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);
529 if(maxSpecific.size() == 1)
return maxSpecific[0];
532 for(
auto cur : maxSpecific) cur->printSignature(std::cerr) <<
"\n";
533 throw diag.ReportError(loc_) <<
"ambiguous method found for name: " << name;
540 ETy ER::mapValue(ExprValue& node)
const {
return &node; }
542 ETy ER::evalMemberAccess(DotOp& op,
const ETy lhs,
const ETy id)
const {
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";
560 Q = ast::ExprNodeList{lit};
562 assert(
false &&
"Malformed node. Expected MemberName here.");
564 }
else if(std::holds_alternative<ExprNameWrapper*>(lhs)) {
565 Q = std::get<ExprNameWrapper*>(lhs);
567 Q = std::get<ast::ExprNodeList>(lhs);
570 auto* P = std::get_if<ExprNameWrapper*>(&Q);
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 "
579 auto exprNode = std::get<ast::ExprNode*>(id);
581 if(
auto methodNode = dyn_cast<ex::MethodName*>(exprNode)) {
582 auto newQ = alloc.new_object<ExprNameWrapper>(
583 ExprNameWrapper::Type::MethodName, methodNode, &op);
588 auto fieldNode = dyn_cast<ex::MemberName*>(exprNode);
589 assert(fieldNode &&
"Malformed node. Expected MemberName here.");
591 auto newQ = alloc.new_object<ExprNameWrapper>(
592 ExprNameWrapper::Type::SingleAmbiguousName, fieldNode, &op);
599 switch((*P)->type()) {
600 case ExprNameWrapper::Type::ExpressionName:
601 resolveFieldAccess(newQ);
603 case ExprNameWrapper::Type::TypeName:
604 resolveTypeAccess(newQ);
606 case ExprNameWrapper::Type::PackageName:
607 resolvePackageAccess(newQ);
613 resolveFieldAccess(newQ);
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));
626 ETy ER::evalUnaryOp(UnaryOp& op,
const ETy rhs)
const {
627 ast::ExprNodeList list{};
628 list.concat(resolveExprNode(rhs));
633 ast::
DeclContext const* ER::getMethodParent(ExprNameWrapper* Q)
const {
634 Q->verifyInvariants(ExprNameWrapper::Type::MethodName);
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);
642 throw diag.ReportError(loc_)
643 <<
"method call to non-typed declaration: " << declOrType
->name();
645 auto type = typedDecl->type();
647 throw diag.ReportError(loc_)
648 <<
"method call to void-typed declaration: " << declOrType
->name();
650 ty = GetTypeAsDecl(type, *NR);
652 throw diag.ReportError(loc_)
653 <<
"method call to non-reference type: " << declOrType
->name();
656 assert(ty &&
"Expected non-null type here");
662 if(mod.isPublic())
return true;
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;
670 if(
auto other_cu = dyn_cast<ast::CompilationUnit>(targetClass->parent())) {
671 if(cu_->getPackageName() == other_cu->getPackageName())
return true;
676 ETy ER::evalMethodCall(MethodOp& op,
const ETy method,
677 const op_array& args)
const {
679 ExprNameWrapper* Q =
nullptr;
682 bool isSingleNameMethod =
false;
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);
698 throw diag.ReportError(loc_) <<
"malformed method call expression";
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);
711 auto ctx = getMethodParent(Q);
712 auto methodDecl = resolveMethodOverload(ctx, Q->node->name(), argtys,
false);
715 if(!isAccessible(methodDecl->modifiers(), methodDecl->parent())) {
716 throw diag.ReportError(loc_)
717 <<
"method call to non-accessible method: " << methodDecl->name();
721 if(isSingleNameMethod && methodDecl->modifiers().isStatic()) {
722 throw diag.ReportError(loc_)
723 <<
"attempted to call static method using single name: "
724 << methodDecl->name();
728 if(Q->prev().has_value() && Q->prevIfWrapper() &&
729 Q->prevAsWrapper()->type() == ExprNameWrapper::Type::TypeName) {
731 if(!methodDecl->modifiers().isStatic()) {
732 throw diag.ReportError(loc_)
733 <<
"attempted to call non-static method: " << methodDecl->name();
738 Q->reclassify(ExprNameWrapper::Type::ExpressionName, methodDecl,
nullptr);
741 ast::ExprNodeList list{};
748 ETy ER::evalNewObject(NewOp& op,
const ETy object,
const op_array& args)
const {
750 ex::TypeNode* expr =
nullptr;
751 if(std::holds_alternative<ast::ExprNode*>(object)) {
752 expr = cast<ex::TypeNode>(std::get<ast::ExprNode*>(object));
754 assert(
false &&
"Grammar wrong, object must be an atomic ExprNode*");
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);
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());
778 auto methodDecl = resolveMethodOverload(ctx,
"", argtys,
true);
779 expr->overrideDecl(methodDecl);
782 ast::ExprNodeList list{};
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);
795 assert(
false &&
"Grammar wrong, type must be an atomic ExprNode*");
797 list.concat(resolveExprNode(size));
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));
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);
817 assert(
false &&
"Grammar wrong, type must be an atomic ExprNode*");
819 list.concat(resolveExprNode(value));
824 bool ER::validate(ETy
const& value)
const {
825 if(
auto wrapper = std::get_if<ExprNameWrapper*>(&value)) {
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)) {