xref: /llvm-project/flang/lib/Semantics/pointer-assignment.cpp (revision b8513e439351b11a90b8aa69311cf57572405826)
164ab3302SCarolineConcatto //===-- lib/Semantics/pointer-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 "pointer-assignment.h"
10573fc618SPeter Klausler #include "definable.h"
1164ab3302SCarolineConcatto #include "flang/Common/idioms.h"
1264ab3302SCarolineConcatto #include "flang/Common/restorer.h"
1315c93766SPeter Klausler #include "flang/Common/template.h"
1464ab3302SCarolineConcatto #include "flang/Evaluate/characteristics.h"
1564ab3302SCarolineConcatto #include "flang/Evaluate/expression.h"
1664ab3302SCarolineConcatto #include "flang/Evaluate/fold.h"
1764ab3302SCarolineConcatto #include "flang/Evaluate/tools.h"
1864ab3302SCarolineConcatto #include "flang/Parser/message.h"
1964ab3302SCarolineConcatto #include "flang/Parser/parse-tree-visitor.h"
2064ab3302SCarolineConcatto #include "flang/Parser/parse-tree.h"
2164ab3302SCarolineConcatto #include "flang/Semantics/expression.h"
2264ab3302SCarolineConcatto #include "flang/Semantics/symbol.h"
2364ab3302SCarolineConcatto #include "flang/Semantics/tools.h"
248670e499SCaroline Concatto #include "llvm/Support/raw_ostream.h"
2564ab3302SCarolineConcatto #include <optional>
2664ab3302SCarolineConcatto #include <set>
2764ab3302SCarolineConcatto #include <string>
2864ab3302SCarolineConcatto #include <type_traits>
2964ab3302SCarolineConcatto 
3064ab3302SCarolineConcatto // Semantic checks for pointer assignment.
3164ab3302SCarolineConcatto 
3264ab3302SCarolineConcatto namespace Fortran::semantics {
3364ab3302SCarolineConcatto 
3464ab3302SCarolineConcatto using namespace parser::literals;
3564ab3302SCarolineConcatto using evaluate::characteristics::DummyDataObject;
3664ab3302SCarolineConcatto using evaluate::characteristics::FunctionResult;
3764ab3302SCarolineConcatto using evaluate::characteristics::Procedure;
3864ab3302SCarolineConcatto using evaluate::characteristics::TypeAndShape;
3964ab3302SCarolineConcatto using parser::MessageFixedText;
4064ab3302SCarolineConcatto using parser::MessageFormattedText;
4164ab3302SCarolineConcatto 
4264ab3302SCarolineConcatto class PointerAssignmentChecker {
4364ab3302SCarolineConcatto public:
44191d4872SPeter Klausler   PointerAssignmentChecker(SemanticsContext &context, const Scope &scope,
45191d4872SPeter Klausler       parser::CharBlock source, const std::string &description)
46573fc618SPeter Klausler       : context_{context}, scope_{scope}, source_{source}, description_{
47573fc618SPeter Klausler                                                                description} {}
48573fc618SPeter Klausler   PointerAssignmentChecker(
49191d4872SPeter Klausler       SemanticsContext &context, const Scope &scope, const Symbol &lhs)
50573fc618SPeter Klausler       : context_{context}, scope_{scope}, source_{lhs.name()},
51562bfe12Speter klausler         description_{"pointer '"s + lhs.name().ToString() + '\''}, lhs_{&lhs} {
52191d4872SPeter Klausler     set_lhsType(TypeAndShape::Characterize(lhs, foldingContext_));
5364ab3302SCarolineConcatto     set_isContiguous(lhs.attrs().test(Attr::CONTIGUOUS));
5464ab3302SCarolineConcatto     set_isVolatile(lhs.attrs().test(Attr::VOLATILE));
5564ab3302SCarolineConcatto   }
5664ab3302SCarolineConcatto   PointerAssignmentChecker &set_lhsType(std::optional<TypeAndShape> &&);
5764ab3302SCarolineConcatto   PointerAssignmentChecker &set_isContiguous(bool);
5864ab3302SCarolineConcatto   PointerAssignmentChecker &set_isVolatile(bool);
5964ab3302SCarolineConcatto   PointerAssignmentChecker &set_isBoundsRemapping(bool);
60f82ee155SPeter Klausler   PointerAssignmentChecker &set_isAssumedRank(bool);
611fa9ef62SPeter Klausler   PointerAssignmentChecker &set_pointerComponentLHS(const Symbol *);
62573fc618SPeter Klausler   bool CheckLeftHandSide(const SomeExpr &);
634171f80dSpeter klausler   bool Check(const SomeExpr &);
6464ab3302SCarolineConcatto 
6564ab3302SCarolineConcatto private:
66b8641bfcSPeter Klausler   bool CharacterizeProcedure();
674171f80dSpeter klausler   template <typename T> bool Check(const T &);
684171f80dSpeter klausler   template <typename T> bool Check(const evaluate::Expr<T> &);
694171f80dSpeter klausler   template <typename T> bool Check(const evaluate::FunctionRef<T> &);
704171f80dSpeter klausler   template <typename T> bool Check(const evaluate::Designator<T> &);
714171f80dSpeter klausler   bool Check(const evaluate::NullPointer &);
724171f80dSpeter klausler   bool Check(const evaluate::ProcedureDesignator &);
734171f80dSpeter klausler   bool Check(const evaluate::ProcedureRef &);
7464ab3302SCarolineConcatto   // Target is a procedure
7595f4ca7fSPeter Klausler   bool Check(parser::CharBlock rhsName, bool isCall,
7695f4ca7fSPeter Klausler       const Procedure * = nullptr,
7795f4ca7fSPeter Klausler       const evaluate::SpecificIntrinsic *specific = nullptr);
7864ab3302SCarolineConcatto   bool LhsOkForUnlimitedPoly() const;
79*b8513e43SPeter Klausler   std::optional<MessageFormattedText> CheckRanks(const TypeAndShape &rhs) const;
8064ab3302SCarolineConcatto   template <typename... A> parser::Message *Say(A &&...);
810f973ac7SPeter Klausler   template <typename FeatureOrUsageWarning, typename... A>
820f973ac7SPeter Klausler   parser::Message *Warn(FeatureOrUsageWarning, A &&...);
8364ab3302SCarolineConcatto 
84191d4872SPeter Klausler   SemanticsContext &context_;
85191d4872SPeter Klausler   evaluate::FoldingContext &foldingContext_{context_.foldingContext()};
86573fc618SPeter Klausler   const Scope &scope_;
8764ab3302SCarolineConcatto   const parser::CharBlock source_;
8864ab3302SCarolineConcatto   const std::string description_;
8964ab3302SCarolineConcatto   const Symbol *lhs_{nullptr};
9064ab3302SCarolineConcatto   std::optional<TypeAndShape> lhsType_;
9164ab3302SCarolineConcatto   std::optional<Procedure> procedure_;
92b8641bfcSPeter Klausler   bool characterizedProcedure_{false};
9364ab3302SCarolineConcatto   bool isContiguous_{false};
9464ab3302SCarolineConcatto   bool isVolatile_{false};
9564ab3302SCarolineConcatto   bool isBoundsRemapping_{false};
96f82ee155SPeter Klausler   bool isAssumedRank_{false};
971fa9ef62SPeter Klausler   const Symbol *pointerComponentLHS_{nullptr};
9864ab3302SCarolineConcatto };
9964ab3302SCarolineConcatto 
10064ab3302SCarolineConcatto PointerAssignmentChecker &PointerAssignmentChecker::set_lhsType(
10164ab3302SCarolineConcatto     std::optional<TypeAndShape> &&lhsType) {
10264ab3302SCarolineConcatto   lhsType_ = std::move(lhsType);
10364ab3302SCarolineConcatto   return *this;
10464ab3302SCarolineConcatto }
10564ab3302SCarolineConcatto 
10664ab3302SCarolineConcatto PointerAssignmentChecker &PointerAssignmentChecker::set_isContiguous(
10764ab3302SCarolineConcatto     bool isContiguous) {
10864ab3302SCarolineConcatto   isContiguous_ = isContiguous;
10964ab3302SCarolineConcatto   return *this;
11064ab3302SCarolineConcatto }
11164ab3302SCarolineConcatto 
11264ab3302SCarolineConcatto PointerAssignmentChecker &PointerAssignmentChecker::set_isVolatile(
11364ab3302SCarolineConcatto     bool isVolatile) {
11464ab3302SCarolineConcatto   isVolatile_ = isVolatile;
11564ab3302SCarolineConcatto   return *this;
11664ab3302SCarolineConcatto }
11764ab3302SCarolineConcatto 
11864ab3302SCarolineConcatto PointerAssignmentChecker &PointerAssignmentChecker::set_isBoundsRemapping(
11964ab3302SCarolineConcatto     bool isBoundsRemapping) {
12064ab3302SCarolineConcatto   isBoundsRemapping_ = isBoundsRemapping;
12164ab3302SCarolineConcatto   return *this;
12264ab3302SCarolineConcatto }
12364ab3302SCarolineConcatto 
124f82ee155SPeter Klausler PointerAssignmentChecker &PointerAssignmentChecker::set_isAssumedRank(
125f82ee155SPeter Klausler     bool isAssumedRank) {
126f82ee155SPeter Klausler   isAssumedRank_ = isAssumedRank;
127f82ee155SPeter Klausler   return *this;
128f82ee155SPeter Klausler }
129f82ee155SPeter Klausler 
1301fa9ef62SPeter Klausler PointerAssignmentChecker &PointerAssignmentChecker::set_pointerComponentLHS(
1311fa9ef62SPeter Klausler     const Symbol *symbol) {
1321fa9ef62SPeter Klausler   pointerComponentLHS_ = symbol;
1331fa9ef62SPeter Klausler   return *this;
1341fa9ef62SPeter Klausler }
1351fa9ef62SPeter Klausler 
136b8641bfcSPeter Klausler bool PointerAssignmentChecker::CharacterizeProcedure() {
137b8641bfcSPeter Klausler   if (!characterizedProcedure_) {
138b8641bfcSPeter Klausler     characterizedProcedure_ = true;
139b8641bfcSPeter Klausler     if (lhs_ && IsProcedure(*lhs_)) {
140191d4872SPeter Klausler       procedure_ = Procedure::Characterize(*lhs_, foldingContext_);
141b8641bfcSPeter Klausler     }
142b8641bfcSPeter Klausler   }
143b8641bfcSPeter Klausler   return procedure_.has_value();
144b8641bfcSPeter Klausler }
145b8641bfcSPeter Klausler 
146573fc618SPeter Klausler bool PointerAssignmentChecker::CheckLeftHandSide(const SomeExpr &lhs) {
147191d4872SPeter Klausler   if (auto whyNot{WhyNotDefinable(foldingContext_.messages().at(), scope_,
148573fc618SPeter Klausler           DefinabilityFlags{DefinabilityFlag::PointerDefinition}, lhs)}) {
1491fa9ef62SPeter Klausler     if (auto *msg{Say(
150573fc618SPeter Klausler             "The left-hand side of a pointer assignment is not definable"_err_en_US)}) {
151d5285fefSPeter Klausler       msg->Attach(std::move(whyNot->set_severity(parser::Severity::Because)));
152573fc618SPeter Klausler     }
153573fc618SPeter Klausler     return false;
1549ab292d7SPeter Klausler   } else if (evaluate::IsAssumedRank(lhs)) {
1559ab292d7SPeter Klausler     Say("The left-hand side of a pointer assignment must not be an assumed-rank dummy argument"_err_en_US);
1569ab292d7SPeter Klausler     return false;
157573fc618SPeter Klausler   } else {
158573fc618SPeter Klausler     return true;
159573fc618SPeter Klausler   }
160573fc618SPeter Klausler }
161573fc618SPeter Klausler 
1624171f80dSpeter klausler template <typename T> bool PointerAssignmentChecker::Check(const T &) {
16364ab3302SCarolineConcatto   // Catch-all case for really bad target expression
16464ab3302SCarolineConcatto   Say("Target associated with %s must be a designator or a call to a"
16564ab3302SCarolineConcatto       " pointer-valued function"_err_en_US,
16664ab3302SCarolineConcatto       description_);
1674171f80dSpeter klausler   return false;
16864ab3302SCarolineConcatto }
16964ab3302SCarolineConcatto 
17064ab3302SCarolineConcatto template <typename T>
1714171f80dSpeter klausler bool PointerAssignmentChecker::Check(const evaluate::Expr<T> &x) {
172cd03e96fSPeter Klausler   return common::visit([&](const auto &x) { return Check(x); }, x.u);
17364ab3302SCarolineConcatto }
17464ab3302SCarolineConcatto 
1754171f80dSpeter klausler bool PointerAssignmentChecker::Check(const SomeExpr &rhs) {
17664ab3302SCarolineConcatto   if (HasVectorSubscript(rhs)) { // C1025
17764ab3302SCarolineConcatto     Say("An array section with a vector subscript may not be a pointer target"_err_en_US);
1784171f80dSpeter klausler     return false;
1791fa9ef62SPeter Klausler   }
1801fa9ef62SPeter Klausler   if (ExtractCoarrayRef(rhs)) { // C1026
18164ab3302SCarolineConcatto     Say("A coindexed object may not be a pointer target"_err_en_US);
1824171f80dSpeter klausler     return false;
18364ab3302SCarolineConcatto   }
1841fa9ef62SPeter Klausler   if (!common::visit([&](const auto &x) { return Check(x); }, rhs.u)) {
1851fa9ef62SPeter Klausler     return false;
1861fa9ef62SPeter Klausler   }
1871fa9ef62SPeter Klausler   if (IsNullPointer(rhs)) {
1881fa9ef62SPeter Klausler     return true;
1891fa9ef62SPeter Klausler   }
1901fa9ef62SPeter Klausler   if (lhs_ && IsProcedure(*lhs_)) {
1911fa9ef62SPeter Klausler     return true;
1921fa9ef62SPeter Klausler   }
1931fa9ef62SPeter Klausler   if (const auto *pureProc{FindPureProcedureContaining(scope_)}) {
1941fa9ef62SPeter Klausler     if (pointerComponentLHS_) { // C1594(4) is a hard error
1951fa9ef62SPeter Klausler       if (const Symbol * object{FindExternallyVisibleObject(rhs, *pureProc)}) {
1961fa9ef62SPeter Klausler         if (auto *msg{Say(
1971fa9ef62SPeter Klausler                 "Externally visible object '%s' may not be associated with pointer component '%s' in a pure procedure"_err_en_US,
1981fa9ef62SPeter Klausler                 object->name(), pointerComponentLHS_->name())}) {
1991fa9ef62SPeter Klausler           msg->Attach(object->name(), "Object declaration"_en_US)
2001fa9ef62SPeter Klausler               .Attach(
2011fa9ef62SPeter Klausler                   pointerComponentLHS_->name(), "Pointer declaration"_en_US);
2021fa9ef62SPeter Klausler         }
2031fa9ef62SPeter Klausler         return false;
2041fa9ef62SPeter Klausler       }
2051fa9ef62SPeter Klausler     } else if (const Symbol * base{GetFirstSymbol(rhs)}) {
2061fa9ef62SPeter Klausler       if (const char *why{WhyBaseObjectIsSuspicious(
2071fa9ef62SPeter Klausler               base->GetUltimate(), scope_)}) { // C1594(3)
208191d4872SPeter Klausler         evaluate::SayWithDeclaration(foldingContext_.messages(), *base,
2091fa9ef62SPeter Klausler             "A pure subprogram may not use '%s' as the target of pointer assignment because it is %s"_err_en_US,
2101fa9ef62SPeter Klausler             base->name(), why);
2111fa9ef62SPeter Klausler         return false;
2121fa9ef62SPeter Klausler       }
2131fa9ef62SPeter Klausler     }
2141fa9ef62SPeter Klausler   }
2151fa9ef62SPeter Klausler   if (isContiguous_) {
216191d4872SPeter Klausler     if (auto contiguous{evaluate::IsContiguous(rhs, foldingContext_)}) {
2171fa9ef62SPeter Klausler       if (!*contiguous) {
2181fa9ef62SPeter Klausler         Say("CONTIGUOUS pointer may not be associated with a discontiguous target"_err_en_US);
2191fa9ef62SPeter Klausler         return false;
2201fa9ef62SPeter Klausler       }
2210f973ac7SPeter Klausler     } else {
2220f973ac7SPeter Klausler       Warn(common::UsageWarning::PointerToPossibleNoncontiguous,
2230f973ac7SPeter Klausler           "Target of CONTIGUOUS pointer association is not known to be contiguous"_warn_en_US);
2241fa9ef62SPeter Klausler     }
2251fa9ef62SPeter Klausler   }
2261fa9ef62SPeter Klausler   // Warn about undefinable data targets
2270f973ac7SPeter Klausler   if (auto because{
2280f973ac7SPeter Klausler           WhyNotDefinable(foldingContext_.messages().at(), scope_, {}, rhs)}) {
2290f973ac7SPeter Klausler     if (auto *msg{Warn(common::UsageWarning::PointerToUndefinable,
2300f973ac7SPeter Klausler             "Pointer target is not a definable variable"_warn_en_US)}) {
2310f973ac7SPeter Klausler       msg->Attach(std::move(because->set_severity(parser::Severity::Because)));
2321fa9ef62SPeter Klausler       return false;
2331fa9ef62SPeter Klausler     }
234191d4872SPeter Klausler   }
2351fa9ef62SPeter Klausler   return true;
23664ab3302SCarolineConcatto }
23764ab3302SCarolineConcatto 
2384171f80dSpeter klausler bool PointerAssignmentChecker::Check(const evaluate::NullPointer &) {
2394171f80dSpeter klausler   return true; // P => NULL() without MOLD=; always OK
24064ab3302SCarolineConcatto }
24164ab3302SCarolineConcatto 
24264ab3302SCarolineConcatto template <typename T>
2434171f80dSpeter klausler bool PointerAssignmentChecker::Check(const evaluate::FunctionRef<T> &f) {
24464ab3302SCarolineConcatto   std::string funcName;
24564ab3302SCarolineConcatto   const auto *symbol{f.proc().GetSymbol()};
24664ab3302SCarolineConcatto   if (symbol) {
24764ab3302SCarolineConcatto     funcName = symbol->name().ToString();
24864ab3302SCarolineConcatto   } else if (const auto *intrinsic{f.proc().GetSpecificIntrinsic()}) {
24964ab3302SCarolineConcatto     funcName = intrinsic->name;
25064ab3302SCarolineConcatto   }
251cb263919SPeter Klausler   auto proc{
252cb263919SPeter Klausler       Procedure::Characterize(f.proc(), foldingContext_, /*emitError=*/true)};
25364ab3302SCarolineConcatto   if (!proc) {
2544171f80dSpeter klausler     return false;
25564ab3302SCarolineConcatto   }
25664ab3302SCarolineConcatto   std::optional<MessageFixedText> msg;
25764ab3302SCarolineConcatto   const auto &funcResult{proc->functionResult}; // C1025
25864ab3302SCarolineConcatto   if (!funcResult) {
25964ab3302SCarolineConcatto     msg = "%s is associated with the non-existent result of reference to"
26064ab3302SCarolineConcatto           " procedure"_err_en_US;
261b8641bfcSPeter Klausler   } else if (CharacterizeProcedure()) {
26264ab3302SCarolineConcatto     // Shouldn't be here in this function unless lhs is an object pointer.
26364ab3302SCarolineConcatto     msg = "Procedure %s is associated with the result of a reference to"
26464ab3302SCarolineConcatto           " function '%s' that does not return a procedure pointer"_err_en_US;
26564ab3302SCarolineConcatto   } else if (funcResult->IsProcedurePointer()) {
26664ab3302SCarolineConcatto     msg = "Object %s is associated with the result of a reference to"
26764ab3302SCarolineConcatto           " function '%s' that is a procedure pointer"_err_en_US;
26864ab3302SCarolineConcatto   } else if (!funcResult->attrs.test(FunctionResult::Attr::Pointer)) {
26964ab3302SCarolineConcatto     msg = "%s is associated with the result of a reference to function '%s'"
27064ab3302SCarolineConcatto           " that is a not a pointer"_err_en_US;
27164ab3302SCarolineConcatto   } else if (isContiguous_ &&
27264ab3302SCarolineConcatto       !funcResult->attrs.test(FunctionResult::Attr::Contiguous)) {
2730f973ac7SPeter Klausler     auto restorer{common::ScopedSet(lhs_, symbol)};
2740f973ac7SPeter Klausler     if (Warn(common::UsageWarning::PointerToPossibleNoncontiguous,
2750f973ac7SPeter Klausler             "CONTIGUOUS %s is associated with the result of reference to function '%s' that is not known to be contiguous"_warn_en_US,
2760f973ac7SPeter Klausler             description_, funcName)) {
2770f973ac7SPeter Klausler       return false;
278505f6da1SPeter Klausler     }
27964ab3302SCarolineConcatto   } else if (lhsType_) {
28064ab3302SCarolineConcatto     const auto *frTypeAndShape{funcResult->GetTypeAndShape()};
28164ab3302SCarolineConcatto     CHECK(frTypeAndShape);
282*b8513e43SPeter Klausler     if (frTypeAndShape->type().IsUnlimitedPolymorphic() &&
283*b8513e43SPeter Klausler         LhsOkForUnlimitedPoly()) {
284*b8513e43SPeter Klausler       // Special case exception to type checking (F'2023 C1017);
285*b8513e43SPeter Klausler       // still check rank compatibility.
286*b8513e43SPeter Klausler       if (auto msg{CheckRanks(*frTypeAndShape)}) {
287*b8513e43SPeter Klausler         Say(*msg);
288*b8513e43SPeter Klausler         return false;
289*b8513e43SPeter Klausler       }
290*b8513e43SPeter Klausler     } else if (!lhsType_->IsCompatibleWith(foldingContext_.messages(),
291*b8513e43SPeter Klausler                    *frTypeAndShape, "pointer", "function result",
292*b8513e43SPeter Klausler                    /*omitShapeConformanceCheck=*/isBoundsRemapping_ ||
293*b8513e43SPeter Klausler                        isAssumedRank_,
294dfecbcaeSpeter klausler                    evaluate::CheckConformanceFlags::BothDeferredShape)) {
295bcb2591bSpeter klausler       return false; // IsCompatibleWith() emitted message
29664ab3302SCarolineConcatto     }
29764ab3302SCarolineConcatto   }
29864ab3302SCarolineConcatto   if (msg) {
29964ab3302SCarolineConcatto     auto restorer{common::ScopedSet(lhs_, symbol)};
30064ab3302SCarolineConcatto     Say(*msg, description_, funcName);
3014171f80dSpeter klausler     return false;
30264ab3302SCarolineConcatto   }
3034171f80dSpeter klausler   return true;
30464ab3302SCarolineConcatto }
30564ab3302SCarolineConcatto 
30664ab3302SCarolineConcatto template <typename T>
3074171f80dSpeter klausler bool PointerAssignmentChecker::Check(const evaluate::Designator<T> &d) {
30864ab3302SCarolineConcatto   const Symbol *last{d.GetLastSymbol()};
30964ab3302SCarolineConcatto   const Symbol *base{d.GetBaseObject().symbol()};
31064ab3302SCarolineConcatto   if (!last || !base) {
31164ab3302SCarolineConcatto     // P => "character literal"(1:3)
3121fa9ef62SPeter Klausler     Say("Pointer target is not a named entity"_err_en_US);
3134171f80dSpeter klausler     return false;
31464ab3302SCarolineConcatto   }
31564ab3302SCarolineConcatto   std::optional<std::variant<MessageFixedText, MessageFormattedText>> msg;
316b8641bfcSPeter Klausler   if (CharacterizeProcedure()) {
31764ab3302SCarolineConcatto     // Shouldn't be here in this function unless lhs is an object pointer.
31864ab3302SCarolineConcatto     msg = "In assignment to procedure %s, the target is not a procedure or"
31964ab3302SCarolineConcatto           " procedure pointer"_err_en_US;
32064ab3302SCarolineConcatto   } else if (!evaluate::GetLastTarget(GetSymbolVector(d))) { // C1025
32164ab3302SCarolineConcatto     msg = "In assignment to object %s, the target '%s' is not an object with"
32264ab3302SCarolineConcatto           " POINTER or TARGET attributes"_err_en_US;
323191d4872SPeter Klausler   } else if (auto rhsType{TypeAndShape::Characterize(d, foldingContext_)}) {
32464ab3302SCarolineConcatto     if (!lhsType_) {
32564ab3302SCarolineConcatto       msg = "%s associated with object '%s' with incompatible type or"
32664ab3302SCarolineConcatto             " shape"_err_en_US;
32764ab3302SCarolineConcatto     } else if (rhsType->corank() > 0 &&
32864ab3302SCarolineConcatto         (isVolatile_ != last->attrs().test(Attr::VOLATILE))) { // C1020
32964ab3302SCarolineConcatto       // TODO: what if A is VOLATILE in A%B%C?  need a better test here
33064ab3302SCarolineConcatto       if (isVolatile_) {
33164ab3302SCarolineConcatto         msg = "Pointer may not be VOLATILE when target is a"
33264ab3302SCarolineConcatto               " non-VOLATILE coarray"_err_en_US;
33364ab3302SCarolineConcatto       } else {
33464ab3302SCarolineConcatto         msg = "Pointer must be VOLATILE when target is a"
33564ab3302SCarolineConcatto               " VOLATILE coarray"_err_en_US;
33664ab3302SCarolineConcatto       }
337*b8513e43SPeter Klausler     } else if (auto m{CheckRanks(*rhsType)}) {
338*b8513e43SPeter Klausler       msg = std::move(*m);
33964ab3302SCarolineConcatto     } else if (rhsType->type().IsUnlimitedPolymorphic()) {
34064ab3302SCarolineConcatto       if (!LhsOkForUnlimitedPoly()) {
34164ab3302SCarolineConcatto         msg = "Pointer type must be unlimited polymorphic or non-extensible"
34264ab3302SCarolineConcatto               " derived type when target is unlimited polymorphic"_err_en_US;
34364ab3302SCarolineConcatto       }
344*b8513e43SPeter Klausler     } else if (!lhsType_->type().IsTkLenCompatibleWith(rhsType->type())) {
34564ab3302SCarolineConcatto       msg = MessageFormattedText{
34664ab3302SCarolineConcatto           "Target type %s is not compatible with pointer type %s"_err_en_US,
34764ab3302SCarolineConcatto           rhsType->type().AsFortran(), lhsType_->type().AsFortran()};
34864ab3302SCarolineConcatto     }
34964ab3302SCarolineConcatto   }
35064ab3302SCarolineConcatto   if (msg) {
35164ab3302SCarolineConcatto     auto restorer{common::ScopedSet(lhs_, last)};
35264ab3302SCarolineConcatto     if (auto *m{std::get_if<MessageFixedText>(&*msg)}) {
3538670e499SCaroline Concatto       std::string buf;
3548670e499SCaroline Concatto       llvm::raw_string_ostream ss{buf};
35564ab3302SCarolineConcatto       d.AsFortran(ss);
356d5dd7d23SYoungsuk Kim       Say(*m, description_, buf);
35764ab3302SCarolineConcatto     } else {
35864ab3302SCarolineConcatto       Say(std::get<MessageFormattedText>(*msg));
35964ab3302SCarolineConcatto     }
3604171f80dSpeter klausler     return false;
36133c27f28SPeter Klausler   } else {
36233c27f28SPeter Klausler     context_.NoteDefinedSymbol(*base);
3634171f80dSpeter klausler     return true;
36464ab3302SCarolineConcatto   }
36533c27f28SPeter Klausler }
36664ab3302SCarolineConcatto 
36764ab3302SCarolineConcatto // Common handling for procedure pointer right-hand sides
36895f4ca7fSPeter Klausler bool PointerAssignmentChecker::Check(parser::CharBlock rhsName, bool isCall,
36995f4ca7fSPeter Klausler     const Procedure *rhsProcedure,
37095f4ca7fSPeter Klausler     const evaluate::SpecificIntrinsic *specific) {
37162d874f2SPeter Klausler   std::string whyNot;
372e86591b3SPeter Klausler   std::optional<std::string> warning;
373b8641bfcSPeter Klausler   CharacterizeProcedure();
37462d874f2SPeter Klausler   if (std::optional<MessageFixedText> msg{evaluate::CheckProcCompatibility(
3751c530b3dSPeter Klausler           isCall, procedure_, rhsProcedure, specific, whyNot, warning,
3761c530b3dSPeter Klausler           /*ignoreImplicitVsExplicit=*/isCall)}) {
37762d874f2SPeter Klausler     Say(std::move(*msg), description_, rhsName, whyNot);
3784171f80dSpeter klausler     return false;
37964ab3302SCarolineConcatto   }
3800f973ac7SPeter Klausler   if (warning) {
3810f973ac7SPeter Klausler     Warn(common::UsageWarning::ProcDummyArgShapes,
3820f973ac7SPeter Klausler         "%s and %s may not be completely compatible procedures: %s"_warn_en_US,
383e86591b3SPeter Klausler         description_, rhsName, std::move(*warning));
384e86591b3SPeter Klausler   }
3854171f80dSpeter klausler   return true;
38664ab3302SCarolineConcatto }
38764ab3302SCarolineConcatto 
3884171f80dSpeter klausler bool PointerAssignmentChecker::Check(const evaluate::ProcedureDesignator &d) {
3890c0b2ea9SPeter Klausler   const Symbol *symbol{d.GetSymbol()};
3900c0b2ea9SPeter Klausler   if (symbol) {
391bd28a0a5SPeter Klausler     if (const auto *subp{
392bd28a0a5SPeter Klausler             symbol->GetUltimate().detailsIf<SubprogramDetails>()}) {
393bd28a0a5SPeter Klausler       if (subp->stmtFunction()) {
394191d4872SPeter Klausler         evaluate::SayWithDeclaration(foldingContext_.messages(), *symbol,
395bd28a0a5SPeter Klausler             "Statement function '%s' may not be the target of a pointer assignment"_err_en_US,
396bd28a0a5SPeter Klausler             symbol->name());
397bd28a0a5SPeter Klausler         return false;
398bd28a0a5SPeter Klausler       }
3990f973ac7SPeter Klausler     } else if (symbol->has<ProcBindingDetails>()) {
4000f973ac7SPeter Klausler       evaluate::AttachDeclaration(
4010f973ac7SPeter Klausler           Warn(common::LanguageFeature::BindingAsProcedure,
402fee041f6SPeter Klausler               "Procedure binding '%s' used as target of a pointer assignment"_port_en_US,
4030f973ac7SPeter Klausler               symbol->name()),
4040f973ac7SPeter Klausler           *symbol);
405bd28a0a5SPeter Klausler     }
406bd28a0a5SPeter Klausler   }
407cb263919SPeter Klausler   if (auto chars{
408cb263919SPeter Klausler           Procedure::Characterize(d, foldingContext_, /*emitError=*/true)}) {
4090c0b2ea9SPeter Klausler     // Disregard the elemental attribute of RHS intrinsics.
4100c0b2ea9SPeter Klausler     if (symbol && symbol->GetUltimate().attrs().test(Attr::INTRINSIC)) {
4110c0b2ea9SPeter Klausler       chars->attrs.reset(Procedure::Attr::Elemental);
4120c0b2ea9SPeter Klausler     }
41395f4ca7fSPeter Klausler     return Check(d.GetName(), false, &*chars, d.GetSpecificIntrinsic());
41464ab3302SCarolineConcatto   } else {
4154171f80dSpeter klausler     return Check(d.GetName(), false);
41664ab3302SCarolineConcatto   }
41764ab3302SCarolineConcatto }
41864ab3302SCarolineConcatto 
4194171f80dSpeter klausler bool PointerAssignmentChecker::Check(const evaluate::ProcedureRef &ref) {
42015c93766SPeter Klausler   auto chars{Procedure::Characterize(ref, foldingContext_)};
42115c93766SPeter Klausler   return Check(ref.proc().GetName(), true, common::GetPtrFromOptional(chars));
42264ab3302SCarolineConcatto }
42364ab3302SCarolineConcatto 
42464ab3302SCarolineConcatto // The target can be unlimited polymorphic if the pointer is, or if it is
42564ab3302SCarolineConcatto // a non-extensible derived type.
42664ab3302SCarolineConcatto bool PointerAssignmentChecker::LhsOkForUnlimitedPoly() const {
42764ab3302SCarolineConcatto   const auto &type{lhsType_->type()};
42864ab3302SCarolineConcatto   if (type.category() != TypeCategory::Derived || type.IsAssumedType()) {
42964ab3302SCarolineConcatto     return false;
43064ab3302SCarolineConcatto   } else if (type.IsUnlimitedPolymorphic()) {
43164ab3302SCarolineConcatto     return true;
43264ab3302SCarolineConcatto   } else {
43364ab3302SCarolineConcatto     return !IsExtensibleType(&type.GetDerivedTypeSpec());
43464ab3302SCarolineConcatto   }
43564ab3302SCarolineConcatto }
43664ab3302SCarolineConcatto 
437*b8513e43SPeter Klausler std::optional<MessageFormattedText> PointerAssignmentChecker::CheckRanks(
438*b8513e43SPeter Klausler     const TypeAndShape &rhs) const {
439*b8513e43SPeter Klausler   if (!isBoundsRemapping_ &&
440*b8513e43SPeter Klausler       !lhsType_->attrs().test(TypeAndShape::Attr::AssumedRank)) {
441*b8513e43SPeter Klausler     int lhsRank{lhsType_->Rank()};
442*b8513e43SPeter Klausler     int rhsRank{rhs.Rank()};
443*b8513e43SPeter Klausler     if (lhsRank != rhsRank) {
444*b8513e43SPeter Klausler       return MessageFormattedText{
445*b8513e43SPeter Klausler           "Pointer has rank %d but target has rank %d"_err_en_US, lhsRank,
446*b8513e43SPeter Klausler           rhsRank};
447*b8513e43SPeter Klausler     }
448*b8513e43SPeter Klausler   }
449*b8513e43SPeter Klausler   return std::nullopt;
450*b8513e43SPeter Klausler }
451*b8513e43SPeter Klausler 
45264ab3302SCarolineConcatto template <typename... A>
45364ab3302SCarolineConcatto parser::Message *PointerAssignmentChecker::Say(A &&...x) {
454191d4872SPeter Klausler   auto *msg{foldingContext_.messages().Say(std::forward<A>(x)...)};
455641ede93Speter klausler   if (msg) {
45664ab3302SCarolineConcatto     if (lhs_) {
45764ab3302SCarolineConcatto       return evaluate::AttachDeclaration(msg, *lhs_);
458641ede93Speter klausler     }
459641ede93Speter klausler     if (!source_.empty()) {
46064ab3302SCarolineConcatto       msg->Attach(source_, "Declaration of %s"_en_US, description_);
46164ab3302SCarolineConcatto     }
462641ede93Speter klausler   }
46364ab3302SCarolineConcatto   return msg;
46464ab3302SCarolineConcatto }
46564ab3302SCarolineConcatto 
4660f973ac7SPeter Klausler template <typename FeatureOrUsageWarning, typename... A>
4670f973ac7SPeter Klausler parser::Message *PointerAssignmentChecker::Warn(
4680f973ac7SPeter Klausler     FeatureOrUsageWarning warning, A &&...x) {
4690f973ac7SPeter Klausler   auto *msg{context_.Warn(
4700f973ac7SPeter Klausler       warning, foldingContext_.messages().at(), std::forward<A>(x)...)};
4710f973ac7SPeter Klausler   if (msg) {
4720f973ac7SPeter Klausler     if (lhs_) {
4730f973ac7SPeter Klausler       return evaluate::AttachDeclaration(msg, *lhs_);
4740f973ac7SPeter Klausler     }
4750f973ac7SPeter Klausler     if (!source_.empty()) {
4760f973ac7SPeter Klausler       msg->Attach(source_, "Declaration of %s"_en_US, description_);
4770f973ac7SPeter Klausler     }
4780f973ac7SPeter Klausler   }
4790f973ac7SPeter Klausler   return msg;
4800f973ac7SPeter Klausler }
4810f973ac7SPeter Klausler 
48264ab3302SCarolineConcatto // Verify that any bounds on the LHS of a pointer assignment are valid.
48364ab3302SCarolineConcatto // Return true if it is a bound-remapping so we can perform further checks.
48464ab3302SCarolineConcatto static bool CheckPointerBounds(
48564ab3302SCarolineConcatto     evaluate::FoldingContext &context, const evaluate::Assignment &assignment) {
48664ab3302SCarolineConcatto   auto &messages{context.messages()};
48764ab3302SCarolineConcatto   const SomeExpr &lhs{assignment.lhs};
48864ab3302SCarolineConcatto   const SomeExpr &rhs{assignment.rhs};
48964ab3302SCarolineConcatto   bool isBoundsRemapping{false};
490cd03e96fSPeter Klausler   std::size_t numBounds{common::visit(
49164ab3302SCarolineConcatto       common::visitors{
49264ab3302SCarolineConcatto           [&](const evaluate::Assignment::BoundsSpec &bounds) {
49364ab3302SCarolineConcatto             return bounds.size();
49464ab3302SCarolineConcatto           },
49564ab3302SCarolineConcatto           [&](const evaluate::Assignment::BoundsRemapping &bounds) {
49664ab3302SCarolineConcatto             isBoundsRemapping = true;
49764ab3302SCarolineConcatto             evaluate::ExtentExpr lhsSizeExpr{1};
49864ab3302SCarolineConcatto             for (const auto &bound : bounds) {
49964ab3302SCarolineConcatto               lhsSizeExpr = std::move(lhsSizeExpr) *
50064ab3302SCarolineConcatto                   (common::Clone(bound.second) - common::Clone(bound.first) +
50164ab3302SCarolineConcatto                       evaluate::ExtentExpr{1});
50264ab3302SCarolineConcatto             }
50364ab3302SCarolineConcatto             if (std::optional<std::int64_t> lhsSize{evaluate::ToInt64(
50464ab3302SCarolineConcatto                     evaluate::Fold(context, std::move(lhsSizeExpr)))}) {
50564ab3302SCarolineConcatto               if (auto shape{evaluate::GetShape(context, rhs)}) {
50664ab3302SCarolineConcatto                 if (std::optional<std::int64_t> rhsSize{
50764ab3302SCarolineConcatto                         evaluate::ToInt64(evaluate::Fold(
50864ab3302SCarolineConcatto                             context, evaluate::GetSize(std::move(*shape))))}) {
50964ab3302SCarolineConcatto                   if (*lhsSize > *rhsSize) {
51064ab3302SCarolineConcatto                     messages.Say(
51164ab3302SCarolineConcatto                         "Pointer bounds require %d elements but target has"
51264ab3302SCarolineConcatto                         " only %d"_err_en_US,
51364ab3302SCarolineConcatto                         *lhsSize, *rhsSize); // 10.2.2.3(9)
51464ab3302SCarolineConcatto                   }
51564ab3302SCarolineConcatto                 }
51664ab3302SCarolineConcatto               }
51764ab3302SCarolineConcatto             }
51864ab3302SCarolineConcatto             return bounds.size();
51964ab3302SCarolineConcatto           },
52064ab3302SCarolineConcatto           [](const auto &) -> std::size_t {
52164ab3302SCarolineConcatto             DIE("not valid for pointer assignment");
52264ab3302SCarolineConcatto           },
52364ab3302SCarolineConcatto       },
52464ab3302SCarolineConcatto       assignment.u)};
52564ab3302SCarolineConcatto   if (numBounds > 0) {
52664ab3302SCarolineConcatto     if (lhs.Rank() != static_cast<int>(numBounds)) {
52764ab3302SCarolineConcatto       messages.Say("Pointer '%s' has rank %d but the number of bounds specified"
52864ab3302SCarolineConcatto                    " is %d"_err_en_US,
52964ab3302SCarolineConcatto           lhs.AsFortran(), lhs.Rank(), numBounds); // C1018
53064ab3302SCarolineConcatto     }
53164ab3302SCarolineConcatto   }
53264ab3302SCarolineConcatto   if (isBoundsRemapping && rhs.Rank() != 1 &&
533641ede93Speter klausler       !evaluate::IsSimplyContiguous(rhs, context)) {
53464ab3302SCarolineConcatto     messages.Say("Pointer bounds remapping target must have rank 1 or be"
53564ab3302SCarolineConcatto                  " simply contiguous"_err_en_US); // 10.2.2.3(9)
53664ab3302SCarolineConcatto   }
53764ab3302SCarolineConcatto   return isBoundsRemapping;
53864ab3302SCarolineConcatto }
53964ab3302SCarolineConcatto 
540191d4872SPeter Klausler bool CheckPointerAssignment(SemanticsContext &context,
541573fc618SPeter Klausler     const evaluate::Assignment &assignment, const Scope &scope) {
542573fc618SPeter Klausler   return CheckPointerAssignment(context, assignment.lhs, assignment.rhs, scope,
543f82ee155SPeter Klausler       CheckPointerBounds(context.foldingContext(), assignment),
544f82ee155SPeter Klausler       /*isAssumedRank=*/false);
5454171f80dSpeter klausler }
5464171f80dSpeter klausler 
547191d4872SPeter Klausler bool CheckPointerAssignment(SemanticsContext &context, const SomeExpr &lhs,
548f82ee155SPeter Klausler     const SomeExpr &rhs, const Scope &scope, bool isBoundsRemapping,
549f82ee155SPeter Klausler     bool isAssumedRank) {
55064ab3302SCarolineConcatto   const Symbol *pointer{GetLastSymbol(lhs)};
55164ab3302SCarolineConcatto   if (!pointer) {
5524171f80dSpeter klausler     return false; // error was reported
55364ab3302SCarolineConcatto   }
554573fc618SPeter Klausler   PointerAssignmentChecker checker{context, scope, *pointer};
555573fc618SPeter Klausler   checker.set_isBoundsRemapping(isBoundsRemapping);
556f82ee155SPeter Klausler   checker.set_isAssumedRank(isAssumedRank);
557ad8f22c3SPeter Klausler   bool lhsOk{checker.CheckLeftHandSide(lhs)};
558ad8f22c3SPeter Klausler   bool rhsOk{checker.Check(rhs)};
559ad8f22c3SPeter Klausler   return lhsOk && rhsOk; // don't short-circuit
56064ab3302SCarolineConcatto }
56164ab3302SCarolineConcatto 
562191d4872SPeter Klausler bool CheckStructConstructorPointerComponent(SemanticsContext &context,
563573fc618SPeter Klausler     const Symbol &lhs, const SomeExpr &rhs, const Scope &scope) {
5641fa9ef62SPeter Klausler   return PointerAssignmentChecker{context, scope, lhs}
5651fa9ef62SPeter Klausler       .set_pointerComponentLHS(&lhs)
5661fa9ef62SPeter Klausler       .Check(rhs);
56764ab3302SCarolineConcatto }
56864ab3302SCarolineConcatto 
569191d4872SPeter Klausler bool CheckPointerAssignment(SemanticsContext &context, parser::CharBlock source,
570191d4872SPeter Klausler     const std::string &description, const DummyDataObject &lhs,
571f82ee155SPeter Klausler     const SomeExpr &rhs, const Scope &scope, bool isAssumedRank) {
572573fc618SPeter Klausler   return PointerAssignmentChecker{context, scope, source, description}
57364ab3302SCarolineConcatto       .set_lhsType(common::Clone(lhs.type))
57464ab3302SCarolineConcatto       .set_isContiguous(lhs.attrs.test(DummyDataObject::Attr::Contiguous))
57564ab3302SCarolineConcatto       .set_isVolatile(lhs.attrs.test(DummyDataObject::Attr::Volatile))
576f82ee155SPeter Klausler       .set_isAssumedRank(isAssumedRank)
57764ab3302SCarolineConcatto       .Check(rhs);
57864ab3302SCarolineConcatto }
57964ab3302SCarolineConcatto 
5800c0b2ea9SPeter Klausler bool CheckInitialDataPointerTarget(SemanticsContext &context,
5810c0b2ea9SPeter Klausler     const SomeExpr &pointer, const SomeExpr &init, const Scope &scope) {
582191d4872SPeter Klausler   return evaluate::IsInitialDataTarget(
583191d4872SPeter Klausler              init, &context.foldingContext().messages()) &&
584f82ee155SPeter Klausler       CheckPointerAssignment(context, pointer, init, scope,
585f82ee155SPeter Klausler           /*isBoundsRemapping=*/false,
586f82ee155SPeter Klausler           /*isAssumedRank=*/false);
5874171f80dSpeter klausler }
5884171f80dSpeter klausler 
5891f879005STim Keith } // namespace Fortran::semantics
590