diff options
| author | Peter Collingbourne <peter@pcc.me.uk> | 2025-07-18 13:26:00 -0700 |
|---|---|---|
| committer | Peter Collingbourne <peter@pcc.me.uk> | 2025-07-18 13:26:00 -0700 |
| commit | 9bf3524731070cadc6175707314f3b6ca37190d5 (patch) | |
| tree | 86dcab7604336b01ae938fe81062c29ff69efba8 /flang/lib/Semantics | |
| parent | 3a84c15cc13b6daf8e812592898ab6c7f19091a9 (diff) | |
| parent | 4f43f0606c3d7e1ce6d069583b5e59f036e112ce (diff) | |
Created using spr 1.3.6-beta.1
Diffstat (limited to 'flang/lib/Semantics')
| -rw-r--r-- | flang/lib/Semantics/check-declarations.cpp | 48 | ||||
| -rw-r--r-- | flang/lib/Semantics/check-do-forall.cpp | 4 | ||||
| -rw-r--r-- | flang/lib/Semantics/check-omp-structure.cpp | 3 | ||||
| -rw-r--r-- | flang/lib/Semantics/expression.cpp | 68 | ||||
| -rw-r--r-- | flang/lib/Semantics/resolve-labels.cpp | 25 | ||||
| -rw-r--r-- | flang/lib/Semantics/resolve-names.cpp | 8 | ||||
| -rw-r--r-- | flang/lib/Semantics/runtime-type-info.cpp | 28 | ||||
| -rw-r--r-- | flang/lib/Semantics/semantics.cpp | 5 | ||||
| -rw-r--r-- | flang/lib/Semantics/tools.cpp | 8 |
9 files changed, 129 insertions, 68 deletions
diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp index f9d64485f140..a2f2906af10b 100644 --- a/flang/lib/Semantics/check-declarations.cpp +++ b/flang/lib/Semantics/check-declarations.cpp @@ -151,8 +151,8 @@ private: void CheckProcedureAssemblyName(const Symbol &symbol); void CheckExplicitSave(const Symbol &); parser::Messages WhyNotInteroperableDerivedType(const Symbol &); - parser::Messages WhyNotInteroperableObject( - const Symbol &, bool allowNonInteroperableType = false); + parser::Messages WhyNotInteroperableObject(const Symbol &, + bool allowNonInteroperableType = false, bool forCommonBlock = false); parser::Messages WhyNotInteroperableFunctionResult(const Symbol &); parser::Messages WhyNotInteroperableProcedure(const Symbol &, bool isError); void CheckBindC(const Symbol &); @@ -519,11 +519,35 @@ void CheckHelper::Check(const Symbol &symbol) { } void CheckHelper::CheckCommonBlock(const Symbol &symbol) { + auto restorer{messages_.SetLocation(symbol.name())}; CheckGlobalName(symbol); if (symbol.attrs().test(Attr::BIND_C)) { CheckBindC(symbol); + for (auto ref : symbol.get<CommonBlockDetails>().objects()) { + if (ref->has<ObjectEntityDetails>()) { + if (auto msgs{WhyNotInteroperableObject(*ref, + /*allowInteroperableType=*/false, /*forCommonBlock=*/true)}; + !msgs.empty()) { + parser::Message &reason{msgs.messages().front()}; + parser::Message *msg{nullptr}; + if (reason.IsFatal()) { + msg = messages_.Say(symbol.name(), + "'%s' may not be a member of BIND(C) COMMON block /%s/"_err_en_US, + ref->name(), symbol.name()); + } else { + msg = messages_.Say(symbol.name(), + "'%s' should not be a member of BIND(C) COMMON block /%s/"_warn_en_US, + ref->name(), symbol.name()); + } + if (msg) { + msg->Attach( + std::move(reason.set_severity(parser::Severity::Because))); + } + } + } + } } - for (MutableSymbolRef ref : symbol.get<CommonBlockDetails>().objects()) { + for (auto ref : symbol.get<CommonBlockDetails>().objects()) { if (ref->test(Symbol::Flag::CrayPointee)) { messages_.Say(ref->name(), "Cray pointee '%s' may not be a member of a COMMON block"_err_en_US, @@ -3154,14 +3178,16 @@ parser::Messages CheckHelper::WhyNotInteroperableDerivedType( } parser::Messages CheckHelper::WhyNotInteroperableObject( - const Symbol &symbol, bool allowNonInteroperableType) { + const Symbol &symbol, bool allowNonInteroperableType, bool forCommonBlock) { parser::Messages msgs; - if (examinedByWhyNotInteroperable_.find(symbol) != - examinedByWhyNotInteroperable_.end()) { - return msgs; + if (!forCommonBlock) { + if (examinedByWhyNotInteroperable_.find(symbol) != + examinedByWhyNotInteroperable_.end()) { + return msgs; + } + examinedByWhyNotInteroperable_.insert(symbol); } bool isExplicitBindC{symbol.attrs().test(Attr::BIND_C)}; - examinedByWhyNotInteroperable_.insert(symbol); CHECK(symbol.has<ObjectEntityDetails>()); if (isExplicitBindC && !symbol.owner().IsModule()) { msgs.Say(symbol.name(), @@ -3258,7 +3284,7 @@ parser::Messages CheckHelper::WhyNotInteroperableObject( msgs.Say(symbol.name(), "An interoperable pointer must not be CONTIGUOUS"_err_en_US); } - if (msgs.AnyFatalError()) { + if (!forCommonBlock && msgs.AnyFatalError()) { examinedByWhyNotInteroperable_.erase(symbol); } return msgs; @@ -3338,8 +3364,8 @@ parser::Messages CheckHelper::WhyNotInteroperableProcedure( // on the C side by either a cdesc_t * or a void *. F'2023 18.3.7 (5) bool allowNonInteroperableType{!dummy->attrs().test(Attr::VALUE) && (IsDescriptor(*dummy) || IsAssumedType(*dummy))}; - dummyMsgs = - WhyNotInteroperableObject(*dummy, allowNonInteroperableType); + dummyMsgs = WhyNotInteroperableObject( + *dummy, allowNonInteroperableType, /*forCommonBlock=*/false); } else { CheckBindC(*dummy); } diff --git a/flang/lib/Semantics/check-do-forall.cpp b/flang/lib/Semantics/check-do-forall.cpp index cc1d4bf58745..e258df86a4b1 100644 --- a/flang/lib/Semantics/check-do-forall.cpp +++ b/flang/lib/Semantics/check-do-forall.cpp @@ -1180,7 +1180,9 @@ void DoForallChecker::Leave(const parser::IoControlSpec &ioControlSpec) { void DoForallChecker::Leave(const parser::OutputImpliedDo &outputImpliedDo) { const auto &control{std::get<parser::IoImpliedDoControl>(outputImpliedDo.t)}; const parser::Name &name{control.name.thing.thing}; - context_.CheckIndexVarRedefine(name.source, *name.symbol); + if (name.symbol) { + context_.CheckIndexVarRedefine(name.source, *name.symbol); + } } void DoForallChecker::Leave(const parser::StatVariable &statVariable) { diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp index 2425265e196c..e4a94efcc6b5 100644 --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -1156,8 +1156,7 @@ void OmpStructureChecker::CheckThreadprivateOrDeclareTargetVar( (sym->has<MainProgramDetails>() || sym->has<ModuleDetails>())) { context_.Say(name->source, - "The module name or main program name cannot be in a " - "%s " + "The module name cannot be in a %s " "directive"_err_en_US, ContextDirectiveAsFortran()); } else if (!IsSaved(*name->symbol) && diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp index f4af738284ed..14473724f0f4 100644 --- a/flang/lib/Semantics/expression.cpp +++ b/flang/lib/Semantics/expression.cpp @@ -178,7 +178,7 @@ public: } // Find and return a user-defined assignment std::optional<ProcedureRef> TryDefinedAssignment(); - std::optional<ProcedureRef> GetDefinedAssignmentProc(); + std::optional<ProcedureRef> GetDefinedAssignmentProc(bool &isAmbiguous); std::optional<DynamicType> GetType(std::size_t) const; void Dump(llvm::raw_ostream &); @@ -191,7 +191,7 @@ private: MaybeExpr AnalyzeExprOrWholeAssumedSizeArray(const parser::Expr &); bool AreConformable() const; const Symbol *FindBoundOp(parser::CharBlock, int passIndex, - const Symbol *&generic, bool isSubroutine); + const Symbol *&generic, bool isSubroutine, bool *isAmbiguous = nullptr); void AddAssignmentConversion( const DynamicType &lhsType, const DynamicType &rhsType); bool OkLogicalIntegerAssignment(TypeCategory lhs, TypeCategory rhs); @@ -199,7 +199,8 @@ private: bool IsBOZLiteral(std::size_t i) const { return evaluate::IsBOZLiteral(GetExpr(i)); } - void SayNoMatch(const std::string &, bool isAssignment = false); + void SayNoMatch( + const std::string &, bool isAssignment = false, bool isAmbiguous = false); std::string TypeAsFortran(std::size_t); bool AnyUntypedOrMissingOperand(); @@ -1269,7 +1270,7 @@ MaybeExpr ExpressionAnalyzer::Analyze( MaybeExpr ExpressionAnalyzer::Analyze(const parser::SubstringInquiry &x) { if (MaybeExpr substring{Analyze(x.v)}) { CHECK(x.source.size() >= 8); - int nameLen{x.source.end()[-1] == 'n' ? 3 /*LEN*/ : 4 /*KIND*/}; + int nameLen{x.source.back() == 'n' ? 3 /*LEN*/ : 4 /*KIND*/}; parser::CharBlock name{ x.source.end() - nameLen, static_cast<std::size_t>(nameLen)}; CHECK(name == "len" || name == "kind"); @@ -4781,7 +4782,9 @@ std::optional<ProcedureRef> ArgumentAnalyzer::TryDefinedAssignment() { return std::nullopt; // user-defined assignment not allowed for these args } auto restorer{context_.GetContextualMessages().SetLocation(source_)}; - if (std::optional<ProcedureRef> procRef{GetDefinedAssignmentProc()}) { + bool isAmbiguous{false}; + if (std::optional<ProcedureRef> procRef{ + GetDefinedAssignmentProc(isAmbiguous)}) { if (context_.inWhereBody() && !procRef->proc().IsElemental()) { // C1032 context_.Say( "Defined assignment in WHERE must be elemental, but '%s' is not"_err_en_US, @@ -4791,9 +4794,11 @@ std::optional<ProcedureRef> ArgumentAnalyzer::TryDefinedAssignment() { return std::move(*procRef); } if (isDefined == Tristate::Yes) { - if (!lhsType || !rhsType || (lhsRank != rhsRank && rhsRank != 0) || + if (isAmbiguous || !lhsType || !rhsType || + (lhsRank != rhsRank && rhsRank != 0) || !OkLogicalIntegerAssignment(lhsType->category(), rhsType->category())) { - SayNoMatch("ASSIGNMENT(=)", true); + SayNoMatch( + "ASSIGNMENT(=)", /*isAssignment=*/true, /*isAmbiguous=*/isAmbiguous); } } else if (!fatalErrors_) { CheckAssignmentConformance(); @@ -4822,13 +4827,15 @@ bool ArgumentAnalyzer::OkLogicalIntegerAssignment( return true; } -std::optional<ProcedureRef> ArgumentAnalyzer::GetDefinedAssignmentProc() { +std::optional<ProcedureRef> ArgumentAnalyzer::GetDefinedAssignmentProc( + bool &isAmbiguous) { const Symbol *proc{nullptr}; bool isProcElemental{false}; std::optional<int> passedObjectIndex; std::string oprNameString{"assignment(=)"}; parser::CharBlock oprName{oprNameString}; const auto &scope{context_.context().FindScope(source_)}; + isAmbiguous = false; { auto restorer{context_.GetContextualMessages().DiscardMessages()}; if (const Symbol *symbol{scope.FindSymbol(oprName)}) { @@ -4842,8 +4849,8 @@ std::optional<ProcedureRef> ArgumentAnalyzer::GetDefinedAssignmentProc() { for (std::size_t i{0}; (!proc || isProcElemental) && i < actuals_.size(); ++i) { const Symbol *generic{nullptr}; - if (const Symbol * - binding{FindBoundOp(oprName, i, generic, /*isSubroutine=*/true)}) { + if (const Symbol *binding{FindBoundOp(oprName, i, generic, + /*isSubroutine=*/true, /*isAmbiguous=*/&isAmbiguous)}) { // ignore inaccessible type-bound ASSIGNMENT(=) generic if (!CheckAccessibleSymbol(scope, DEREF(generic))) { const Symbol *resolution{GetBindingResolution(GetType(i), *binding)}; @@ -4967,7 +4974,8 @@ bool ArgumentAnalyzer::AreConformable() const { // Look for a type-bound operator in the type of arg number passIndex. const Symbol *ArgumentAnalyzer::FindBoundOp(parser::CharBlock oprName, - int passIndex, const Symbol *&generic, bool isSubroutine) { + int passIndex, const Symbol *&generic, bool isSubroutine, + bool *isAmbiguous) { const auto *type{GetDerivedTypeSpec(GetType(passIndex))}; const semantics::Scope *scope{type ? type->scope() : nullptr}; if (scope) { @@ -4989,6 +4997,9 @@ const Symbol *ArgumentAnalyzer::FindBoundOp(parser::CharBlock oprName, // Use the most recent override of the binding, if any return scope->FindComponent(binding->name()); } else { + if (isAmbiguous) { + *isAmbiguous = pair.second; + } context_.EmitGenericResolutionError(*generic, pair.second, isSubroutine); } } @@ -5072,40 +5083,37 @@ void ArgumentAnalyzer::ConvertBOZAssignmentRHS(const DynamicType &lhsType) { } // Report error resolving opr when there is a user-defined one available -void ArgumentAnalyzer::SayNoMatch(const std::string &opr, bool isAssignment) { +void ArgumentAnalyzer::SayNoMatch( + const std::string &opr, bool isAssignment, bool isAmbiguous) { std::string type0{TypeAsFortran(0)}; auto rank0{actuals_[0]->Rank()}; + std::string prefix{"No intrinsic or user-defined "s + opr + " matches"}; + if (isAmbiguous) { + prefix = "Multiple specific procedures for the generic "s + opr + " match"; + } if (actuals_.size() == 1) { if (rank0 > 0) { - context_.Say("No intrinsic or user-defined %s matches " - "rank %d array of %s"_err_en_US, - opr, rank0, type0); + context_.Say("%s rank %d array of %s"_err_en_US, prefix, rank0, type0); } else { - context_.Say("No intrinsic or user-defined %s matches " - "operand type %s"_err_en_US, - opr, type0); + context_.Say("%s operand type %s"_err_en_US, prefix, type0); } } else { std::string type1{TypeAsFortran(1)}; auto rank1{actuals_[1]->Rank()}; if (rank0 > 0 && rank1 > 0 && rank0 != rank1) { - context_.Say("No intrinsic or user-defined %s matches " - "rank %d array of %s and rank %d array of %s"_err_en_US, - opr, rank0, type0, rank1, type1); + context_.Say("%s rank %d array of %s and rank %d array of %s"_err_en_US, + prefix, rank0, type0, rank1, type1); } else if (isAssignment && rank0 != rank1) { if (rank0 == 0) { - context_.Say("No intrinsic or user-defined %s matches " - "scalar %s and rank %d array of %s"_err_en_US, - opr, type0, rank1, type1); + context_.Say("%s scalar %s and rank %d array of %s"_err_en_US, prefix, + type0, rank1, type1); } else { - context_.Say("No intrinsic or user-defined %s matches " - "rank %d array of %s and scalar %s"_err_en_US, - opr, rank0, type0, type1); + context_.Say("%s rank %d array of %s and scalar %s"_err_en_US, prefix, + rank0, type0, type1); } } else { - context_.Say("No intrinsic or user-defined %s matches " - "operand types %s and %s"_err_en_US, - opr, type0, type1); + context_.Say( + "%s operand types %s and %s"_err_en_US, prefix, type0, type1); } } } diff --git a/flang/lib/Semantics/resolve-labels.cpp b/flang/lib/Semantics/resolve-labels.cpp index b0cbc4b56e88..27e259fab387 100644 --- a/flang/lib/Semantics/resolve-labels.cpp +++ b/flang/lib/Semantics/resolve-labels.cpp @@ -489,15 +489,30 @@ public: // C1401 void Post(const parser::MainProgram &mainProgram) { + // Uppercase the name of the main program, so that its symbol name + // would be unique from similarly named non-main-program symbols. + auto upperCaseCharBlock = [](const parser::CharBlock &cb) { + char *ch{const_cast<char *>(cb.begin())}; + char *endCh{ch + cb.size()}; + while (ch != endCh) { + *ch++ = parser::ToUpperCaseLetter(*ch); + } + }; + const parser::CharBlock *progName{nullptr}; + if (const auto &program{ + std::get<std::optional<parser::Statement<parser::ProgramStmt>>>( + mainProgram.t)}) { + progName = &program->statement.v.source; + upperCaseCharBlock(*progName); + } if (const parser::CharBlock * endName{GetStmtName(std::get<parser::Statement<parser::EndProgramStmt>>( mainProgram.t))}) { - if (const auto &program{ - std::get<std::optional<parser::Statement<parser::ProgramStmt>>>( - mainProgram.t)}) { - if (*endName != program->statement.v.source) { + upperCaseCharBlock(*endName); + if (progName) { + if (*endName != *progName) { context_.Say(*endName, "END PROGRAM name mismatch"_err_en_US) - .Attach(program->statement.v.source, "should be"_en_US); + .Attach(*progName, "should be"_en_US); } } else { context_.Say(*endName, diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp index d0336c9cb661..b3268605e7c0 100644 --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -2139,7 +2139,7 @@ bool ImplicitRules::isImplicitNoneExternal() const { const DeclTypeSpec *ImplicitRules::GetType( SourceName name, bool respectImplicitNoneType) const { - char ch{name.begin()[0]}; + char ch{name.front()}; if (isImplicitNoneType_ && respectImplicitNoneType) { return nullptr; } else if (auto it{map_.find(ch)}; it != map_.end()) { @@ -8574,8 +8574,10 @@ bool ResolveNamesVisitor::Pre(const parser::ImportStmt &x) { } else { Say(name, "A distinct '%s' is already present in this scope"_err_en_US) - .Attach(symbol->name(), "Previous declaration of '%s'"_en_US) - .Attach(outer->name(), "Declaration of '%s' in host scope"_en_US); + .Attach(symbol->name(), "Previous declaration of '%s'"_en_US, + symbol->name().ToString()) + .Attach(outer->name(), "Declaration of '%s' in host scope"_en_US, + outer->name().ToString()); } } } else { diff --git a/flang/lib/Semantics/runtime-type-info.cpp b/flang/lib/Semantics/runtime-type-info.cpp index 51ba21a9e5ed..5916a07df774 100644 --- a/flang/lib/Semantics/runtime-type-info.cpp +++ b/flang/lib/Semantics/runtime-type-info.cpp @@ -1131,7 +1131,7 @@ void RuntimeTableBuilder::DescribeSpecialProc( if (auto proc{evaluate::characteristics::Procedure::Characterize( specific, context_.foldingContext())}) { std::uint8_t isArgDescriptorSet{0}; - std::uint8_t isArgContiguousSet{0}; + bool specialCaseFlag{0}; int argThatMightBeDescriptor{0}; MaybeExpr which; if (isAssignment) { @@ -1197,7 +1197,7 @@ void RuntimeTableBuilder::DescribeSpecialProc( TypeAndShape::Attr::AssumedShape) || dummyData.attrs.test(evaluate::characteristics:: DummyDataObject::Attr::Contiguous)) { - isArgContiguousSet |= 1; + specialCaseFlag = true; } } } @@ -1216,7 +1216,7 @@ void RuntimeTableBuilder::DescribeSpecialProc( return; } if (ddo->type.type().IsPolymorphic()) { - isArgDescriptorSet |= 1; + argThatMightBeDescriptor = 1; } switch (io.value()) { case common::DefinedIo::ReadFormatted: @@ -1232,6 +1232,9 @@ void RuntimeTableBuilder::DescribeSpecialProc( which = writeUnformattedEnum_; break; } + if (context_.defaultKinds().GetDefaultKind(TypeCategory::Integer) == 8) { + specialCaseFlag = true; // UNIT= & IOSTAT= INTEGER(8) + } } if (argThatMightBeDescriptor != 0) { if (const auto *dummyData{ @@ -1262,8 +1265,8 @@ void RuntimeTableBuilder::DescribeSpecialProc( } CHECK(bindingIndex <= 255); AddValue(values, specialSchema_, "istypebound"s, IntExpr<1>(bindingIndex)); - AddValue(values, specialSchema_, "isargcontiguousset"s, - IntExpr<1>(isArgContiguousSet)); + AddValue(values, specialSchema_, "specialcaseflag"s, + IntExpr<1>(specialCaseFlag)); AddValue(values, specialSchema_, procCompName, SomeExpr{evaluate::ProcedureDesignator{specific}}); // index might already be present in the case of an override @@ -1383,19 +1386,26 @@ CollectNonTbpDefinedIoGenericInterfaces( } else { // Local scope's specific overrides host's for this type bool updated{false}; + std::uint8_t flags{0}; + if (declType->IsPolymorphic()) { + flags |= IsDtvArgPolymorphic; + } + if (scope.context().GetDefaultKind(TypeCategory::Integer) == + 8) { + flags |= DefinedIoInteger8; + } for (auto [iter, end]{result.equal_range(dtDesc)}; iter != end; ++iter) { NonTbpDefinedIo &nonTbp{iter->second}; if (nonTbp.definedIo == which) { nonTbp.subroutine = &*specific; - nonTbp.isDtvArgPolymorphic = declType->IsPolymorphic(); + nonTbp.flags = flags; updated = true; } } if (!updated) { - result.emplace(dtDesc, - NonTbpDefinedIo{ - &*specific, which, declType->IsPolymorphic()}); + result.emplace( + dtDesc, NonTbpDefinedIo{&*specific, which, flags}); } } } diff --git a/flang/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp index ab78605d01f4..b15ed057b52f 100644 --- a/flang/lib/Semantics/semantics.cpp +++ b/flang/lib/Semantics/semantics.cpp @@ -376,8 +376,7 @@ const DeclTypeSpec &SemanticsContext::MakeLogicalType(int kind) { } bool SemanticsContext::AnyFatalError() const { - return !messages_.empty() && - (warningsAreErrors_ || messages_.AnyFatalError()); + return messages_.AnyFatalError(warningsAreErrors_); } bool SemanticsContext::HasError(const Symbol &symbol) { return errorSymbols_.count(symbol) > 0; @@ -658,7 +657,7 @@ void Semantics::EmitMessages(llvm::raw_ostream &os) { context_.messages().ResolveProvenances(context_.allCookedSources()); context_.messages().Emit(os, context_.allCookedSources(), /*echoSourceLine=*/true, &context_.languageFeatures(), - /*maxErrorsToEmit=*/context_.maxErrors()); + context_.maxErrors(), context_.warningsAreErrors()); } void SemanticsContext::DumpSymbols(llvm::raw_ostream &os) { diff --git a/flang/lib/Semantics/tools.cpp b/flang/lib/Semantics/tools.cpp index d27d250b3f11..5a5b02e1ac3c 100644 --- a/flang/lib/Semantics/tools.cpp +++ b/flang/lib/Semantics/tools.cpp @@ -348,9 +348,9 @@ const Symbol &BypassGeneric(const Symbol &symbol) { const Symbol &GetCrayPointer(const Symbol &crayPointee) { const Symbol *found{nullptr}; - for (const auto &[pointee, pointer] : - crayPointee.GetUltimate().owner().crayPointers()) { - if (pointee == crayPointee.name()) { + const Symbol &ultimate{crayPointee.GetUltimate()}; + for (const auto &[pointee, pointer] : ultimate.owner().crayPointers()) { + if (pointee == ultimate.name()) { found = &pointer.get(); break; } @@ -1740,7 +1740,7 @@ std::forward_list<std::string> GetOperatorNames( std::forward_list<std::string> GetAllNames( const SemanticsContext &context, const SourceName &name) { std::string str{name.ToString()}; - if (!name.empty() && name.end()[-1] == ')' && + if (!name.empty() && name.back() == ')' && name.ToString().rfind("operator(", 0) == 0) { for (int i{0}; i != common::LogicalOperator_enumSize; ++i) { auto names{GetOperatorNames(context, common::LogicalOperator{i})}; |
