164ab3302SCarolineConcatto //===-- lib/Semantics/check-data.cpp --------------------------------------===//
264ab3302SCarolineConcatto //
364ab3302SCarolineConcatto // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
464ab3302SCarolineConcatto // See https://llvm.org/LICENSE.txt for license information.
564ab3302SCarolineConcatto // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
664ab3302SCarolineConcatto //
764ab3302SCarolineConcatto //===----------------------------------------------------------------------===//
864ab3302SCarolineConcatto 
9a20d48d7Speter klausler // DATA statement semantic analysis.
10a20d48d7Speter klausler // - Applies static semantic checks to the variables in each data-stmt-set with
11a20d48d7Speter klausler //   class DataVarChecker;
124ac617f4Speter klausler // - Invokes conversion of DATA statement values to static initializers
13a20d48d7Speter klausler 
1464ab3302SCarolineConcatto #include "check-data.h"
154ac617f4Speter klausler #include "data-to-inits.h"
163a1afd8cSpeter klausler #include "flang/Evaluate/traverse.h"
17a20d48d7Speter klausler #include "flang/Parser/parse-tree.h"
18a20d48d7Speter klausler #include "flang/Parser/tools.h"
19a20d48d7Speter klausler #include "flang/Semantics/tools.h"
204ac617f4Speter klausler #include <algorithm>
214ac617f4Speter klausler #include <vector>
2264ab3302SCarolineConcatto 
2364ab3302SCarolineConcatto namespace Fortran::semantics {
2464ab3302SCarolineConcatto 
253a1afd8cSpeter klausler // Ensures that references to an implied DO loop control variable are
263a1afd8cSpeter klausler // represented as such in the "body" of the implied DO loop.
Enter(const parser::DataImpliedDo & x)273a1afd8cSpeter klausler void DataChecker::Enter(const parser::DataImpliedDo &x) {
283a1afd8cSpeter klausler   auto name{std::get<parser::DataImpliedDo::Bounds>(x.t).name.thing.thing};
293a1afd8cSpeter klausler   int kind{evaluate::ResultType<evaluate::ImpliedDoIndex>::kind};
303a1afd8cSpeter klausler   if (const auto dynamicType{evaluate::DynamicType::From(*name.symbol)}) {
31a20d48d7Speter klausler     if (dynamicType->category() == TypeCategory::Integer) {
323a1afd8cSpeter klausler       kind = dynamicType->kind();
333a1afd8cSpeter klausler     }
34a20d48d7Speter klausler   }
353a1afd8cSpeter klausler   exprAnalyzer_.AddImpliedDo(name.source, kind);
363a1afd8cSpeter klausler }
373a1afd8cSpeter klausler 
Leave(const parser::DataImpliedDo & x)383a1afd8cSpeter klausler void DataChecker::Leave(const parser::DataImpliedDo &x) {
393a1afd8cSpeter klausler   auto name{std::get<parser::DataImpliedDo::Bounds>(x.t).name.thing.thing};
403a1afd8cSpeter klausler   exprAnalyzer_.RemoveImpliedDo(name.source);
413a1afd8cSpeter klausler }
423a1afd8cSpeter klausler 
43a20d48d7Speter klausler // DataVarChecker applies static checks once to each variable that appears
44a20d48d7Speter klausler // in a data-stmt-set.  These checks are independent of the values that
45a20d48d7Speter klausler // correspond to the variables.
463a1afd8cSpeter klausler class DataVarChecker : public evaluate::AllTraverse<DataVarChecker, true> {
473a1afd8cSpeter klausler public:
483a1afd8cSpeter klausler   using Base = evaluate::AllTraverse<DataVarChecker, true>;
DataVarChecker(SemanticsContext & c,parser::CharBlock src)493a1afd8cSpeter klausler   DataVarChecker(SemanticsContext &c, parser::CharBlock src)
503a1afd8cSpeter klausler       : Base{*this}, context_{c}, source_{src} {}
513a1afd8cSpeter klausler   using Base::operator();
HasComponentWithoutSubscripts() const523a1afd8cSpeter klausler   bool HasComponentWithoutSubscripts() const {
533a1afd8cSpeter klausler     return hasComponent_ && !hasSubscript_;
543a1afd8cSpeter klausler   }
operator ()(const Symbol & symbol)55a20d48d7Speter klausler   bool operator()(const Symbol &symbol) { // C876
56a20d48d7Speter klausler     // 8.6.7p(2) - precludes non-pointers of derived types with
57a20d48d7Speter klausler     // default component values
58a20d48d7Speter klausler     const Scope &scope{context_.FindScope(source_)};
59a20d48d7Speter klausler     bool isFirstSymbol{isFirstSymbol_};
60a20d48d7Speter klausler     isFirstSymbol_ = false;
61a20d48d7Speter klausler     if (const char *whyNot{IsAutomatic(symbol) ? "Automatic variable"
62a20d48d7Speter klausler                 : IsDummy(symbol)              ? "Dummy argument"
63a20d48d7Speter klausler                 : IsFunctionResult(symbol)     ? "Function result"
64a20d48d7Speter klausler                 : IsAllocatable(symbol)        ? "Allocatable"
65c4f67ea1SPeter Klausler                 : IsInitialized(symbol, true /*ignore DATA*/,
66c4f67ea1SPeter Klausler                       true /*ignore allocatable components*/)
67c4f67ea1SPeter Klausler                 ? "Default-initialized"
68a20d48d7Speter klausler                 : IsProcedure(symbol) && !IsPointer(symbol) ? "Procedure"
69a20d48d7Speter klausler                 // remaining checks don't apply to components
70a20d48d7Speter klausler                 : !isFirstSymbol                   ? nullptr
71a20d48d7Speter klausler                 : IsHostAssociated(symbol, scope)  ? "Host-associated object"
72a20d48d7Speter klausler                 : IsUseAssociated(symbol, scope)   ? "USE-associated object"
737f8da079Speter klausler                 : symbol.has<AssocEntityDetails>() ? "Construct association"
7457705df2Speter klausler                 : IsPointer(symbol) && (hasComponent_ || hasSubscript_)
7557705df2Speter klausler                 ? "Target of pointer"
76a20d48d7Speter klausler                 : nullptr}) {
77a20d48d7Speter klausler       context_.Say(source_,
78a20d48d7Speter klausler           "%s '%s' must not be initialized in a DATA statement"_err_en_US,
79a20d48d7Speter klausler           whyNot, symbol.name());
80a20d48d7Speter klausler       return false;
8163a2987dSPeter Klausler     }
8263a2987dSPeter Klausler     if (IsProcedurePointer(symbol)) {
83a20d48d7Speter klausler       context_.Say(source_,
84a53967cdSPeter Klausler           "Procedure pointer '%s' in a DATA statement is not standard"_port_en_US,
85a20d48d7Speter klausler           symbol.name());
86a20d48d7Speter klausler     }
8763a2987dSPeter Klausler     if (IsInBlankCommon(symbol)) {
8863a2987dSPeter Klausler       context_.Say(source_,
89a53967cdSPeter Klausler           "Blank COMMON object '%s' in a DATA statement is not standard"_port_en_US,
9063a2987dSPeter Klausler           symbol.name());
9163a2987dSPeter Klausler     }
92a20d48d7Speter klausler     return true;
93a20d48d7Speter klausler   }
operator ()(const evaluate::Component & component)943a1afd8cSpeter klausler   bool operator()(const evaluate::Component &component) {
953a1afd8cSpeter klausler     hasComponent_ = true;
9670f1b4b4SAnchu Rajendran     const Symbol &lastSymbol{component.GetLastSymbol()};
9770f1b4b4SAnchu Rajendran     if (isPointerAllowed_) {
9870f1b4b4SAnchu Rajendran       if (IsPointer(lastSymbol) && hasSubscript_) { // C877
9970f1b4b4SAnchu Rajendran         context_.Say(source_,
10070f1b4b4SAnchu Rajendran             "Rightmost data object pointer '%s' must not be subscripted"_err_en_US,
10170f1b4b4SAnchu Rajendran             lastSymbol.name().ToString());
10270f1b4b4SAnchu Rajendran         return false;
10370f1b4b4SAnchu Rajendran       }
10470f1b4b4SAnchu Rajendran       RestrictPointer();
10570f1b4b4SAnchu Rajendran     } else {
10670f1b4b4SAnchu Rajendran       if (IsPointer(lastSymbol)) { // C877
10770f1b4b4SAnchu Rajendran         context_.Say(source_,
10870f1b4b4SAnchu Rajendran             "Data object must not contain pointer '%s' as a non-rightmost part"_err_en_US,
10970f1b4b4SAnchu Rajendran             lastSymbol.name().ToString());
11070f1b4b4SAnchu Rajendran         return false;
11170f1b4b4SAnchu Rajendran       }
11270f1b4b4SAnchu Rajendran     }
11370f1b4b4SAnchu Rajendran     return (*this)(component.base()) && (*this)(lastSymbol);
11470f1b4b4SAnchu Rajendran   }
operator ()(const evaluate::ArrayRef & arrayRef)11570f1b4b4SAnchu Rajendran   bool operator()(const evaluate::ArrayRef &arrayRef) {
11670f1b4b4SAnchu Rajendran     hasSubscript_ = true;
11770f1b4b4SAnchu Rajendran     return (*this)(arrayRef.base()) && (*this)(arrayRef.subscript());
11870f1b4b4SAnchu Rajendran   }
operator ()(const evaluate::Substring & substring)11970f1b4b4SAnchu Rajendran   bool operator()(const evaluate::Substring &substring) {
12070f1b4b4SAnchu Rajendran     hasSubscript_ = true;
12170f1b4b4SAnchu Rajendran     return (*this)(substring.parent()) && (*this)(substring.lower()) &&
12270f1b4b4SAnchu Rajendran         (*this)(substring.upper());
12370f1b4b4SAnchu Rajendran   }
operator ()(const evaluate::CoarrayRef &)12470f1b4b4SAnchu Rajendran   bool operator()(const evaluate::CoarrayRef &) { // C874
12570f1b4b4SAnchu Rajendran     context_.Say(
12670f1b4b4SAnchu Rajendran         source_, "Data object must not be a coindexed variable"_err_en_US);
12770f1b4b4SAnchu Rajendran     return false;
12870f1b4b4SAnchu Rajendran   }
operator ()(const evaluate::Subscript & subs)1293a1afd8cSpeter klausler   bool operator()(const evaluate::Subscript &subs) {
13070f1b4b4SAnchu Rajendran     DataVarChecker subscriptChecker{context_, source_};
13170f1b4b4SAnchu Rajendran     subscriptChecker.RestrictPointer();
132*cd03e96fSPeter Klausler     return common::visit(
1333a1afd8cSpeter klausler                common::visitors{
1343a1afd8cSpeter klausler                    [&](const evaluate::IndirectSubscriptIntegerExpr &expr) {
1353a1afd8cSpeter klausler                      return CheckSubscriptExpr(expr);
1363a1afd8cSpeter klausler                    },
1373a1afd8cSpeter klausler                    [&](const evaluate::Triplet &triplet) {
1383a1afd8cSpeter klausler                      return CheckSubscriptExpr(triplet.lower()) &&
1393a1afd8cSpeter klausler                          CheckSubscriptExpr(triplet.upper()) &&
1403a1afd8cSpeter klausler                          CheckSubscriptExpr(triplet.stride());
1413a1afd8cSpeter klausler                    },
1423a1afd8cSpeter klausler                },
14370f1b4b4SAnchu Rajendran                subs.u) &&
14470f1b4b4SAnchu Rajendran         subscriptChecker(subs.u);
1453a1afd8cSpeter klausler   }
1463a1afd8cSpeter klausler   template <typename T>
operator ()(const evaluate::FunctionRef<T> &) const1473a1afd8cSpeter klausler   bool operator()(const evaluate::FunctionRef<T> &) const { // C875
1483a1afd8cSpeter klausler     context_.Say(source_,
1493a1afd8cSpeter klausler         "Data object variable must not be a function reference"_err_en_US);
1503a1afd8cSpeter klausler     return false;
1513a1afd8cSpeter klausler   }
RestrictPointer()15270f1b4b4SAnchu Rajendran   void RestrictPointer() { isPointerAllowed_ = false; }
1533a1afd8cSpeter klausler 
1543a1afd8cSpeter klausler private:
CheckSubscriptExpr(const std::optional<evaluate::IndirectSubscriptIntegerExpr> & x) const1553a1afd8cSpeter klausler   bool CheckSubscriptExpr(
1563a1afd8cSpeter klausler       const std::optional<evaluate::IndirectSubscriptIntegerExpr> &x) const {
1573a1afd8cSpeter klausler     return !x || CheckSubscriptExpr(*x);
1583a1afd8cSpeter klausler   }
CheckSubscriptExpr(const evaluate::IndirectSubscriptIntegerExpr & expr) const1593a1afd8cSpeter klausler   bool CheckSubscriptExpr(
1603a1afd8cSpeter klausler       const evaluate::IndirectSubscriptIntegerExpr &expr) const {
1613a1afd8cSpeter klausler     return CheckSubscriptExpr(expr.value());
1623a1afd8cSpeter klausler   }
CheckSubscriptExpr(const evaluate::Expr<evaluate::SubscriptInteger> & expr) const1633a1afd8cSpeter klausler   bool CheckSubscriptExpr(
1643a1afd8cSpeter klausler       const evaluate::Expr<evaluate::SubscriptInteger> &expr) const {
1653a1afd8cSpeter klausler     if (!evaluate::IsConstantExpr(expr)) { // C875,C881
1663a1afd8cSpeter klausler       context_.Say(
1673a1afd8cSpeter klausler           source_, "Data object must have constant subscripts"_err_en_US);
1683a1afd8cSpeter klausler       return false;
1693a1afd8cSpeter klausler     } else {
1703a1afd8cSpeter klausler       return true;
1713a1afd8cSpeter klausler     }
1723a1afd8cSpeter klausler   }
1733a1afd8cSpeter klausler 
1743a1afd8cSpeter klausler   SemanticsContext &context_;
1753a1afd8cSpeter klausler   parser::CharBlock source_;
1763a1afd8cSpeter klausler   bool hasComponent_{false};
1773a1afd8cSpeter klausler   bool hasSubscript_{false};
17870f1b4b4SAnchu Rajendran   bool isPointerAllowed_{true};
179a20d48d7Speter klausler   bool isFirstSymbol_{true};
1803a1afd8cSpeter klausler };
1813a1afd8cSpeter klausler 
Leave(const parser::DataIDoObject & object)1823a1afd8cSpeter klausler void DataChecker::Leave(const parser::DataIDoObject &object) {
1833a1afd8cSpeter klausler   if (const auto *designator{
1843a1afd8cSpeter klausler           std::get_if<parser::Scalar<common::Indirection<parser::Designator>>>(
1853a1afd8cSpeter klausler               &object.u)}) {
1863a1afd8cSpeter klausler     if (MaybeExpr expr{exprAnalyzer_.Analyze(*designator)}) {
1873a1afd8cSpeter klausler       auto source{designator->thing.value().source};
18870f1b4b4SAnchu Rajendran       if (evaluate::IsConstantExpr(*expr)) { // C878,C879
189a20d48d7Speter klausler         exprAnalyzer_.context().Say(
1903a1afd8cSpeter klausler             source, "Data implied do object must be a variable"_err_en_US);
1913a1afd8cSpeter klausler       } else {
1923a1afd8cSpeter klausler         DataVarChecker checker{exprAnalyzer_.context(), source};
193a20d48d7Speter klausler         if (checker(*expr)) {
194a20d48d7Speter klausler           if (checker.HasComponentWithoutSubscripts()) { // C880
195a20d48d7Speter klausler             exprAnalyzer_.context().Say(source,
1963a1afd8cSpeter klausler                 "Data implied do structure component must be subscripted"_err_en_US);
197a20d48d7Speter klausler           } else {
198a20d48d7Speter klausler             return;
199c1c01212SAnchu Rajendran           }
200c1c01212SAnchu Rajendran         }
201c1c01212SAnchu Rajendran       }
202c1c01212SAnchu Rajendran     }
203a20d48d7Speter klausler     currentSetHasFatalErrors_ = true;
204a20d48d7Speter klausler   }
2054ac617f4Speter klausler }
206c1c01212SAnchu Rajendran 
Leave(const parser::DataStmtObject & dataObject)207c1c01212SAnchu Rajendran void DataChecker::Leave(const parser::DataStmtObject &dataObject) {
208*cd03e96fSPeter Klausler   common::visit(
209*cd03e96fSPeter Klausler       common::visitors{
210a20d48d7Speter klausler           [](const parser::DataImpliedDo &) { // has own Enter()/Leave()
211a20d48d7Speter klausler           },
212a20d48d7Speter klausler           [&](const auto &var) {
213a20d48d7Speter klausler             auto expr{exprAnalyzer_.Analyze(var)};
214a20d48d7Speter klausler             if (!expr ||
215a20d48d7Speter klausler                 !DataVarChecker{exprAnalyzer_.context(),
216a20d48d7Speter klausler                     parser::FindSourceLocation(dataObject)}(*expr)) {
217a20d48d7Speter klausler               currentSetHasFatalErrors_ = true;
218a20d48d7Speter klausler             }
219a20d48d7Speter klausler           },
220a20d48d7Speter klausler       },
221a20d48d7Speter klausler       dataObject.u);
222a20d48d7Speter klausler }
223a20d48d7Speter klausler 
Leave(const parser::DataStmtSet & set)224a20d48d7Speter klausler void DataChecker::Leave(const parser::DataStmtSet &set) {
225a20d48d7Speter klausler   if (!currentSetHasFatalErrors_) {
2264ac617f4Speter klausler     AccumulateDataInitializations(inits_, exprAnalyzer_, set);
227a20d48d7Speter klausler   }
228a20d48d7Speter klausler   currentSetHasFatalErrors_ = false;
229a20d48d7Speter klausler }
230a20d48d7Speter klausler 
231c14cf92bSPeter Klausler // Handle legacy DATA-style initialization, e.g. REAL PI/3.14159/, for
232c14cf92bSPeter Klausler // variables and components (esp. for DEC STRUCTUREs)
LegacyDataInit(const A & decl)233c14cf92bSPeter Klausler template <typename A> void DataChecker::LegacyDataInit(const A &decl) {
234c14cf92bSPeter Klausler   if (const auto &init{
235c14cf92bSPeter Klausler           std::get<std::optional<parser::Initialization>>(decl.t)}) {
236c14cf92bSPeter Klausler     const Symbol *name{std::get<parser::Name>(decl.t).symbol};
237c14cf92bSPeter Klausler     const auto *list{
238c14cf92bSPeter Klausler         std::get_if<std::list<common::Indirection<parser::DataStmtValue>>>(
239c14cf92bSPeter Klausler             &init->u)};
240c14cf92bSPeter Klausler     if (name && list) {
241c14cf92bSPeter Klausler       AccumulateDataInitializations(inits_, exprAnalyzer_, *name, *list);
242c14cf92bSPeter Klausler     }
243c14cf92bSPeter Klausler   }
244c14cf92bSPeter Klausler }
245c14cf92bSPeter Klausler 
Leave(const parser::ComponentDecl & decl)246c14cf92bSPeter Klausler void DataChecker::Leave(const parser::ComponentDecl &decl) {
247c14cf92bSPeter Klausler   LegacyDataInit(decl);
248c14cf92bSPeter Klausler }
249c14cf92bSPeter Klausler 
Leave(const parser::EntityDecl & decl)250c14cf92bSPeter Klausler void DataChecker::Leave(const parser::EntityDecl &decl) {
251c14cf92bSPeter Klausler   LegacyDataInit(decl);
252c14cf92bSPeter Klausler }
253c14cf92bSPeter Klausler 
CompileDataInitializationsIntoInitializers()254a20d48d7Speter klausler void DataChecker::CompileDataInitializationsIntoInitializers() {
2554ac617f4Speter klausler   ConvertToInitializers(inits_, exprAnalyzer_);
256a20d48d7Speter klausler }
257a20d48d7Speter klausler 
2581f879005STim Keith } // namespace Fortran::semantics
259