1 //===-- lib/Semantics/definable.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 "definable.h" 10 #include "flang/Evaluate/tools.h" 11 #include "flang/Semantics/tools.h" 12 13 using namespace Fortran::parser::literals; 14 15 namespace Fortran::semantics { 16 17 template <typename... A> 18 static parser::Message BlameSymbol(parser::CharBlock at, 19 const parser::MessageFixedText &text, const Symbol &original, A &&...x) { 20 parser::Message message{at, text, original.name(), std::forward<A>(x)...}; 21 message.set_severity(parser::Severity::Because); 22 evaluate::AttachDeclaration(message, original); 23 return message; 24 } 25 26 static bool IsPointerDummyOfPureFunction(const Symbol &x) { 27 return IsPointerDummy(x) && FindPureProcedureContaining(x.owner()) && 28 x.owner().symbol() && IsFunction(*x.owner().symbol()); 29 } 30 31 // See C1594, first paragraph. These conditions enable checks on both 32 // left-hand and right-hand sides in various circumstances. 33 const char *WhyBaseObjectIsSuspicious(const Symbol &x, const Scope &scope) { 34 if (IsHostAssociatedIntoSubprogram(x, scope)) { 35 return "host-associated"; 36 } else if (IsUseAssociated(x, scope)) { 37 return "USE-associated"; 38 } else if (IsPointerDummyOfPureFunction(x)) { 39 return "a POINTER dummy argument of a pure function"; 40 } else if (IsIntentIn(x)) { 41 return "an INTENT(IN) dummy argument"; 42 } else if (FindCommonBlockContaining(x)) { 43 return "in a COMMON block"; 44 } else { 45 return nullptr; 46 } 47 } 48 49 // Checks C1594(1,2); false if check fails 50 static std::optional<parser::Message> CheckDefinabilityInPureScope( 51 SourceName at, const Symbol &original, const Symbol &ultimate, 52 const Scope &context, const Scope &pure) { 53 if (pure.symbol()) { 54 if (const char *why{WhyBaseObjectIsSuspicious(ultimate, context)}) { 55 return BlameSymbol(at, 56 "'%s' may not be defined in pure subprogram '%s' because it is %s"_en_US, 57 original, pure.symbol()->name(), why); 58 } 59 } 60 return std::nullopt; 61 } 62 63 // When a DataRef contains pointers, gets the rightmost one (unless it is 64 // the entity being defined, in which case the last pointer above it); 65 // otherwise, returns the leftmost symbol. The resulting symbol is the 66 // relevant base object for definabiliy checking. Examples: 67 // ptr1%ptr2 => ... -> ptr1 68 // nonptr%ptr => ... -> nonptr 69 // nonptr%ptr = ... -> ptr 70 // ptr1%ptr2 = ... -> ptr2 71 // ptr1%ptr2%nonptr = ... -> ptr2 72 // nonptr1%nonptr2 = ... -> nonptr1 73 static const Symbol &GetRelevantSymbol(const evaluate::DataRef &dataRef, 74 bool isPointerDefinition, bool acceptAllocatable) { 75 if (isPointerDefinition) { 76 if (const auto *component{std::get_if<evaluate::Component>(&dataRef.u)}) { 77 if (IsPointer(component->GetLastSymbol()) || 78 (acceptAllocatable && IsAllocatable(component->GetLastSymbol()))) { 79 return GetRelevantSymbol(component->base(), false, false); 80 } 81 } 82 } 83 if (const Symbol * lastPointer{GetLastPointerSymbol(dataRef)}) { 84 return *lastPointer; 85 } else { 86 return dataRef.GetFirstSymbol(); 87 } 88 } 89 90 // Check the leftmost (or only) symbol from a data-ref or expression. 91 static std::optional<parser::Message> WhyNotDefinableBase(parser::CharBlock at, 92 const Scope &scope, DefinabilityFlags flags, const Symbol &original) { 93 const Symbol &ultimate{original.GetUltimate()}; 94 bool isPointerDefinition{flags.test(DefinabilityFlag::PointerDefinition)}; 95 bool acceptAllocatable{flags.test(DefinabilityFlag::AcceptAllocatable)}; 96 bool isTargetDefinition{!isPointerDefinition && IsPointer(ultimate)}; 97 if (const auto *association{ultimate.detailsIf<AssocEntityDetails>()}) { 98 if (association->rank().has_value()) { 99 return std::nullopt; // SELECT RANK always modifiable variable 100 } else if (!IsVariable(association->expr())) { 101 return BlameSymbol(at, 102 "'%s' is construct associated with an expression"_en_US, original); 103 } else if (evaluate::HasVectorSubscript(association->expr().value())) { 104 return BlameSymbol(at, 105 "Construct association '%s' has a vector subscript"_en_US, original); 106 } else if (auto dataRef{evaluate::ExtractDataRef( 107 *association->expr(), true, true)}) { 108 return WhyNotDefinableBase(at, scope, flags, 109 GetRelevantSymbol(*dataRef, isPointerDefinition, acceptAllocatable)); 110 } 111 } 112 if (isTargetDefinition) { 113 } else if (!isPointerDefinition && !IsVariableName(ultimate)) { 114 return BlameSymbol(at, "'%s' is not a variable"_en_US, original); 115 } else if (IsProtected(ultimate) && IsUseAssociated(original, scope)) { 116 return BlameSymbol(at, "'%s' is protected in this scope"_en_US, original); 117 } else if (IsIntentIn(ultimate)) { 118 return BlameSymbol( 119 at, "'%s' is an INTENT(IN) dummy argument"_en_US, original); 120 } 121 if (const Scope * pure{FindPureProcedureContaining(scope)}) { 122 // Additional checking for pure subprograms. 123 if (!isTargetDefinition) { 124 if (auto msg{CheckDefinabilityInPureScope( 125 at, original, ultimate, scope, *pure)}) { 126 return msg; 127 } 128 } 129 if (const Symbol * 130 visible{FindExternallyVisibleObject( 131 ultimate, *pure, isPointerDefinition)}) { 132 return BlameSymbol(at, 133 "'%s' is externally visible via '%s' and not definable in a pure subprogram"_en_US, 134 original, visible->name()); 135 } 136 } 137 return std::nullopt; 138 } 139 140 static std::optional<parser::Message> WhyNotDefinableLast(parser::CharBlock at, 141 const Scope &scope, DefinabilityFlags flags, const Symbol &original) { 142 const Symbol &ultimate{original.GetUltimate()}; 143 if (flags.test(DefinabilityFlag::PointerDefinition)) { 144 if (flags.test(DefinabilityFlag::AcceptAllocatable)) { 145 if (!IsAllocatableOrPointer(ultimate)) { 146 return BlameSymbol( 147 at, "'%s' is neither a pointer nor an allocatable"_en_US, original); 148 } 149 } else if (!IsPointer(ultimate)) { 150 return BlameSymbol(at, "'%s' is not a pointer"_en_US, original); 151 } 152 return std::nullopt; // pointer assignment - skip following checks 153 } 154 if (IsOrContainsEventOrLockComponent(ultimate)) { 155 return BlameSymbol(at, 156 "'%s' is an entity with either an EVENT_TYPE or LOCK_TYPE"_en_US, 157 original); 158 } 159 if (FindPureProcedureContaining(scope)) { 160 if (auto dyType{evaluate::DynamicType::From(ultimate)}) { 161 if (!flags.test(DefinabilityFlag::PolymorphicOkInPure)) { 162 if (dyType->IsPolymorphic()) { // C1596 163 return BlameSymbol(at, 164 "'%s' is polymorphic in a pure subprogram"_because_en_US, 165 original); 166 } 167 } 168 if (const Symbol * impure{HasImpureFinal(ultimate)}) { 169 return BlameSymbol(at, 170 "'%s' has an impure FINAL procedure '%s'"_because_en_US, original, 171 impure->name()); 172 } 173 if (const DerivedTypeSpec * derived{GetDerivedTypeSpec(dyType)}) { 174 if (!flags.test(DefinabilityFlag::PolymorphicOkInPure)) { 175 if (auto bad{FindPolymorphicAllocatableUltimateComponent(*derived)}) { 176 return BlameSymbol(at, 177 "'%s' has polymorphic component '%s' in a pure subprogram"_because_en_US, 178 original, bad.BuildResultDesignatorName()); 179 } 180 } 181 } 182 } 183 } 184 return std::nullopt; 185 } 186 187 // Checks a data-ref 188 static std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at, 189 const Scope &scope, DefinabilityFlags flags, 190 const evaluate::DataRef &dataRef) { 191 const Symbol &base{GetRelevantSymbol(dataRef, 192 flags.test(DefinabilityFlag::PointerDefinition), 193 flags.test(DefinabilityFlag::AcceptAllocatable))}; 194 if (auto whyNot{WhyNotDefinableBase(at, scope, flags, base)}) { 195 return whyNot; 196 } else { 197 return WhyNotDefinableLast(at, scope, flags, dataRef.GetLastSymbol()); 198 } 199 } 200 201 // Checks a NOPASS procedure pointer component 202 static std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at, 203 const Scope &scope, DefinabilityFlags flags, 204 const evaluate::Component &component) { 205 const evaluate::DataRef &dataRef{component.base()}; 206 const Symbol &base{GetRelevantSymbol(dataRef, false, false)}; 207 DefinabilityFlags baseFlags{flags}; 208 baseFlags.reset(DefinabilityFlag::PointerDefinition); 209 return WhyNotDefinableBase(at, scope, baseFlags, base); 210 } 211 212 std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at, 213 const Scope &scope, DefinabilityFlags flags, const Symbol &original) { 214 if (auto base{WhyNotDefinableBase(at, scope, flags, original)}) { 215 return base; 216 } 217 return WhyNotDefinableLast(at, scope, flags, original); 218 } 219 220 std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at, 221 const Scope &scope, DefinabilityFlags flags, 222 const evaluate::Expr<evaluate::SomeType> &expr) { 223 if (auto dataRef{evaluate::ExtractDataRef(expr, true, true)}) { 224 if (evaluate::HasVectorSubscript(expr)) { 225 if (flags.test(DefinabilityFlag::VectorSubscriptIsOk)) { 226 if (auto type{expr.GetType()}) { 227 if (!type->IsUnlimitedPolymorphic() && 228 type->category() == TypeCategory::Derived) { 229 // Seek the FINAL subroutine that should but cannot be called 230 // for this definition of an array with a vector-valued subscript. 231 // If there's an elemental FINAL subroutine, all is well; otherwise, 232 // if there is a FINAL subroutine with a matching or assumed rank 233 // dummy argument, there's no way to call it. 234 int rank{expr.Rank()}; 235 const DerivedTypeSpec *spec{&type->GetDerivedTypeSpec()}; 236 while (spec) { 237 bool anyElemental{false}; 238 const Symbol *anyRankMatch{nullptr}; 239 for (auto ref : FinalsForDerivedTypeInstantiation(*spec)) { 240 const Symbol &ultimate{ref->GetUltimate()}; 241 anyElemental |= ultimate.attrs().test(Attr::ELEMENTAL); 242 if (const auto *subp{ultimate.detailsIf<SubprogramDetails>()}) { 243 if (!subp->dummyArgs().empty()) { 244 if (const Symbol * arg{subp->dummyArgs()[0]}) { 245 const auto *object{arg->detailsIf<ObjectEntityDetails>()}; 246 if (arg->Rank() == rank || 247 (object && object->IsAssumedRank())) { 248 anyRankMatch = &*ref; 249 } 250 } 251 } 252 } 253 } 254 if (anyRankMatch && !anyElemental) { 255 return parser::Message{at, 256 "Variable '%s' has a vector subscript and cannot be finalized by non-elemental subroutine '%s'"_because_en_US, 257 expr.AsFortran(), anyRankMatch->name()}; 258 } 259 const auto *parent{FindParentTypeSpec(*spec)}; 260 spec = parent ? parent->AsDerived() : nullptr; 261 } 262 } 263 } 264 } else { 265 return parser::Message{at, 266 "Variable '%s' has a vector subscript"_because_en_US, 267 expr.AsFortran()}; 268 } 269 } 270 if (FindPureProcedureContaining(scope) && 271 evaluate::ExtractCoarrayRef(expr)) { 272 return parser::Message(at, 273 "A pure subprogram may not define the coindexed object '%s'"_because_en_US, 274 expr.AsFortran()); 275 } 276 return WhyNotDefinable(at, scope, flags, *dataRef); 277 } else if (evaluate::IsNullPointer(expr)) { 278 return parser::Message{ 279 at, "'%s' is a null pointer"_because_en_US, expr.AsFortran()}; 280 } else if (flags.test(DefinabilityFlag::PointerDefinition)) { 281 if (const auto *procDesignator{ 282 std::get_if<evaluate::ProcedureDesignator>(&expr.u)}) { 283 // Defining a procedure pointer 284 if (const Symbol * procSym{procDesignator->GetSymbol()}) { 285 if (evaluate::ExtractCoarrayRef(expr)) { // C1027 286 return BlameSymbol(at, 287 "Procedure pointer '%s' may not be a coindexed object"_because_en_US, 288 *procSym, expr.AsFortran()); 289 } 290 if (const auto *component{procDesignator->GetComponent()}) { 291 return WhyNotDefinable(at, scope, flags, *component); 292 } else { 293 return WhyNotDefinable(at, scope, flags, *procSym); 294 } 295 } 296 } 297 return parser::Message{ 298 at, "'%s' is not a definable pointer"_because_en_US, expr.AsFortran()}; 299 } else if (!evaluate::IsVariable(expr)) { 300 return parser::Message{at, 301 "'%s' is not a variable or pointer"_because_en_US, expr.AsFortran()}; 302 } else { 303 return std::nullopt; 304 } 305 } 306 307 } // namespace Fortran::semantics 308