164ab3302SCarolineConcatto //===-- lib/Semantics/assignment.cpp --------------------------------------===//
264ab3302SCarolineConcatto //
364ab3302SCarolineConcatto // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
464ab3302SCarolineConcatto // See https://llvm.org/LICENSE.txt for license information.
564ab3302SCarolineConcatto // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
664ab3302SCarolineConcatto //
764ab3302SCarolineConcatto //===----------------------------------------------------------------------===//
864ab3302SCarolineConcatto 
964ab3302SCarolineConcatto #include "assignment.h"
1064ab3302SCarolineConcatto #include "pointer-assignment.h"
1164ab3302SCarolineConcatto #include "flang/Common/idioms.h"
1264ab3302SCarolineConcatto #include "flang/Common/restorer.h"
1364ab3302SCarolineConcatto #include "flang/Evaluate/characteristics.h"
1464ab3302SCarolineConcatto #include "flang/Evaluate/expression.h"
1564ab3302SCarolineConcatto #include "flang/Evaluate/fold.h"
1664ab3302SCarolineConcatto #include "flang/Evaluate/tools.h"
1764ab3302SCarolineConcatto #include "flang/Parser/message.h"
1864ab3302SCarolineConcatto #include "flang/Parser/parse-tree-visitor.h"
1964ab3302SCarolineConcatto #include "flang/Parser/parse-tree.h"
2064ab3302SCarolineConcatto #include "flang/Semantics/expression.h"
2164ab3302SCarolineConcatto #include "flang/Semantics/symbol.h"
2264ab3302SCarolineConcatto #include "flang/Semantics/tools.h"
2364ab3302SCarolineConcatto #include <optional>
2464ab3302SCarolineConcatto #include <set>
2564ab3302SCarolineConcatto #include <string>
2664ab3302SCarolineConcatto #include <type_traits>
2764ab3302SCarolineConcatto 
2864ab3302SCarolineConcatto using namespace Fortran::parser::literals;
2964ab3302SCarolineConcatto 
3064ab3302SCarolineConcatto namespace Fortran::semantics {
3164ab3302SCarolineConcatto 
3264ab3302SCarolineConcatto class AssignmentContext {
3364ab3302SCarolineConcatto public:
AssignmentContext(SemanticsContext & context)3464ab3302SCarolineConcatto   explicit AssignmentContext(SemanticsContext &context) : context_{context} {}
3564ab3302SCarolineConcatto   AssignmentContext(AssignmentContext &&) = default;
3664ab3302SCarolineConcatto   AssignmentContext(const AssignmentContext &) = delete;
operator ==(const AssignmentContext & x) const3764ab3302SCarolineConcatto   bool operator==(const AssignmentContext &x) const { return this == &x; }
3864ab3302SCarolineConcatto 
3964ab3302SCarolineConcatto   template <typename A> void PushWhereContext(const A &);
4064ab3302SCarolineConcatto   void PopWhereContext();
4164ab3302SCarolineConcatto   void Analyze(const parser::AssignmentStmt &);
4264ab3302SCarolineConcatto   void Analyze(const parser::PointerAssignmentStmt &);
4364ab3302SCarolineConcatto   void Analyze(const parser::ConcurrentControl &);
4464ab3302SCarolineConcatto 
4564ab3302SCarolineConcatto private:
46c97e1c0aSTim Keith   bool CheckForPureContext(const SomeExpr &lhs, const SomeExpr &rhs,
4764ab3302SCarolineConcatto       parser::CharBlock rhsSource, bool isPointerAssignment);
4864ab3302SCarolineConcatto   void CheckShape(parser::CharBlock, const SomeExpr *);
4964ab3302SCarolineConcatto   template <typename... A>
Say(parser::CharBlock at,A &&...args)5064ab3302SCarolineConcatto   parser::Message *Say(parser::CharBlock at, A &&...args) {
5164ab3302SCarolineConcatto     return &context_.Say(at, std::forward<A>(args)...);
5264ab3302SCarolineConcatto   }
foldingContext()5364ab3302SCarolineConcatto   evaluate::FoldingContext &foldingContext() {
5464ab3302SCarolineConcatto     return context_.foldingContext();
5564ab3302SCarolineConcatto   }
5664ab3302SCarolineConcatto 
5764ab3302SCarolineConcatto   SemanticsContext &context_;
5864ab3302SCarolineConcatto   int whereDepth_{0}; // number of WHEREs currently nested in
5964ab3302SCarolineConcatto   // shape of masks in LHS of assignments in current WHERE:
6064ab3302SCarolineConcatto   std::vector<std::optional<std::int64_t>> whereExtents_;
6164ab3302SCarolineConcatto };
6264ab3302SCarolineConcatto 
Analyze(const parser::AssignmentStmt & stmt)6364ab3302SCarolineConcatto void AssignmentContext::Analyze(const parser::AssignmentStmt &stmt) {
6464ab3302SCarolineConcatto   if (const evaluate::Assignment * assignment{GetAssignment(stmt)}) {
6564ab3302SCarolineConcatto     const SomeExpr &lhs{assignment->lhs};
6664ab3302SCarolineConcatto     const SomeExpr &rhs{assignment->rhs};
6764ab3302SCarolineConcatto     auto lhsLoc{std::get<parser::Variable>(stmt.t).GetSource()};
6864ab3302SCarolineConcatto     auto rhsLoc{std::get<parser::Expr>(stmt.t).source};
69a0a1f519STim Keith     if (CheckForPureContext(lhs, rhs, rhsLoc, false)) {
70a0a1f519STim Keith       const Scope &scope{context_.FindScope(lhsLoc)};
71a40dbe7cSTim Keith       if (auto whyNot{WhyNotModifiable(lhsLoc, lhs, scope, true)}) {
72a0a1f519STim Keith         if (auto *msg{Say(lhsLoc,
73f187d64cSpeter klausler                 "Left-hand side of assignment is not modifiable"_err_en_US)}) {
74a0a1f519STim Keith           msg->Attach(*whyNot);
75a0a1f519STim Keith         }
76a0a1f519STim Keith       }
77a0a1f519STim Keith     }
7864ab3302SCarolineConcatto     if (whereDepth_ > 0) {
7964ab3302SCarolineConcatto       CheckShape(lhsLoc, &lhs);
8064ab3302SCarolineConcatto     }
8164ab3302SCarolineConcatto   }
8264ab3302SCarolineConcatto }
8364ab3302SCarolineConcatto 
Analyze(const parser::PointerAssignmentStmt & stmt)8464ab3302SCarolineConcatto void AssignmentContext::Analyze(const parser::PointerAssignmentStmt &stmt) {
8564ab3302SCarolineConcatto   CHECK(whereDepth_ == 0);
8664ab3302SCarolineConcatto   if (const evaluate::Assignment * assignment{GetAssignment(stmt)}) {
8764ab3302SCarolineConcatto     const SomeExpr &lhs{assignment->lhs};
8864ab3302SCarolineConcatto     const SomeExpr &rhs{assignment->rhs};
8964ab3302SCarolineConcatto     CheckForPureContext(lhs, rhs, std::get<parser::Expr>(stmt.t).source, true);
9064ab3302SCarolineConcatto     auto restorer{
9164ab3302SCarolineConcatto         foldingContext().messages().SetLocation(context_.location().value())};
9264ab3302SCarolineConcatto     CheckPointerAssignment(foldingContext(), *assignment);
9364ab3302SCarolineConcatto   }
9464ab3302SCarolineConcatto }
9564ab3302SCarolineConcatto 
9664ab3302SCarolineConcatto // C1594 checks
IsPointerDummyOfPureFunction(const Symbol & x)9764ab3302SCarolineConcatto static bool IsPointerDummyOfPureFunction(const Symbol &x) {
9864ab3302SCarolineConcatto   return IsPointerDummy(x) && FindPureProcedureContaining(x.owner()) &&
9964ab3302SCarolineConcatto       x.owner().symbol() && IsFunction(*x.owner().symbol());
10064ab3302SCarolineConcatto }
10164ab3302SCarolineConcatto 
WhyBaseObjectIsSuspicious(const Symbol & x,const Scope & scope)10264ab3302SCarolineConcatto static const char *WhyBaseObjectIsSuspicious(
10364ab3302SCarolineConcatto     const Symbol &x, const Scope &scope) {
10464ab3302SCarolineConcatto   // See C1594, first paragraph.  These conditions enable checks on both
10564ab3302SCarolineConcatto   // left-hand and right-hand sides in various circumstances.
10664ab3302SCarolineConcatto   if (IsHostAssociated(x, scope)) {
10764ab3302SCarolineConcatto     return "host-associated";
10864ab3302SCarolineConcatto   } else if (IsUseAssociated(x, scope)) {
10964ab3302SCarolineConcatto     return "USE-associated";
11064ab3302SCarolineConcatto   } else if (IsPointerDummyOfPureFunction(x)) {
11164ab3302SCarolineConcatto     return "a POINTER dummy argument of a pure function";
11264ab3302SCarolineConcatto   } else if (IsIntentIn(x)) {
11364ab3302SCarolineConcatto     return "an INTENT(IN) dummy argument";
11464ab3302SCarolineConcatto   } else if (FindCommonBlockContaining(x)) {
11564ab3302SCarolineConcatto     return "in a COMMON block";
11664ab3302SCarolineConcatto   } else {
11764ab3302SCarolineConcatto     return nullptr;
11864ab3302SCarolineConcatto   }
11964ab3302SCarolineConcatto }
12064ab3302SCarolineConcatto 
121c97e1c0aSTim Keith // Checks C1594(1,2); false if check fails
CheckDefinabilityInPureScope(parser::ContextualMessages & messages,const Symbol & lhs,const Scope & context,const Scope & pure)122c97e1c0aSTim Keith bool CheckDefinabilityInPureScope(parser::ContextualMessages &messages,
12364ab3302SCarolineConcatto     const Symbol &lhs, const Scope &context, const Scope &pure) {
12464ab3302SCarolineConcatto   if (pure.symbol()) {
12564ab3302SCarolineConcatto     if (const char *why{WhyBaseObjectIsSuspicious(lhs, context)}) {
12664ab3302SCarolineConcatto       evaluate::SayWithDeclaration(messages, lhs,
12764ab3302SCarolineConcatto           "Pure subprogram '%s' may not define '%s' because it is %s"_err_en_US,
12864ab3302SCarolineConcatto           pure.symbol()->name(), lhs.name(), why);
129c97e1c0aSTim Keith       return false;
13064ab3302SCarolineConcatto     }
13164ab3302SCarolineConcatto   }
132c97e1c0aSTim Keith   return true;
13364ab3302SCarolineConcatto }
13464ab3302SCarolineConcatto 
GetPointerComponentDesignatorName(const SomeExpr & expr)13564ab3302SCarolineConcatto static std::optional<std::string> GetPointerComponentDesignatorName(
13664ab3302SCarolineConcatto     const SomeExpr &expr) {
13764ab3302SCarolineConcatto   if (const auto *derived{
13864ab3302SCarolineConcatto           evaluate::GetDerivedTypeSpec(evaluate::DynamicType::From(expr))}) {
13964ab3302SCarolineConcatto     UltimateComponentIterator ultimates{*derived};
14064ab3302SCarolineConcatto     if (auto pointer{
14164ab3302SCarolineConcatto             std::find_if(ultimates.begin(), ultimates.end(), IsPointer)}) {
14264ab3302SCarolineConcatto       return pointer.BuildResultDesignatorName();
14364ab3302SCarolineConcatto     }
14464ab3302SCarolineConcatto   }
14564ab3302SCarolineConcatto   return std::nullopt;
14664ab3302SCarolineConcatto }
14764ab3302SCarolineConcatto 
148c97e1c0aSTim Keith // Checks C1594(5,6); false if check fails
CheckCopyabilityInPureScope(parser::ContextualMessages & messages,const SomeExpr & expr,const Scope & scope)149c97e1c0aSTim Keith bool CheckCopyabilityInPureScope(parser::ContextualMessages &messages,
15064ab3302SCarolineConcatto     const SomeExpr &expr, const Scope &scope) {
15164ab3302SCarolineConcatto   if (const Symbol * base{GetFirstSymbol(expr)}) {
15264ab3302SCarolineConcatto     if (const char *why{WhyBaseObjectIsSuspicious(*base, scope)}) {
15364ab3302SCarolineConcatto       if (auto pointer{GetPointerComponentDesignatorName(expr)}) {
15464ab3302SCarolineConcatto         evaluate::SayWithDeclaration(messages, *base,
155c97e1c0aSTim Keith             "A pure subprogram may not copy the value of '%s' because it is %s"
156c97e1c0aSTim Keith             " and has the POINTER component '%s'"_err_en_US,
15764ab3302SCarolineConcatto             base->name(), why, *pointer);
158c97e1c0aSTim Keith         return false;
15964ab3302SCarolineConcatto       }
16064ab3302SCarolineConcatto     }
16164ab3302SCarolineConcatto   }
162c97e1c0aSTim Keith   return true;
16364ab3302SCarolineConcatto }
16464ab3302SCarolineConcatto 
CheckForPureContext(const SomeExpr & lhs,const SomeExpr & rhs,parser::CharBlock source,bool isPointerAssignment)165c97e1c0aSTim Keith bool AssignmentContext::CheckForPureContext(const SomeExpr &lhs,
16664ab3302SCarolineConcatto     const SomeExpr &rhs, parser::CharBlock source, bool isPointerAssignment) {
16764ab3302SCarolineConcatto   const Scope &scope{context_.FindScope(source)};
16864ab3302SCarolineConcatto   if (const Scope * pure{FindPureProcedureContaining(scope)}) {
16964ab3302SCarolineConcatto     parser::ContextualMessages messages{
17064ab3302SCarolineConcatto         context_.location().value(), &context_.messages()};
17164ab3302SCarolineConcatto     if (evaluate::ExtractCoarrayRef(lhs)) {
17264ab3302SCarolineConcatto       messages.Say(
17364ab3302SCarolineConcatto           "A pure subprogram may not define a coindexed object"_err_en_US);
17464ab3302SCarolineConcatto     } else if (const Symbol * base{GetFirstSymbol(lhs)}) {
17564ab3302SCarolineConcatto       if (const auto *assoc{base->detailsIf<AssocEntityDetails>()}) {
17684a099dfSpeter klausler         auto dataRef{ExtractDataRef(assoc->expr(), true)};
17764ab3302SCarolineConcatto         // ASSOCIATE(a=>x) -- check x, not a, for "a=..."
178c97e1c0aSTim Keith         base = dataRef ? &dataRef->GetFirstSymbol() : nullptr;
17964ab3302SCarolineConcatto       }
180a0a1f519STim Keith       if (base &&
181a0a1f519STim Keith           !CheckDefinabilityInPureScope(messages, *base, scope, *pure)) {
182c97e1c0aSTim Keith         return false;
18364ab3302SCarolineConcatto       }
18464ab3302SCarolineConcatto     }
18564ab3302SCarolineConcatto     if (isPointerAssignment) {
18664ab3302SCarolineConcatto       if (const Symbol * base{GetFirstSymbol(rhs)}) {
18764ab3302SCarolineConcatto         if (const char *why{
18864ab3302SCarolineConcatto                 WhyBaseObjectIsSuspicious(*base, scope)}) { // C1594(3)
18964ab3302SCarolineConcatto           evaluate::SayWithDeclaration(messages, *base,
19064ab3302SCarolineConcatto               "A pure subprogram may not use '%s' as the target of pointer assignment because it is %s"_err_en_US,
19164ab3302SCarolineConcatto               base->name(), why);
192c97e1c0aSTim Keith           return false;
19364ab3302SCarolineConcatto         }
19464ab3302SCarolineConcatto       }
195c97e1c0aSTim Keith     } else if (auto type{evaluate::DynamicType::From(lhs)}) {
19664ab3302SCarolineConcatto       // C1596 checks for polymorphic deallocation in a pure subprogram
19764ab3302SCarolineConcatto       // due to automatic reallocation on assignment
19864ab3302SCarolineConcatto       if (type->IsPolymorphic()) {
19964ab3302SCarolineConcatto         context_.Say(
20064ab3302SCarolineConcatto             "Deallocation of polymorphic object is not permitted in a pure subprogram"_err_en_US);
201c97e1c0aSTim Keith         return false;
20264ab3302SCarolineConcatto       }
20364ab3302SCarolineConcatto       if (const DerivedTypeSpec * derived{GetDerivedTypeSpec(type)}) {
20464ab3302SCarolineConcatto         if (auto bad{FindPolymorphicAllocatableNonCoarrayUltimateComponent(
20564ab3302SCarolineConcatto                 *derived)}) {
20664ab3302SCarolineConcatto           evaluate::SayWithDeclaration(messages, *bad,
20764ab3302SCarolineConcatto               "Deallocation of polymorphic non-coarray component '%s' is not permitted in a pure subprogram"_err_en_US,
20864ab3302SCarolineConcatto               bad.BuildResultDesignatorName());
209c97e1c0aSTim Keith           return false;
21064ab3302SCarolineConcatto         } else {
211c97e1c0aSTim Keith           return CheckCopyabilityInPureScope(messages, rhs, scope);
21264ab3302SCarolineConcatto         }
21364ab3302SCarolineConcatto       }
21464ab3302SCarolineConcatto     }
21564ab3302SCarolineConcatto   }
216c97e1c0aSTim Keith   return true;
21764ab3302SCarolineConcatto }
21864ab3302SCarolineConcatto 
219*2472b686SPeixinQiao // 10.2.3.1(2) The masks and LHS of assignments must be arrays of the same shape
CheckShape(parser::CharBlock at,const SomeExpr * expr)22064ab3302SCarolineConcatto void AssignmentContext::CheckShape(parser::CharBlock at, const SomeExpr *expr) {
22164ab3302SCarolineConcatto   if (auto shape{evaluate::GetShape(foldingContext(), expr)}) {
22264ab3302SCarolineConcatto     std::size_t size{shape->size()};
223*2472b686SPeixinQiao     if (size == 0) {
224*2472b686SPeixinQiao       Say(at, "The mask or variable must not be scalar"_err_en_US);
225*2472b686SPeixinQiao     }
22664ab3302SCarolineConcatto     if (whereDepth_ == 0) {
22764ab3302SCarolineConcatto       whereExtents_.resize(size);
22864ab3302SCarolineConcatto     } else if (whereExtents_.size() != size) {
22964ab3302SCarolineConcatto       Say(at,
23064ab3302SCarolineConcatto           "Must have rank %zd to match prior mask or assignment of"
23164ab3302SCarolineConcatto           " WHERE construct"_err_en_US,
23264ab3302SCarolineConcatto           whereExtents_.size());
23364ab3302SCarolineConcatto       return;
23464ab3302SCarolineConcatto     }
23564ab3302SCarolineConcatto     for (std::size_t i{0}; i < size; ++i) {
23664ab3302SCarolineConcatto       if (std::optional<std::int64_t> extent{evaluate::ToInt64((*shape)[i])}) {
23764ab3302SCarolineConcatto         if (!whereExtents_[i]) {
23864ab3302SCarolineConcatto           whereExtents_[i] = *extent;
23964ab3302SCarolineConcatto         } else if (*whereExtents_[i] != *extent) {
24064ab3302SCarolineConcatto           Say(at,
24164ab3302SCarolineConcatto               "Dimension %d must have extent %jd to match prior mask or"
24264ab3302SCarolineConcatto               " assignment of WHERE construct"_err_en_US,
24376d71354STim Keith               i + 1, *whereExtents_[i]);
24464ab3302SCarolineConcatto         }
24564ab3302SCarolineConcatto       }
24664ab3302SCarolineConcatto     }
24764ab3302SCarolineConcatto   }
24864ab3302SCarolineConcatto }
24964ab3302SCarolineConcatto 
PushWhereContext(const A & x)25064ab3302SCarolineConcatto template <typename A> void AssignmentContext::PushWhereContext(const A &x) {
25164ab3302SCarolineConcatto   const auto &expr{std::get<parser::LogicalExpr>(x.t)};
2527e225423SPeter Klausler   CheckShape(expr.thing.value().source, GetExpr(context_, expr));
25364ab3302SCarolineConcatto   ++whereDepth_;
25464ab3302SCarolineConcatto }
25564ab3302SCarolineConcatto 
PopWhereContext()25664ab3302SCarolineConcatto void AssignmentContext::PopWhereContext() {
25764ab3302SCarolineConcatto   --whereDepth_;
25864ab3302SCarolineConcatto   if (whereDepth_ == 0) {
25964ab3302SCarolineConcatto     whereExtents_.clear();
26064ab3302SCarolineConcatto   }
26164ab3302SCarolineConcatto }
26264ab3302SCarolineConcatto 
~AssignmentChecker()26364ab3302SCarolineConcatto AssignmentChecker::~AssignmentChecker() {}
26464ab3302SCarolineConcatto 
AssignmentChecker(SemanticsContext & context)26564ab3302SCarolineConcatto AssignmentChecker::AssignmentChecker(SemanticsContext &context)
26664ab3302SCarolineConcatto     : context_{new AssignmentContext{context}} {}
Enter(const parser::AssignmentStmt & x)26764ab3302SCarolineConcatto void AssignmentChecker::Enter(const parser::AssignmentStmt &x) {
26864ab3302SCarolineConcatto   context_.value().Analyze(x);
26964ab3302SCarolineConcatto }
Enter(const parser::PointerAssignmentStmt & x)27064ab3302SCarolineConcatto void AssignmentChecker::Enter(const parser::PointerAssignmentStmt &x) {
27164ab3302SCarolineConcatto   context_.value().Analyze(x);
27264ab3302SCarolineConcatto }
Enter(const parser::WhereStmt & x)27364ab3302SCarolineConcatto void AssignmentChecker::Enter(const parser::WhereStmt &x) {
27464ab3302SCarolineConcatto   context_.value().PushWhereContext(x);
27564ab3302SCarolineConcatto }
Leave(const parser::WhereStmt &)27664ab3302SCarolineConcatto void AssignmentChecker::Leave(const parser::WhereStmt &) {
27764ab3302SCarolineConcatto   context_.value().PopWhereContext();
27864ab3302SCarolineConcatto }
Enter(const parser::WhereConstructStmt & x)27964ab3302SCarolineConcatto void AssignmentChecker::Enter(const parser::WhereConstructStmt &x) {
28064ab3302SCarolineConcatto   context_.value().PushWhereContext(x);
28164ab3302SCarolineConcatto }
Leave(const parser::EndWhereStmt &)28264ab3302SCarolineConcatto void AssignmentChecker::Leave(const parser::EndWhereStmt &) {
28364ab3302SCarolineConcatto   context_.value().PopWhereContext();
28464ab3302SCarolineConcatto }
Enter(const parser::MaskedElsewhereStmt & x)28564ab3302SCarolineConcatto void AssignmentChecker::Enter(const parser::MaskedElsewhereStmt &x) {
28664ab3302SCarolineConcatto   context_.value().PushWhereContext(x);
28764ab3302SCarolineConcatto }
Leave(const parser::MaskedElsewhereStmt &)28864ab3302SCarolineConcatto void AssignmentChecker::Leave(const parser::MaskedElsewhereStmt &) {
28964ab3302SCarolineConcatto   context_.value().PopWhereContext();
29064ab3302SCarolineConcatto }
29164ab3302SCarolineConcatto 
29284a099dfSpeter klausler } // namespace Fortran::semantics
29364ab3302SCarolineConcatto template class Fortran::common::Indirection<
29464ab3302SCarolineConcatto     Fortran::semantics::AssignmentContext>;
295