xref: /llvm-project/flang/lib/Evaluate/check-expression.cpp (revision 27004e027312a59e3b6645f5df58e97c2a2da6ef)
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/characteristics.h"
11 #include "flang/Evaluate/intrinsics.h"
12 #include "flang/Evaluate/tools.h"
13 #include "flang/Evaluate/traverse.h"
14 #include "flang/Evaluate/type.h"
15 #include "flang/Semantics/symbol.h"
16 #include "flang/Semantics/tools.h"
17 #include <set>
18 #include <string>
19 
20 namespace Fortran::evaluate {
21 
22 // Constant expression predicates IsConstantExpr() & IsScopeInvariantExpr().
23 // This code determines whether an expression is a "constant expression"
24 // in the sense of section 10.1.12.  This is not the same thing as being
25 // able to fold it (yet) into a known constant value; specifically,
26 // the expression may reference derived type kind parameters whose values
27 // are not yet known.
28 //
29 // The variant form (IsScopeInvariantExpr()) also accepts symbols that are
30 // INTENT(IN) dummy arguments without the VALUE attribute.
31 template <bool INVARIANT>
32 class IsConstantExprHelper
33     : public AllTraverse<IsConstantExprHelper<INVARIANT>, true> {
34 public:
35   using Base = AllTraverse<IsConstantExprHelper, true>;
36   IsConstantExprHelper() : Base{*this} {}
37   using Base::operator();
38 
39   // A missing expression is not considered to be constant.
40   template <typename A> bool operator()(const std::optional<A> &x) const {
41     return x && (*this)(*x);
42   }
43 
44   bool operator()(const TypeParamInquiry &inq) const {
45     return INVARIANT || semantics::IsKindTypeParameter(inq.parameter());
46   }
47   bool operator()(const semantics::Symbol &symbol) const {
48     const auto &ultimate{GetAssociationRoot(symbol)};
49     return IsNamedConstant(ultimate) || IsImpliedDoIndex(ultimate) ||
50         IsInitialProcedureTarget(ultimate) ||
51         ultimate.has<semantics::TypeParamDetails>() ||
52         (INVARIANT && IsIntentIn(symbol) && !IsOptional(symbol) &&
53             !symbol.attrs().test(semantics::Attr::VALUE));
54   }
55   bool operator()(const CoarrayRef &) const { return false; }
56   bool operator()(const semantics::ParamValue &param) const {
57     return param.isExplicit() && (*this)(param.GetExplicit());
58   }
59   bool operator()(const ProcedureRef &) const;
60   bool operator()(const StructureConstructor &constructor) const {
61     for (const auto &[symRef, expr] : constructor) {
62       if (!IsConstantStructureConstructorComponent(*symRef, expr.value())) {
63         return false;
64       }
65     }
66     return true;
67   }
68   bool operator()(const Component &component) const {
69     return (*this)(component.base());
70   }
71   // Forbid integer division by zero in constants.
72   template <int KIND>
73   bool operator()(
74       const Divide<Type<TypeCategory::Integer, KIND>> &division) const {
75     using T = Type<TypeCategory::Integer, KIND>;
76     if (const auto divisor{GetScalarConstantValue<T>(division.right())}) {
77       return !divisor->IsZero() && (*this)(division.left());
78     } else {
79       return false;
80     }
81   }
82 
83   bool operator()(const Constant<SomeDerived> &) const { return true; }
84   bool operator()(const DescriptorInquiry &x) const {
85     const Symbol &sym{x.base().GetLastSymbol()};
86     return INVARIANT && !IsAllocatable(sym) &&
87         (!IsDummy(sym) ||
88             (IsIntentIn(sym) && !IsOptional(sym) &&
89                 !sym.attrs().test(semantics::Attr::VALUE)));
90   }
91 
92 private:
93   bool IsConstantStructureConstructorComponent(
94       const Symbol &, const Expr<SomeType> &) const;
95   bool IsConstantExprShape(const Shape &) const;
96 };
97 
98 template <bool INVARIANT>
99 bool IsConstantExprHelper<INVARIANT>::IsConstantStructureConstructorComponent(
100     const Symbol &component, const Expr<SomeType> &expr) const {
101   if (IsAllocatable(component)) {
102     return IsNullObjectPointer(expr);
103   } else if (IsPointer(component)) {
104     return IsNullPointer(expr) || IsInitialDataTarget(expr) ||
105         IsInitialProcedureTarget(expr);
106   } else {
107     return (*this)(expr);
108   }
109 }
110 
111 template <bool INVARIANT>
112 bool IsConstantExprHelper<INVARIANT>::operator()(
113     const ProcedureRef &call) const {
114   // LBOUND, UBOUND, and SIZE with truly constant DIM= arguments will have
115   // been rewritten into DescriptorInquiry operations.
116   if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&call.proc().u)}) {
117     if (intrinsic->name == "kind" ||
118         intrinsic->name == IntrinsicProcTable::InvalidName ||
119         call.arguments().empty() || !call.arguments()[0]) {
120       // kind is always a constant, and we avoid cascading errors by considering
121       // invalid calls to intrinsics to be constant
122       return true;
123     } else if (intrinsic->name == "lbound") {
124       auto base{ExtractNamedEntity(call.arguments()[0]->UnwrapExpr())};
125       return base && IsConstantExprShape(GetLBOUNDs(*base));
126     } else if (intrinsic->name == "ubound") {
127       auto base{ExtractNamedEntity(call.arguments()[0]->UnwrapExpr())};
128       return base && IsConstantExprShape(GetUBOUNDs(*base));
129     } else if (intrinsic->name == "shape" || intrinsic->name == "size") {
130       auto shape{GetShape(call.arguments()[0]->UnwrapExpr())};
131       return shape && IsConstantExprShape(*shape);
132     }
133     // TODO: STORAGE_SIZE
134   }
135   return false;
136 }
137 
138 template <bool INVARIANT>
139 bool IsConstantExprHelper<INVARIANT>::IsConstantExprShape(
140     const Shape &shape) const {
141   for (const auto &extent : shape) {
142     if (!(*this)(extent)) {
143       return false;
144     }
145   }
146   return true;
147 }
148 
149 template <typename A> bool IsConstantExpr(const A &x) {
150   return IsConstantExprHelper<false>{}(x);
151 }
152 template bool IsConstantExpr(const Expr<SomeType> &);
153 template bool IsConstantExpr(const Expr<SomeInteger> &);
154 template bool IsConstantExpr(const Expr<SubscriptInteger> &);
155 template bool IsConstantExpr(const StructureConstructor &);
156 
157 // IsScopeInvariantExpr()
158 template <typename A> bool IsScopeInvariantExpr(const A &x) {
159   return IsConstantExprHelper<true>{}(x);
160 }
161 template bool IsScopeInvariantExpr(const Expr<SomeType> &);
162 template bool IsScopeInvariantExpr(const Expr<SomeInteger> &);
163 template bool IsScopeInvariantExpr(const Expr<SubscriptInteger> &);
164 
165 // IsActuallyConstant()
166 struct IsActuallyConstantHelper {
167   template <typename A> bool operator()(const A &) { return false; }
168   template <typename T> bool operator()(const Constant<T> &) { return true; }
169   template <typename T> bool operator()(const Parentheses<T> &x) {
170     return (*this)(x.left());
171   }
172   template <typename T> bool operator()(const Expr<T> &x) {
173     return common::visit([=](const auto &y) { return (*this)(y); }, x.u);
174   }
175   bool operator()(const Expr<SomeType> &x) {
176     return common::visit([this](const auto &y) { return (*this)(y); }, x.u);
177   }
178   bool operator()(const StructureConstructor &x) {
179     for (const auto &pair : x) {
180       const Expr<SomeType> &y{pair.second.value()};
181       if (!(*this)(y) && !IsNullPointer(y)) {
182         return false;
183       }
184     }
185     return true;
186   }
187   template <typename A> bool operator()(const A *x) { return x && (*this)(*x); }
188   template <typename A> bool operator()(const std::optional<A> &x) {
189     return x && (*this)(*x);
190   }
191 };
192 
193 template <typename A> bool IsActuallyConstant(const A &x) {
194   return IsActuallyConstantHelper{}(x);
195 }
196 
197 template bool IsActuallyConstant(const Expr<SomeType> &);
198 template bool IsActuallyConstant(const Expr<SomeInteger> &);
199 template bool IsActuallyConstant(const Expr<SubscriptInteger> &);
200 template bool IsActuallyConstant(const std::optional<Expr<SubscriptInteger>> &);
201 
202 // Object pointer initialization checking predicate IsInitialDataTarget().
203 // This code determines whether an expression is allowable as the static
204 // data address used to initialize a pointer with "=> x".  See C765.
205 class IsInitialDataTargetHelper
206     : public AllTraverse<IsInitialDataTargetHelper, true> {
207 public:
208   using Base = AllTraverse<IsInitialDataTargetHelper, true>;
209   using Base::operator();
210   explicit IsInitialDataTargetHelper(parser::ContextualMessages *m)
211       : Base{*this}, messages_{m} {}
212 
213   bool emittedMessage() const { return emittedMessage_; }
214 
215   bool operator()(const BOZLiteralConstant &) const { return false; }
216   bool operator()(const NullPointer &) const { return true; }
217   template <typename T> bool operator()(const Constant<T> &) const {
218     return false;
219   }
220   bool operator()(const semantics::Symbol &symbol) {
221     // This function checks only base symbols, not components.
222     const Symbol &ultimate{symbol.GetUltimate()};
223     if (const auto *assoc{
224             ultimate.detailsIf<semantics::AssocEntityDetails>()}) {
225       if (const auto &expr{assoc->expr()}) {
226         if (IsVariable(*expr)) {
227           return (*this)(*expr);
228         } else if (messages_) {
229           messages_->Say(
230               "An initial data target may not be an associated expression ('%s')"_err_en_US,
231               ultimate.name());
232           emittedMessage_ = true;
233         }
234       }
235       return false;
236     } else if (!ultimate.attrs().test(semantics::Attr::TARGET)) {
237       if (messages_) {
238         messages_->Say(
239             "An initial data target may not be a reference to an object '%s' that lacks the TARGET attribute"_err_en_US,
240             ultimate.name());
241         emittedMessage_ = true;
242       }
243       return false;
244     } else if (!IsSaved(ultimate)) {
245       if (messages_) {
246         messages_->Say(
247             "An initial data target may not be a reference to an object '%s' that lacks the SAVE attribute"_err_en_US,
248             ultimate.name());
249         emittedMessage_ = true;
250       }
251       return false;
252     } else {
253       return CheckVarOrComponent(ultimate);
254     }
255   }
256   bool operator()(const StaticDataObject &) const { return false; }
257   bool operator()(const TypeParamInquiry &) const { return false; }
258   bool operator()(const Triplet &x) const {
259     return IsConstantExpr(x.lower()) && IsConstantExpr(x.upper()) &&
260         IsConstantExpr(x.stride());
261   }
262   bool operator()(const Subscript &x) const {
263     return common::visit(common::visitors{
264                              [&](const Triplet &t) { return (*this)(t); },
265                              [&](const auto &y) {
266                                return y.value().Rank() == 0 &&
267                                    IsConstantExpr(y.value());
268                              },
269                          },
270         x.u);
271   }
272   bool operator()(const CoarrayRef &) const { return false; }
273   bool operator()(const Component &x) {
274     return CheckVarOrComponent(x.GetLastSymbol()) && (*this)(x.base());
275   }
276   bool operator()(const Substring &x) const {
277     return IsConstantExpr(x.lower()) && IsConstantExpr(x.upper()) &&
278         (*this)(x.parent());
279   }
280   bool operator()(const DescriptorInquiry &) const { return false; }
281   template <typename T> bool operator()(const ArrayConstructor<T> &) const {
282     return false;
283   }
284   bool operator()(const StructureConstructor &) const { return false; }
285   template <typename D, typename R, typename... O>
286   bool operator()(const Operation<D, R, O...> &) const {
287     return false;
288   }
289   template <typename T> bool operator()(const Parentheses<T> &x) const {
290     return (*this)(x.left());
291   }
292   bool operator()(const ProcedureRef &x) const {
293     if (const SpecificIntrinsic * intrinsic{x.proc().GetSpecificIntrinsic()}) {
294       return intrinsic->characteristics.value().attrs.test(
295           characteristics::Procedure::Attr::NullPointer);
296     }
297     return false;
298   }
299   bool operator()(const Relational<SomeType> &) const { return false; }
300 
301 private:
302   bool CheckVarOrComponent(const semantics::Symbol &symbol) {
303     const Symbol &ultimate{symbol.GetUltimate()};
304     if (IsAllocatable(ultimate)) {
305       if (messages_) {
306         messages_->Say(
307             "An initial data target may not be a reference to an ALLOCATABLE '%s'"_err_en_US,
308             ultimate.name());
309         emittedMessage_ = true;
310       }
311       return false;
312     } else if (ultimate.Corank() > 0) {
313       if (messages_) {
314         messages_->Say(
315             "An initial data target may not be a reference to a coarray '%s'"_err_en_US,
316             ultimate.name());
317         emittedMessage_ = true;
318       }
319       return false;
320     }
321     return true;
322   }
323 
324   parser::ContextualMessages *messages_;
325   bool emittedMessage_{false};
326 };
327 
328 bool IsInitialDataTarget(
329     const Expr<SomeType> &x, parser::ContextualMessages *messages) {
330   IsInitialDataTargetHelper helper{messages};
331   bool result{helper(x)};
332   if (!result && messages && !helper.emittedMessage()) {
333     messages->Say(
334         "An initial data target must be a designator with constant subscripts"_err_en_US);
335   }
336   return result;
337 }
338 
339 bool IsInitialProcedureTarget(const semantics::Symbol &symbol) {
340   const auto &ultimate{symbol.GetUltimate()};
341   return common::visit(
342       common::visitors{
343           [](const semantics::SubprogramDetails &subp) {
344             return !subp.isDummy();
345           },
346           [](const semantics::SubprogramNameDetails &) { return true; },
347           [&](const semantics::ProcEntityDetails &proc) {
348             return !semantics::IsPointer(ultimate) && !proc.isDummy();
349           },
350           [](const auto &) { return false; },
351       },
352       ultimate.details());
353 }
354 
355 bool IsInitialProcedureTarget(const ProcedureDesignator &proc) {
356   if (const auto *intrin{proc.GetSpecificIntrinsic()}) {
357     return !intrin->isRestrictedSpecific;
358   } else if (proc.GetComponent()) {
359     return false;
360   } else {
361     return IsInitialProcedureTarget(DEREF(proc.GetSymbol()));
362   }
363 }
364 
365 bool IsInitialProcedureTarget(const Expr<SomeType> &expr) {
366   if (const auto *proc{std::get_if<ProcedureDesignator>(&expr.u)}) {
367     return IsInitialProcedureTarget(*proc);
368   } else {
369     return IsNullProcedurePointer(expr);
370   }
371 }
372 
373 // Converts, folds, and then checks type, rank, and shape of an
374 // initialization expression for a named constant, a non-pointer
375 // variable static initialization, a component default initializer,
376 // a type parameter default value, or instantiated type parameter value.
377 std::optional<Expr<SomeType>> NonPointerInitializationExpr(const Symbol &symbol,
378     Expr<SomeType> &&x, FoldingContext &context,
379     const semantics::Scope *instantiation) {
380   CHECK(!IsPointer(symbol));
381   if (auto symTS{
382           characteristics::TypeAndShape::Characterize(symbol, context)}) {
383     auto xType{x.GetType()};
384     auto converted{ConvertToType(symTS->type(), Expr<SomeType>{x})};
385     if (!converted &&
386         symbol.owner().context().IsEnabled(
387             common::LanguageFeature::LogicalIntegerAssignment)) {
388       converted = DataConstantConversionExtension(context, symTS->type(), x);
389       if (converted &&
390           symbol.owner().context().ShouldWarn(
391               common::LanguageFeature::LogicalIntegerAssignment)) {
392         context.messages().Say(
393             "nonstandard usage: initialization of %s with %s"_port_en_US,
394             symTS->type().AsFortran(), x.GetType().value().AsFortran());
395       }
396     }
397     if (converted) {
398       auto folded{Fold(context, std::move(*converted))};
399       if (IsActuallyConstant(folded)) {
400         int symRank{GetRank(symTS->shape())};
401         if (IsImpliedShape(symbol)) {
402           if (folded.Rank() == symRank) {
403             return ArrayConstantBoundChanger{
404                 std::move(*AsConstantExtents(
405                     context, GetRawLowerBounds(context, NamedEntity{symbol})))}
406                 .ChangeLbounds(std::move(folded));
407           } else {
408             context.messages().Say(
409                 "Implied-shape parameter '%s' has rank %d but its initializer has rank %d"_err_en_US,
410                 symbol.name(), symRank, folded.Rank());
411           }
412         } else if (auto extents{AsConstantExtents(context, symTS->shape())}) {
413           if (folded.Rank() == 0 && symRank == 0) {
414             // symbol and constant are both scalars
415             return {std::move(folded)};
416           } else if (folded.Rank() == 0 && symRank > 0) {
417             // expand the scalar constant to an array
418             return ScalarConstantExpander{std::move(*extents),
419                 AsConstantExtents(
420                     context, GetRawLowerBounds(context, NamedEntity{symbol}))}
421                 .Expand(std::move(folded));
422           } else if (auto resultShape{GetShape(context, folded)}) {
423             if (CheckConformance(context.messages(), symTS->shape(),
424                     *resultShape, CheckConformanceFlags::None,
425                     "initialized object", "initialization expression")
426                     .value_or(false /*fail if not known now to conform*/)) {
427               // make a constant array with adjusted lower bounds
428               return ArrayConstantBoundChanger{
429                   std::move(*AsConstantExtents(context,
430                       GetRawLowerBounds(context, NamedEntity{symbol})))}
431                   .ChangeLbounds(std::move(folded));
432             }
433           }
434         } else if (IsNamedConstant(symbol)) {
435           if (IsExplicitShape(symbol)) {
436             context.messages().Say(
437                 "Named constant '%s' array must have constant shape"_err_en_US,
438                 symbol.name());
439           } else {
440             // Declaration checking handles other cases
441           }
442         } else {
443           context.messages().Say(
444               "Shape of initialized object '%s' must be constant"_err_en_US,
445               symbol.name());
446         }
447       } else if (IsErrorExpr(folded)) {
448       } else if (IsLenTypeParameter(symbol)) {
449         return {std::move(folded)};
450       } else if (IsKindTypeParameter(symbol)) {
451         if (instantiation) {
452           context.messages().Say(
453               "Value of kind type parameter '%s' (%s) must be a scalar INTEGER constant"_err_en_US,
454               symbol.name(), folded.AsFortran());
455         } else {
456           return {std::move(folded)};
457         }
458       } else if (IsNamedConstant(symbol)) {
459         context.messages().Say(
460             "Value of named constant '%s' (%s) cannot be computed as a constant value"_err_en_US,
461             symbol.name(), folded.AsFortran());
462       } else {
463         context.messages().Say(
464             "Initialization expression for '%s' (%s) cannot be computed as a constant value"_err_en_US,
465             symbol.name(), folded.AsFortran());
466       }
467     } else if (xType) {
468       context.messages().Say(
469           "Initialization expression cannot be converted to declared type of '%s' from %s"_err_en_US,
470           symbol.name(), xType->AsFortran());
471     } else {
472       context.messages().Say(
473           "Initialization expression cannot be converted to declared type of '%s'"_err_en_US,
474           symbol.name());
475     }
476   }
477   return std::nullopt;
478 }
479 
480 static bool IsNonLocal(const semantics::Symbol &symbol) {
481   return semantics::IsDummy(symbol) || symbol.has<semantics::UseDetails>() ||
482       symbol.owner().kind() == semantics::Scope::Kind::Module ||
483       semantics::FindCommonBlockContaining(symbol) ||
484       symbol.has<semantics::HostAssocDetails>();
485 }
486 
487 static bool IsPermissibleInquiry(const semantics::Symbol &firstSymbol,
488     const semantics::Symbol &lastSymbol, DescriptorInquiry::Field field,
489     const semantics::Scope &localScope) {
490   if (IsNonLocal(firstSymbol)) {
491     return true;
492   }
493   if (&localScope != &firstSymbol.owner()) {
494     return true;
495   }
496   // Inquiries on local objects may not access a deferred bound or length.
497   // (This code used to be a switch, but it proved impossible to write it
498   // thus without running afoul of bogus warnings from different C++
499   // compilers.)
500   if (field == DescriptorInquiry::Field::Rank) {
501     return true; // always known
502   }
503   const auto *object{lastSymbol.detailsIf<semantics::ObjectEntityDetails>()};
504   if (field == DescriptorInquiry::Field::LowerBound ||
505       field == DescriptorInquiry::Field::Extent ||
506       field == DescriptorInquiry::Field::Stride) {
507     return object && !object->shape().CanBeDeferredShape();
508   }
509   if (field == DescriptorInquiry::Field::Len) {
510     return object && object->type() &&
511         object->type()->category() == semantics::DeclTypeSpec::Character &&
512         !object->type()->characterTypeSpec().length().isDeferred();
513   }
514   return false;
515 }
516 
517 // Specification expression validation (10.1.11(2), C1010)
518 class CheckSpecificationExprHelper
519     : public AnyTraverse<CheckSpecificationExprHelper,
520           std::optional<std::string>> {
521 public:
522   using Result = std::optional<std::string>;
523   using Base = AnyTraverse<CheckSpecificationExprHelper, Result>;
524   explicit CheckSpecificationExprHelper(
525       const semantics::Scope &s, FoldingContext &context)
526       : Base{*this}, scope_{s}, context_{context} {}
527   using Base::operator();
528 
529   Result operator()(const CoarrayRef &) const { return "coindexed reference"; }
530 
531   Result operator()(const semantics::Symbol &symbol) const {
532     const auto &ultimate{symbol.GetUltimate()};
533     if (const auto *assoc{
534             ultimate.detailsIf<semantics::AssocEntityDetails>()}) {
535       return (*this)(assoc->expr());
536     } else if (semantics::IsNamedConstant(ultimate) ||
537         ultimate.owner().IsModule() || ultimate.owner().IsSubmodule()) {
538       return std::nullopt;
539     } else if (scope_.IsDerivedType() &&
540         IsVariableName(ultimate)) { // C750, C754
541       return "derived type component or type parameter value not allowed to "
542              "reference variable '"s +
543           ultimate.name().ToString() + "'";
544     } else if (IsDummy(ultimate)) {
545       if (ultimate.attrs().test(semantics::Attr::OPTIONAL)) {
546         return "reference to OPTIONAL dummy argument '"s +
547             ultimate.name().ToString() + "'";
548       } else if (!inInquiry_ &&
549           ultimate.attrs().test(semantics::Attr::INTENT_OUT)) {
550         return "reference to INTENT(OUT) dummy argument '"s +
551             ultimate.name().ToString() + "'";
552       } else if (ultimate.has<semantics::ObjectEntityDetails>()) {
553         return std::nullopt;
554       } else {
555         return "dummy procedure argument";
556       }
557     } else if (&symbol.owner() != &scope_ || &ultimate.owner() != &scope_) {
558       return std::nullopt; // host association is in play
559     } else if (const auto *object{
560                    ultimate.detailsIf<semantics::ObjectEntityDetails>()}) {
561       if (object->commonBlock()) {
562         return std::nullopt;
563       }
564     }
565     if (inInquiry_) {
566       return std::nullopt;
567     } else {
568       return "reference to local entity '"s + ultimate.name().ToString() + "'";
569     }
570   }
571 
572   Result operator()(const Component &x) const {
573     // Don't look at the component symbol.
574     return (*this)(x.base());
575   }
576   Result operator()(const ArrayRef &x) const {
577     if (auto result{(*this)(x.base())}) {
578       return result;
579     }
580     // The subscripts don't get special protection for being in a
581     // specification inquiry context;
582     auto restorer{common::ScopedSet(inInquiry_, false)};
583     return (*this)(x.subscript());
584   }
585   Result operator()(const Substring &x) const {
586     if (auto result{(*this)(x.parent())}) {
587       return result;
588     }
589     // The bounds don't get special protection for being in a
590     // specification inquiry context;
591     auto restorer{common::ScopedSet(inInquiry_, false)};
592     if (auto result{(*this)(x.lower())}) {
593       return result;
594     }
595     return (*this)(x.upper());
596   }
597   Result operator()(const DescriptorInquiry &x) const {
598     // Many uses of SIZE(), LBOUND(), &c. that are valid in specification
599     // expressions will have been converted to expressions over descriptor
600     // inquiries by Fold().
601     // Catch REAL, ALLOCATABLE :: X(:); REAL :: Y(SIZE(X))
602     if (IsPermissibleInquiry(x.base().GetFirstSymbol(),
603             x.base().GetLastSymbol(), x.field(), scope_)) {
604       auto restorer{common::ScopedSet(inInquiry_, true)};
605       return (*this)(x.base());
606     } else if (IsConstantExpr(x)) {
607       return std::nullopt;
608     } else {
609       return "non-constant descriptor inquiry not allowed for local object";
610     }
611   }
612 
613   Result operator()(const TypeParamInquiry &inq) const {
614     if (scope_.IsDerivedType() && !IsConstantExpr(inq) &&
615         inq.base() /* X%T, not local T */) { // C750, C754
616       return "non-constant reference to a type parameter inquiry not "
617              "allowed for derived type components or type parameter values";
618     }
619     return std::nullopt;
620   }
621 
622   Result operator()(const ProcedureRef &x) const {
623     bool inInquiry{false};
624     if (const auto *symbol{x.proc().GetSymbol()}) {
625       const Symbol &ultimate{symbol->GetUltimate()};
626       if (!semantics::IsPureProcedure(ultimate)) {
627         return "reference to impure function '"s + ultimate.name().ToString() +
628             "'";
629       }
630       if (semantics::IsStmtFunction(ultimate)) {
631         return "reference to statement function '"s +
632             ultimate.name().ToString() + "'";
633       }
634       if (scope_.IsDerivedType()) { // C750, C754
635         return "reference to function '"s + ultimate.name().ToString() +
636             "' not allowed for derived type components or type parameter"
637             " values";
638       }
639       if (auto procChars{
640               characteristics::Procedure::Characterize(x.proc(), context_)}) {
641         const auto iter{std::find_if(procChars->dummyArguments.begin(),
642             procChars->dummyArguments.end(),
643             [](const characteristics::DummyArgument &dummy) {
644               return std::holds_alternative<characteristics::DummyProcedure>(
645                   dummy.u);
646             })};
647         if (iter != procChars->dummyArguments.end()) {
648           return "reference to function '"s + ultimate.name().ToString() +
649               "' with dummy procedure argument '" + iter->name + '\'';
650         }
651       }
652       // References to internal functions are caught in expression semantics.
653       // TODO: other checks for standard module procedures
654     } else { // intrinsic
655       const SpecificIntrinsic &intrin{DEREF(x.proc().GetSpecificIntrinsic())};
656       inInquiry = context_.intrinsics().GetIntrinsicClass(intrin.name) ==
657           IntrinsicClass::inquiryFunction;
658       if (scope_.IsDerivedType()) { // C750, C754
659         if ((context_.intrinsics().IsIntrinsic(intrin.name) &&
660                 badIntrinsicsForComponents_.find(intrin.name) !=
661                     badIntrinsicsForComponents_.end())) {
662           return "reference to intrinsic '"s + intrin.name +
663               "' not allowed for derived type components or type parameter"
664               " values";
665         }
666         if (inInquiry && !IsConstantExpr(x)) {
667           return "non-constant reference to inquiry intrinsic '"s +
668               intrin.name +
669               "' not allowed for derived type components or type"
670               " parameter values";
671         }
672       }
673       // Type-determined inquiries (DIGITS, HUGE, &c.) will have already been
674       // folded and won't arrive here.  Inquiries that are represented with
675       // DescriptorInquiry operations (LBOUND) are checked elsewhere.  If a
676       // call that makes it to here satisfies the requirements of a constant
677       // expression (as Fortran defines it), it's fine.
678       if (IsConstantExpr(x)) {
679         return std::nullopt;
680       }
681       if (intrin.name == "present") {
682         return std::nullopt; // always ok
683       }
684       // Catch CHARACTER(:), ALLOCATABLE :: X; CHARACTER(LEN(X)) :: Y
685       if (inInquiry && x.arguments().size() >= 1) {
686         if (const auto &arg{x.arguments().at(0)}) {
687           if (auto dataRef{ExtractDataRef(*arg, true, true)}) {
688             if (intrin.name == "allocated" || intrin.name == "associated" ||
689                 intrin.name == "is_contiguous") { // ok
690             } else if (intrin.name == "len" &&
691                 IsPermissibleInquiry(dataRef->GetFirstSymbol(),
692                     dataRef->GetLastSymbol(), DescriptorInquiry::Field::Len,
693                     scope_)) { // ok
694             } else if (intrin.name == "lbound" &&
695                 IsPermissibleInquiry(dataRef->GetFirstSymbol(),
696                     dataRef->GetLastSymbol(),
697                     DescriptorInquiry::Field::LowerBound, scope_)) { // ok
698             } else if ((intrin.name == "shape" || intrin.name == "size" ||
699                            intrin.name == "sizeof" ||
700                            intrin.name == "storage_size" ||
701                            intrin.name == "ubound") &&
702                 IsPermissibleInquiry(dataRef->GetFirstSymbol(),
703                     dataRef->GetLastSymbol(), DescriptorInquiry::Field::Extent,
704                     scope_)) { // ok
705             } else {
706               return "non-constant inquiry function '"s + intrin.name +
707                   "' not allowed for local object";
708             }
709           }
710         }
711       }
712     }
713     auto restorer{common::ScopedSet(inInquiry_, inInquiry)};
714     return (*this)(x.arguments());
715   }
716 
717 private:
718   const semantics::Scope &scope_;
719   FoldingContext &context_;
720   // Contextual information: this flag is true when in an argument to
721   // an inquiry intrinsic like SIZE().
722   mutable bool inInquiry_{false};
723   const std::set<std::string> badIntrinsicsForComponents_{
724       "allocated", "associated", "extends_type_of", "present", "same_type_as"};
725 };
726 
727 template <typename A>
728 void CheckSpecificationExpr(
729     const A &x, const semantics::Scope &scope, FoldingContext &context) {
730   if (auto why{CheckSpecificationExprHelper{scope, context}(x)}) {
731     context.messages().Say(
732         "Invalid specification expression: %s"_err_en_US, *why);
733   }
734 }
735 
736 template void CheckSpecificationExpr(
737     const Expr<SomeType> &, const semantics::Scope &, FoldingContext &);
738 template void CheckSpecificationExpr(
739     const Expr<SomeInteger> &, const semantics::Scope &, FoldingContext &);
740 template void CheckSpecificationExpr(
741     const Expr<SubscriptInteger> &, const semantics::Scope &, FoldingContext &);
742 template void CheckSpecificationExpr(const std::optional<Expr<SomeType>> &,
743     const semantics::Scope &, FoldingContext &);
744 template void CheckSpecificationExpr(const std::optional<Expr<SomeInteger>> &,
745     const semantics::Scope &, FoldingContext &);
746 template void CheckSpecificationExpr(
747     const std::optional<Expr<SubscriptInteger>> &, const semantics::Scope &,
748     FoldingContext &);
749 
750 // IsContiguous() -- 9.5.4
751 class IsContiguousHelper
752     : public AnyTraverse<IsContiguousHelper, std::optional<bool>> {
753 public:
754   using Result = std::optional<bool>; // tri-state
755   using Base = AnyTraverse<IsContiguousHelper, Result>;
756   explicit IsContiguousHelper(FoldingContext &c) : Base{*this}, context_{c} {}
757   using Base::operator();
758 
759   Result operator()(const semantics::Symbol &symbol) const {
760     const auto &ultimate{symbol.GetUltimate()};
761     if (ultimate.attrs().test(semantics::Attr::CONTIGUOUS)) {
762       return true;
763     } else if (ultimate.Rank() == 0) {
764       // Extension: accept scalars as a degenerate case of
765       // simple contiguity to allow their use in contexts like
766       // data targets in pointer assignments with remapping.
767       return true;
768     } else if (semantics::IsPointer(ultimate) ||
769         semantics::IsAssumedShape(ultimate)) {
770       return std::nullopt;
771     } else if (const auto *details{
772                    ultimate.detailsIf<semantics::ObjectEntityDetails>()}) {
773       return !details->IsAssumedRank();
774     } else {
775       return Base::operator()(ultimate);
776     }
777   }
778 
779   Result operator()(const ArrayRef &x) const {
780     if (x.Rank() == 0) {
781       return true; // scalars considered contiguous
782     }
783     int subscriptRank{0};
784     auto baseLbounds{GetLBOUNDs(context_, x.base())};
785     auto baseUbounds{GetUBOUNDs(context_, x.base())};
786     auto subscripts{CheckSubscripts(
787         x.subscript(), subscriptRank, &baseLbounds, &baseUbounds)};
788     if (!subscripts.value_or(false)) {
789       return subscripts; // subscripts not known to be contiguous
790     } else if (subscriptRank > 0) {
791       // a(1)%b(:,:) is contiguous if and only if a(1)%b is contiguous.
792       return (*this)(x.base());
793     } else {
794       // a(:)%b(1,1) is (probably) not contiguous.
795       return std::nullopt;
796     }
797   }
798   Result operator()(const CoarrayRef &x) const {
799     int rank{0};
800     return CheckSubscripts(x.subscript(), rank).has_value();
801   }
802   Result operator()(const Component &x) const {
803     if (x.base().Rank() == 0) {
804       return (*this)(x.GetLastSymbol());
805     } else {
806       // TODO could be true if base contiguous and this is only component, or
807       // if base has only one element?
808       return std::nullopt;
809     }
810   }
811   Result operator()(const ComplexPart &x) const {
812     return x.complex().Rank() == 0;
813   }
814   Result operator()(const Substring &) const { return std::nullopt; }
815 
816   Result operator()(const ProcedureRef &x) const {
817     if (auto chars{
818             characteristics::Procedure::Characterize(x.proc(), context_)}) {
819       if (chars->functionResult) {
820         const auto &result{*chars->functionResult};
821         return !result.IsProcedurePointer() &&
822             result.attrs.test(characteristics::FunctionResult::Attr::Pointer) &&
823             result.attrs.test(
824                 characteristics::FunctionResult::Attr::Contiguous);
825       }
826     }
827     return std::nullopt;
828   }
829 
830   Result operator()(const NullPointer &) const { return true; }
831 
832 private:
833   // Returns "true" for a provably empty or simply contiguous array section;
834   // return "false" for a provably nonempty discontiguous section or for use
835   // of a vector subscript.
836   std::optional<bool> CheckSubscripts(const std::vector<Subscript> &subscript,
837       int &rank, const Shape *baseLbounds = nullptr,
838       const Shape *baseUbounds = nullptr) const {
839     bool anyTriplet{false};
840     rank = 0;
841     // Detect any provably empty dimension in this array section, which would
842     // render the whole section empty and therefore vacuously contiguous.
843     std::optional<bool> result;
844     for (auto j{subscript.size()}; j-- > 0;) {
845       if (const auto *triplet{std::get_if<Triplet>(&subscript[j].u)}) {
846         ++rank;
847         if (auto stride{ToInt64(triplet->stride())}) {
848           const Expr<SubscriptInteger> *lowerBound{triplet->GetLower()};
849           if (!lowerBound && baseLbounds && j < baseLbounds->size()) {
850             lowerBound = common::GetPtrFromOptional(baseLbounds->at(j));
851           }
852           const Expr<SubscriptInteger> *upperBound{triplet->GetUpper()};
853           if (!upperBound && baseUbounds && j < baseUbounds->size()) {
854             upperBound = common::GetPtrFromOptional(baseUbounds->at(j));
855           }
856           std::optional<ConstantSubscript> lowerVal{lowerBound
857                   ? ToInt64(Fold(context_, Expr<SubscriptInteger>{*lowerBound}))
858                   : std::nullopt};
859           std::optional<ConstantSubscript> upperVal{upperBound
860                   ? ToInt64(Fold(context_, Expr<SubscriptInteger>{*upperBound}))
861                   : std::nullopt};
862           if (lowerVal && upperVal) {
863             if (*lowerVal < *upperVal) {
864               if (*stride < 0) {
865                 result = true; // empty dimension
866               } else if (!result && *stride > 1 &&
867                   *lowerVal + *stride <= *upperVal) {
868                 result = false; // discontiguous if not empty
869               }
870             } else if (*lowerVal > *upperVal) {
871               if (*stride > 0) {
872                 result = true; // empty dimension
873               } else if (!result && *stride < 0 &&
874                   *lowerVal + *stride >= *upperVal) {
875                 result = false; // discontiguous if not empty
876               }
877             }
878           }
879         }
880       } else if (subscript[j].Rank() > 0) {
881         ++rank;
882         if (!result) {
883           result = false; // vector subscript
884         }
885       }
886     }
887     if (rank == 0) {
888       result = true; // scalar
889     }
890     if (result) {
891       return result;
892     }
893     // Not provably discontiguous at this point.
894     // Return "true" if simply contiguous, otherwise nullopt.
895     for (auto j{subscript.size()}; j-- > 0;) {
896       if (const auto *triplet{std::get_if<Triplet>(&subscript[j].u)}) {
897         auto stride{ToInt64(triplet->stride())};
898         if (!stride || stride != 1) {
899           return std::nullopt;
900         } else if (anyTriplet) {
901           if (triplet->GetLower() || triplet->GetUpper()) {
902             // all triplets before the last one must be just ":" for
903             // simple contiguity
904             return std::nullopt;
905           }
906         } else {
907           anyTriplet = true;
908         }
909         ++rank;
910       } else if (anyTriplet) {
911         return std::nullopt;
912       }
913     }
914     return true; // simply contiguous
915   }
916 
917   FoldingContext &context_;
918 };
919 
920 template <typename A>
921 std::optional<bool> IsContiguous(const A &x, FoldingContext &context) {
922   if (IsVariable(x)) {
923     return IsContiguousHelper{context}(x);
924   } else {
925     return true; // not a variable
926   }
927 }
928 
929 template std::optional<bool> IsContiguous(
930     const Expr<SomeType> &, FoldingContext &);
931 template std::optional<bool> IsContiguous(const ArrayRef &, FoldingContext &);
932 template std::optional<bool> IsContiguous(const Substring &, FoldingContext &);
933 template std::optional<bool> IsContiguous(const Component &, FoldingContext &);
934 template std::optional<bool> IsContiguous(
935     const ComplexPart &, FoldingContext &);
936 template std::optional<bool> IsContiguous(const CoarrayRef &, FoldingContext &);
937 
938 // IsErrorExpr()
939 struct IsErrorExprHelper : public AnyTraverse<IsErrorExprHelper, bool> {
940   using Result = bool;
941   using Base = AnyTraverse<IsErrorExprHelper, Result>;
942   IsErrorExprHelper() : Base{*this} {}
943   using Base::operator();
944 
945   bool operator()(const SpecificIntrinsic &x) {
946     return x.name == IntrinsicProcTable::InvalidName;
947   }
948 };
949 
950 template <typename A> bool IsErrorExpr(const A &x) {
951   return IsErrorExprHelper{}(x);
952 }
953 
954 template bool IsErrorExpr(const Expr<SomeType> &);
955 
956 // C1577
957 // TODO: Also check C1579 & C1582 here
958 class StmtFunctionChecker
959     : public AnyTraverse<StmtFunctionChecker, std::optional<parser::Message>> {
960 public:
961   using Result = std::optional<parser::Message>;
962   using Base = AnyTraverse<StmtFunctionChecker, Result>;
963   StmtFunctionChecker(const Symbol &sf, FoldingContext &context)
964       : Base{*this}, sf_{sf}, context_{context} {}
965   using Base::operator();
966 
967   template <typename T> Result operator()(const ArrayConstructor<T> &) const {
968     return parser::Message{sf_.name(),
969         "Statement function '%s' should not contain an array constructor"_port_en_US,
970         sf_.name()};
971   }
972   Result operator()(const StructureConstructor &) const {
973     return parser::Message{sf_.name(),
974         "Statement function '%s' should not contain a structure constructor"_port_en_US,
975         sf_.name()};
976   }
977   Result operator()(const TypeParamInquiry &) const {
978     return parser::Message{sf_.name(),
979         "Statement function '%s' should not contain a type parameter inquiry"_port_en_US,
980         sf_.name()};
981   }
982   Result operator()(const ProcedureDesignator &proc) const {
983     if (const Symbol * symbol{proc.GetSymbol()}) {
984       const Symbol &ultimate{symbol->GetUltimate()};
985       if (const auto *subp{
986               ultimate.detailsIf<semantics::SubprogramDetails>()}) {
987         if (subp->stmtFunction() && &ultimate.owner() == &sf_.owner()) {
988           if (ultimate.name().begin() > sf_.name().begin()) {
989             return parser::Message{sf_.name(),
990                 "Statement function '%s' may not reference another statement function '%s' that is defined later"_err_en_US,
991                 sf_.name(), ultimate.name()};
992           }
993         }
994       }
995       if (auto chars{
996               characteristics::Procedure::Characterize(proc, context_)}) {
997         if (!chars->CanBeCalledViaImplicitInterface()) {
998           return parser::Message(sf_.name(),
999               "Statement function '%s' should not reference function '%s' that requires an explicit interface"_port_en_US,
1000               sf_.name(), symbol->name());
1001         }
1002       }
1003     }
1004     if (proc.Rank() > 0) {
1005       return parser::Message(sf_.name(),
1006           "Statement function '%s' should not reference a function that returns an array"_port_en_US,
1007           sf_.name());
1008     }
1009     return std::nullopt;
1010   }
1011   Result operator()(const ActualArgument &arg) const {
1012     if (const auto *expr{arg.UnwrapExpr()}) {
1013       if (auto result{(*this)(*expr)}) {
1014         return result;
1015       }
1016       if (expr->Rank() > 0 && !UnwrapWholeSymbolOrComponentDataRef(*expr)) {
1017         return parser::Message(sf_.name(),
1018             "Statement function '%s' should not pass an array argument that is not a whole array"_port_en_US,
1019             sf_.name());
1020       }
1021     }
1022     return std::nullopt;
1023   }
1024 
1025 private:
1026   const Symbol &sf_;
1027   FoldingContext &context_;
1028 };
1029 
1030 std::optional<parser::Message> CheckStatementFunction(
1031     const Symbol &sf, const Expr<SomeType> &expr, FoldingContext &context) {
1032   return StmtFunctionChecker{sf, context}(expr);
1033 }
1034 
1035 } // namespace Fortran::evaluate
1036