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