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