xref: /llvm-project/flang/lib/Semantics/assignment.cpp (revision d5285fef00f6c5a725a515118192dd117fc3c665)
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   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     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)}) {
74           msg->Attach(
75               std::move(whyNot->set_severity(parser::Severity::Because)));
76         }
77       } else {
78         context_.Say(std::move(*whyNot));
79       }
80     }
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(=)
84     } else {
85       CheckForPureContext(rhs, rhsLoc);
86     }
87     if (whereDepth_ > 0) {
88       CheckShape(lhsLoc, &lhs);
89     }
90   }
91 }
92 
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));
99   }
100 }
101 
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};
107     if (auto pointer{
108             std::find_if(potentials.begin(), potentials.end(), IsPointer)}) {
109       return pointer.BuildResultDesignatorName();
110     }
111   }
112   return std::nullopt;
113 }
114 
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)}) {
119     if (const char *why{
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);
126         return false;
127       }
128     }
129   }
130   return true;
131 }
132 
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);
140   } else {
141     return true;
142   }
143 }
144 
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()};
149     if (size == 0) {
150       Say(at, "The mask or variable must not be scalar"_err_en_US);
151     }
152     if (whereDepth_ == 0) {
153       whereExtents_.resize(size);
154     } else if (whereExtents_.size() != size) {
155       Say(at,
156           "Must have rank %zd to match prior mask or assignment of"
157           " WHERE construct"_err_en_US,
158           whereExtents_.size());
159       return;
160     }
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) {
166           Say(at,
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]);
170         }
171       }
172     }
173   }
174 }
175 
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));
179   ++whereDepth_;
180 }
181 
182 void AssignmentContext::PopWhereContext() {
183   --whereDepth_;
184   if (whereDepth_ == 0) {
185     whereExtents_.clear();
186   }
187 }
188 
189 AssignmentChecker::~AssignmentChecker() {}
190 
191 AssignmentChecker::AssignmentChecker(SemanticsContext &context)
192     : context_{new AssignmentContext{context}} {}
193 void AssignmentChecker::Enter(const parser::AssignmentStmt &x) {
194   context_.value().Analyze(x);
195 }
196 void AssignmentChecker::Enter(const parser::PointerAssignmentStmt &x) {
197   context_.value().Analyze(x);
198 }
199 void AssignmentChecker::Enter(const parser::WhereStmt &x) {
200   context_.value().PushWhereContext(x);
201 }
202 void AssignmentChecker::Leave(const parser::WhereStmt &) {
203   context_.value().PopWhereContext();
204 }
205 void AssignmentChecker::Enter(const parser::WhereConstructStmt &x) {
206   context_.value().PushWhereContext(x);
207 }
208 void AssignmentChecker::Leave(const parser::EndWhereStmt &) {
209   context_.value().PopWhereContext();
210 }
211 void AssignmentChecker::Enter(const parser::MaskedElsewhereStmt &x) {
212   context_.value().PushWhereContext(x);
213 }
214 void AssignmentChecker::Leave(const parser::MaskedElsewhereStmt &) {
215   context_.value().PopWhereContext();
216 }
217 
218 } // namespace Fortran::semantics
219 template class Fortran::common::Indirection<
220     Fortran::semantics::AssignmentContext>;
221