xref: /llvm-project/flang/include/flang/Semantics/expression.h (revision fc97d2e68b03bc2979395e84b645e5b3ba35aecd)
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