xref: /llvm-project/flang/lib/Semantics/check-data.cpp (revision 0f973ac783aa100cfbce1cd2c6e8a3a8f648fae7)
1 //===-- lib/Semantics/check-data.cpp --------------------------------------===//
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 // DATA statement semantic analysis.
10 // - Applies static semantic checks to the variables in each data-stmt-set with
11 //   class DataVarChecker;
12 // - Invokes conversion of DATA statement values to static initializers
13 
14 #include "check-data.h"
15 #include "data-to-inits.h"
16 #include "flang/Evaluate/traverse.h"
17 #include "flang/Parser/parse-tree.h"
18 #include "flang/Parser/tools.h"
19 #include "flang/Semantics/tools.h"
20 #include <algorithm>
21 #include <vector>
22 
23 namespace Fortran::semantics {
24 
25 // Ensures that references to an implied DO loop control variable are
26 // represented as such in the "body" of the implied DO loop.
27 void DataChecker::Enter(const parser::DataImpliedDo &x) {
28   auto name{std::get<parser::DataImpliedDo::Bounds>(x.t).name.thing.thing};
29   int kind{evaluate::ResultType<evaluate::ImpliedDoIndex>::kind};
30   if (const auto dynamicType{evaluate::DynamicType::From(*name.symbol)}) {
31     if (dynamicType->category() == TypeCategory::Integer) {
32       kind = dynamicType->kind();
33     }
34   }
35   exprAnalyzer_.AddImpliedDo(name.source, kind);
36 }
37 
38 void DataChecker::Leave(const parser::DataImpliedDo &x) {
39   auto name{std::get<parser::DataImpliedDo::Bounds>(x.t).name.thing.thing};
40   exprAnalyzer_.RemoveImpliedDo(name.source);
41 }
42 
43 // DataVarChecker applies static checks once to each variable that appears
44 // in a data-stmt-set.  These checks are independent of the values that
45 // correspond to the variables.
46 class DataVarChecker : public evaluate::AllTraverse<DataVarChecker, true> {
47 public:
48   using Base = evaluate::AllTraverse<DataVarChecker, true>;
49   DataVarChecker(SemanticsContext &c, parser::CharBlock src)
50       : Base{*this}, context_{c}, source_{src} {}
51   using Base::operator();
52   bool HasComponentWithoutSubscripts() const {
53     return hasComponent_ && !hasSubscript_;
54   }
55   bool operator()(const Symbol &symbol) { // C876
56     // 8.6.7p(2) - precludes non-pointers of derived types with
57     // default component values
58     const Scope &scope{context_.FindScope(source_)};
59     bool isFirstSymbol{isFirstSymbol_};
60     isFirstSymbol_ = false;
61     // Ordered so that most egregious errors are first
62     if (const char *whyNot{IsProcedure(symbol) && !IsPointer(symbol)
63                 ? "Procedure"
64                 : isFirstSymbol && IsHostAssociated(symbol, scope)
65                 ? "Host-associated object"
66                 : isFirstSymbol && IsUseAssociated(symbol, scope)
67                 ? "USE-associated object"
68                 : IsDummy(symbol)          ? "Dummy argument"
69                 : IsFunctionResult(symbol) ? "Function result"
70                 : IsAutomatic(symbol)      ? "Automatic variable"
71                 : IsAllocatable(symbol)    ? "Allocatable"
72                 : IsInitialized(symbol, true /*ignore DATA*/,
73                       true /*ignore allocatable components*/,
74                       true /*ignore uninitialized pointer components*/)
75                 ? "Default-initialized"
76                 : symbol.has<AssocEntityDetails>() ? "Construct association"
77                 : isFirstSymbol && IsPointer(symbol) &&
78                     (hasComponent_ || hasSubscript_)
79                 ? "Target of pointer"
80                 : nullptr}) {
81       context_.Say(source_,
82           "%s '%s' must not be initialized in a DATA statement"_err_en_US,
83           whyNot, symbol.name());
84       return false;
85     }
86     if (IsProcedurePointer(symbol)) {
87       if (!context_.IsEnabled(common::LanguageFeature::DataStmtExtensions)) {
88         context_.Say(source_,
89             "Procedure pointer '%s' may not appear in a DATA statement"_err_en_US,
90             symbol.name());
91         return false;
92       } else {
93         context_.Warn(common::LanguageFeature::DataStmtExtensions, source_,
94             "Procedure pointer '%s' in a DATA statement is not standard"_port_en_US,
95             symbol.name());
96       }
97     }
98     if (IsInBlankCommon(symbol)) {
99       if (!context_.IsEnabled(common::LanguageFeature::DataStmtExtensions)) {
100         context_.Say(source_,
101             "Blank COMMON object '%s' may not appear in a DATA statement"_err_en_US,
102             symbol.name());
103         return false;
104       } else {
105         context_.Warn(common::LanguageFeature::DataStmtExtensions, source_,
106             "Blank COMMON object '%s' in a DATA statement is not standard"_port_en_US,
107             symbol.name());
108       }
109     }
110     return true;
111   }
112   bool operator()(const evaluate::Component &component) {
113     hasComponent_ = true;
114     const Symbol &lastSymbol{component.GetLastSymbol()};
115     if (isPointerAllowed_) {
116       if (IsPointer(lastSymbol) && hasSubscript_) { // C877
117         context_.Say(source_,
118             "Rightmost data object pointer '%s' must not be subscripted"_err_en_US,
119             lastSymbol.name().ToString());
120         return false;
121       }
122       auto restorer{common::ScopedSet(isPointerAllowed_, false)};
123       return (*this)(component.base()) && (*this)(lastSymbol);
124     } else if (IsPointer(lastSymbol)) { // C877
125       context_.Say(source_,
126           "Data object must not contain pointer '%s' as a non-rightmost part"_err_en_US,
127           lastSymbol.name().ToString());
128       return false;
129     } else {
130       return (*this)(component.base()) && (*this)(lastSymbol);
131     }
132   }
133   bool operator()(const evaluate::ArrayRef &arrayRef) {
134     hasSubscript_ = true;
135     return (*this)(arrayRef.base()) && (*this)(arrayRef.subscript());
136   }
137   bool operator()(const evaluate::Substring &substring) {
138     hasSubscript_ = true;
139     return (*this)(substring.parent()) && (*this)(substring.lower()) &&
140         (*this)(substring.upper());
141   }
142   bool operator()(const evaluate::CoarrayRef &) { // C874
143     context_.Say(
144         source_, "Data object must not be a coindexed variable"_err_en_US);
145     return false;
146   }
147   bool operator()(const evaluate::Subscript &subs) {
148     auto restorer1{common::ScopedSet(isPointerAllowed_, false)};
149     auto restorer2{common::ScopedSet(isFunctionAllowed_, true)};
150     return common::visit(
151         common::visitors{
152             [&](const evaluate::IndirectSubscriptIntegerExpr &expr) {
153               return CheckSubscriptExpr(expr);
154             },
155             [&](const evaluate::Triplet &triplet) {
156               return CheckSubscriptExpr(triplet.lower()) &&
157                   CheckSubscriptExpr(triplet.upper()) &&
158                   CheckSubscriptExpr(triplet.stride());
159             },
160         },
161         subs.u);
162   }
163   template <typename T>
164   bool operator()(const evaluate::FunctionRef<T> &) const { // C875
165     if (isFunctionAllowed_) {
166       // Must have been validated as a constant expression
167       return true;
168     } else {
169       context_.Say(source_,
170           "Data object variable must not be a function reference"_err_en_US);
171       return false;
172     }
173   }
174 
175 private:
176   bool CheckSubscriptExpr(
177       const std::optional<evaluate::IndirectSubscriptIntegerExpr> &x) const {
178     return !x || CheckSubscriptExpr(*x);
179   }
180   bool CheckSubscriptExpr(
181       const evaluate::IndirectSubscriptIntegerExpr &expr) const {
182     return CheckSubscriptExpr(expr.value());
183   }
184   bool CheckSubscriptExpr(
185       const evaluate::Expr<evaluate::SubscriptInteger> &expr) const {
186     if (!evaluate::IsConstantExpr(expr)) { // C875,C881
187       context_.Say(
188           source_, "Data object must have constant subscripts"_err_en_US);
189       return false;
190     } else {
191       return true;
192     }
193   }
194 
195   SemanticsContext &context_;
196   parser::CharBlock source_;
197   bool hasComponent_{false};
198   bool hasSubscript_{false};
199   bool isPointerAllowed_{true};
200   bool isFirstSymbol_{true};
201   bool isFunctionAllowed_{false};
202 };
203 
204 static bool IsValidDataObject(const SomeExpr &expr) { // C878, C879
205   return !evaluate::IsConstantExpr(expr) &&
206       (evaluate::IsVariable(expr) || evaluate::IsProcedurePointer(expr));
207 }
208 
209 void DataChecker::Leave(const parser::DataIDoObject &object) {
210   if (const auto *designator{
211           std::get_if<parser::Scalar<common::Indirection<parser::Designator>>>(
212               &object.u)}) {
213     if (MaybeExpr expr{exprAnalyzer_.Analyze(*designator)}) {
214       auto source{designator->thing.value().source};
215       DataVarChecker checker{exprAnalyzer_.context(), source};
216       if (checker(*expr)) {
217         if (checker.HasComponentWithoutSubscripts()) { // C880
218           exprAnalyzer_.context().Say(source,
219               "Data implied do structure component must be subscripted"_err_en_US);
220         } else if (!IsValidDataObject(*expr)) {
221           exprAnalyzer_.context().Say(
222               source, "Data implied do object must be a variable"_err_en_US);
223         } else {
224           return;
225         }
226       }
227     }
228     currentSetHasFatalErrors_ = true;
229   }
230 }
231 
232 void DataChecker::Leave(const parser::DataStmtObject &dataObject) {
233   common::visit(
234       common::visitors{
235           [](const parser::DataImpliedDo &) { // has own Enter()/Leave()
236           },
237           [&](const auto &var) {
238             auto expr{exprAnalyzer_.Analyze(var)};
239             auto source{parser::FindSourceLocation(dataObject)};
240             if (!expr ||
241                 !DataVarChecker{exprAnalyzer_.context(), source}(*expr)) {
242               currentSetHasFatalErrors_ = true;
243             } else if (!IsValidDataObject(*expr)) {
244               exprAnalyzer_.context().Say(
245                   source, "Data statement object must be a variable"_err_en_US);
246               currentSetHasFatalErrors_ = true;
247             }
248           },
249       },
250       dataObject.u);
251 }
252 
253 void DataChecker::Leave(const parser::DataStmtSet &set) {
254   if (!currentSetHasFatalErrors_) {
255     AccumulateDataInitializations(inits_, exprAnalyzer_, set);
256   }
257   currentSetHasFatalErrors_ = false;
258 }
259 
260 // Handle legacy DATA-style initialization, e.g. REAL PI/3.14159/, for
261 // variables and components (esp. for DEC STRUCTUREs)
262 template <typename A> void DataChecker::LegacyDataInit(const A &decl) {
263   if (const auto &init{
264           std::get<std::optional<parser::Initialization>>(decl.t)}) {
265     const Symbol *name{std::get<parser::Name>(decl.t).symbol};
266     const auto *list{
267         std::get_if<std::list<common::Indirection<parser::DataStmtValue>>>(
268             &init->u)};
269     if (name && list) {
270       AccumulateDataInitializations(inits_, exprAnalyzer_, *name, *list);
271     }
272   }
273 }
274 
275 void DataChecker::Leave(const parser::ComponentDecl &decl) {
276   LegacyDataInit(decl);
277 }
278 
279 void DataChecker::Leave(const parser::EntityDecl &decl) {
280   LegacyDataInit(decl);
281 }
282 
283 void DataChecker::CompileDataInitializationsIntoInitializers() {
284   ConvertToInitializers(inits_, exprAnalyzer_);
285 }
286 
287 } // namespace Fortran::semantics
288