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. 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 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>; 493a1afd8cSpeter klausler DataVarChecker(SemanticsContext &c, parser::CharBlock src) 503a1afd8cSpeter klausler : Base{*this}, context_{c}, source_{src} {} 513a1afd8cSpeter klausler using Base::operator(); 523a1afd8cSpeter klausler bool HasComponentWithoutSubscripts() const { 533a1afd8cSpeter klausler return hasComponent_ && !hasSubscript_; 543a1afd8cSpeter klausler } 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; 61f89d2bebSPeter Klausler // Ordered so that most egregious errors are first 62f89d2bebSPeter Klausler if (const char *whyNot{IsProcedure(symbol) && !IsPointer(symbol) 63f89d2bebSPeter Klausler ? "Procedure" 64f89d2bebSPeter Klausler : isFirstSymbol && IsHostAssociated(symbol, scope) 65f89d2bebSPeter Klausler ? "Host-associated object" 66f89d2bebSPeter Klausler : isFirstSymbol && IsUseAssociated(symbol, scope) 67f89d2bebSPeter Klausler ? "USE-associated object" 68a20d48d7Speter klausler : IsDummy(symbol) ? "Dummy argument" 69a20d48d7Speter klausler : IsFunctionResult(symbol) ? "Function result" 70f89d2bebSPeter Klausler : IsAutomatic(symbol) ? "Automatic variable" 71a20d48d7Speter klausler : IsAllocatable(symbol) ? "Allocatable" 72c4f67ea1SPeter Klausler : IsInitialized(symbol, true /*ignore DATA*/, 7327cf6ba1SPeter Klausler true /*ignore allocatable components*/, 7427cf6ba1SPeter Klausler true /*ignore uninitialized pointer components*/) 75c4f67ea1SPeter Klausler ? "Default-initialized" 767f8da079Speter klausler : symbol.has<AssocEntityDetails>() ? "Construct association" 77f89d2bebSPeter Klausler : isFirstSymbol && IsPointer(symbol) && 78f89d2bebSPeter Klausler (hasComponent_ || hasSubscript_) 7957705df2Speter klausler ? "Target of pointer" 80a20d48d7Speter klausler : nullptr}) { 81a20d48d7Speter klausler context_.Say(source_, 82a20d48d7Speter klausler "%s '%s' must not be initialized in a DATA statement"_err_en_US, 83a20d48d7Speter klausler whyNot, symbol.name()); 84a20d48d7Speter klausler return false; 8563a2987dSPeter Klausler } 8663a2987dSPeter Klausler if (IsProcedurePointer(symbol)) { 871c91d9bdSPeter Klausler if (!context_.IsEnabled(common::LanguageFeature::DataStmtExtensions)) { 881c91d9bdSPeter Klausler context_.Say(source_, 891c91d9bdSPeter Klausler "Procedure pointer '%s' may not appear in a DATA statement"_err_en_US, 901c91d9bdSPeter Klausler symbol.name()); 911c91d9bdSPeter Klausler return false; 92*0f973ac7SPeter Klausler } else { 93*0f973ac7SPeter Klausler context_.Warn(common::LanguageFeature::DataStmtExtensions, source_, 94a53967cdSPeter Klausler "Procedure pointer '%s' in a DATA statement is not standard"_port_en_US, 95a20d48d7Speter klausler symbol.name()); 96a20d48d7Speter klausler } 971c91d9bdSPeter Klausler } 9863a2987dSPeter Klausler if (IsInBlankCommon(symbol)) { 991c91d9bdSPeter Klausler if (!context_.IsEnabled(common::LanguageFeature::DataStmtExtensions)) { 1001c91d9bdSPeter Klausler context_.Say(source_, 1011c91d9bdSPeter Klausler "Blank COMMON object '%s' may not appear in a DATA statement"_err_en_US, 1021c91d9bdSPeter Klausler symbol.name()); 1031c91d9bdSPeter Klausler return false; 104*0f973ac7SPeter Klausler } else { 105*0f973ac7SPeter Klausler context_.Warn(common::LanguageFeature::DataStmtExtensions, source_, 106a53967cdSPeter Klausler "Blank COMMON object '%s' in a DATA statement is not standard"_port_en_US, 10763a2987dSPeter Klausler symbol.name()); 10863a2987dSPeter Klausler } 1091c91d9bdSPeter Klausler } 110a20d48d7Speter klausler return true; 111a20d48d7Speter klausler } 1123a1afd8cSpeter klausler bool operator()(const evaluate::Component &component) { 1133a1afd8cSpeter klausler hasComponent_ = true; 11470f1b4b4SAnchu Rajendran const Symbol &lastSymbol{component.GetLastSymbol()}; 11570f1b4b4SAnchu Rajendran if (isPointerAllowed_) { 11670f1b4b4SAnchu Rajendran if (IsPointer(lastSymbol) && hasSubscript_) { // C877 11770f1b4b4SAnchu Rajendran context_.Say(source_, 11870f1b4b4SAnchu Rajendran "Rightmost data object pointer '%s' must not be subscripted"_err_en_US, 11970f1b4b4SAnchu Rajendran lastSymbol.name().ToString()); 12070f1b4b4SAnchu Rajendran return false; 12170f1b4b4SAnchu Rajendran } 1227e013d60SPeter Klausler auto restorer{common::ScopedSet(isPointerAllowed_, false)}; 1237e013d60SPeter Klausler return (*this)(component.base()) && (*this)(lastSymbol); 1247e013d60SPeter Klausler } else if (IsPointer(lastSymbol)) { // C877 12570f1b4b4SAnchu Rajendran context_.Say(source_, 12670f1b4b4SAnchu Rajendran "Data object must not contain pointer '%s' as a non-rightmost part"_err_en_US, 12770f1b4b4SAnchu Rajendran lastSymbol.name().ToString()); 12870f1b4b4SAnchu Rajendran return false; 1297e013d60SPeter Klausler } else { 13070f1b4b4SAnchu Rajendran return (*this)(component.base()) && (*this)(lastSymbol); 13170f1b4b4SAnchu Rajendran } 1327e013d60SPeter Klausler } 13370f1b4b4SAnchu Rajendran bool operator()(const evaluate::ArrayRef &arrayRef) { 13470f1b4b4SAnchu Rajendran hasSubscript_ = true; 13570f1b4b4SAnchu Rajendran return (*this)(arrayRef.base()) && (*this)(arrayRef.subscript()); 13670f1b4b4SAnchu Rajendran } 13770f1b4b4SAnchu Rajendran bool operator()(const evaluate::Substring &substring) { 13870f1b4b4SAnchu Rajendran hasSubscript_ = true; 13970f1b4b4SAnchu Rajendran return (*this)(substring.parent()) && (*this)(substring.lower()) && 14070f1b4b4SAnchu Rajendran (*this)(substring.upper()); 14170f1b4b4SAnchu Rajendran } 14270f1b4b4SAnchu Rajendran bool operator()(const evaluate::CoarrayRef &) { // C874 14370f1b4b4SAnchu Rajendran context_.Say( 14470f1b4b4SAnchu Rajendran source_, "Data object must not be a coindexed variable"_err_en_US); 14570f1b4b4SAnchu Rajendran return false; 14670f1b4b4SAnchu Rajendran } 1473a1afd8cSpeter klausler bool operator()(const evaluate::Subscript &subs) { 1487e013d60SPeter Klausler auto restorer1{common::ScopedSet(isPointerAllowed_, false)}; 1497e013d60SPeter Klausler auto restorer2{common::ScopedSet(isFunctionAllowed_, true)}; 150cd03e96fSPeter Klausler return common::visit( 1513a1afd8cSpeter klausler common::visitors{ 1523a1afd8cSpeter klausler [&](const evaluate::IndirectSubscriptIntegerExpr &expr) { 1533a1afd8cSpeter klausler return CheckSubscriptExpr(expr); 1543a1afd8cSpeter klausler }, 1553a1afd8cSpeter klausler [&](const evaluate::Triplet &triplet) { 1563a1afd8cSpeter klausler return CheckSubscriptExpr(triplet.lower()) && 1573a1afd8cSpeter klausler CheckSubscriptExpr(triplet.upper()) && 1583a1afd8cSpeter klausler CheckSubscriptExpr(triplet.stride()); 1593a1afd8cSpeter klausler }, 1603a1afd8cSpeter klausler }, 1617e013d60SPeter Klausler subs.u); 1623a1afd8cSpeter klausler } 1633a1afd8cSpeter klausler template <typename T> 1643a1afd8cSpeter klausler bool operator()(const evaluate::FunctionRef<T> &) const { // C875 1657e013d60SPeter Klausler if (isFunctionAllowed_) { 1667e013d60SPeter Klausler // Must have been validated as a constant expression 1677e013d60SPeter Klausler return true; 1687e013d60SPeter Klausler } else { 1693a1afd8cSpeter klausler context_.Say(source_, 1703a1afd8cSpeter klausler "Data object variable must not be a function reference"_err_en_US); 1713a1afd8cSpeter klausler return false; 1723a1afd8cSpeter klausler } 1737e013d60SPeter Klausler } 1743a1afd8cSpeter klausler 1753a1afd8cSpeter klausler private: 1763a1afd8cSpeter klausler bool CheckSubscriptExpr( 1773a1afd8cSpeter klausler const std::optional<evaluate::IndirectSubscriptIntegerExpr> &x) const { 1783a1afd8cSpeter klausler return !x || CheckSubscriptExpr(*x); 1793a1afd8cSpeter klausler } 1803a1afd8cSpeter klausler bool CheckSubscriptExpr( 1813a1afd8cSpeter klausler const evaluate::IndirectSubscriptIntegerExpr &expr) const { 1823a1afd8cSpeter klausler return CheckSubscriptExpr(expr.value()); 1833a1afd8cSpeter klausler } 1843a1afd8cSpeter klausler bool CheckSubscriptExpr( 1853a1afd8cSpeter klausler const evaluate::Expr<evaluate::SubscriptInteger> &expr) const { 1863a1afd8cSpeter klausler if (!evaluate::IsConstantExpr(expr)) { // C875,C881 1873a1afd8cSpeter klausler context_.Say( 1883a1afd8cSpeter klausler source_, "Data object must have constant subscripts"_err_en_US); 1893a1afd8cSpeter klausler return false; 1903a1afd8cSpeter klausler } else { 1913a1afd8cSpeter klausler return true; 1923a1afd8cSpeter klausler } 1933a1afd8cSpeter klausler } 1943a1afd8cSpeter klausler 1953a1afd8cSpeter klausler SemanticsContext &context_; 1963a1afd8cSpeter klausler parser::CharBlock source_; 1973a1afd8cSpeter klausler bool hasComponent_{false}; 1983a1afd8cSpeter klausler bool hasSubscript_{false}; 19970f1b4b4SAnchu Rajendran bool isPointerAllowed_{true}; 200a20d48d7Speter klausler bool isFirstSymbol_{true}; 2017e013d60SPeter Klausler bool isFunctionAllowed_{false}; 2023a1afd8cSpeter klausler }; 2033a1afd8cSpeter klausler 2041eb9948fSPeter Klausler static bool IsValidDataObject(const SomeExpr &expr) { // C878, C879 2051eb9948fSPeter Klausler return !evaluate::IsConstantExpr(expr) && 2061eb9948fSPeter Klausler (evaluate::IsVariable(expr) || evaluate::IsProcedurePointer(expr)); 2071eb9948fSPeter Klausler } 2081eb9948fSPeter Klausler 2093a1afd8cSpeter klausler void DataChecker::Leave(const parser::DataIDoObject &object) { 2103a1afd8cSpeter klausler if (const auto *designator{ 2113a1afd8cSpeter klausler std::get_if<parser::Scalar<common::Indirection<parser::Designator>>>( 2123a1afd8cSpeter klausler &object.u)}) { 2133a1afd8cSpeter klausler if (MaybeExpr expr{exprAnalyzer_.Analyze(*designator)}) { 2143a1afd8cSpeter klausler auto source{designator->thing.value().source}; 2153a1afd8cSpeter klausler DataVarChecker checker{exprAnalyzer_.context(), source}; 216a20d48d7Speter klausler if (checker(*expr)) { 217a20d48d7Speter klausler if (checker.HasComponentWithoutSubscripts()) { // C880 218a20d48d7Speter klausler exprAnalyzer_.context().Say(source, 2193a1afd8cSpeter klausler "Data implied do structure component must be subscripted"_err_en_US); 2201eb9948fSPeter Klausler } else if (!IsValidDataObject(*expr)) { 2211eb9948fSPeter Klausler exprAnalyzer_.context().Say( 2221eb9948fSPeter Klausler source, "Data implied do object must be a variable"_err_en_US); 223a20d48d7Speter klausler } else { 224a20d48d7Speter klausler return; 225c1c01212SAnchu Rajendran } 226c1c01212SAnchu Rajendran } 227c1c01212SAnchu Rajendran } 228a20d48d7Speter klausler currentSetHasFatalErrors_ = true; 229a20d48d7Speter klausler } 2304ac617f4Speter klausler } 231c1c01212SAnchu Rajendran 232c1c01212SAnchu Rajendran void DataChecker::Leave(const parser::DataStmtObject &dataObject) { 233cd03e96fSPeter Klausler common::visit( 234cd03e96fSPeter Klausler common::visitors{ 235a20d48d7Speter klausler [](const parser::DataImpliedDo &) { // has own Enter()/Leave() 236a20d48d7Speter klausler }, 237a20d48d7Speter klausler [&](const auto &var) { 238a20d48d7Speter klausler auto expr{exprAnalyzer_.Analyze(var)}; 2391eb9948fSPeter Klausler auto source{parser::FindSourceLocation(dataObject)}; 240a20d48d7Speter klausler if (!expr || 2411eb9948fSPeter Klausler !DataVarChecker{exprAnalyzer_.context(), source}(*expr)) { 2421eb9948fSPeter Klausler currentSetHasFatalErrors_ = true; 2431eb9948fSPeter Klausler } else if (!IsValidDataObject(*expr)) { 2441eb9948fSPeter Klausler exprAnalyzer_.context().Say( 2451eb9948fSPeter Klausler source, "Data statement object must be a variable"_err_en_US); 246a20d48d7Speter klausler currentSetHasFatalErrors_ = true; 247a20d48d7Speter klausler } 248a20d48d7Speter klausler }, 249a20d48d7Speter klausler }, 250a20d48d7Speter klausler dataObject.u); 251a20d48d7Speter klausler } 252a20d48d7Speter klausler 253a20d48d7Speter klausler void DataChecker::Leave(const parser::DataStmtSet &set) { 254a20d48d7Speter klausler if (!currentSetHasFatalErrors_) { 2554ac617f4Speter klausler AccumulateDataInitializations(inits_, exprAnalyzer_, set); 256a20d48d7Speter klausler } 257a20d48d7Speter klausler currentSetHasFatalErrors_ = false; 258a20d48d7Speter klausler } 259a20d48d7Speter klausler 260c14cf92bSPeter Klausler // Handle legacy DATA-style initialization, e.g. REAL PI/3.14159/, for 261c14cf92bSPeter Klausler // variables and components (esp. for DEC STRUCTUREs) 262c14cf92bSPeter Klausler template <typename A> void DataChecker::LegacyDataInit(const A &decl) { 263c14cf92bSPeter Klausler if (const auto &init{ 264c14cf92bSPeter Klausler std::get<std::optional<parser::Initialization>>(decl.t)}) { 265c14cf92bSPeter Klausler const Symbol *name{std::get<parser::Name>(decl.t).symbol}; 266c14cf92bSPeter Klausler const auto *list{ 267c14cf92bSPeter Klausler std::get_if<std::list<common::Indirection<parser::DataStmtValue>>>( 268c14cf92bSPeter Klausler &init->u)}; 269c14cf92bSPeter Klausler if (name && list) { 270c14cf92bSPeter Klausler AccumulateDataInitializations(inits_, exprAnalyzer_, *name, *list); 271c14cf92bSPeter Klausler } 272c14cf92bSPeter Klausler } 273c14cf92bSPeter Klausler } 274c14cf92bSPeter Klausler 275c14cf92bSPeter Klausler void DataChecker::Leave(const parser::ComponentDecl &decl) { 276c14cf92bSPeter Klausler LegacyDataInit(decl); 277c14cf92bSPeter Klausler } 278c14cf92bSPeter Klausler 279c14cf92bSPeter Klausler void DataChecker::Leave(const parser::EntityDecl &decl) { 280c14cf92bSPeter Klausler LegacyDataInit(decl); 281c14cf92bSPeter Klausler } 282c14cf92bSPeter Klausler 283a20d48d7Speter klausler void DataChecker::CompileDataInitializationsIntoInitializers() { 2844ac617f4Speter klausler ConvertToInitializers(inits_, exprAnalyzer_); 285a20d48d7Speter klausler } 286a20d48d7Speter klausler 2871f879005STim Keith } // namespace Fortran::semantics 288