1 //===-- lib/Semantics/check-data.cpp --------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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
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"
23 namespace Fortran::semantics
{
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();
35 exprAnalyzer_
.AddImpliedDo(name
.source
, kind
);
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
);
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> {
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_
;
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 if (const char *whyNot
{IsAutomatic(symbol
) ? "Automatic variable"
62 : IsDummy(symbol
) ? "Dummy argument"
63 : IsFunctionResult(symbol
) ? "Function result"
64 : IsAllocatable(symbol
) ? "Allocatable"
65 : IsInitialized(symbol
, true /*ignore DATA*/,
66 true /*ignore allocatable components*/)
67 ? "Default-initialized"
68 : IsProcedure(symbol
) && !IsPointer(symbol
) ? "Procedure"
69 // remaining checks don't apply to components
70 : !isFirstSymbol
? nullptr
71 : IsHostAssociated(symbol
, scope
) ? "Host-associated object"
72 : IsUseAssociated(symbol
, scope
) ? "USE-associated object"
73 : symbol
.has
<AssocEntityDetails
>() ? "Construct association"
74 : IsPointer(symbol
) && (hasComponent_
|| hasSubscript_
)
78 "%s '%s' must not be initialized in a DATA statement"_err_en_US
,
79 whyNot
, symbol
.name());
82 if (IsProcedurePointer(symbol
)) {
84 "Procedure pointer '%s' in a DATA statement is not standard"_port_en_US
,
87 if (IsInBlankCommon(symbol
)) {
89 "Blank COMMON object '%s' in a DATA statement is not standard"_port_en_US
,
94 bool operator()(const evaluate::Component
&component
) {
96 const Symbol
&lastSymbol
{component
.GetLastSymbol()};
97 if (isPointerAllowed_
) {
98 if (IsPointer(lastSymbol
) && hasSubscript_
) { // C877
100 "Rightmost data object pointer '%s' must not be subscripted"_err_en_US
,
101 lastSymbol
.name().ToString());
106 if (IsPointer(lastSymbol
)) { // C877
107 context_
.Say(source_
,
108 "Data object must not contain pointer '%s' as a non-rightmost part"_err_en_US
,
109 lastSymbol
.name().ToString());
113 return (*this)(component
.base()) && (*this)(lastSymbol
);
115 bool operator()(const evaluate::ArrayRef
&arrayRef
) {
116 hasSubscript_
= true;
117 return (*this)(arrayRef
.base()) && (*this)(arrayRef
.subscript());
119 bool operator()(const evaluate::Substring
&substring
) {
120 hasSubscript_
= true;
121 return (*this)(substring
.parent()) && (*this)(substring
.lower()) &&
122 (*this)(substring
.upper());
124 bool operator()(const evaluate::CoarrayRef
&) { // C874
126 source_
, "Data object must not be a coindexed variable"_err_en_US
);
129 bool operator()(const evaluate::Subscript
&subs
) {
130 DataVarChecker subscriptChecker
{context_
, source_
};
131 subscriptChecker
.RestrictPointer();
132 return common::visit(
134 [&](const evaluate::IndirectSubscriptIntegerExpr
&expr
) {
135 return CheckSubscriptExpr(expr
);
137 [&](const evaluate::Triplet
&triplet
) {
138 return CheckSubscriptExpr(triplet
.lower()) &&
139 CheckSubscriptExpr(triplet
.upper()) &&
140 CheckSubscriptExpr(triplet
.stride());
144 subscriptChecker(subs
.u
);
146 template <typename T
>
147 bool operator()(const evaluate::FunctionRef
<T
> &) const { // C875
148 context_
.Say(source_
,
149 "Data object variable must not be a function reference"_err_en_US
);
152 void RestrictPointer() { isPointerAllowed_
= false; }
155 bool CheckSubscriptExpr(
156 const std::optional
<evaluate::IndirectSubscriptIntegerExpr
> &x
) const {
157 return !x
|| CheckSubscriptExpr(*x
);
159 bool CheckSubscriptExpr(
160 const evaluate::IndirectSubscriptIntegerExpr
&expr
) const {
161 return CheckSubscriptExpr(expr
.value());
163 bool CheckSubscriptExpr(
164 const evaluate::Expr
<evaluate::SubscriptInteger
> &expr
) const {
165 if (!evaluate::IsConstantExpr(expr
)) { // C875,C881
167 source_
, "Data object must have constant subscripts"_err_en_US
);
174 SemanticsContext
&context_
;
175 parser::CharBlock source_
;
176 bool hasComponent_
{false};
177 bool hasSubscript_
{false};
178 bool isPointerAllowed_
{true};
179 bool isFirstSymbol_
{true};
182 void DataChecker::Leave(const parser::DataIDoObject
&object
) {
183 if (const auto *designator
{
184 std::get_if
<parser::Scalar
<common::Indirection
<parser::Designator
>>>(
186 if (MaybeExpr expr
{exprAnalyzer_
.Analyze(*designator
)}) {
187 auto source
{designator
->thing
.value().source
};
188 if (evaluate::IsConstantExpr(*expr
)) { // C878,C879
189 exprAnalyzer_
.context().Say(
190 source
, "Data implied do object must be a variable"_err_en_US
);
192 DataVarChecker checker
{exprAnalyzer_
.context(), source
};
193 if (checker(*expr
)) {
194 if (checker
.HasComponentWithoutSubscripts()) { // C880
195 exprAnalyzer_
.context().Say(source
,
196 "Data implied do structure component must be subscripted"_err_en_US
);
203 currentSetHasFatalErrors_
= true;
207 void DataChecker::Leave(const parser::DataStmtObject
&dataObject
) {
210 [](const parser::DataImpliedDo
&) { // has own Enter()/Leave()
212 [&](const auto &var
) {
213 auto expr
{exprAnalyzer_
.Analyze(var
)};
215 !DataVarChecker
{exprAnalyzer_
.context(),
216 parser::FindSourceLocation(dataObject
)}(*expr
)) {
217 currentSetHasFatalErrors_
= true;
224 void DataChecker::Leave(const parser::DataStmtSet
&set
) {
225 if (!currentSetHasFatalErrors_
) {
226 AccumulateDataInitializations(inits_
, exprAnalyzer_
, set
);
228 currentSetHasFatalErrors_
= false;
231 // Handle legacy DATA-style initialization, e.g. REAL PI/3.14159/, for
232 // variables and components (esp. for DEC STRUCTUREs)
233 template <typename A
> void DataChecker::LegacyDataInit(const A
&decl
) {
234 if (const auto &init
{
235 std::get
<std::optional
<parser::Initialization
>>(decl
.t
)}) {
236 const Symbol
*name
{std::get
<parser::Name
>(decl
.t
).symbol
};
238 std::get_if
<std::list
<common::Indirection
<parser::DataStmtValue
>>>(
241 AccumulateDataInitializations(inits_
, exprAnalyzer_
, *name
, *list
);
246 void DataChecker::Leave(const parser::ComponentDecl
&decl
) {
247 LegacyDataInit(decl
);
250 void DataChecker::Leave(const parser::EntityDecl
&decl
) {
251 LegacyDataInit(decl
);
254 void DataChecker::CompileDataInitializationsIntoInitializers() {
255 ConvertToInitializers(inits_
, exprAnalyzer_
);
258 } // namespace Fortran::semantics