1 //===-- lib/Evaluate/check-expression.cpp ---------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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/semantics.h"
16 #include "flang/Semantics/symbol.h"
17 #include "flang/Semantics/tools.h"
21 namespace Fortran::evaluate
{
23 // Constant expression predicates IsConstantExpr() & IsScopeInvariantExpr().
24 // This code determines whether an expression is a "constant expression"
25 // in the sense of section 10.1.12. This is not the same thing as being
26 // able to fold it (yet) into a known constant value; specifically,
27 // the expression may reference derived type kind parameters whose values
30 // The variant form (IsScopeInvariantExpr()) also accepts symbols that are
31 // INTENT(IN) dummy arguments without the VALUE attribute.
32 template <bool INVARIANT
>
33 class IsConstantExprHelper
34 : public AllTraverse
<IsConstantExprHelper
<INVARIANT
>, true> {
36 using Base
= AllTraverse
<IsConstantExprHelper
, true>;
37 IsConstantExprHelper() : Base
{*this} {}
38 using Base::operator();
40 // A missing expression is not considered to be constant.
41 template <typename A
> bool operator()(const std::optional
<A
> &x
) const {
42 return x
&& (*this)(*x
);
45 bool operator()(const TypeParamInquiry
&inq
) const {
46 return INVARIANT
|| semantics::IsKindTypeParameter(inq
.parameter());
48 bool operator()(const semantics::Symbol
&symbol
) const {
49 const auto &ultimate
{GetAssociationRoot(symbol
)};
50 return IsNamedConstant(ultimate
) || IsImpliedDoIndex(ultimate
) ||
51 IsInitialProcedureTarget(ultimate
) ||
52 ultimate
.has
<semantics::TypeParamDetails
>() ||
53 (INVARIANT
&& IsIntentIn(symbol
) && !IsOptional(symbol
) &&
54 !symbol
.attrs().test(semantics::Attr::VALUE
));
56 bool operator()(const CoarrayRef
&) const { return false; }
57 bool operator()(const semantics::ParamValue
¶m
) const {
58 return param
.isExplicit() && (*this)(param
.GetExplicit());
60 bool operator()(const ProcedureRef
&) const;
61 bool operator()(const StructureConstructor
&constructor
) const {
62 for (const auto &[symRef
, expr
] : constructor
) {
63 if (!IsConstantStructureConstructorComponent(*symRef
, expr
.value())) {
69 bool operator()(const Component
&component
) const {
70 return (*this)(component
.base());
72 // Forbid integer division by zero in constants.
75 const Divide
<Type
<TypeCategory::Integer
, KIND
>> &division
) const {
76 using T
= Type
<TypeCategory::Integer
, KIND
>;
77 if (const auto divisor
{GetScalarConstantValue
<T
>(division
.right())}) {
78 return !divisor
->IsZero() && (*this)(division
.left());
84 bool operator()(const Constant
<SomeDerived
> &) const { return true; }
85 bool operator()(const DescriptorInquiry
&x
) const {
86 const Symbol
&sym
{x
.base().GetLastSymbol()};
87 return INVARIANT
&& !IsAllocatable(sym
) &&
89 (IsIntentIn(sym
) && !IsOptional(sym
) &&
90 !sym
.attrs().test(semantics::Attr::VALUE
)));
94 bool IsConstantStructureConstructorComponent(
95 const Symbol
&, const Expr
<SomeType
> &) const;
96 bool IsConstantExprShape(const Shape
&) const;
99 template <bool INVARIANT
>
100 bool IsConstantExprHelper
<INVARIANT
>::IsConstantStructureConstructorComponent(
101 const Symbol
&component
, const Expr
<SomeType
> &expr
) const {
102 if (IsAllocatable(component
)) {
103 return IsNullObjectPointer(expr
);
104 } else if (IsPointer(component
)) {
105 return IsNullPointer(expr
) || IsInitialDataTarget(expr
) ||
106 IsInitialProcedureTarget(expr
);
108 return (*this)(expr
);
112 template <bool INVARIANT
>
113 bool IsConstantExprHelper
<INVARIANT
>::operator()(
114 const ProcedureRef
&call
) const {
115 // LBOUND, UBOUND, and SIZE with truly constant DIM= arguments will have
116 // been rewritten into DescriptorInquiry operations.
117 if (const auto *intrinsic
{std::get_if
<SpecificIntrinsic
>(&call
.proc().u
)}) {
118 const characteristics::Procedure
&proc
{intrinsic
->characteristics
.value()};
119 if (intrinsic
->name
== "kind" ||
120 intrinsic
->name
== IntrinsicProcTable::InvalidName
||
121 call
.arguments().empty() || !call
.arguments()[0]) {
122 // kind is always a constant, and we avoid cascading errors by considering
123 // invalid calls to intrinsics to be constant
125 } else if (intrinsic
->name
== "lbound") {
126 auto base
{ExtractNamedEntity(call
.arguments()[0]->UnwrapExpr())};
127 return base
&& IsConstantExprShape(GetLBOUNDs(*base
));
128 } else if (intrinsic
->name
== "ubound") {
129 auto base
{ExtractNamedEntity(call
.arguments()[0]->UnwrapExpr())};
130 return base
&& IsConstantExprShape(GetUBOUNDs(*base
));
131 } else if (intrinsic
->name
== "shape" || intrinsic
->name
== "size") {
132 auto shape
{GetShape(call
.arguments()[0]->UnwrapExpr())};
133 return shape
&& IsConstantExprShape(*shape
);
134 } else if (proc
.IsPure()) {
135 for (const auto &arg
: call
.arguments()) {
138 } else if (const auto *expr
{arg
->UnwrapExpr()};
139 !expr
|| !(*this)(*expr
)) {
145 // TODO: STORAGE_SIZE
150 template <bool INVARIANT
>
151 bool IsConstantExprHelper
<INVARIANT
>::IsConstantExprShape(
152 const Shape
&shape
) const {
153 for (const auto &extent
: shape
) {
154 if (!(*this)(extent
)) {
161 template <typename A
> bool IsConstantExpr(const A
&x
) {
162 return IsConstantExprHelper
<false>{}(x
);
164 template bool IsConstantExpr(const Expr
<SomeType
> &);
165 template bool IsConstantExpr(const Expr
<SomeInteger
> &);
166 template bool IsConstantExpr(const Expr
<SubscriptInteger
> &);
167 template bool IsConstantExpr(const StructureConstructor
&);
169 // IsScopeInvariantExpr()
170 template <typename A
> bool IsScopeInvariantExpr(const A
&x
) {
171 return IsConstantExprHelper
<true>{}(x
);
173 template bool IsScopeInvariantExpr(const Expr
<SomeType
> &);
174 template bool IsScopeInvariantExpr(const Expr
<SomeInteger
> &);
175 template bool IsScopeInvariantExpr(const Expr
<SubscriptInteger
> &);
177 // IsActuallyConstant()
178 struct IsActuallyConstantHelper
{
179 template <typename A
> bool operator()(const A
&) { return false; }
180 template <typename T
> bool operator()(const Constant
<T
> &) { return true; }
181 template <typename T
> bool operator()(const Parentheses
<T
> &x
) {
182 return (*this)(x
.left());
184 template <typename T
> bool operator()(const Expr
<T
> &x
) {
185 return common::visit([=](const auto &y
) { return (*this)(y
); }, x
.u
);
187 bool operator()(const Expr
<SomeType
> &x
) {
188 return common::visit([this](const auto &y
) { return (*this)(y
); }, x
.u
);
190 bool operator()(const StructureConstructor
&x
) {
191 for (const auto &pair
: x
) {
192 const Expr
<SomeType
> &y
{pair
.second
.value()};
193 const auto sym
{pair
.first
};
194 const bool compIsConstant
{(*this)(y
)};
195 // If an allocatable component is initialized by a constant,
196 // the structure constructor is not a constant.
197 if ((!compIsConstant
&& !IsNullPointer(y
)) ||
198 (compIsConstant
&& IsAllocatable(sym
))) {
204 template <typename A
> bool operator()(const A
*x
) { return x
&& (*this)(*x
); }
205 template <typename A
> bool operator()(const std::optional
<A
> &x
) {
206 return x
&& (*this)(*x
);
210 template <typename A
> bool IsActuallyConstant(const A
&x
) {
211 return IsActuallyConstantHelper
{}(x
);
214 template bool IsActuallyConstant(const Expr
<SomeType
> &);
215 template bool IsActuallyConstant(const Expr
<SomeInteger
> &);
216 template bool IsActuallyConstant(const Expr
<SubscriptInteger
> &);
217 template bool IsActuallyConstant(const std::optional
<Expr
<SubscriptInteger
>> &);
219 // Object pointer initialization checking predicate IsInitialDataTarget().
220 // This code determines whether an expression is allowable as the static
221 // data address used to initialize a pointer with "=> x". See C765.
222 class IsInitialDataTargetHelper
223 : public AllTraverse
<IsInitialDataTargetHelper
, true> {
225 using Base
= AllTraverse
<IsInitialDataTargetHelper
, true>;
226 using Base::operator();
227 explicit IsInitialDataTargetHelper(parser::ContextualMessages
*m
)
228 : Base
{*this}, messages_
{m
} {}
230 bool emittedMessage() const { return emittedMessage_
; }
232 bool operator()(const BOZLiteralConstant
&) const { return false; }
233 bool operator()(const NullPointer
&) const { return true; }
234 template <typename T
> bool operator()(const Constant
<T
> &) const {
237 bool operator()(const semantics::Symbol
&symbol
) {
238 // This function checks only base symbols, not components.
239 const Symbol
&ultimate
{symbol
.GetUltimate()};
240 if (const auto *assoc
{
241 ultimate
.detailsIf
<semantics::AssocEntityDetails
>()}) {
242 if (const auto &expr
{assoc
->expr()}) {
243 if (IsVariable(*expr
)) {
244 return (*this)(*expr
);
245 } else if (messages_
) {
247 "An initial data target may not be an associated expression ('%s')"_err_en_US
,
249 emittedMessage_
= true;
253 } else if (!CheckVarOrComponent(ultimate
)) {
255 } else if (!ultimate
.attrs().test(semantics::Attr::TARGET
)) {
258 "An initial data target may not be a reference to an object '%s' that lacks the TARGET attribute"_err_en_US
,
260 emittedMessage_
= true;
263 } else if (!IsSaved(ultimate
)) {
266 "An initial data target may not be a reference to an object '%s' that lacks the SAVE attribute"_err_en_US
,
268 emittedMessage_
= true;
275 bool operator()(const StaticDataObject
&) const { return false; }
276 bool operator()(const TypeParamInquiry
&) const { return false; }
277 bool operator()(const Triplet
&x
) const {
278 return IsConstantExpr(x
.lower()) && IsConstantExpr(x
.upper()) &&
279 IsConstantExpr(x
.stride());
281 bool operator()(const Subscript
&x
) const {
282 return common::visit(common::visitors
{
283 [&](const Triplet
&t
) { return (*this)(t
); },
285 return y
.value().Rank() == 0 &&
286 IsConstantExpr(y
.value());
291 bool operator()(const CoarrayRef
&) const { return false; }
292 bool operator()(const Component
&x
) {
293 return CheckVarOrComponent(x
.GetLastSymbol()) && (*this)(x
.base());
295 bool operator()(const Substring
&x
) const {
296 return IsConstantExpr(x
.lower()) && IsConstantExpr(x
.upper()) &&
299 bool operator()(const DescriptorInquiry
&) const { return false; }
300 template <typename T
> bool operator()(const ArrayConstructor
<T
> &) const {
303 bool operator()(const StructureConstructor
&) const { return false; }
304 template <typename D
, typename R
, typename
... O
>
305 bool operator()(const Operation
<D
, R
, O
...> &) const {
308 template <typename T
> bool operator()(const Parentheses
<T
> &x
) const {
309 return (*this)(x
.left());
311 bool operator()(const ProcedureRef
&x
) const {
312 if (const SpecificIntrinsic
* intrinsic
{x
.proc().GetSpecificIntrinsic()}) {
313 return intrinsic
->characteristics
.value().attrs
.test(
314 characteristics::Procedure::Attr::NullPointer
);
318 bool operator()(const Relational
<SomeType
> &) const { return false; }
321 bool CheckVarOrComponent(const semantics::Symbol
&symbol
) {
322 const Symbol
&ultimate
{symbol
.GetUltimate()};
323 const char *unacceptable
{nullptr};
324 if (ultimate
.Corank() > 0) {
325 unacceptable
= "a coarray";
326 } else if (IsAllocatable(ultimate
)) {
327 unacceptable
= "an ALLOCATABLE";
328 } else if (IsPointer(ultimate
)) {
329 unacceptable
= "a POINTER";
335 "An initial data target may not be a reference to %s '%s'"_err_en_US
,
336 unacceptable
, ultimate
.name());
337 emittedMessage_
= true;
342 parser::ContextualMessages
*messages_
;
343 bool emittedMessage_
{false};
346 bool IsInitialDataTarget(
347 const Expr
<SomeType
> &x
, parser::ContextualMessages
*messages
) {
348 IsInitialDataTargetHelper helper
{messages
};
349 bool result
{helper(x
)};
350 if (!result
&& messages
&& !helper
.emittedMessage()) {
352 "An initial data target must be a designator with constant subscripts"_err_en_US
);
357 bool IsInitialProcedureTarget(const semantics::Symbol
&symbol
) {
358 const auto &ultimate
{symbol
.GetUltimate()};
359 return common::visit(
361 [&](const semantics::SubprogramDetails
&subp
) {
362 return !subp
.isDummy() && !subp
.stmtFunction() &&
363 symbol
.owner().kind() != semantics::Scope::Kind::MainProgram
&&
364 symbol
.owner().kind() != semantics::Scope::Kind::Subprogram
;
366 [](const semantics::SubprogramNameDetails
&x
) {
367 return x
.kind() != semantics::SubprogramKind::Internal
;
369 [&](const semantics::ProcEntityDetails
&proc
) {
370 return !semantics::IsPointer(ultimate
) && !proc
.isDummy();
372 [](const auto &) { return false; },
377 bool IsInitialProcedureTarget(const ProcedureDesignator
&proc
) {
378 if (const auto *intrin
{proc
.GetSpecificIntrinsic()}) {
379 return !intrin
->isRestrictedSpecific
;
380 } else if (proc
.GetComponent()) {
383 return IsInitialProcedureTarget(DEREF(proc
.GetSymbol()));
387 bool IsInitialProcedureTarget(const Expr
<SomeType
> &expr
) {
388 if (const auto *proc
{std::get_if
<ProcedureDesignator
>(&expr
.u
)}) {
389 return IsInitialProcedureTarget(*proc
);
391 return IsNullProcedurePointer(expr
);
395 // Converts, folds, and then checks type, rank, and shape of an
396 // initialization expression for a named constant, a non-pointer
397 // variable static initialization, a component default initializer,
398 // a type parameter default value, or instantiated type parameter value.
399 std::optional
<Expr
<SomeType
>> NonPointerInitializationExpr(const Symbol
&symbol
,
400 Expr
<SomeType
> &&x
, FoldingContext
&context
,
401 const semantics::Scope
*instantiation
) {
402 CHECK(!IsPointer(symbol
));
404 characteristics::TypeAndShape::Characterize(symbol
, context
)}) {
405 auto xType
{x
.GetType()};
406 auto converted
{ConvertToType(symTS
->type(), Expr
<SomeType
>{x
})};
408 symbol
.owner().context().IsEnabled(
409 common::LanguageFeature::LogicalIntegerAssignment
)) {
410 converted
= DataConstantConversionExtension(context
, symTS
->type(), x
);
412 symbol
.owner().context().ShouldWarn(
413 common::LanguageFeature::LogicalIntegerAssignment
)) {
414 context
.messages().Say(
415 common::LanguageFeature::LogicalIntegerAssignment
,
416 "nonstandard usage: initialization of %s with %s"_port_en_US
,
417 symTS
->type().AsFortran(), x
.GetType().value().AsFortran());
421 auto folded
{Fold(context
, std::move(*converted
))};
422 if (IsActuallyConstant(folded
)) {
423 int symRank
{symTS
->Rank()};
424 if (IsImpliedShape(symbol
)) {
425 if (folded
.Rank() == symRank
) {
426 return ArrayConstantBoundChanger
{
427 std::move(*AsConstantExtents(
428 context
, GetRawLowerBounds(context
, NamedEntity
{symbol
})))}
429 .ChangeLbounds(std::move(folded
));
431 context
.messages().Say(
432 "Implied-shape parameter '%s' has rank %d but its initializer has rank %d"_err_en_US
,
433 symbol
.name(), symRank
, folded
.Rank());
435 } else if (auto extents
{AsConstantExtents(context
, symTS
->shape())};
436 extents
&& !HasNegativeExtent(*extents
)) {
437 if (folded
.Rank() == 0 && symRank
== 0) {
438 // symbol and constant are both scalars
439 return {std::move(folded
)};
440 } else if (folded
.Rank() == 0 && symRank
> 0) {
441 // expand the scalar constant to an array
442 return ScalarConstantExpander
{std::move(*extents
),
444 context
, GetRawLowerBounds(context
, NamedEntity
{symbol
}))}
445 .Expand(std::move(folded
));
446 } else if (auto resultShape
{GetShape(context
, folded
)}) {
447 CHECK(symTS
->shape()); // Assumed-ranks cannot be initialized.
448 if (CheckConformance(context
.messages(), *symTS
->shape(),
449 *resultShape
, CheckConformanceFlags::None
,
450 "initialized object", "initialization expression")
451 .value_or(false /*fail if not known now to conform*/)) {
452 // make a constant array with adjusted lower bounds
453 return ArrayConstantBoundChanger
{
454 std::move(*AsConstantExtents(context
,
455 GetRawLowerBounds(context
, NamedEntity
{symbol
})))}
456 .ChangeLbounds(std::move(folded
));
459 } else if (IsNamedConstant(symbol
)) {
460 if (IsExplicitShape(symbol
)) {
461 context
.messages().Say(
462 "Named constant '%s' array must have constant shape"_err_en_US
,
465 // Declaration checking handles other cases
468 context
.messages().Say(
469 "Shape of initialized object '%s' must be constant"_err_en_US
,
472 } else if (IsErrorExpr(folded
)) {
473 } else if (IsLenTypeParameter(symbol
)) {
474 return {std::move(folded
)};
475 } else if (IsKindTypeParameter(symbol
)) {
477 context
.messages().Say(
478 "Value of kind type parameter '%s' (%s) must be a scalar INTEGER constant"_err_en_US
,
479 symbol
.name(), folded
.AsFortran());
481 return {std::move(folded
)};
483 } else if (IsNamedConstant(symbol
)) {
484 if (symbol
.name() == "numeric_storage_size" &&
485 symbol
.owner().IsModule() &&
486 DEREF(symbol
.owner().symbol()).name() == "iso_fortran_env") {
487 // Very special case: numeric_storage_size is not folded until
488 // it read from the iso_fortran_env module file, as its value
489 // depends on compilation options.
490 return {std::move(folded
)};
492 context
.messages().Say(
493 "Value of named constant '%s' (%s) cannot be computed as a constant value"_err_en_US
,
494 symbol
.name(), folded
.AsFortran());
496 context
.messages().Say(
497 "Initialization expression for '%s' (%s) cannot be computed as a constant value"_err_en_US
,
498 symbol
.name(), x
.AsFortran());
501 context
.messages().Say(
502 "Initialization expression cannot be converted to declared type of '%s' from %s"_err_en_US
,
503 symbol
.name(), xType
->AsFortran());
505 context
.messages().Say(
506 "Initialization expression cannot be converted to declared type of '%s'"_err_en_US
,
513 // Specification expression validation (10.1.11(2), C1010)
514 class CheckSpecificationExprHelper
515 : public AnyTraverse
<CheckSpecificationExprHelper
,
516 std::optional
<std::string
>> {
518 using Result
= std::optional
<std::string
>;
519 using Base
= AnyTraverse
<CheckSpecificationExprHelper
, Result
>;
520 explicit CheckSpecificationExprHelper(const semantics::Scope
&s
,
521 FoldingContext
&context
, bool forElementalFunctionResult
)
522 : Base
{*this}, scope_
{s
}, context_
{context
},
523 forElementalFunctionResult_
{forElementalFunctionResult
} {}
524 using Base::operator();
526 Result
operator()(const CoarrayRef
&) const { return "coindexed reference"; }
528 Result
operator()(const semantics::Symbol
&symbol
) const {
529 const auto &ultimate
{symbol
.GetUltimate()};
530 const auto *object
{ultimate
.detailsIf
<semantics::ObjectEntityDetails
>()};
531 bool isInitialized
{semantics::IsSaved(ultimate
) &&
532 !IsAllocatable(ultimate
) && object
&&
533 (ultimate
.test(Symbol::Flag::InDataStmt
) ||
534 object
->init().has_value())};
535 if (const auto *assoc
{
536 ultimate
.detailsIf
<semantics::AssocEntityDetails
>()}) {
537 return (*this)(assoc
->expr());
538 } else if (semantics::IsNamedConstant(ultimate
) ||
539 ultimate
.owner().IsModule() || ultimate
.owner().IsSubmodule()) {
541 } else if (scope_
.IsDerivedType() &&
542 IsVariableName(ultimate
)) { // C750, C754
543 return "derived type component or type parameter value not allowed to "
544 "reference variable '"s
+
545 ultimate
.name().ToString() + "'";
546 } else if (IsDummy(ultimate
)) {
547 if (!inInquiry_
&& forElementalFunctionResult_
) {
548 return "dependence on value of dummy argument '"s
+
549 ultimate
.name().ToString() + "'";
550 } else if (ultimate
.attrs().test(semantics::Attr::OPTIONAL
)) {
551 return "reference to OPTIONAL dummy argument '"s
+
552 ultimate
.name().ToString() + "'";
553 } else if (!inInquiry_
&&
554 ultimate
.attrs().test(semantics::Attr::INTENT_OUT
)) {
555 return "reference to INTENT(OUT) dummy argument '"s
+
556 ultimate
.name().ToString() + "'";
557 } else if (ultimate
.has
<semantics::ObjectEntityDetails
>()) {
560 return "dummy procedure argument";
562 } else if (&symbol
.owner() != &scope_
|| &ultimate
.owner() != &scope_
) {
563 return std::nullopt
; // host association is in play
564 } else if (isInitialized
&&
565 context_
.languageFeatures().IsEnabled(
566 common::LanguageFeature::SavedLocalInSpecExpr
)) {
567 if (!scope_
.IsModuleFile() &&
568 context_
.languageFeatures().ShouldWarn(
569 common::LanguageFeature::SavedLocalInSpecExpr
)) {
570 context_
.messages().Say(common::LanguageFeature::SavedLocalInSpecExpr
,
571 "specification expression refers to local object '%s' (initialized and saved)"_port_en_US
,
572 ultimate
.name().ToString());
575 } else if (const auto *object
{
576 ultimate
.detailsIf
<semantics::ObjectEntityDetails
>()}) {
577 if (object
->commonBlock()) {
584 return "reference to local entity '"s
+ ultimate
.name().ToString() + "'";
588 Result
operator()(const Component
&x
) const {
589 // Don't look at the component symbol.
590 return (*this)(x
.base());
592 Result
operator()(const ArrayRef
&x
) const {
593 if (auto result
{(*this)(x
.base())}) {
596 // The subscripts don't get special protection for being in a
597 // specification inquiry context;
598 auto restorer
{common::ScopedSet(inInquiry_
, false)};
599 return (*this)(x
.subscript());
601 Result
operator()(const Substring
&x
) const {
602 if (auto result
{(*this)(x
.parent())}) {
605 // The bounds don't get special protection for being in a
606 // specification inquiry context;
607 auto restorer
{common::ScopedSet(inInquiry_
, false)};
608 if (auto result
{(*this)(x
.lower())}) {
611 return (*this)(x
.upper());
613 Result
operator()(const DescriptorInquiry
&x
) const {
614 // Many uses of SIZE(), LBOUND(), &c. that are valid in specification
615 // expressions will have been converted to expressions over descriptor
616 // inquiries by Fold().
617 // Catch REAL, ALLOCATABLE :: X(:); REAL :: Y(SIZE(X))
618 if (IsPermissibleInquiry(
619 x
.base().GetFirstSymbol(), x
.base().GetLastSymbol(), x
.field())) {
620 auto restorer
{common::ScopedSet(inInquiry_
, true)};
621 return (*this)(x
.base());
622 } else if (IsConstantExpr(x
)) {
625 return "non-constant descriptor inquiry not allowed for local object";
629 Result
operator()(const TypeParamInquiry
&inq
) const {
630 if (scope_
.IsDerivedType()) {
631 if (!IsConstantExpr(inq
) &&
632 inq
.base() /* X%T, not local T */) { // C750, C754
633 return "non-constant reference to a type parameter inquiry not allowed "
634 "for derived type components or type parameter values";
636 } else if (inq
.base() &&
637 IsInquiryAlwaysPermissible(inq
.base()->GetFirstSymbol())) {
638 auto restorer
{common::ScopedSet(inInquiry_
, true)};
639 return (*this)(inq
.base());
640 } else if (!IsConstantExpr(inq
)) {
641 return "non-constant type parameter inquiry not allowed for local object";
646 Result
operator()(const ProcedureRef
&x
) const {
647 bool inInquiry
{false};
648 if (const auto *symbol
{x
.proc().GetSymbol()}) {
649 const Symbol
&ultimate
{symbol
->GetUltimate()};
650 if (!semantics::IsPureProcedure(ultimate
)) {
651 return "reference to impure function '"s
+ ultimate
.name().ToString() +
654 if (semantics::IsStmtFunction(ultimate
)) {
655 return "reference to statement function '"s
+
656 ultimate
.name().ToString() + "'";
658 if (scope_
.IsDerivedType()) { // C750, C754
659 return "reference to function '"s
+ ultimate
.name().ToString() +
660 "' not allowed for derived type components or type parameter"
663 if (auto procChars
{characteristics::Procedure::Characterize(
664 x
.proc(), context_
, /*emitError=*/true)}) {
665 const auto iter
{std::find_if(procChars
->dummyArguments
.begin(),
666 procChars
->dummyArguments
.end(),
667 [](const characteristics::DummyArgument
&dummy
) {
668 return std::holds_alternative
<characteristics::DummyProcedure
>(
671 if (iter
!= procChars
->dummyArguments
.end() &&
672 ultimate
.name().ToString() != "__builtin_c_funloc") {
673 return "reference to function '"s
+ ultimate
.name().ToString() +
674 "' with dummy procedure argument '" + iter
->name
+ '\'';
677 // References to internal functions are caught in expression semantics.
678 // TODO: other checks for standard module procedures
679 } else { // intrinsic
680 const SpecificIntrinsic
&intrin
{DEREF(x
.proc().GetSpecificIntrinsic())};
681 inInquiry
= context_
.intrinsics().GetIntrinsicClass(intrin
.name
) ==
682 IntrinsicClass::inquiryFunction
;
683 if (scope_
.IsDerivedType()) { // C750, C754
684 if ((context_
.intrinsics().IsIntrinsic(intrin
.name
) &&
685 badIntrinsicsForComponents_
.find(intrin
.name
) !=
686 badIntrinsicsForComponents_
.end())) {
687 return "reference to intrinsic '"s
+ intrin
.name
+
688 "' not allowed for derived type components or type parameter"
691 if (inInquiry
&& !IsConstantExpr(x
)) {
692 return "non-constant reference to inquiry intrinsic '"s
+
694 "' not allowed for derived type components or type"
698 // Type-determined inquiries (DIGITS, HUGE, &c.) will have already been
699 // folded and won't arrive here. Inquiries that are represented with
700 // DescriptorInquiry operations (LBOUND) are checked elsewhere. If a
701 // call that makes it to here satisfies the requirements of a constant
702 // expression (as Fortran defines it), it's fine.
703 if (IsConstantExpr(x
)) {
706 if (intrin
.name
== "present") {
707 return std::nullopt
; // always ok
709 // Catch CHARACTER(:), ALLOCATABLE :: X; CHARACTER(LEN(X)) :: Y
710 if (inInquiry
&& x
.arguments().size() >= 1) {
711 if (const auto &arg
{x
.arguments().at(0)}) {
712 if (auto dataRef
{ExtractDataRef(*arg
, true, true)}) {
713 if (intrin
.name
== "allocated" || intrin
.name
== "associated" ||
714 intrin
.name
== "is_contiguous") { // ok
715 } else if (intrin
.name
== "len" &&
716 IsPermissibleInquiry(dataRef
->GetFirstSymbol(),
717 dataRef
->GetLastSymbol(),
718 DescriptorInquiry::Field::Len
)) { // ok
719 } else if (intrin
.name
== "lbound" &&
720 IsPermissibleInquiry(dataRef
->GetFirstSymbol(),
721 dataRef
->GetLastSymbol(),
722 DescriptorInquiry::Field::LowerBound
)) { // ok
723 } else if ((intrin
.name
== "shape" || intrin
.name
== "size" ||
724 intrin
.name
== "sizeof" ||
725 intrin
.name
== "storage_size" ||
726 intrin
.name
== "ubound") &&
727 IsPermissibleInquiry(dataRef
->GetFirstSymbol(),
728 dataRef
->GetLastSymbol(),
729 DescriptorInquiry::Field::Extent
)) { // ok
731 return "non-constant inquiry function '"s
+ intrin
.name
+
732 "' not allowed for local object";
738 auto restorer
{common::ScopedSet(inInquiry_
, inInquiry
)};
739 return (*this)(x
.arguments());
743 const semantics::Scope
&scope_
;
744 FoldingContext
&context_
;
745 // Contextual information: this flag is true when in an argument to
746 // an inquiry intrinsic like SIZE().
747 mutable bool inInquiry_
{false};
748 bool forElementalFunctionResult_
{false}; // F'2023 C15121
749 const std::set
<std::string
> badIntrinsicsForComponents_
{
750 "allocated", "associated", "extends_type_of", "present", "same_type_as"};
752 bool IsInquiryAlwaysPermissible(const semantics::Symbol
&) const;
753 bool IsPermissibleInquiry(const semantics::Symbol
&firstSymbol
,
754 const semantics::Symbol
&lastSymbol
,
755 DescriptorInquiry::Field field
) const;
758 bool CheckSpecificationExprHelper::IsInquiryAlwaysPermissible(
759 const semantics::Symbol
&symbol
) const {
760 if (&symbol
.owner() != &scope_
|| symbol
.has
<semantics::UseDetails
>() ||
761 symbol
.owner().kind() == semantics::Scope::Kind::Module
||
762 semantics::FindCommonBlockContaining(symbol
) ||
763 symbol
.has
<semantics::HostAssocDetails
>()) {
764 return true; // it's nonlocal
765 } else if (semantics::IsDummy(symbol
) && !forElementalFunctionResult_
) {
772 bool CheckSpecificationExprHelper::IsPermissibleInquiry(
773 const semantics::Symbol
&firstSymbol
, const semantics::Symbol
&lastSymbol
,
774 DescriptorInquiry::Field field
) const {
775 if (IsInquiryAlwaysPermissible(firstSymbol
)) {
778 // Inquiries on local objects may not access a deferred bound or length.
779 // (This code used to be a switch, but it proved impossible to write it
780 // thus without running afoul of bogus warnings from different C++
782 if (field
== DescriptorInquiry::Field::Rank
) {
783 return true; // always known
785 const auto *object
{lastSymbol
.detailsIf
<semantics::ObjectEntityDetails
>()};
786 if (field
== DescriptorInquiry::Field::LowerBound
||
787 field
== DescriptorInquiry::Field::Extent
||
788 field
== DescriptorInquiry::Field::Stride
) {
789 return object
&& !object
->shape().CanBeDeferredShape();
791 if (field
== DescriptorInquiry::Field::Len
) {
792 return object
&& object
->type() &&
793 object
->type()->category() == semantics::DeclTypeSpec::Character
&&
794 !object
->type()->characterTypeSpec().length().isDeferred();
799 template <typename A
>
800 void CheckSpecificationExpr(const A
&x
, const semantics::Scope
&scope
,
801 FoldingContext
&context
, bool forElementalFunctionResult
) {
802 CheckSpecificationExprHelper helper
{
803 scope
, context
, forElementalFunctionResult
};
804 if (auto why
{helper(x
)}) {
805 context
.messages().Say("Invalid specification expression%s: %s"_err_en_US
,
806 forElementalFunctionResult
? " for elemental function result" : "",
811 template void CheckSpecificationExpr(const Expr
<SomeType
> &,
812 const semantics::Scope
&, FoldingContext
&,
813 bool forElementalFunctionResult
);
814 template void CheckSpecificationExpr(const Expr
<SomeInteger
> &,
815 const semantics::Scope
&, FoldingContext
&,
816 bool forElementalFunctionResult
);
817 template void CheckSpecificationExpr(const Expr
<SubscriptInteger
> &,
818 const semantics::Scope
&, FoldingContext
&,
819 bool forElementalFunctionResult
);
820 template void CheckSpecificationExpr(const std::optional
<Expr
<SomeType
>> &,
821 const semantics::Scope
&, FoldingContext
&,
822 bool forElementalFunctionResult
);
823 template void CheckSpecificationExpr(const std::optional
<Expr
<SomeInteger
>> &,
824 const semantics::Scope
&, FoldingContext
&,
825 bool forElementalFunctionResult
);
826 template void CheckSpecificationExpr(
827 const std::optional
<Expr
<SubscriptInteger
>> &, const semantics::Scope
&,
828 FoldingContext
&, bool forElementalFunctionResult
);
830 // IsContiguous() -- 9.5.4
831 class IsContiguousHelper
832 : public AnyTraverse
<IsContiguousHelper
, std::optional
<bool>> {
834 using Result
= std::optional
<bool>; // tri-state
835 using Base
= AnyTraverse
<IsContiguousHelper
, Result
>;
836 explicit IsContiguousHelper(FoldingContext
&c
) : Base
{*this}, context_
{c
} {}
837 using Base::operator();
839 template <typename T
> Result
operator()(const Constant
<T
> &) const {
842 Result
operator()(const StaticDataObject
&) const { return true; }
843 Result
operator()(const semantics::Symbol
&symbol
) const {
844 const auto &ultimate
{symbol
.GetUltimate()};
845 if (ultimate
.attrs().test(semantics::Attr::CONTIGUOUS
)) {
847 } else if (!IsVariable(symbol
)) {
849 } else if (ultimate
.Rank() == 0) {
850 // Extension: accept scalars as a degenerate case of
851 // simple contiguity to allow their use in contexts like
852 // data targets in pointer assignments with remapping.
854 } else if (const auto *details
{
855 ultimate
.detailsIf
<semantics::AssocEntityDetails
>()}) {
856 // RANK(*) associating entity is contiguous.
857 if (details
->IsAssumedSize()) {
860 return Base::operator()(ultimate
); // use expr
862 } else if (semantics::IsPointer(ultimate
) ||
863 semantics::IsAssumedShape(ultimate
) || IsAssumedRank(ultimate
)) {
865 } else if (ultimate
.has
<semantics::ObjectEntityDetails
>()) {
868 return Base::operator()(ultimate
);
872 Result
operator()(const ArrayRef
&x
) const {
874 return true; // scalars considered contiguous
876 int subscriptRank
{0};
877 auto baseLbounds
{GetLBOUNDs(context_
, x
.base())};
878 auto baseUbounds
{GetUBOUNDs(context_
, x
.base())};
879 auto subscripts
{CheckSubscripts(
880 x
.subscript(), subscriptRank
, &baseLbounds
, &baseUbounds
)};
881 if (!subscripts
.value_or(false)) {
882 return subscripts
; // subscripts not known to be contiguous
883 } else if (subscriptRank
> 0) {
884 // a(1)%b(:,:) is contiguous if and only if a(1)%b is contiguous.
885 return (*this)(x
.base());
887 // a(:)%b(1,1) is (probably) not contiguous.
891 Result
operator()(const CoarrayRef
&x
) const {
893 return CheckSubscripts(x
.subscript(), rank
).has_value();
895 Result
operator()(const Component
&x
) const {
896 if (x
.base().Rank() == 0) {
897 return (*this)(x
.GetLastSymbol());
899 if (Result baseIsContiguous
{(*this)(x
.base())}) {
900 if (!*baseIsContiguous
) {
903 // TODO could be true if base contiguous and this is only component, or
904 // if base has only one element?
909 Result
operator()(const ComplexPart
&x
) const {
910 return x
.complex().Rank() == 0;
912 Result
operator()(const Substring
&x
) const {
914 return true; // scalar substring always contiguous
916 // Substrings with rank must have DataRefs as their parents
917 const DataRef
&parentDataRef
{DEREF(x
.GetParentIf
<DataRef
>())};
918 std::optional
<std::int64_t> len
;
919 if (auto lenExpr
{parentDataRef
.LEN()}) {
920 len
= ToInt64(Fold(context_
, std::move(*lenExpr
)));
923 return true; // empty substrings
924 } else if (*len
== 1) {
925 // Substrings can't be incomplete; is base array contiguous?
926 return (*this)(parentDataRef
);
930 std::optional
<std::int64_t> upper
;
931 bool upperIsLen
{false};
932 if (auto upperExpr
{x
.upper()}) {
933 upper
= ToInt64(Fold(context_
, common::Clone(*upperExpr
)));
936 return true; // substring(n:0) empty
938 upperIsLen
= len
&& *upper
>= *len
;
939 } else if (const auto *inquiry
{
940 UnwrapConvertedExpr
<DescriptorInquiry
>(*upperExpr
)};
941 inquiry
&& inquiry
->field() == DescriptorInquiry::Field::Len
) {
943 &parentDataRef
.GetLastSymbol() == &inquiry
->base().GetLastSymbol();
946 upperIsLen
= true; // substring(n:)
948 if (auto lower
{ToInt64(Fold(context_
, x
.lower()))}) {
949 if (*lower
== 1 && upperIsLen
) {
950 // known complete substring; is base contiguous?
951 return (*this)(parentDataRef
);
953 if (*upper
< *lower
) {
954 return true; // empty substring(3:2)
955 } else if (*lower
> 1) {
956 return false; // known incomplete substring
957 } else if (len
&& *upper
< *len
) {
958 return false; // known incomplete substring
962 return std::nullopt
; // contiguity not known
965 Result
operator()(const ProcedureRef
&x
) const {
966 if (auto chars
{characteristics::Procedure::Characterize(
967 x
.proc(), context_
, /*emitError=*/true)}) {
968 if (chars
->functionResult
) {
969 const auto &result
{*chars
->functionResult
};
970 if (!result
.IsProcedurePointer()) {
971 if (result
.attrs
.test(
972 characteristics::FunctionResult::Attr::Contiguous
)) {
975 if (!result
.attrs
.test(
976 characteristics::FunctionResult::Attr::Pointer
)) {
979 if (const auto *type
{result
.GetTypeAndShape()};
980 type
&& type
->Rank() == 0) {
981 return true; // pointer to scalar
983 // Must be non-CONTIGUOUS pointer to array
990 Result
operator()(const NullPointer
&) const { return true; }
993 // Returns "true" for a provably empty or simply contiguous array section;
994 // return "false" for a provably nonempty discontiguous section or for use
995 // of a vector subscript.
996 std::optional
<bool> CheckSubscripts(const std::vector
<Subscript
> &subscript
,
997 int &rank
, const Shape
*baseLbounds
= nullptr,
998 const Shape
*baseUbounds
= nullptr) const {
999 bool anyTriplet
{false};
1001 // Detect any provably empty dimension in this array section, which would
1002 // render the whole section empty and therefore vacuously contiguous.
1003 std::optional
<bool> result
;
1004 bool mayBeEmpty
{false};
1005 auto dims
{subscript
.size()};
1006 std::vector
<bool> knownPartialSlice(dims
, false);
1007 for (auto j
{dims
}; j
-- > 0;) {
1008 std::optional
<ConstantSubscript
> dimLbound
;
1009 std::optional
<ConstantSubscript
> dimUbound
;
1010 std::optional
<ConstantSubscript
> dimExtent
;
1011 if (baseLbounds
&& j
< baseLbounds
->size()) {
1012 if (const auto &lb
{baseLbounds
->at(j
)}) {
1013 dimLbound
= ToInt64(Fold(context_
, Expr
<SubscriptInteger
>{*lb
}));
1016 if (baseUbounds
&& j
< baseUbounds
->size()) {
1017 if (const auto &ub
{baseUbounds
->at(j
)}) {
1018 dimUbound
= ToInt64(Fold(context_
, Expr
<SubscriptInteger
>{*ub
}));
1021 if (dimLbound
&& dimUbound
) {
1022 if (*dimLbound
<= *dimUbound
) {
1023 dimExtent
= *dimUbound
- *dimLbound
+ 1;
1025 // This is an empty dimension.
1031 if (const auto *triplet
{std::get_if
<Triplet
>(&subscript
[j
].u
)}) {
1033 if (auto stride
{ToInt64(triplet
->stride())}) {
1034 const Expr
<SubscriptInteger
> *lowerBound
{triplet
->GetLower()};
1035 const Expr
<SubscriptInteger
> *upperBound
{triplet
->GetUpper()};
1036 std::optional
<ConstantSubscript
> lowerVal
{lowerBound
1037 ? ToInt64(Fold(context_
, Expr
<SubscriptInteger
>{*lowerBound
}))
1039 std::optional
<ConstantSubscript
> upperVal
{upperBound
1040 ? ToInt64(Fold(context_
, Expr
<SubscriptInteger
>{*upperBound
}))
1042 if (lowerVal
&& upperVal
) {
1043 if (*lowerVal
< *upperVal
) {
1045 result
= true; // empty dimension
1046 } else if (!result
&& *stride
> 1 &&
1047 *lowerVal
+ *stride
<= *upperVal
) {
1048 result
= false; // discontiguous if not empty
1050 } else if (*lowerVal
> *upperVal
) {
1052 result
= true; // empty dimension
1053 } else if (!result
&& *stride
< 0 &&
1054 *lowerVal
+ *stride
>= *upperVal
) {
1055 result
= false; // discontiguous if not empty
1066 } else if (subscript
[j
].Rank() > 0) {
1069 result
= false; // vector subscript
1073 // Scalar subscript.
1074 if (dimExtent
&& *dimExtent
> 1) {
1075 knownPartialSlice
[j
] = true;
1080 result
= true; // scalar
1085 // Not provably discontiguous at this point.
1086 // Return "true" if simply contiguous, otherwise nullopt.
1087 for (auto j
{subscript
.size()}; j
-- > 0;) {
1088 if (const auto *triplet
{std::get_if
<Triplet
>(&subscript
[j
].u
)}) {
1089 auto stride
{ToInt64(triplet
->stride())};
1090 if (!stride
|| stride
!= 1) {
1091 return std::nullopt
;
1092 } else if (anyTriplet
) {
1093 if (triplet
->GetLower() || triplet
->GetUpper()) {
1094 // all triplets before the last one must be just ":" for
1095 // simple contiguity
1096 return std::nullopt
;
1102 } else if (anyTriplet
) {
1103 // If the section cannot be empty, and this dimension's
1104 // scalar subscript is known not to cover the whole
1105 // dimension, then the array section is provably
1107 return (mayBeEmpty
|| !knownPartialSlice
[j
])
1109 : std::make_optional(false);
1112 return true; // simply contiguous
1115 FoldingContext
&context_
;
1118 template <typename A
>
1119 std::optional
<bool> IsContiguous(const A
&x
, FoldingContext
&context
) {
1120 return IsContiguousHelper
{context
}(x
);
1123 template std::optional
<bool> IsContiguous(
1124 const Expr
<SomeType
> &, FoldingContext
&);
1125 template std::optional
<bool> IsContiguous(const ArrayRef
&, FoldingContext
&);
1126 template std::optional
<bool> IsContiguous(const Substring
&, FoldingContext
&);
1127 template std::optional
<bool> IsContiguous(const Component
&, FoldingContext
&);
1128 template std::optional
<bool> IsContiguous(
1129 const ComplexPart
&, FoldingContext
&);
1130 template std::optional
<bool> IsContiguous(const CoarrayRef
&, FoldingContext
&);
1131 template std::optional
<bool> IsContiguous(const Symbol
&, FoldingContext
&);
1134 struct IsErrorExprHelper
: public AnyTraverse
<IsErrorExprHelper
, bool> {
1135 using Result
= bool;
1136 using Base
= AnyTraverse
<IsErrorExprHelper
, Result
>;
1137 IsErrorExprHelper() : Base
{*this} {}
1138 using Base::operator();
1140 bool operator()(const SpecificIntrinsic
&x
) {
1141 return x
.name
== IntrinsicProcTable::InvalidName
;
1145 template <typename A
> bool IsErrorExpr(const A
&x
) {
1146 return IsErrorExprHelper
{}(x
);
1149 template bool IsErrorExpr(const Expr
<SomeType
> &);
1152 // TODO: Also check C1579 & C1582 here
1153 class StmtFunctionChecker
1154 : public AnyTraverse
<StmtFunctionChecker
, std::optional
<parser::Message
>> {
1156 using Result
= std::optional
<parser::Message
>;
1157 using Base
= AnyTraverse
<StmtFunctionChecker
, Result
>;
1159 static constexpr auto feature
{
1160 common::LanguageFeature::StatementFunctionExtensions
};
1162 StmtFunctionChecker(const Symbol
&sf
, FoldingContext
&context
)
1163 : Base
{*this}, sf_
{sf
}, context_
{context
} {
1164 if (!context_
.languageFeatures().IsEnabled(feature
)) {
1165 severity_
= parser::Severity::Error
;
1166 } else if (context_
.languageFeatures().ShouldWarn(feature
)) {
1167 severity_
= parser::Severity::Portability
;
1170 using Base::operator();
1172 Result
Return(parser::Message
&&msg
) const {
1174 msg
.set_severity(*severity_
);
1175 if (*severity_
!= parser::Severity::Error
) {
1176 msg
.set_languageFeature(feature
);
1179 return std::move(msg
);
1182 template <typename T
> Result
operator()(const ArrayConstructor
<T
> &) const {
1184 return Return(parser::Message
{sf_
.name(),
1185 "Statement function '%s' should not contain an array constructor"_port_en_US
,
1188 return std::nullopt
;
1191 Result
operator()(const StructureConstructor
&) const {
1193 return Return(parser::Message
{sf_
.name(),
1194 "Statement function '%s' should not contain a structure constructor"_port_en_US
,
1197 return std::nullopt
;
1200 Result
operator()(const TypeParamInquiry
&) const {
1202 return Return(parser::Message
{sf_
.name(),
1203 "Statement function '%s' should not contain a type parameter inquiry"_port_en_US
,
1206 return std::nullopt
;
1209 Result
operator()(const ProcedureDesignator
&proc
) const {
1210 if (const Symbol
* symbol
{proc
.GetSymbol()}) {
1211 const Symbol
&ultimate
{symbol
->GetUltimate()};
1212 if (const auto *subp
{
1213 ultimate
.detailsIf
<semantics::SubprogramDetails
>()}) {
1214 if (subp
->stmtFunction() && &ultimate
.owner() == &sf_
.owner()) {
1215 if (ultimate
.name().begin() > sf_
.name().begin()) {
1216 return parser::Message
{sf_
.name(),
1217 "Statement function '%s' may not reference another statement function '%s' that is defined later"_err_en_US
,
1218 sf_
.name(), ultimate
.name()};
1222 if (auto chars
{characteristics::Procedure::Characterize(
1223 proc
, context_
, /*emitError=*/true)}) {
1224 if (!chars
->CanBeCalledViaImplicitInterface()) {
1226 return Return(parser::Message
{sf_
.name(),
1227 "Statement function '%s' should not reference function '%s' that requires an explicit interface"_port_en_US
,
1228 sf_
.name(), symbol
->name()});
1233 if (proc
.Rank() > 0) {
1235 return Return(parser::Message
{sf_
.name(),
1236 "Statement function '%s' should not reference a function that returns an array"_port_en_US
,
1240 return std::nullopt
;
1242 Result
operator()(const ActualArgument
&arg
) const {
1243 if (const auto *expr
{arg
.UnwrapExpr()}) {
1244 if (auto result
{(*this)(*expr
)}) {
1247 if (expr
->Rank() > 0 && !UnwrapWholeSymbolOrComponentDataRef(*expr
)) {
1249 return Return(parser::Message
{sf_
.name(),
1250 "Statement function '%s' should not pass an array argument that is not a whole array"_port_en_US
,
1255 return std::nullopt
;
1260 FoldingContext
&context_
;
1261 std::optional
<parser::Severity
> severity_
;
1264 std::optional
<parser::Message
> CheckStatementFunction(
1265 const Symbol
&sf
, const Expr
<SomeType
> &expr
, FoldingContext
&context
) {
1266 return StmtFunctionChecker
{sf
, context
}(expr
);
1269 } // namespace Fortran::evaluate