xref: /llvm-project/flang/lib/Semantics/assignment.cpp (revision 64ab3302d5a130c00b66a6957b2e7f0c9b9c537d)
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 "pointer-assignment.h"
11 #include "flang/Common/idioms.h"
12 #include "flang/Common/restorer.h"
13 #include "flang/Evaluate/characteristics.h"
14 #include "flang/Evaluate/expression.h"
15 #include "flang/Evaluate/fold.h"
16 #include "flang/Evaluate/tools.h"
17 #include "flang/Parser/message.h"
18 #include "flang/Parser/parse-tree-visitor.h"
19 #include "flang/Parser/parse-tree.h"
20 #include "flang/Semantics/expression.h"
21 #include "flang/Semantics/symbol.h"
22 #include "flang/Semantics/tools.h"
23 #include <optional>
24 #include <set>
25 #include <string>
26 #include <type_traits>
27 
28 using namespace Fortran::parser::literals;
29 
30 namespace Fortran::semantics {
31 
32 class AssignmentContext {
33 public:
34   explicit AssignmentContext(SemanticsContext &context) : context_{context} {}
35   AssignmentContext(AssignmentContext &&) = default;
36   AssignmentContext(const AssignmentContext &) = delete;
37   bool operator==(const AssignmentContext &x) const { return this == &x; }
38 
39   template<typename A> void PushWhereContext(const A &);
40   void PopWhereContext();
41   void Analyze(const parser::AssignmentStmt &);
42   void Analyze(const parser::PointerAssignmentStmt &);
43   void Analyze(const parser::ConcurrentControl &);
44 
45 private:
46   void CheckForPureContext(const SomeExpr &lhs, const SomeExpr &rhs,
47       parser::CharBlock rhsSource, bool isPointerAssignment);
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)...);
52   }
53   evaluate::FoldingContext &foldingContext() {
54     return context_.foldingContext();
55   }
56 
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_;
61 };
62 
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     auto rhsLoc{std::get<parser::Expr>(stmt.t).source};
69     if (whereDepth_ > 0) {
70       CheckShape(lhsLoc, &lhs);
71     }
72     CheckForPureContext(lhs, rhs, rhsLoc, false);
73   }
74 }
75 
76 void AssignmentContext::Analyze(const parser::PointerAssignmentStmt &stmt) {
77   CHECK(whereDepth_ == 0);
78   if (const evaluate::Assignment * assignment{GetAssignment(stmt)}) {
79     const SomeExpr &lhs{assignment->lhs};
80     const SomeExpr &rhs{assignment->rhs};
81     CheckForPureContext(lhs, rhs, std::get<parser::Expr>(stmt.t).source, true);
82     auto restorer{
83         foldingContext().messages().SetLocation(context_.location().value())};
84     CheckPointerAssignment(foldingContext(), *assignment);
85   }
86 }
87 
88 // C1594 checks
89 static bool IsPointerDummyOfPureFunction(const Symbol &x) {
90   return IsPointerDummy(x) && FindPureProcedureContaining(x.owner()) &&
91       x.owner().symbol() && IsFunction(*x.owner().symbol());
92 }
93 
94 static const char *WhyBaseObjectIsSuspicious(
95     const Symbol &x, const Scope &scope) {
96   // See C1594, first paragraph.  These conditions enable checks on both
97   // left-hand and right-hand sides in various circumstances.
98   if (IsHostAssociated(x, scope)) {
99     return "host-associated";
100   } else if (IsUseAssociated(x, scope)) {
101     return "USE-associated";
102   } else if (IsPointerDummyOfPureFunction(x)) {
103     return "a POINTER dummy argument of a pure function";
104   } else if (IsIntentIn(x)) {
105     return "an INTENT(IN) dummy argument";
106   } else if (FindCommonBlockContaining(x)) {
107     return "in a COMMON block";
108   } else {
109     return nullptr;
110   }
111 }
112 
113 // Checks C1594(1,2)
114 void CheckDefinabilityInPureScope(parser::ContextualMessages &messages,
115     const Symbol &lhs, const Scope &context, const Scope &pure) {
116   if (pure.symbol()) {
117     if (const char *why{WhyBaseObjectIsSuspicious(lhs, context)}) {
118       evaluate::SayWithDeclaration(messages, lhs,
119           "Pure subprogram '%s' may not define '%s' because it is %s"_err_en_US,
120           pure.symbol()->name(), lhs.name(), why);
121     }
122   }
123 }
124 
125 static std::optional<std::string> GetPointerComponentDesignatorName(
126     const SomeExpr &expr) {
127   if (const auto *derived{
128           evaluate::GetDerivedTypeSpec(evaluate::DynamicType::From(expr))}) {
129     UltimateComponentIterator ultimates{*derived};
130     if (auto pointer{
131             std::find_if(ultimates.begin(), ultimates.end(), IsPointer)}) {
132       return pointer.BuildResultDesignatorName();
133     }
134   }
135   return std::nullopt;
136 }
137 
138 // Checks C1594(5,6)
139 void CheckCopyabilityInPureScope(parser::ContextualMessages &messages,
140     const SomeExpr &expr, const Scope &scope) {
141   if (const Symbol * base{GetFirstSymbol(expr)}) {
142     if (const char *why{WhyBaseObjectIsSuspicious(*base, scope)}) {
143       if (auto pointer{GetPointerComponentDesignatorName(expr)}) {
144         evaluate::SayWithDeclaration(messages, *base,
145             "A pure subprogram may not copy the value of '%s' because it is %s and has the POINTER component '%s'"_err_en_US,
146             base->name(), why, *pointer);
147       }
148     }
149   }
150 }
151 
152 void AssignmentContext::CheckForPureContext(const SomeExpr &lhs,
153     const SomeExpr &rhs, parser::CharBlock source, bool isPointerAssignment) {
154   const Scope &scope{context_.FindScope(source)};
155   if (const Scope * pure{FindPureProcedureContaining(scope)}) {
156     parser::ContextualMessages messages{
157         context_.location().value(), &context_.messages()};
158     if (evaluate::ExtractCoarrayRef(lhs)) {
159       messages.Say(
160           "A pure subprogram may not define a coindexed object"_err_en_US);
161     } else if (const Symbol * base{GetFirstSymbol(lhs)}) {
162       if (const auto *assoc{base->detailsIf<AssocEntityDetails>()}) {
163         if (auto dataRef{ExtractDataRef(assoc->expr())}) {
164           // ASSOCIATE(a=>x) -- check x, not a, for "a=..."
165           CheckDefinabilityInPureScope(
166               messages, dataRef->GetFirstSymbol(), scope, *pure);
167         }
168       } else {
169         CheckDefinabilityInPureScope(messages, *base, scope, *pure);
170       }
171     }
172     if (isPointerAssignment) {
173       if (const Symbol * base{GetFirstSymbol(rhs)}) {
174         if (const char *why{
175                 WhyBaseObjectIsSuspicious(*base, scope)}) {  // C1594(3)
176           evaluate::SayWithDeclaration(messages, *base,
177               "A pure subprogram may not use '%s' as the target of pointer assignment because it is %s"_err_en_US,
178               base->name(), why);
179         }
180       }
181     } else {
182       if (auto type{evaluate::DynamicType::From(lhs)}) {
183         // C1596 checks for polymorphic deallocation in a pure subprogram
184         // due to automatic reallocation on assignment
185         if (type->IsPolymorphic()) {
186           context_.Say(
187               "Deallocation of polymorphic object is not permitted in a pure subprogram"_err_en_US);
188         }
189         if (const DerivedTypeSpec * derived{GetDerivedTypeSpec(type)}) {
190           if (auto bad{FindPolymorphicAllocatableNonCoarrayUltimateComponent(
191                   *derived)}) {
192             evaluate::SayWithDeclaration(messages, *bad,
193                 "Deallocation of polymorphic non-coarray component '%s' is not permitted in a pure subprogram"_err_en_US,
194                 bad.BuildResultDesignatorName());
195           } else {
196             CheckCopyabilityInPureScope(messages, rhs, scope);
197           }
198         }
199       }
200     }
201   }
202 }
203 
204 // 10.2.3.1(2) The masks and LHS of assignments must all have the same shape
205 void AssignmentContext::CheckShape(parser::CharBlock at, const SomeExpr *expr) {
206   if (auto shape{evaluate::GetShape(foldingContext(), expr)}) {
207     std::size_t size{shape->size()};
208     if (whereDepth_ == 0) {
209       whereExtents_.resize(size);
210     } else if (whereExtents_.size() != size) {
211       Say(at,
212           "Must have rank %zd to match prior mask or assignment of"
213           " WHERE construct"_err_en_US,
214           whereExtents_.size());
215       return;
216     }
217     for (std::size_t i{0}; i < size; ++i) {
218       if (std::optional<std::int64_t> extent{evaluate::ToInt64((*shape)[i])}) {
219         if (!whereExtents_[i]) {
220           whereExtents_[i] = *extent;
221         } else if (*whereExtents_[i] != *extent) {
222           Say(at,
223               "Dimension %d must have extent %jd to match prior mask or"
224               " assignment of WHERE construct"_err_en_US,
225               i + 1, static_cast<std::intmax_t>(*whereExtents_[i]));
226         }
227       }
228     }
229   }
230 }
231 
232 template<typename A> void AssignmentContext::PushWhereContext(const A &x) {
233   const auto &expr{std::get<parser::LogicalExpr>(x.t)};
234   CheckShape(expr.thing.value().source, GetExpr(expr));
235   ++whereDepth_;
236 }
237 
238 void AssignmentContext::PopWhereContext() {
239   --whereDepth_;
240   if (whereDepth_ == 0) {
241     whereExtents_.clear();
242   }
243 }
244 
245 AssignmentChecker::~AssignmentChecker() {}
246 
247 AssignmentChecker::AssignmentChecker(SemanticsContext &context)
248   : context_{new AssignmentContext{context}} {}
249 void AssignmentChecker::Enter(const parser::AssignmentStmt &x) {
250   context_.value().Analyze(x);
251 }
252 void AssignmentChecker::Enter(const parser::PointerAssignmentStmt &x) {
253   context_.value().Analyze(x);
254 }
255 void AssignmentChecker::Enter(const parser::WhereStmt &x) {
256   context_.value().PushWhereContext(x);
257 }
258 void AssignmentChecker::Leave(const parser::WhereStmt &) {
259   context_.value().PopWhereContext();
260 }
261 void AssignmentChecker::Enter(const parser::WhereConstructStmt &x) {
262   context_.value().PushWhereContext(x);
263 }
264 void AssignmentChecker::Leave(const parser::EndWhereStmt &) {
265   context_.value().PopWhereContext();
266 }
267 void AssignmentChecker::Enter(const parser::MaskedElsewhereStmt &x) {
268   context_.value().PushWhereContext(x);
269 }
270 void AssignmentChecker::Leave(const parser::MaskedElsewhereStmt &) {
271   context_.value().PopWhereContext();
272 }
273 
274 }
275 template class Fortran::common::Indirection<
276     Fortran::semantics::AssignmentContext>;
277