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