1 //===-- lib/Semantics/assignment.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 #include "assignment.h"
10 #include "definable.h"
11 #include "pointer-assignment.h"
12 #include "flang/Common/idioms.h"
13 #include "flang/Common/restorer.h"
14 #include "flang/Evaluate/characteristics.h"
15 #include "flang/Evaluate/expression.h"
16 #include "flang/Evaluate/fold.h"
17 #include "flang/Evaluate/tools.h"
18 #include "flang/Parser/message.h"
19 #include "flang/Parser/parse-tree-visitor.h"
20 #include "flang/Parser/parse-tree.h"
21 #include "flang/Semantics/expression.h"
22 #include "flang/Semantics/symbol.h"
23 #include "flang/Semantics/tools.h"
27 #include <type_traits>
29 using namespace Fortran::parser::literals
;
31 namespace Fortran::semantics
{
33 class AssignmentContext
{
35 explicit AssignmentContext(SemanticsContext
&context
) : context_
{context
} {}
36 AssignmentContext(AssignmentContext
&&) = default;
37 AssignmentContext(const AssignmentContext
&) = delete;
38 bool operator==(const AssignmentContext
&x
) const { return this == &x
; }
40 template <typename A
> void PushWhereContext(const A
&);
41 void PopWhereContext();
42 void Analyze(const parser::AssignmentStmt
&);
43 void Analyze(const parser::PointerAssignmentStmt
&);
44 void Analyze(const parser::ConcurrentControl
&);
47 bool CheckForPureContext(const SomeExpr
&rhs
, parser::CharBlock rhsSource
);
48 void CheckShape(parser::CharBlock
, const SomeExpr
*);
49 template <typename
... A
>
50 parser::Message
*Say(parser::CharBlock at
, A
&&...args
) {
51 return &context_
.Say(at
, std::forward
<A
>(args
)...);
53 evaluate::FoldingContext
&foldingContext() {
54 return context_
.foldingContext();
57 SemanticsContext
&context_
;
58 int whereDepth_
{0}; // number of WHEREs currently nested in
59 // shape of masks in LHS of assignments in current WHERE:
60 std::vector
<std::optional
<std::int64_t>> whereExtents_
;
63 void AssignmentContext::Analyze(const parser::AssignmentStmt
&stmt
) {
64 if (const evaluate::Assignment
* assignment
{GetAssignment(stmt
)}) {
65 const SomeExpr
&lhs
{assignment
->lhs
};
66 const SomeExpr
&rhs
{assignment
->rhs
};
67 auto lhsLoc
{std::get
<parser::Variable
>(stmt
.t
).GetSource()};
68 const Scope
&scope
{context_
.FindScope(lhsLoc
)};
69 if (auto whyNot
{WhyNotDefinable(lhsLoc
, scope
,
70 DefinabilityFlags
{DefinabilityFlag::VectorSubscriptIsOk
}, lhs
)}) {
71 if (whyNot
->IsFatal()) {
72 if (auto *msg
{Say(lhsLoc
,
73 "Left-hand side of assignment is not definable"_err_en_US
)}) {
75 std::move(whyNot
->set_severity(parser::Severity::Because
)));
78 context_
.Say(std::move(*whyNot
));
81 auto rhsLoc
{std::get
<parser::Expr
>(stmt
.t
).source
};
82 if (std::holds_alternative
<evaluate::ProcedureRef
>(assignment
->u
)) {
83 // it's a defined ASSIGNMENT(=)
85 CheckForPureContext(rhs
, rhsLoc
);
87 if (whereDepth_
> 0) {
88 CheckShape(lhsLoc
, &lhs
);
93 void AssignmentContext::Analyze(const parser::PointerAssignmentStmt
&stmt
) {
94 CHECK(whereDepth_
== 0);
95 if (const evaluate::Assignment
* assignment
{GetAssignment(stmt
)}) {
96 parser::CharBlock at
{context_
.location().value()};
97 auto restorer
{foldingContext().messages().SetLocation(at
)};
98 CheckPointerAssignment(context_
, *assignment
, context_
.FindScope(at
));
102 static std::optional
<std::string
> GetPointerComponentDesignatorName(
103 const SomeExpr
&expr
) {
104 if (const auto *derived
{
105 evaluate::GetDerivedTypeSpec(evaluate::DynamicType::From(expr
))}) {
106 PotentialAndPointerComponentIterator potentials
{*derived
};
108 std::find_if(potentials
.begin(), potentials
.end(), IsPointer
)}) {
109 return pointer
.BuildResultDesignatorName();
115 // Checks C1594(5,6); false if check fails
116 bool CheckCopyabilityInPureScope(parser::ContextualMessages
&messages
,
117 const SomeExpr
&expr
, const Scope
&scope
) {
118 if (const Symbol
* base
{GetFirstSymbol(expr
)}) {
120 WhyBaseObjectIsSuspicious(base
->GetUltimate(), scope
)}) {
121 if (auto pointer
{GetPointerComponentDesignatorName(expr
)}) {
122 evaluate::SayWithDeclaration(messages
, *base
,
123 "A pure subprogram may not copy the value of '%s' because it is %s"
124 " and has the POINTER potential subobject component '%s'"_err_en_US
,
125 base
->name(), why
, *pointer
);
133 bool AssignmentContext::CheckForPureContext(
134 const SomeExpr
&rhs
, parser::CharBlock rhsSource
) {
135 const Scope
&scope
{context_
.FindScope(rhsSource
)};
136 if (FindPureProcedureContaining(scope
)) {
137 parser::ContextualMessages messages
{
138 context_
.location().value(), &context_
.messages()};
139 return CheckCopyabilityInPureScope(messages
, rhs
, scope
);
145 // 10.2.3.1(2) The masks and LHS of assignments must be arrays of the same shape
146 void AssignmentContext::CheckShape(parser::CharBlock at
, const SomeExpr
*expr
) {
147 if (auto shape
{evaluate::GetShape(foldingContext(), expr
)}) {
148 std::size_t size
{shape
->size()};
150 Say(at
, "The mask or variable must not be scalar"_err_en_US
);
152 if (whereDepth_
== 0) {
153 whereExtents_
.resize(size
);
154 } else if (whereExtents_
.size() != size
) {
156 "Must have rank %zd to match prior mask or assignment of"
157 " WHERE construct"_err_en_US
,
158 whereExtents_
.size());
161 for (std::size_t i
{0}; i
< size
; ++i
) {
162 if (std::optional
<std::int64_t> extent
{evaluate::ToInt64((*shape
)[i
])}) {
163 if (!whereExtents_
[i
]) {
164 whereExtents_
[i
] = *extent
;
165 } else if (*whereExtents_
[i
] != *extent
) {
167 "Dimension %d must have extent %jd to match prior mask or"
168 " assignment of WHERE construct"_err_en_US
,
169 i
+ 1, *whereExtents_
[i
]);
176 template <typename A
> void AssignmentContext::PushWhereContext(const A
&x
) {
177 const auto &expr
{std::get
<parser::LogicalExpr
>(x
.t
)};
178 CheckShape(expr
.thing
.value().source
, GetExpr(context_
, expr
));
182 void AssignmentContext::PopWhereContext() {
184 if (whereDepth_
== 0) {
185 whereExtents_
.clear();
189 AssignmentChecker::~AssignmentChecker() {}
191 AssignmentChecker::AssignmentChecker(SemanticsContext
&context
)
192 : context_
{new AssignmentContext
{context
}} {}
193 void AssignmentChecker::Enter(const parser::AssignmentStmt
&x
) {
194 context_
.value().Analyze(x
);
196 void AssignmentChecker::Enter(const parser::PointerAssignmentStmt
&x
) {
197 context_
.value().Analyze(x
);
199 void AssignmentChecker::Enter(const parser::WhereStmt
&x
) {
200 context_
.value().PushWhereContext(x
);
202 void AssignmentChecker::Leave(const parser::WhereStmt
&) {
203 context_
.value().PopWhereContext();
205 void AssignmentChecker::Enter(const parser::WhereConstructStmt
&x
) {
206 context_
.value().PushWhereContext(x
);
208 void AssignmentChecker::Leave(const parser::EndWhereStmt
&) {
209 context_
.value().PopWhereContext();
211 void AssignmentChecker::Enter(const parser::MaskedElsewhereStmt
&x
) {
212 context_
.value().PushWhereContext(x
);
214 void AssignmentChecker::Leave(const parser::MaskedElsewhereStmt
&) {
215 context_
.value().PopWhereContext();
218 } // namespace Fortran::semantics
219 template class Fortran::common::Indirection
<
220 Fortran::semantics::AssignmentContext
>;