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 bool 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 auto shape{evaluate::GetShape(foldingContext(), lhs)}; 70 if (shape && !shape->empty() && !shape->back().has_value()) { // C1014 71 Say(lhsLoc, 72 "Left-hand side of assignment may not be a whole assumed-size array"_err_en_US); 73 } 74 if (CheckForPureContext(lhs, rhs, rhsLoc, false)) { 75 const Scope &scope{context_.FindScope(lhsLoc)}; 76 if (auto whyNot{WhyNotModifiable(lhsLoc, lhs, scope)}) { 77 if (auto *msg{Say(lhsLoc, 78 "Left-hand side of assignment is not modifiable"_err_en_US)}) { 79 msg->Attach(*whyNot); 80 } 81 } 82 } 83 if (whereDepth_ > 0) { 84 CheckShape(lhsLoc, &lhs); 85 } 86 } 87 } 88 89 void AssignmentContext::Analyze(const parser::PointerAssignmentStmt &stmt) { 90 CHECK(whereDepth_ == 0); 91 if (const evaluate::Assignment * assignment{GetAssignment(stmt)}) { 92 const SomeExpr &lhs{assignment->lhs}; 93 const SomeExpr &rhs{assignment->rhs}; 94 CheckForPureContext(lhs, rhs, std::get<parser::Expr>(stmt.t).source, true); 95 auto restorer{ 96 foldingContext().messages().SetLocation(context_.location().value())}; 97 CheckPointerAssignment(foldingContext(), *assignment); 98 } 99 } 100 101 // C1594 checks 102 static bool IsPointerDummyOfPureFunction(const Symbol &x) { 103 return IsPointerDummy(x) && FindPureProcedureContaining(x.owner()) && 104 x.owner().symbol() && IsFunction(*x.owner().symbol()); 105 } 106 107 static const char *WhyBaseObjectIsSuspicious( 108 const Symbol &x, const Scope &scope) { 109 // See C1594, first paragraph. These conditions enable checks on both 110 // left-hand and right-hand sides in various circumstances. 111 if (IsHostAssociated(x, scope)) { 112 return "host-associated"; 113 } else if (IsUseAssociated(x, scope)) { 114 return "USE-associated"; 115 } else if (IsPointerDummyOfPureFunction(x)) { 116 return "a POINTER dummy argument of a pure function"; 117 } else if (IsIntentIn(x)) { 118 return "an INTENT(IN) dummy argument"; 119 } else if (FindCommonBlockContaining(x)) { 120 return "in a COMMON block"; 121 } else { 122 return nullptr; 123 } 124 } 125 126 // Checks C1594(1,2); false if check fails 127 bool CheckDefinabilityInPureScope(parser::ContextualMessages &messages, 128 const Symbol &lhs, const Scope &context, const Scope &pure) { 129 if (pure.symbol()) { 130 if (const char *why{WhyBaseObjectIsSuspicious(lhs, context)}) { 131 evaluate::SayWithDeclaration(messages, lhs, 132 "Pure subprogram '%s' may not define '%s' because it is %s"_err_en_US, 133 pure.symbol()->name(), lhs.name(), why); 134 return false; 135 } 136 } 137 return true; 138 } 139 140 static std::optional<std::string> GetPointerComponentDesignatorName( 141 const SomeExpr &expr) { 142 if (const auto *derived{ 143 evaluate::GetDerivedTypeSpec(evaluate::DynamicType::From(expr))}) { 144 UltimateComponentIterator ultimates{*derived}; 145 if (auto pointer{ 146 std::find_if(ultimates.begin(), ultimates.end(), IsPointer)}) { 147 return pointer.BuildResultDesignatorName(); 148 } 149 } 150 return std::nullopt; 151 } 152 153 // Checks C1594(5,6); false if check fails 154 bool CheckCopyabilityInPureScope(parser::ContextualMessages &messages, 155 const SomeExpr &expr, const Scope &scope) { 156 if (const Symbol * base{GetFirstSymbol(expr)}) { 157 if (const char *why{WhyBaseObjectIsSuspicious(*base, scope)}) { 158 if (auto pointer{GetPointerComponentDesignatorName(expr)}) { 159 evaluate::SayWithDeclaration(messages, *base, 160 "A pure subprogram may not copy the value of '%s' because it is %s" 161 " and has the POINTER component '%s'"_err_en_US, 162 base->name(), why, *pointer); 163 return false; 164 } 165 } 166 } 167 return true; 168 } 169 170 bool AssignmentContext::CheckForPureContext(const SomeExpr &lhs, 171 const SomeExpr &rhs, parser::CharBlock source, bool isPointerAssignment) { 172 const Scope &scope{context_.FindScope(source)}; 173 if (const Scope * pure{FindPureProcedureContaining(scope)}) { 174 parser::ContextualMessages messages{ 175 context_.location().value(), &context_.messages()}; 176 if (evaluate::ExtractCoarrayRef(lhs)) { 177 messages.Say( 178 "A pure subprogram may not define a coindexed object"_err_en_US); 179 } else if (const Symbol * base{GetFirstSymbol(lhs)}) { 180 if (const auto *assoc{base->detailsIf<AssocEntityDetails>()}) { 181 auto dataRef{ExtractDataRef(assoc->expr())}; 182 // ASSOCIATE(a=>x) -- check x, not a, for "a=..." 183 base = dataRef ? &dataRef->GetFirstSymbol() : nullptr; 184 } 185 if (base && 186 !CheckDefinabilityInPureScope(messages, *base, scope, *pure)) { 187 return false; 188 } 189 } 190 if (isPointerAssignment) { 191 if (const Symbol * base{GetFirstSymbol(rhs)}) { 192 if (const char *why{ 193 WhyBaseObjectIsSuspicious(*base, scope)}) { // C1594(3) 194 evaluate::SayWithDeclaration(messages, *base, 195 "A pure subprogram may not use '%s' as the target of pointer assignment because it is %s"_err_en_US, 196 base->name(), why); 197 return false; 198 } 199 } 200 } else if (auto type{evaluate::DynamicType::From(lhs)}) { 201 // C1596 checks for polymorphic deallocation in a pure subprogram 202 // due to automatic reallocation on assignment 203 if (type->IsPolymorphic()) { 204 context_.Say( 205 "Deallocation of polymorphic object is not permitted in a pure subprogram"_err_en_US); 206 return false; 207 } 208 if (const DerivedTypeSpec * derived{GetDerivedTypeSpec(type)}) { 209 if (auto bad{FindPolymorphicAllocatableNonCoarrayUltimateComponent( 210 *derived)}) { 211 evaluate::SayWithDeclaration(messages, *bad, 212 "Deallocation of polymorphic non-coarray component '%s' is not permitted in a pure subprogram"_err_en_US, 213 bad.BuildResultDesignatorName()); 214 return false; 215 } else { 216 return CheckCopyabilityInPureScope(messages, rhs, scope); 217 } 218 } 219 } 220 } 221 return true; 222 } 223 224 // 10.2.3.1(2) The masks and LHS of assignments must all have the same shape 225 void AssignmentContext::CheckShape(parser::CharBlock at, const SomeExpr *expr) { 226 if (auto shape{evaluate::GetShape(foldingContext(), expr)}) { 227 std::size_t size{shape->size()}; 228 if (whereDepth_ == 0) { 229 whereExtents_.resize(size); 230 } else if (whereExtents_.size() != size) { 231 Say(at, 232 "Must have rank %zd to match prior mask or assignment of" 233 " WHERE construct"_err_en_US, 234 whereExtents_.size()); 235 return; 236 } 237 for (std::size_t i{0}; i < size; ++i) { 238 if (std::optional<std::int64_t> extent{evaluate::ToInt64((*shape)[i])}) { 239 if (!whereExtents_[i]) { 240 whereExtents_[i] = *extent; 241 } else if (*whereExtents_[i] != *extent) { 242 Say(at, 243 "Dimension %d must have extent %jd to match prior mask or" 244 " assignment of WHERE construct"_err_en_US, 245 i + 1, static_cast<std::intmax_t>(*whereExtents_[i])); 246 } 247 } 248 } 249 } 250 } 251 252 template<typename A> void AssignmentContext::PushWhereContext(const A &x) { 253 const auto &expr{std::get<parser::LogicalExpr>(x.t)}; 254 CheckShape(expr.thing.value().source, GetExpr(expr)); 255 ++whereDepth_; 256 } 257 258 void AssignmentContext::PopWhereContext() { 259 --whereDepth_; 260 if (whereDepth_ == 0) { 261 whereExtents_.clear(); 262 } 263 } 264 265 AssignmentChecker::~AssignmentChecker() {} 266 267 AssignmentChecker::AssignmentChecker(SemanticsContext &context) 268 : context_{new AssignmentContext{context}} {} 269 void AssignmentChecker::Enter(const parser::AssignmentStmt &x) { 270 context_.value().Analyze(x); 271 } 272 void AssignmentChecker::Enter(const parser::PointerAssignmentStmt &x) { 273 context_.value().Analyze(x); 274 } 275 void AssignmentChecker::Enter(const parser::WhereStmt &x) { 276 context_.value().PushWhereContext(x); 277 } 278 void AssignmentChecker::Leave(const parser::WhereStmt &) { 279 context_.value().PopWhereContext(); 280 } 281 void AssignmentChecker::Enter(const parser::WhereConstructStmt &x) { 282 context_.value().PushWhereContext(x); 283 } 284 void AssignmentChecker::Leave(const parser::EndWhereStmt &) { 285 context_.value().PopWhereContext(); 286 } 287 void AssignmentChecker::Enter(const parser::MaskedElsewhereStmt &x) { 288 context_.value().PushWhereContext(x); 289 } 290 void AssignmentChecker::Leave(const parser::MaskedElsewhereStmt &) { 291 context_.value().PopWhereContext(); 292 } 293 294 } 295 template class Fortran::common::Indirection< 296 Fortran::semantics::AssignmentContext>; 297