xref: /llvm-project/flang/lib/Semantics/assignment.cpp (revision 962863d988195917b7d2ccfb83a3a166e01ffc77)
1 //===-- lib/Semantics/assignment.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 #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"
24 #include <optional>
25 #include <set>
26 #include <string>
27 #include <type_traits>
28 
29 using namespace Fortran::parser::literals;
30 
31 namespace Fortran::semantics {
32 
33 class AssignmentContext {
34 public:
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; }
39 
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 &);
45 
46 private:
47   bool CheckForPureContext(const SomeExpr &rhs, parser::CharBlock rhsSource,
48       bool isPointerAssignment);
49   void CheckShape(parser::CharBlock, const SomeExpr *);
50   template <typename... A>
51   parser::Message *Say(parser::CharBlock at, A &&...args) {
52     return &context_.Say(at, std::forward<A>(args)...);
53   }
54   evaluate::FoldingContext &foldingContext() {
55     return context_.foldingContext();
56   }
57 
58   SemanticsContext &context_;
59   int whereDepth_{0}; // number of WHEREs currently nested in
60   // shape of masks in LHS of assignments in current WHERE:
61   std::vector<std::optional<std::int64_t>> whereExtents_;
62 };
63 
64 void AssignmentContext::Analyze(const parser::AssignmentStmt &stmt) {
65   if (const evaluate::Assignment * assignment{GetAssignment(stmt)}) {
66     const SomeExpr &lhs{assignment->lhs};
67     const SomeExpr &rhs{assignment->rhs};
68     auto lhsLoc{std::get<parser::Variable>(stmt.t).GetSource()};
69     const Scope &scope{context_.FindScope(lhsLoc)};
70     if (auto whyNot{WhyNotDefinable(lhsLoc, scope,
71             DefinabilityFlags{DefinabilityFlag::VectorSubscriptIsOk}, lhs)}) {
72       if (auto *msg{Say(lhsLoc,
73               "Left-hand side of assignment is not definable"_err_en_US)}) {
74         msg->Attach(std::move(*whyNot));
75       }
76     }
77     auto rhsLoc{std::get<parser::Expr>(stmt.t).source};
78     CheckForPureContext(rhs, rhsLoc, false);
79     if (whereDepth_ > 0) {
80       CheckShape(lhsLoc, &lhs);
81     }
82   }
83 }
84 
85 void AssignmentContext::Analyze(const parser::PointerAssignmentStmt &stmt) {
86   CHECK(whereDepth_ == 0);
87   if (const evaluate::Assignment * assignment{GetAssignment(stmt)}) {
88     const SomeExpr &rhs{assignment->rhs};
89     CheckForPureContext(rhs, std::get<parser::Expr>(stmt.t).source, true);
90     parser::CharBlock at{context_.location().value()};
91     auto restorer{foldingContext().messages().SetLocation(at)};
92     const Scope &scope{context_.FindScope(at)};
93     CheckPointerAssignment(foldingContext(), *assignment, scope);
94   }
95 }
96 
97 static std::optional<std::string> GetPointerComponentDesignatorName(
98     const SomeExpr &expr) {
99   if (const auto *derived{
100           evaluate::GetDerivedTypeSpec(evaluate::DynamicType::From(expr))}) {
101     PotentialAndPointerComponentIterator potentials{*derived};
102     if (auto pointer{
103             std::find_if(potentials.begin(), potentials.end(), IsPointer)}) {
104       return pointer.BuildResultDesignatorName();
105     }
106   }
107   return std::nullopt;
108 }
109 
110 // Checks C1594(5,6); false if check fails
111 bool CheckCopyabilityInPureScope(parser::ContextualMessages &messages,
112     const SomeExpr &expr, const Scope &scope) {
113   if (const Symbol * base{GetFirstSymbol(expr)}) {
114     if (const char *why{
115             WhyBaseObjectIsSuspicious(base->GetUltimate(), scope)}) {
116       if (auto pointer{GetPointerComponentDesignatorName(expr)}) {
117         evaluate::SayWithDeclaration(messages, *base,
118             "A pure subprogram may not copy the value of '%s' because it is %s"
119             " and has the POINTER potential subobject component '%s'"_err_en_US,
120             base->name(), why, *pointer);
121         return false;
122       }
123     }
124   }
125   return true;
126 }
127 
128 bool AssignmentContext::CheckForPureContext(const SomeExpr &rhs,
129     parser::CharBlock rhsSource, bool isPointerAssignment) {
130   const Scope &scope{context_.FindScope(rhsSource)};
131   if (!FindPureProcedureContaining(scope)) {
132     return true;
133   }
134   parser::ContextualMessages messages{
135       context_.location().value(), &context_.messages()};
136   if (isPointerAssignment) {
137     if (const Symbol * base{GetFirstSymbol(rhs)}) {
138       if (const char *why{WhyBaseObjectIsSuspicious(
139               base->GetUltimate(), scope)}) { // C1594(3)
140         evaluate::SayWithDeclaration(messages, *base,
141             "A pure subprogram may not use '%s' as the target of pointer assignment because it is %s"_err_en_US,
142             base->name(), why);
143         return false;
144       }
145     }
146   } else {
147     return CheckCopyabilityInPureScope(messages, rhs, scope);
148   }
149   return true;
150 }
151 
152 // 10.2.3.1(2) The masks and LHS of assignments must be arrays of the same shape
153 void AssignmentContext::CheckShape(parser::CharBlock at, const SomeExpr *expr) {
154   if (auto shape{evaluate::GetShape(foldingContext(), expr)}) {
155     std::size_t size{shape->size()};
156     if (size == 0) {
157       Say(at, "The mask or variable must not be scalar"_err_en_US);
158     }
159     if (whereDepth_ == 0) {
160       whereExtents_.resize(size);
161     } else if (whereExtents_.size() != size) {
162       Say(at,
163           "Must have rank %zd to match prior mask or assignment of"
164           " WHERE construct"_err_en_US,
165           whereExtents_.size());
166       return;
167     }
168     for (std::size_t i{0}; i < size; ++i) {
169       if (std::optional<std::int64_t> extent{evaluate::ToInt64((*shape)[i])}) {
170         if (!whereExtents_[i]) {
171           whereExtents_[i] = *extent;
172         } else if (*whereExtents_[i] != *extent) {
173           Say(at,
174               "Dimension %d must have extent %jd to match prior mask or"
175               " assignment of WHERE construct"_err_en_US,
176               i + 1, *whereExtents_[i]);
177         }
178       }
179     }
180   }
181 }
182 
183 template <typename A> void AssignmentContext::PushWhereContext(const A &x) {
184   const auto &expr{std::get<parser::LogicalExpr>(x.t)};
185   CheckShape(expr.thing.value().source, GetExpr(context_, expr));
186   ++whereDepth_;
187 }
188 
189 void AssignmentContext::PopWhereContext() {
190   --whereDepth_;
191   if (whereDepth_ == 0) {
192     whereExtents_.clear();
193   }
194 }
195 
196 AssignmentChecker::~AssignmentChecker() {}
197 
198 AssignmentChecker::AssignmentChecker(SemanticsContext &context)
199     : context_{new AssignmentContext{context}} {}
200 void AssignmentChecker::Enter(const parser::AssignmentStmt &x) {
201   context_.value().Analyze(x);
202 }
203 void AssignmentChecker::Enter(const parser::PointerAssignmentStmt &x) {
204   context_.value().Analyze(x);
205 }
206 void AssignmentChecker::Enter(const parser::WhereStmt &x) {
207   context_.value().PushWhereContext(x);
208 }
209 void AssignmentChecker::Leave(const parser::WhereStmt &) {
210   context_.value().PopWhereContext();
211 }
212 void AssignmentChecker::Enter(const parser::WhereConstructStmt &x) {
213   context_.value().PushWhereContext(x);
214 }
215 void AssignmentChecker::Leave(const parser::EndWhereStmt &) {
216   context_.value().PopWhereContext();
217 }
218 void AssignmentChecker::Enter(const parser::MaskedElsewhereStmt &x) {
219   context_.value().PushWhereContext(x);
220 }
221 void AssignmentChecker::Leave(const parser::MaskedElsewhereStmt &) {
222   context_.value().PopWhereContext();
223 }
224 
225 } // namespace Fortran::semantics
226 template class Fortran::common::Indirection<
227     Fortran::semantics::AssignmentContext>;
228