1 //===-- include/flang/Semantics/expression.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 #ifndef FORTRAN_SEMANTICS_EXPRESSION_H_ 10 #define FORTRAN_SEMANTICS_EXPRESSION_H_ 11 12 #include "semantics.h" 13 #include "flang/Common/Fortran.h" 14 #include "flang/Common/indirection.h" 15 #include "flang/Common/restorer.h" 16 #include "flang/Common/visit.h" 17 #include "flang/Evaluate/characteristics.h" 18 #include "flang/Evaluate/check-expression.h" 19 #include "flang/Evaluate/expression.h" 20 #include "flang/Evaluate/fold.h" 21 #include "flang/Evaluate/tools.h" 22 #include "flang/Evaluate/type.h" 23 #include "flang/Parser/char-block.h" 24 #include "flang/Parser/parse-tree-visitor.h" 25 #include "flang/Parser/parse-tree.h" 26 #include "flang/Parser/tools.h" 27 #include <map> 28 #include <optional> 29 #include <type_traits> 30 #include <variant> 31 32 using namespace Fortran::parser::literals; 33 34 namespace Fortran::parser { 35 struct SourceLocationFindingVisitor { 36 template <typename A> bool Pre(const A &x) { 37 if constexpr (HasSource<A>::value) { 38 source.ExtendToCover(x.source); 39 return false; 40 } else { 41 return true; 42 } 43 } 44 template <typename A> void Post(const A &) {} 45 void Post(const CharBlock &at) { source.ExtendToCover(at); } 46 47 CharBlock source; 48 }; 49 50 template <typename A> CharBlock FindSourceLocation(const A &x) { 51 SourceLocationFindingVisitor visitor; 52 Walk(x, visitor); 53 return visitor.source; 54 } 55 } // namespace Fortran::parser 56 57 using namespace Fortran::parser::literals; 58 59 // The expression semantic analysis code has its implementation in 60 // namespace Fortran::evaluate, but the exposed API to it is in the 61 // namespace Fortran::semantics (below). 62 // 63 // The ExpressionAnalyzer wraps a SemanticsContext reference 64 // and implements constraint checking on expressions using the 65 // parse tree node wrappers that mirror the grammar annotations used 66 // in the Fortran standard (i.e., scalar-, constant-, &c.). 67 68 namespace Fortran::evaluate { 69 70 class IntrinsicProcTable; 71 72 struct SetExprHelper { 73 explicit SetExprHelper(GenericExprWrapper &&expr) : expr_{std::move(expr)} {} 74 void Set(parser::TypedExpr &x) { 75 x.Reset(new GenericExprWrapper{std::move(expr_)}, 76 evaluate::GenericExprWrapper::Deleter); 77 } 78 template <typename T> void Set(const common::Indirection<T> &x) { 79 Set(x.value()); 80 } 81 template <typename T> void Set(const T &x) { 82 if constexpr (parser::HasTypedExpr<T>::value) { 83 Set(x.typedExpr); 84 } else if constexpr (ConstraintTrait<T>) { 85 Set(x.thing); 86 } else if constexpr (WrapperTrait<T>) { 87 Set(x.v); 88 } 89 } 90 91 GenericExprWrapper expr_; 92 }; 93 94 template <typename T> void ResetExpr(const T &x) { 95 SetExprHelper{GenericExprWrapper{/* error indicator */}}.Set(x); 96 } 97 98 template <typename T> void SetExpr(const T &x, Expr<SomeType> &&expr) { 99 SetExprHelper{GenericExprWrapper{std::move(expr)}}.Set(x); 100 } 101 102 class ExpressionAnalyzer { 103 public: 104 using MaybeExpr = std::optional<Expr<SomeType>>; 105 106 explicit ExpressionAnalyzer(semantics::SemanticsContext &sc) : context_{sc} {} 107 ExpressionAnalyzer(semantics::SemanticsContext &sc, FoldingContext &fc) 108 : context_{sc}, foldingContext_{fc} {} 109 ExpressionAnalyzer(const ExpressionAnalyzer &) = default; 110 111 semantics::SemanticsContext &context() const { return context_; } 112 bool inWhereBody() const { return inWhereBody_; } 113 void set_inWhereBody(bool yes = true) { inWhereBody_ = yes; } 114 bool inDataStmtObject() const { return inDataStmtObject_; } 115 void set_inDataStmtObject(bool yes = true) { inDataStmtObject_ = yes; } 116 117 FoldingContext &GetFoldingContext() const { return foldingContext_; } 118 119 parser::ContextualMessages &GetContextualMessages() { 120 return foldingContext_.messages(); 121 } 122 123 template <typename... A> parser::Message *Say(A &&...args) { 124 return GetContextualMessages().Say(std::forward<A>(args)...); 125 } 126 template <typename FeatureOrUsageWarning, typename... A> 127 parser::Message *Warn( 128 FeatureOrUsageWarning warning, parser::CharBlock at, A &&...args) { 129 return context_.Warn(warning, at, std::forward<A>(args)...); 130 } 131 template <typename FeatureOrUsageWarning, typename... A> 132 parser::Message *Warn(FeatureOrUsageWarning warning, A &&...args) { 133 return Warn( 134 warning, GetContextualMessages().at(), std::forward<A>(args)...); 135 } 136 137 template <typename T, typename... A> 138 parser::Message *SayAt(const T &parsed, A &&...args) { 139 return Say(parser::FindSourceLocation(parsed), std::forward<A>(args)...); 140 } 141 142 int GetDefaultKind(common::TypeCategory); 143 DynamicType GetDefaultKindOfType(common::TypeCategory); 144 145 // Return false and emit error if these checks fail: 146 bool CheckIntrinsicKind(TypeCategory, std::int64_t kind); 147 bool CheckIntrinsicSize(TypeCategory, std::int64_t size); 148 149 // Manage a set of active implied DO loops. 150 bool AddImpliedDo(parser::CharBlock, int kind); 151 void RemoveImpliedDo(parser::CharBlock); 152 153 // When the argument is the name of an active implied DO index, returns 154 // its INTEGER kind type parameter. 155 std::optional<int> IsImpliedDo(parser::CharBlock) const; 156 157 common::Restorer<bool> DoNotUseSavedTypedExprs() { 158 return common::ScopedSet(useSavedTypedExprs_, false); 159 } 160 161 Expr<SubscriptInteger> AnalyzeKindSelector(common::TypeCategory category, 162 const std::optional<parser::KindSelector> &); 163 164 MaybeExpr Analyze(const parser::Expr &); 165 MaybeExpr Analyze(const parser::Variable &); 166 MaybeExpr Analyze(const parser::Selector &); 167 MaybeExpr Analyze(const parser::Designator &); 168 MaybeExpr Analyze(const parser::DataStmtValue &); 169 MaybeExpr Analyze(const parser::AllocateObject &); 170 MaybeExpr Analyze(const parser::PointerObject &); 171 172 template <typename A> MaybeExpr Analyze(const common::Indirection<A> &x) { 173 return Analyze(x.value()); 174 } 175 template <typename A> MaybeExpr Analyze(const std::optional<A> &x) { 176 if (x) { 177 return Analyze(*x); 178 } else { 179 return std::nullopt; 180 } 181 } 182 183 // Implement constraint-checking wrappers from the Fortran grammar. 184 template <typename A> MaybeExpr Analyze(const parser::Scalar<A> &x) { 185 auto result{Analyze(x.thing)}; 186 if (result) { 187 if (int rank{result->Rank()}; rank != 0) { 188 SayAt(x, "Must be a scalar value, but is a rank-%d array"_err_en_US, 189 rank); 190 ResetExpr(x); 191 return std::nullopt; 192 } 193 } 194 return result; 195 } 196 template <typename A> MaybeExpr Analyze(const parser::Constant<A> &x) { 197 auto restorer{ 198 GetFoldingContext().messages().SetLocation(FindSourceLocation(x))}; 199 auto result{Analyze(x.thing)}; 200 if (result) { 201 *result = Fold(std::move(*result)); 202 if (!IsConstantExpr(*result)) { // C886, C887, C713 203 SayAt(x, "Must be a constant value"_err_en_US); 204 ResetExpr(x); 205 return std::nullopt; 206 } else { 207 // Save folded expression for later use 208 SetExpr(x, common::Clone(*result)); 209 } 210 } 211 return result; 212 } 213 template <typename A> MaybeExpr Analyze(const parser::Integer<A> &x) { 214 auto result{Analyze(x.thing)}; 215 if (!EnforceTypeConstraint( 216 parser::FindSourceLocation(x), result, TypeCategory::Integer)) { 217 ResetExpr(x); 218 return std::nullopt; 219 } 220 return result; 221 } 222 template <typename A> MaybeExpr Analyze(const parser::Logical<A> &x) { 223 auto result{Analyze(x.thing)}; 224 if (!EnforceTypeConstraint( 225 parser::FindSourceLocation(x), result, TypeCategory::Logical)) { 226 ResetExpr(x); 227 return std::nullopt; 228 } 229 return result; 230 } 231 template <typename A> MaybeExpr Analyze(const parser::DefaultChar<A> &x) { 232 auto result{Analyze(x.thing)}; 233 if (!EnforceTypeConstraint(parser::FindSourceLocation(x), result, 234 TypeCategory::Character, true /* default kind */)) { 235 ResetExpr(x); 236 return std::nullopt; 237 } 238 return result; 239 } 240 241 MaybeExpr Analyze(const parser::Name &); 242 MaybeExpr Analyze(const parser::DataRef &dr) { 243 return Analyze<parser::DataRef>(dr); 244 } 245 MaybeExpr Analyze(const parser::StructureComponent &); 246 MaybeExpr Analyze(const parser::SignedIntLiteralConstant &); 247 MaybeExpr Analyze(const parser::SignedRealLiteralConstant &); 248 MaybeExpr Analyze(const parser::SignedComplexLiteralConstant &); 249 MaybeExpr Analyze(const parser::StructureConstructor &); 250 MaybeExpr Analyze(const parser::InitialDataTarget &); 251 MaybeExpr Analyze(const parser::NullInit &); 252 MaybeExpr Analyze(const parser::StmtFunctionStmt &); 253 254 void Analyze(const parser::CallStmt &); 255 const Assignment *Analyze(const parser::AssignmentStmt &); 256 const Assignment *Analyze(const parser::PointerAssignmentStmt &); 257 258 // Builds a typed Designator from an untyped DataRef 259 MaybeExpr Designate(DataRef &&); 260 261 protected: 262 int IntegerTypeSpecKind(const parser::IntegerTypeSpec &); 263 264 private: 265 // Allows a whole assumed-size array to appear for the lifetime of 266 // the returned value. 267 common::Restorer<bool> AllowWholeAssumedSizeArray() { 268 return common::ScopedSet(isWholeAssumedSizeArrayOk_, true); 269 } 270 271 // Allows an Expr to be a null pointer. 272 common::Restorer<bool> AllowNullPointer() { 273 return common::ScopedSet(isNullPointerOk_, true); 274 } 275 276 MaybeExpr Analyze(const parser::IntLiteralConstant &, bool negated = false); 277 MaybeExpr Analyze(const parser::UnsignedLiteralConstant &); 278 MaybeExpr Analyze(const parser::RealLiteralConstant &); 279 MaybeExpr Analyze(const parser::ComplexPart &); 280 MaybeExpr Analyze(const parser::ComplexLiteralConstant &); 281 MaybeExpr Analyze(const parser::LogicalLiteralConstant &); 282 MaybeExpr Analyze(const parser::CharLiteralConstant &); 283 MaybeExpr Analyze(const parser::HollerithLiteralConstant &); 284 MaybeExpr Analyze(const parser::BOZLiteralConstant &); 285 MaybeExpr Analyze(const parser::NamedConstant &); 286 MaybeExpr Analyze(const parser::DataStmtConstant &); 287 MaybeExpr Analyze(const parser::Substring &); 288 MaybeExpr Analyze(const parser::ArrayElement &); 289 MaybeExpr Analyze(const parser::CoindexedNamedObject &); 290 MaybeExpr Analyze(const parser::CharLiteralConstantSubstring &); 291 MaybeExpr Analyze(const parser::SubstringInquiry &); 292 MaybeExpr Analyze(const parser::ArrayConstructor &); 293 MaybeExpr Analyze(const parser::FunctionReference &, 294 std::optional<parser::StructureConstructor> * = nullptr); 295 MaybeExpr Analyze(const parser::Expr::Parentheses &); 296 MaybeExpr Analyze(const parser::Expr::UnaryPlus &); 297 MaybeExpr Analyze(const parser::Expr::Negate &); 298 MaybeExpr Analyze(const parser::Expr::NOT &); 299 MaybeExpr Analyze(const parser::Expr::PercentLoc &); 300 MaybeExpr Analyze(const parser::Expr::DefinedUnary &); 301 MaybeExpr Analyze(const parser::Expr::Power &); 302 MaybeExpr Analyze(const parser::Expr::Multiply &); 303 MaybeExpr Analyze(const parser::Expr::Divide &); 304 MaybeExpr Analyze(const parser::Expr::Add &); 305 MaybeExpr Analyze(const parser::Expr::Subtract &); 306 MaybeExpr Analyze(const parser::Expr::ComplexConstructor &); 307 MaybeExpr Analyze(const parser::Expr::Concat &); 308 MaybeExpr Analyze(const parser::Expr::LT &); 309 MaybeExpr Analyze(const parser::Expr::LE &); 310 MaybeExpr Analyze(const parser::Expr::EQ &); 311 MaybeExpr Analyze(const parser::Expr::NE &); 312 MaybeExpr Analyze(const parser::Expr::GE &); 313 MaybeExpr Analyze(const parser::Expr::GT &); 314 MaybeExpr Analyze(const parser::Expr::AND &); 315 MaybeExpr Analyze(const parser::Expr::OR &); 316 MaybeExpr Analyze(const parser::Expr::EQV &); 317 MaybeExpr Analyze(const parser::Expr::NEQV &); 318 MaybeExpr Analyze(const parser::Expr::DefinedBinary &); 319 template <typename A> MaybeExpr Analyze(const A &x) { 320 return Analyze(x.u); // default case 321 } 322 template <typename... As> MaybeExpr Analyze(const std::variant<As...> &u) { 323 return common::visit([&](const auto &x) { return Analyze(x); }, u); 324 } 325 326 // Analysis subroutines 327 int AnalyzeKindParam( 328 const std::optional<parser::KindParam> &, int defaultKind); 329 template <typename PARSED> 330 MaybeExpr ExprOrVariable(const PARSED &, parser::CharBlock source); 331 template <typename TYPES, TypeCategory CAT, typename PARSED> 332 MaybeExpr IntLiteralConstant(const PARSED &, bool isNegated = false); 333 MaybeExpr AnalyzeString(std::string &&, int kind); 334 std::optional<Expr<SubscriptInteger>> AsSubscript(MaybeExpr &&); 335 std::optional<Expr<SubscriptInteger>> TripletPart( 336 const std::optional<parser::Subscript> &); 337 std::optional<Subscript> AnalyzeSectionSubscript( 338 const parser::SectionSubscript &); 339 std::vector<Subscript> AnalyzeSectionSubscripts( 340 const std::list<parser::SectionSubscript> &); 341 std::optional<Component> CreateComponent(DataRef &&, const Symbol &, 342 const semantics::Scope &, bool C919bAlreadyEnforced = false); 343 MaybeExpr CompleteSubscripts(ArrayRef &&); 344 MaybeExpr ApplySubscripts(DataRef &&, std::vector<Subscript> &&); 345 void CheckSubscripts(ArrayRef &); 346 bool CheckRanks(const DataRef &); // Return false if error exists. 347 bool CheckPolymorphic(const DataRef &); // ditto 348 bool CheckDataRef(const DataRef &); // ditto 349 std::optional<Expr<SubscriptInteger>> GetSubstringBound( 350 const std::optional<parser::ScalarIntExpr> &); 351 MaybeExpr AnalyzeDefinedOp(const parser::Name &, ActualArguments &&); 352 MaybeExpr FixMisparsedSubstring(const parser::Designator &); 353 354 struct CalleeAndArguments { 355 // A non-component function reference may constitute a misparsed 356 // structure constructor, in which case its derived type's Symbol 357 // will appear here. 358 std::variant<ProcedureDesignator, SymbolRef> u; 359 ActualArguments arguments; 360 }; 361 362 std::optional<CalleeAndArguments> AnalyzeProcedureComponentRef( 363 const parser::ProcComponentRef &, ActualArguments &&, bool isSubroutine); 364 std::optional<characteristics::Procedure> CheckCall( 365 parser::CharBlock, const ProcedureDesignator &, ActualArguments &); 366 using AdjustActuals = 367 std::optional<std::function<bool(const Symbol &, ActualArguments &)>>; 368 const Symbol *ResolveForward(const Symbol &); 369 std::pair<const Symbol *, bool /* failure due ambiguity */> ResolveGeneric( 370 const Symbol &, const ActualArguments &, const AdjustActuals &, 371 bool isSubroutine, bool mightBeStructureConstructor = false); 372 void EmitGenericResolutionError( 373 const Symbol &, bool dueToNullActuals, bool isSubroutine); 374 const Symbol &AccessSpecific( 375 const Symbol &originalGeneric, const Symbol &specific); 376 std::optional<CalleeAndArguments> GetCalleeAndArguments(const parser::Name &, 377 ActualArguments &&, bool isSubroutine = false, 378 bool mightBeStructureConstructor = false); 379 std::optional<CalleeAndArguments> GetCalleeAndArguments( 380 const parser::ProcedureDesignator &, ActualArguments &&, 381 bool isSubroutine, bool mightBeStructureConstructor = false); 382 void CheckBadExplicitType(const SpecificCall &, const Symbol &); 383 void CheckForBadRecursion(parser::CharBlock, const semantics::Symbol &); 384 bool EnforceTypeConstraint(parser::CharBlock, const MaybeExpr &, TypeCategory, 385 bool defaultKind = false); 386 MaybeExpr MakeFunctionRef( 387 parser::CharBlock, ProcedureDesignator &&, ActualArguments &&); 388 MaybeExpr MakeFunctionRef(parser::CharBlock intrinsic, ActualArguments &&); 389 template <typename T> T Fold(T &&expr) { 390 return evaluate::Fold(foldingContext_, std::move(expr)); 391 } 392 bool CheckIsValidForwardReference(const semantics::DerivedTypeSpec &); 393 MaybeExpr AnalyzeComplex(MaybeExpr &&re, MaybeExpr &&im, const char *what); 394 std::optional<Chevrons> AnalyzeChevrons(const parser::CallStmt &); 395 396 MaybeExpr IterativelyAnalyzeSubexpressions(const parser::Expr &); 397 398 semantics::SemanticsContext &context_; 399 FoldingContext &foldingContext_{context_.foldingContext()}; 400 std::map<parser::CharBlock, int> impliedDos_; // values are INTEGER kinds 401 std::map<parser::CharBlock, 402 std::pair<parser::CharBlock, evaluate::characteristics::Procedure>> 403 implicitInterfaces_; 404 bool isWholeAssumedSizeArrayOk_{false}; 405 bool isNullPointerOk_{false}; 406 bool useSavedTypedExprs_{true}; 407 bool inWhereBody_{false}; 408 bool inDataStmtObject_{false}; 409 bool inDataStmtConstant_{false}; 410 bool inStmtFunctionDefinition_{false}; 411 bool iterativelyAnalyzingSubexpressions_{false}; 412 friend class ArgumentAnalyzer; 413 }; 414 415 inline bool AreConformable(int leftRank, int rightRank) { 416 return leftRank == 0 || rightRank == 0 || leftRank == rightRank; 417 } 418 419 template <typename L, typename R> 420 bool AreConformable(const L &left, const R &right) { 421 return AreConformable(left.Rank(), right.Rank()); 422 } 423 424 template <typename L, typename R> 425 void ConformabilityCheck( 426 parser::ContextualMessages &context, const L &left, const R &right) { 427 if (!AreConformable(left, right)) { 428 context.Say("left operand has rank %d, right operand has rank %d"_err_en_US, 429 left.Rank(), right.Rank()); 430 } 431 } 432 } // namespace Fortran::evaluate 433 434 namespace Fortran::semantics { 435 436 // Semantic analysis of one expression, variable, selector, designator, &c. 437 template <typename A> 438 std::optional<evaluate::Expr<evaluate::SomeType>> AnalyzeExpr( 439 SemanticsContext &context, const A &expr) { 440 return evaluate::ExpressionAnalyzer{context}.Analyze(expr); 441 } 442 443 // Semantic analysis of an intrinsic type's KIND parameter expression. 444 evaluate::Expr<evaluate::SubscriptInteger> AnalyzeKindSelector( 445 SemanticsContext &, common::TypeCategory, 446 const std::optional<parser::KindSelector> &); 447 448 // Semantic analysis of all expressions in a parse tree, which becomes 449 // decorated with typed representations for top-level expressions. 450 class ExprChecker { 451 public: 452 explicit ExprChecker(SemanticsContext &); 453 454 template <typename A> bool Pre(const A &) { return true; } 455 template <typename A> void Post(const A &) {} 456 bool Walk(const parser::Program &); 457 458 bool Pre(const parser::Expr &x) { 459 exprAnalyzer_.Analyze(x); 460 return false; 461 } 462 bool Pre(const parser::Variable &x) { 463 exprAnalyzer_.Analyze(x); 464 return false; 465 } 466 bool Pre(const parser::Selector &x) { 467 exprAnalyzer_.Analyze(x); 468 return false; 469 } 470 bool Pre(const parser::DataStmtValue &x) { 471 exprAnalyzer_.Analyze(x); 472 return false; 473 } 474 bool Pre(const parser::AllocateObject &x) { 475 exprAnalyzer_.Analyze(x); 476 return false; 477 } 478 bool Pre(const parser::PointerObject &x) { 479 exprAnalyzer_.Analyze(x); 480 return false; 481 } 482 bool Pre(const parser::DataStmtObject &); 483 void Post(const parser::DataStmtObject &); 484 bool Pre(const parser::DataImpliedDo &); 485 486 bool Pre(const parser::CallStmt &x) { 487 exprAnalyzer_.Analyze(x); 488 return false; 489 } 490 bool Pre(const parser::AssignmentStmt &x) { 491 exprAnalyzer_.Analyze(x); 492 return false; 493 } 494 bool Pre(const parser::PointerAssignmentStmt &x) { 495 exprAnalyzer_.Analyze(x); 496 return false; 497 } 498 499 // Track whether we're in a WHERE statement or construct body 500 bool Pre(const parser::WhereStmt &) { 501 ++whereDepth_; 502 exprAnalyzer_.set_inWhereBody(InWhereBody()); 503 return true; 504 } 505 void Post(const parser::WhereStmt &) { 506 --whereDepth_; 507 exprAnalyzer_.set_inWhereBody(InWhereBody()); 508 } 509 bool Pre(const parser::WhereBodyConstruct &) { 510 ++whereDepth_; 511 exprAnalyzer_.set_inWhereBody(InWhereBody()); 512 return true; 513 } 514 void Post(const parser::WhereBodyConstruct &) { 515 --whereDepth_; 516 exprAnalyzer_.set_inWhereBody(InWhereBody()); 517 } 518 519 bool Pre(const parser::ComponentDefStmt &) { 520 inComponentDefStmt_ = true; 521 return true; 522 } 523 void Post(const parser::ComponentDefStmt &) { inComponentDefStmt_ = false; } 524 bool Pre(const parser::Initialization &x) { 525 // Default component initialization expressions (but not DATA-like ones 526 // as in DEC STRUCTUREs) were already analyzed in name resolution 527 // and PDT instantiation; do not attempt to re-analyze them without 528 // type parameters. 529 return !inComponentDefStmt_ || 530 std::holds_alternative< 531 std::list<common::Indirection<parser::DataStmtValue>>>(x.u); 532 } 533 534 template <typename A> bool Pre(const parser::Scalar<A> &x) { 535 exprAnalyzer_.Analyze(x); 536 return false; 537 } 538 template <typename A> bool Pre(const parser::Constant<A> &x) { 539 exprAnalyzer_.Analyze(x); 540 return false; 541 } 542 template <typename A> bool Pre(const parser::Integer<A> &x) { 543 exprAnalyzer_.Analyze(x); 544 return false; 545 } 546 template <typename A> bool Pre(const parser::Logical<A> &x) { 547 exprAnalyzer_.Analyze(x); 548 return false; 549 } 550 template <typename A> bool Pre(const parser::DefaultChar<A> &x) { 551 exprAnalyzer_.Analyze(x); 552 return false; 553 } 554 555 private: 556 bool InWhereBody() const { return whereDepth_ > 0; } 557 558 SemanticsContext &context_; 559 evaluate::ExpressionAnalyzer exprAnalyzer_{context_}; 560 int whereDepth_{0}; // nesting of WHERE statements & constructs 561 bool inComponentDefStmt_{false}; 562 }; 563 } // namespace Fortran::semantics 564 #endif // FORTRAN_SEMANTICS_EXPRESSION_H_ 565