1 //===-- lib/Evaluate/check-expression.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 "flang/Evaluate/check-expression.h" 10 #include "flang/Evaluate/intrinsics.h" 11 #include "flang/Evaluate/traverse.h" 12 #include "flang/Evaluate/type.h" 13 #include "flang/Semantics/symbol.h" 14 #include "flang/Semantics/tools.h" 15 #include <set> 16 #include <string> 17 18 namespace Fortran::evaluate { 19 20 // Constant expression predicate IsConstantExpr(). 21 // This code determines whether an expression is a "constant expression" 22 // in the sense of section 10.1.12. This is not the same thing as being 23 // able to fold it (yet) into a known constant value; specifically, 24 // the expression may reference derived type kind parameters whose values 25 // are not yet known. 26 class IsConstantExprHelper : public AllTraverse<IsConstantExprHelper, true> { 27 public: 28 using Base = AllTraverse<IsConstantExprHelper, true>; 29 IsConstantExprHelper() : Base{*this} {} 30 using Base::operator(); 31 32 bool operator()(const TypeParamInquiry &inq) const { 33 return IsKindTypeParameter(inq.parameter()); 34 } 35 bool operator()(const semantics::Symbol &symbol) const { 36 const auto &ultimate{symbol.GetUltimate()}; 37 return IsNamedConstant(ultimate) || IsImpliedDoIndex(ultimate) || 38 IsInitialProcedureTarget(ultimate); 39 } 40 bool operator()(const CoarrayRef &) const { return false; } 41 bool operator()(const semantics::ParamValue ¶m) const { 42 return param.isExplicit() && (*this)(param.GetExplicit()); 43 } 44 template <typename T> bool operator()(const FunctionRef<T> &call) const { 45 if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&call.proc().u)}) { 46 // kind is always a constant, and we avoid cascading errors by calling 47 // invalid calls to intrinsics constant 48 return intrinsic->name == "kind" || 49 intrinsic->name == IntrinsicProcTable::InvalidName; 50 // TODO: other inquiry intrinsics 51 } else { 52 return false; 53 } 54 } 55 bool operator()(const StructureConstructor &constructor) const { 56 for (const auto &[symRef, expr] : constructor) { 57 if (!IsConstantStructureConstructorComponent(*symRef, expr.value())) { 58 return false; 59 } 60 } 61 return true; 62 } 63 bool operator()(const Component &component) const { 64 return (*this)(component.base()); 65 } 66 // Forbid integer division by zero in constants. 67 template <int KIND> 68 bool operator()( 69 const Divide<Type<TypeCategory::Integer, KIND>> &division) const { 70 using T = Type<TypeCategory::Integer, KIND>; 71 if (const auto divisor{GetScalarConstantValue<T>(division.right())}) { 72 return !divisor->IsZero() && (*this)(division.left()); 73 } else { 74 return false; 75 } 76 } 77 78 bool operator()(const Constant<SomeDerived> &) const { return true; } 79 80 private: 81 bool IsConstantStructureConstructorComponent( 82 const Symbol &component, const Expr<SomeType> &expr) const { 83 if (IsAllocatable(component)) { 84 return IsNullPointer(expr); 85 } else if (IsPointer(component)) { 86 return IsNullPointer(expr) || IsInitialDataTarget(expr) || 87 IsInitialProcedureTarget(expr); 88 } else { 89 return (*this)(expr); 90 } 91 } 92 }; 93 94 template <typename A> bool IsConstantExpr(const A &x) { 95 return IsConstantExprHelper{}(x); 96 } 97 template bool IsConstantExpr(const Expr<SomeType> &); 98 template bool IsConstantExpr(const Expr<SomeInteger> &); 99 template bool IsConstantExpr(const Expr<SubscriptInteger> &); 100 template bool IsConstantExpr(const StructureConstructor &); 101 102 // Object pointer initialization checking predicate IsInitialDataTarget(). 103 // This code determines whether an expression is allowable as the static 104 // data address used to initialize a pointer with "=> x". See C765. 105 class IsInitialDataTargetHelper 106 : public AllTraverse<IsInitialDataTargetHelper, true> { 107 public: 108 using Base = AllTraverse<IsInitialDataTargetHelper, true>; 109 using Base::operator(); 110 explicit IsInitialDataTargetHelper(parser::ContextualMessages *m) 111 : Base{*this}, messages_{m} {} 112 113 bool emittedMessage() const { return emittedMessage_; } 114 115 bool operator()(const BOZLiteralConstant &) const { return false; } 116 bool operator()(const NullPointer &) const { return true; } 117 template <typename T> bool operator()(const Constant<T> &) const { 118 return false; 119 } 120 bool operator()(const semantics::Symbol &symbol) { 121 const Symbol &ultimate{symbol.GetUltimate()}; 122 if (IsAllocatable(ultimate)) { 123 if (messages_) { 124 messages_->Say( 125 "An initial data target may not be a reference to an ALLOCATABLE '%s'"_err_en_US, 126 ultimate.name()); 127 emittedMessage_ = true; 128 } 129 return false; 130 } else if (ultimate.Corank() > 0) { 131 if (messages_) { 132 messages_->Say( 133 "An initial data target may not be a reference to a coarray '%s'"_err_en_US, 134 ultimate.name()); 135 emittedMessage_ = true; 136 } 137 return false; 138 } else if (!ultimate.attrs().test(semantics::Attr::TARGET)) { 139 if (messages_) { 140 messages_->Say( 141 "An initial data target may not be a reference to an object '%s' that lacks the TARGET attribute"_err_en_US, 142 ultimate.name()); 143 emittedMessage_ = true; 144 } 145 return false; 146 } else if (!IsSaved(ultimate)) { 147 if (messages_) { 148 messages_->Say( 149 "An initial data target may not be a reference to an object '%s' that lacks the SAVE attribute"_err_en_US, 150 ultimate.name()); 151 emittedMessage_ = true; 152 } 153 return false; 154 } 155 return true; 156 } 157 bool operator()(const StaticDataObject &) const { return false; } 158 bool operator()(const TypeParamInquiry &) const { return false; } 159 bool operator()(const Triplet &x) const { 160 return IsConstantExpr(x.lower()) && IsConstantExpr(x.upper()) && 161 IsConstantExpr(x.stride()); 162 } 163 bool operator()(const Subscript &x) const { 164 return std::visit(common::visitors{ 165 [&](const Triplet &t) { return (*this)(t); }, 166 [&](const auto &y) { 167 return y.value().Rank() == 0 && 168 IsConstantExpr(y.value()); 169 }, 170 }, 171 x.u); 172 } 173 bool operator()(const CoarrayRef &) const { return false; } 174 bool operator()(const Substring &x) const { 175 return IsConstantExpr(x.lower()) && IsConstantExpr(x.upper()) && 176 (*this)(x.parent()); 177 } 178 bool operator()(const DescriptorInquiry &) const { return false; } 179 template <typename T> bool operator()(const ArrayConstructor<T> &) const { 180 return false; 181 } 182 bool operator()(const StructureConstructor &) const { return false; } 183 template <typename T> bool operator()(const FunctionRef<T> &) { 184 return false; 185 } 186 template <typename D, typename R, typename... O> 187 bool operator()(const Operation<D, R, O...> &) const { 188 return false; 189 } 190 template <typename T> bool operator()(const Parentheses<T> &x) const { 191 return (*this)(x.left()); 192 } 193 template <typename T> bool operator()(const FunctionRef<T> &x) const { 194 return false; 195 } 196 bool operator()(const Relational<SomeType> &) const { return false; } 197 198 private: 199 parser::ContextualMessages *messages_; 200 bool emittedMessage_{false}; 201 }; 202 203 bool IsInitialDataTarget( 204 const Expr<SomeType> &x, parser::ContextualMessages *messages) { 205 IsInitialDataTargetHelper helper{messages}; 206 bool result{helper(x)}; 207 if (!result && messages && !helper.emittedMessage()) { 208 messages->Say( 209 "An initial data target must be a designator with constant subscripts"_err_en_US); 210 } 211 return result; 212 } 213 214 bool IsInitialProcedureTarget(const semantics::Symbol &symbol) { 215 const auto &ultimate{symbol.GetUltimate()}; 216 return std::visit( 217 common::visitors{ 218 [](const semantics::SubprogramDetails &) { return true; }, 219 [](const semantics::SubprogramNameDetails &) { return true; }, 220 [&](const semantics::ProcEntityDetails &proc) { 221 return !semantics::IsPointer(ultimate) && !proc.isDummy(); 222 }, 223 [](const auto &) { return false; }, 224 }, 225 ultimate.details()); 226 } 227 228 bool IsInitialProcedureTarget(const ProcedureDesignator &proc) { 229 if (const auto *intrin{proc.GetSpecificIntrinsic()}) { 230 return !intrin->isRestrictedSpecific; 231 } else if (proc.GetComponent()) { 232 return false; 233 } else { 234 return IsInitialProcedureTarget(DEREF(proc.GetSymbol())); 235 } 236 } 237 238 bool IsInitialProcedureTarget(const Expr<SomeType> &expr) { 239 if (const auto *proc{std::get_if<ProcedureDesignator>(&expr.u)}) { 240 return IsInitialProcedureTarget(*proc); 241 } else { 242 return IsNullPointer(expr); 243 } 244 } 245 246 // Specification expression validation (10.1.11(2), C1010) 247 class CheckSpecificationExprHelper 248 : public AnyTraverse<CheckSpecificationExprHelper, 249 std::optional<std::string>> { 250 public: 251 using Result = std::optional<std::string>; 252 using Base = AnyTraverse<CheckSpecificationExprHelper, Result>; 253 explicit CheckSpecificationExprHelper( 254 const semantics::Scope &s, const IntrinsicProcTable &table) 255 : Base{*this}, scope_{s}, table_{table} {} 256 using Base::operator(); 257 258 Result operator()(const ProcedureDesignator &) const { 259 return "dummy procedure argument"; 260 } 261 Result operator()(const CoarrayRef &) const { return "coindexed reference"; } 262 263 Result operator()(const semantics::Symbol &symbol) const { 264 const auto &ultimate{symbol.GetUltimate()}; 265 if (semantics::IsNamedConstant(ultimate) || ultimate.owner().IsModule() || 266 ultimate.owner().IsSubmodule()) { 267 return std::nullopt; 268 } else if (scope_.IsDerivedType() && 269 IsVariableName(ultimate)) { // C750, C754 270 return "derived type component or type parameter value not allowed to " 271 "reference variable '"s + 272 ultimate.name().ToString() + "'"; 273 } else if (IsDummy(ultimate)) { 274 if (ultimate.attrs().test(semantics::Attr::OPTIONAL)) { 275 return "reference to OPTIONAL dummy argument '"s + 276 ultimate.name().ToString() + "'"; 277 } else if (ultimate.attrs().test(semantics::Attr::INTENT_OUT)) { 278 return "reference to INTENT(OUT) dummy argument '"s + 279 ultimate.name().ToString() + "'"; 280 } else if (ultimate.has<semantics::ObjectEntityDetails>()) { 281 return std::nullopt; 282 } else { 283 return "dummy procedure argument"; 284 } 285 } else if (const auto *object{ 286 ultimate.detailsIf<semantics::ObjectEntityDetails>()}) { 287 // TODO: what about EQUIVALENCE with data in COMMON? 288 // TODO: does this work for blank COMMON? 289 if (object->commonBlock()) { 290 return std::nullopt; 291 } 292 } 293 for (const semantics::Scope *s{&scope_}; !s->IsGlobal();) { 294 s = &s->parent(); 295 if (s == &ultimate.owner()) { 296 return std::nullopt; 297 } 298 } 299 return "reference to local entity '"s + ultimate.name().ToString() + "'"; 300 } 301 302 Result operator()(const Component &x) const { 303 // Don't look at the component symbol. 304 return (*this)(x.base()); 305 } 306 Result operator()(const DescriptorInquiry &) const { 307 // Subtle: Uses of SIZE(), LBOUND(), &c. that are valid in specification 308 // expressions will have been converted to expressions over descriptor 309 // inquiries by Fold(). 310 return std::nullopt; 311 } 312 313 Result operator()(const TypeParamInquiry &inq) const { 314 if (scope_.IsDerivedType() && !IsConstantExpr(inq) && 315 inq.base() /* X%T, not local T */) { // C750, C754 316 return "non-constant reference to a type parameter inquiry not " 317 "allowed for derived type components or type parameter values"; 318 } 319 return std::nullopt; 320 } 321 322 template <typename T> Result operator()(const FunctionRef<T> &x) const { 323 if (const auto *symbol{x.proc().GetSymbol()}) { 324 if (!semantics::IsPureProcedure(*symbol)) { 325 return "reference to impure function '"s + symbol->name().ToString() + 326 "'"; 327 } 328 if (semantics::IsStmtFunction(*symbol)) { 329 return "reference to statement function '"s + 330 symbol->name().ToString() + "'"; 331 } 332 if (scope_.IsDerivedType()) { // C750, C754 333 return "reference to function '"s + symbol->name().ToString() + 334 "' not allowed for derived type components or type parameter" 335 " values"; 336 } 337 // TODO: other checks for standard module procedures 338 } else { 339 const SpecificIntrinsic &intrin{DEREF(x.proc().GetSpecificIntrinsic())}; 340 if (scope_.IsDerivedType()) { // C750, C754 341 if ((table_.IsIntrinsic(intrin.name) && 342 badIntrinsicsForComponents_.find(intrin.name) != 343 badIntrinsicsForComponents_.end()) || 344 IsProhibitedFunction(intrin.name)) { 345 return "reference to intrinsic '"s + intrin.name + 346 "' not allowed for derived type components or type parameter" 347 " values"; 348 } 349 if (table_.GetIntrinsicClass(intrin.name) == 350 IntrinsicClass::inquiryFunction && 351 !IsConstantExpr(x)) { 352 return "non-constant reference to inquiry intrinsic '"s + 353 intrin.name + 354 "' not allowed for derived type components or type" 355 " parameter values"; 356 } 357 } else if (intrin.name == "present") { 358 return std::nullopt; // no need to check argument(s) 359 } 360 if (IsConstantExpr(x)) { 361 // inquiry functions may not need to check argument(s) 362 return std::nullopt; 363 } 364 } 365 return (*this)(x.arguments()); 366 } 367 368 private: 369 const semantics::Scope &scope_; 370 const IntrinsicProcTable &table_; 371 const std::set<std::string> badIntrinsicsForComponents_{ 372 "allocated", "associated", "extends_type_of", "present", "same_type_as"}; 373 static bool IsProhibitedFunction(std::string name) { return false; } 374 }; 375 376 template <typename A> 377 void CheckSpecificationExpr(const A &x, parser::ContextualMessages &messages, 378 const semantics::Scope &scope, const IntrinsicProcTable &table) { 379 if (auto why{CheckSpecificationExprHelper{scope, table}(x)}) { 380 messages.Say("Invalid specification expression: %s"_err_en_US, *why); 381 } 382 } 383 384 template void CheckSpecificationExpr(const Expr<SomeType> &, 385 parser::ContextualMessages &, const semantics::Scope &, 386 const IntrinsicProcTable &); 387 template void CheckSpecificationExpr(const Expr<SomeInteger> &, 388 parser::ContextualMessages &, const semantics::Scope &, 389 const IntrinsicProcTable &); 390 template void CheckSpecificationExpr(const Expr<SubscriptInteger> &, 391 parser::ContextualMessages &, const semantics::Scope &, 392 const IntrinsicProcTable &); 393 template void CheckSpecificationExpr(const std::optional<Expr<SomeType>> &, 394 parser::ContextualMessages &, const semantics::Scope &, 395 const IntrinsicProcTable &); 396 template void CheckSpecificationExpr(const std::optional<Expr<SomeInteger>> &, 397 parser::ContextualMessages &, const semantics::Scope &, 398 const IntrinsicProcTable &); 399 template void CheckSpecificationExpr( 400 const std::optional<Expr<SubscriptInteger>> &, parser::ContextualMessages &, 401 const semantics::Scope &, const IntrinsicProcTable &); 402 403 // IsSimplyContiguous() -- 9.5.4 404 class IsSimplyContiguousHelper 405 : public AnyTraverse<IsSimplyContiguousHelper, std::optional<bool>> { 406 public: 407 using Result = std::optional<bool>; // tri-state 408 using Base = AnyTraverse<IsSimplyContiguousHelper, Result>; 409 explicit IsSimplyContiguousHelper(const IntrinsicProcTable &t) 410 : Base{*this}, table_{t} {} 411 using Base::operator(); 412 413 Result operator()(const semantics::Symbol &symbol) const { 414 if (symbol.attrs().test(semantics::Attr::CONTIGUOUS) || 415 symbol.Rank() == 0) { 416 return true; 417 } else if (semantics::IsPointer(symbol)) { 418 return false; 419 } else if (const auto *details{ 420 symbol.detailsIf<semantics::ObjectEntityDetails>()}) { 421 // N.B. ALLOCATABLEs are deferred shape, not assumed, and 422 // are obviously contiguous. 423 return !details->IsAssumedShape() && !details->IsAssumedRank(); 424 } else { 425 return false; 426 } 427 } 428 429 Result operator()(const ArrayRef &x) const { 430 const auto &symbol{x.GetLastSymbol()}; 431 if (!(*this)(symbol)) { 432 return false; 433 } else if (auto rank{CheckSubscripts(x.subscript())}) { 434 // a(:)%b(1,1) is not contiguous; a(1)%b(:,:) is 435 return *rank > 0 || x.Rank() == 0; 436 } else { 437 return false; 438 } 439 } 440 Result operator()(const CoarrayRef &x) const { 441 return CheckSubscripts(x.subscript()).has_value(); 442 } 443 Result operator()(const Component &x) const { 444 return x.base().Rank() == 0 && (*this)(x.GetLastSymbol()); 445 } 446 Result operator()(const ComplexPart &) const { return false; } 447 Result operator()(const Substring &) const { return false; } 448 449 template <typename T> Result operator()(const FunctionRef<T> &x) const { 450 if (auto chars{ 451 characteristics::Procedure::Characterize(x.proc(), table_)}) { 452 if (chars->functionResult) { 453 const auto &result{*chars->functionResult}; 454 return !result.IsProcedurePointer() && 455 result.attrs.test(characteristics::FunctionResult::Attr::Pointer) && 456 result.attrs.test( 457 characteristics::FunctionResult::Attr::Contiguous); 458 } 459 } 460 return false; 461 } 462 463 private: 464 // If the subscripts can possibly be on a simply-contiguous array reference, 465 // return the rank. 466 static std::optional<int> CheckSubscripts( 467 const std::vector<Subscript> &subscript) { 468 bool anyTriplet{false}; 469 int rank{0}; 470 for (auto j{subscript.size()}; j-- > 0;) { 471 if (const auto *triplet{std::get_if<Triplet>(&subscript[j].u)}) { 472 if (!triplet->IsStrideOne()) { 473 return std::nullopt; 474 } else if (anyTriplet) { 475 if (triplet->lower() || triplet->upper()) { 476 // all triplets before the last one must be just ":" 477 return std::nullopt; 478 } 479 } else { 480 anyTriplet = true; 481 } 482 ++rank; 483 } else if (anyTriplet || subscript[j].Rank() > 0) { 484 return std::nullopt; 485 } 486 } 487 return rank; 488 } 489 490 const IntrinsicProcTable &table_; 491 }; 492 493 template <typename A> 494 bool IsSimplyContiguous(const A &x, const IntrinsicProcTable &table) { 495 if (IsVariable(x)) { 496 auto known{IsSimplyContiguousHelper{table}(x)}; 497 return known && *known; 498 } else { 499 return true; // not a variable 500 } 501 } 502 503 template bool IsSimplyContiguous( 504 const Expr<SomeType> &, const IntrinsicProcTable &); 505 506 } // namespace Fortran::evaluate 507