FLANG
characteristics.h
1//===-- include/flang/Evaluate/characteristics.h ----------------*- C++ -*-===//
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// Defines data structures to represent "characteristics" of Fortran
10// procedures and other entities as they are specified in section 15.3
11// of Fortran 2018.
12
13#ifndef FORTRAN_EVALUATE_CHARACTERISTICS_H_
14#define FORTRAN_EVALUATE_CHARACTERISTICS_H_
15
16#include "common.h"
17#include "expression.h"
18#include "shape.h"
19#include "tools.h"
20#include "type.h"
21#include "flang/Common/enum-set.h"
22#include "flang/Common/idioms.h"
23#include "flang/Common/indirection.h"
24#include "flang/Parser/char-block.h"
25#include "flang/Semantics/symbol.h"
26#include "flang/Support/Fortran-features.h"
27#include "flang/Support/Fortran.h"
28#include <optional>
29#include <string>
30#include <variant>
31#include <vector>
32
33namespace llvm {
34class raw_ostream;
35}
36
37namespace Fortran::evaluate::characteristics {
38struct Procedure;
39}
40extern template class Fortran::common::Indirection<
42
43namespace Fortran::evaluate::characteristics {
44
45using common::CopyableIndirection;
46
47// Are these procedures distinguishable for a generic name or FINAL?
48std::optional<bool> Distinguishable(const common::LanguageFeatureControl &,
49 const Procedure &, const Procedure &);
50// Are these procedures distinguishable for a generic operator or assignment?
51std::optional<bool> DistinguishableOpOrAssign(
52 const common::LanguageFeatureControl &, const Procedure &,
53 const Procedure &);
54
55// Shapes of function results and dummy arguments have to have
56// the same rank, the same deferred dimensions, and the same
57// values for explicit dimensions when constant.
58bool ShapesAreCompatible(const std::optional<Shape> &,
59 const std::optional<Shape> &, bool *possibleWarning = nullptr);
60
61class TypeAndShape {
62public:
63 ENUM_CLASS(Attr, AssumedRank, AssumedShape, AssumedSize, DeferredShape)
65
66 explicit TypeAndShape(DynamicType t) : type_{t}, shape_{Shape{}} {
67 AcquireLEN();
68 }
69 TypeAndShape(DynamicType t, int rank) : type_{t}, shape_{Shape(rank)} {
70 AcquireLEN();
71 }
72 TypeAndShape(DynamicType t, Shape &&s) : type_{t}, shape_{std::move(s)} {
73 CHECK(IsSubscriptIntegerKind(*shape_));
74 AcquireLEN();
75 }
76 TypeAndShape(DynamicType t, std::optional<Shape> &&s) : type_{t} {
77 shape_ = std::move(s);
78 CHECK(!shape_ || IsSubscriptIntegerKind(*shape_));
79 AcquireLEN();
80 }
81 DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(TypeAndShape)
82
83 bool operator==(const TypeAndShape &) const;
84 bool operator!=(const TypeAndShape &that) const { return !(*this == that); }
85
86 static std::optional<TypeAndShape> Characterize(
87 const semantics::Symbol &, FoldingContext &, bool invariantOnly = true);
88 static std::optional<TypeAndShape> Characterize(
90 bool invariantOnly = true);
91 static std::optional<TypeAndShape> Characterize(
92 const ActualArgument &, FoldingContext &, bool invariantOnly = true);
93
94 // General case for Expr<T>, &c.
95 template <typename A>
96 static std::optional<TypeAndShape> Characterize(
97 const A &x, FoldingContext &context, bool invariantOnly = true) {
98 const auto *symbol{UnwrapWholeSymbolOrComponentDataRef(x)};
99 if (symbol && !symbol->owner().IsDerivedType()) { // Whole variable
100 if (auto result{Characterize(*symbol, context, invariantOnly)}) {
101 return result;
102 }
103 }
104 if (auto type{x.GetType()}) {
105 TypeAndShape result{*type, GetShape(context, x, invariantOnly)};
106 result.corank_ = GetCorank(x);
107 if (type->category() == TypeCategory::Character) {
108 if (const auto *chExpr{
109 UnwrapExpr<Expr<Type<TypeCategory::Character>>>(x)}) {
110 if (auto length{chExpr->LEN()}) {
111 result.set_LEN(std::move(*length));
112 }
113 }
114 }
115 if (symbol) { // component
116 result.AcquireAttrs(*symbol);
117 }
118 return std::move(result.Rewrite(context));
119 }
120 return std::nullopt;
121 }
122
123 // Specialization for character designators
124 static std::optional<TypeAndShape> Characterize(
126 FoldingContext &context, bool invariantOnly = true);
127
128 template <typename A>
129 static std::optional<TypeAndShape> Characterize(const std::optional<A> &x,
130 FoldingContext &context, bool invariantOnly = true) {
131 if (x) {
132 return Characterize(*x, context, invariantOnly);
133 } else {
134 return std::nullopt;
135 }
136 }
137 template <typename A>
138 static std::optional<TypeAndShape> Characterize(
139 A *ptr, FoldingContext &context, bool invariantOnly = true) {
140 if (ptr) {
141 return Characterize(std::as_const(*ptr), context, invariantOnly);
142 } else {
143 return std::nullopt;
144 }
145 }
146
147 DynamicType type() const { return type_; }
148 TypeAndShape &set_type(DynamicType t) {
149 type_ = t;
150 return *this;
151 }
152 const std::optional<Expr<SubscriptInteger>> &LEN() const { return LEN_; }
153 TypeAndShape &set_LEN(Expr<SubscriptInteger> &&len) {
154 CHECK(IsSubscriptIntegerKind(len));
155 LEN_ = std::move(len);
156 return *this;
157 }
158 const std::optional<Shape> &shape() const { return shape_; }
159 const Attrs &attrs() const { return attrs_; }
160 Attrs &attrs() { return attrs_; }
161 bool isPossibleSequenceAssociation() const {
162 return isPossibleSequenceAssociation_;
163 }
164 TypeAndShape &set_isPossibleSequenceAssociation(bool yes) {
165 isPossibleSequenceAssociation_ = yes;
166 return *this;
167 }
168 int corank() const { return corank_; }
169 void set_corank(int n) { corank_ = n; }
170
171 // Return -1 for assumed-rank as a safety.
172 int Rank() const { return shape_ ? GetRank(*shape_) : -1; }
173
174 // Can sequence association apply to this argument?
175 bool CanBeSequenceAssociated() const {
176 constexpr Attrs notAssumedOrExplicitShape{~Attrs{Attr::AssumedSize}};
177 return Rank() > 0 && (attrs() & notAssumedOrExplicitShape).none();
178 }
179
180 bool IsCompatibleWith(parser::ContextualMessages &, const TypeAndShape &that,
181 const char *thisIs = "pointer", const char *thatIs = "target",
182 bool omitShapeConformanceCheck = false,
183 enum CheckConformanceFlags::Flags = CheckConformanceFlags::None) const;
184 std::optional<Expr<SubscriptInteger>> MeasureElementSizeInBytes(
185 FoldingContext &, bool align) const;
186 std::optional<Expr<SubscriptInteger>> MeasureSizeInBytes(
187 FoldingContext &) const;
188
189 bool IsExplicitShape() const {
190 // If it's array and no special attributes are set, then must be
191 // explicit shape.
192 return Rank() > 0 && attrs_.none();
193 }
194
195 // called by Fold() to rewrite in place
196 TypeAndShape &Rewrite(FoldingContext &);
197
198 std::string AsFortran() const;
199 llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
200
201private:
202 static std::optional<TypeAndShape> Characterize(
204 bool invariantOnly = true);
205 void AcquireAttrs(const semantics::Symbol &);
206 void AcquireLEN();
207 void AcquireLEN(const semantics::Symbol &);
208
209 DynamicType type_;
210 std::optional<Expr<SubscriptInteger>> LEN_;
211 std::optional<Shape> shape_;
212 Attrs attrs_;
213 bool isPossibleSequenceAssociation_{false};
214 int corank_{0};
215};
216
217// 15.3.2.2
218struct DummyDataObject {
219 ENUM_CLASS(Attr, Optional, Allocatable, Asynchronous, Contiguous, Value,
220 Volatile, Pointer, Target, DeducedFromActual, OnlyIntrinsicInquiry)
222 static bool IdenticalSignificantAttrs(const Attrs &x, const Attrs &y) {
223 return (x - Attr::DeducedFromActual) == (y - Attr::DeducedFromActual);
224 }
225 DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyDataObject)
226 explicit DummyDataObject(const TypeAndShape &t) : type{t} {}
227 explicit DummyDataObject(TypeAndShape &&t) : type{std::move(t)} {}
228 explicit DummyDataObject(DynamicType t) : type{t} {}
229 bool operator==(const DummyDataObject &) const;
230 bool operator!=(const DummyDataObject &that) const {
231 return !(*this == that);
232 }
233 bool IsCompatibleWith(const DummyDataObject &, std::string *whyNot = nullptr,
234 std::optional<std::string> *warning = nullptr) const;
235 static std::optional<DummyDataObject> Characterize(
237 bool CanBePassedViaImplicitInterface(
238 std::string *whyNot = nullptr, bool checkCUDA = true) const;
239 bool IsPassedByDescriptor(bool isBindC) const;
240 llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
241
242 TypeAndShape type;
243 std::vector<Expr<SubscriptInteger>> coshape;
244 common::Intent intent{common::Intent::Default};
245 Attrs attrs;
246 common::IgnoreTKRSet ignoreTKR;
247 std::optional<common::CUDADataAttr> cudaDataAttr;
248};
249
250// 15.3.2.3
251struct DummyProcedure {
252 ENUM_CLASS(Attr, Pointer, Optional)
254 DECLARE_CONSTRUCTORS_AND_ASSIGNMENTS(DummyProcedure)
255 explicit DummyProcedure(Procedure &&);
256 bool operator==(const DummyProcedure &) const;
257 bool operator!=(const DummyProcedure &that) const { return !(*this == that); }
258 bool IsCompatibleWith(
259 const DummyProcedure &, std::string *whyNot = nullptr) const;
260 bool CanBePassedViaImplicitInterface(std::string *whyNot = nullptr) const;
261 llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
262
263 CopyableIndirection<Procedure> procedure;
264 common::Intent intent{common::Intent::Default};
265 Attrs attrs;
266};
267
268// 15.3.2.4
270 bool operator==(const AlternateReturn &) const { return true; }
271 bool operator!=(const AlternateReturn &) const { return false; }
272 llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
273};
274
275// 15.3.2.1
276struct DummyArgument {
277 DECLARE_CONSTRUCTORS_AND_ASSIGNMENTS(DummyArgument)
278 DummyArgument(std::string &&name, DummyDataObject &&x)
279 : name{std::move(name)}, u{std::move(x)} {}
280 DummyArgument(std::string &&name, DummyProcedure &&x)
281 : name{std::move(name)}, u{std::move(x)} {}
282 explicit DummyArgument(AlternateReturn &&x) : u{std::move(x)} {}
283 ~DummyArgument();
284 bool operator==(const DummyArgument &) const;
285 bool operator!=(const DummyArgument &that) const { return !(*this == that); }
286 static std::optional<DummyArgument> FromActual(std::string &&,
287 const Expr<SomeType> &, FoldingContext &, bool forImplicitInterface);
288 static std::optional<DummyArgument> FromActual(std::string &&,
289 const ActualArgument &, FoldingContext &, bool forImplicitInterface);
290 bool IsOptional() const;
291 void SetOptional(bool = true);
292 common::Intent GetIntent() const;
293 void SetIntent(common::Intent);
294 bool CanBePassedViaImplicitInterface(
295 std::string *whyNot = nullptr, bool checkCUDA = true) const;
296 bool IsTypelessIntrinsicDummy() const;
297 bool IsCompatibleWith(const DummyArgument &, std::string *whyNot = nullptr,
298 std::optional<std::string> *warning = nullptr) const;
299 llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
300
301 // name and pass are not characteristics and so do not participate in
302 // compatibility checks, but they are needed to determine whether
303 // procedures are distinguishable
304 std::string name;
305 bool pass{false}; // is this the PASS argument of its procedure
306 std::variant<DummyDataObject, DummyProcedure, AlternateReturn> u;
307};
308
309using DummyArguments = std::vector<DummyArgument>;
310
311// 15.3.3
312struct FunctionResult {
313 ENUM_CLASS(Attr, Allocatable, Pointer, Contiguous)
315 DECLARE_CONSTRUCTORS_AND_ASSIGNMENTS(FunctionResult)
316 explicit FunctionResult(DynamicType);
317 explicit FunctionResult(TypeAndShape &&);
318 explicit FunctionResult(Procedure &&);
319 ~FunctionResult();
320 bool operator==(const FunctionResult &) const;
321 bool operator!=(const FunctionResult &that) const { return !(*this == that); }
322 static std::optional<FunctionResult> Characterize(
323 const Symbol &, FoldingContext &);
324
325 bool IsAssumedLengthCharacter() const;
326
327 const Procedure *IsProcedurePointer() const {
328 if (const auto *pp{std::get_if<CopyableIndirection<Procedure>>(&u)}) {
329 return &pp->value();
330 } else {
331 return nullptr;
332 }
333 }
334 const TypeAndShape *GetTypeAndShape() const {
335 return std::get_if<TypeAndShape>(&u);
336 }
337 void SetType(DynamicType t) { std::get<TypeAndShape>(u).set_type(t); }
338 bool CanBeReturnedViaImplicitInterface(std::string *whyNot = nullptr) const;
339 bool IsCompatibleWith(
340 const FunctionResult &, std::string *whyNot = nullptr) const;
341
342 llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
343
344 Attrs attrs;
345 std::variant<TypeAndShape, CopyableIndirection<Procedure>> u;
346 std::optional<common::CUDADataAttr> cudaDataAttr;
347};
348
349// 15.3.1
350struct Procedure {
351 ENUM_CLASS(Attr, Pure, Simple, Elemental, BindC, ImplicitInterface,
352 NullPointer, NullAllocatable, Subroutine)
354 Procedure(){};
355 Procedure(FunctionResult &&, DummyArguments &&, Attrs);
356 Procedure(DummyArguments &&, Attrs); // for subroutines and NULL()
357 DECLARE_CONSTRUCTORS_AND_ASSIGNMENTS(Procedure)
358 ~Procedure();
359 bool operator==(const Procedure &) const;
360 bool operator!=(const Procedure &that) const { return !(*this == that); }
361
362 // Characterizes a procedure. If a Symbol, it may be an
363 // "unrestricted specific intrinsic function".
364 // Error messages are produced when a procedure cannot be characterized.
365 static std::optional<Procedure> Characterize(
367 static std::optional<Procedure> Characterize(
368 const ProcedureDesignator &, FoldingContext &, bool emitError);
369 static std::optional<Procedure> Characterize(
370 const ProcedureRef &, FoldingContext &);
371 static std::optional<Procedure> Characterize(
372 const Expr<SomeType> &, FoldingContext &);
373 // Characterizes the procedure being referenced, deducing dummy argument
374 // types from actual arguments in the case of an implicit interface.
375 static std::optional<Procedure> FromActuals(
376 const ProcedureDesignator &, const ActualArguments &, FoldingContext &);
377
378 // At most one of these will return true.
379 // For "EXTERNAL P" with no type for or calls to P, both will be false.
380 bool IsFunction() const { return functionResult.has_value(); }
381 bool IsSubroutine() const { return attrs.test(Attr::Subroutine); }
382
383 bool IsPure() const { return attrs.test(Attr::Pure); }
384 bool IsSimple() const { return attrs.test(Attr::Simple); }
385 bool IsElemental() const { return attrs.test(Attr::Elemental); }
386 bool IsBindC() const { return attrs.test(Attr::BindC); }
387 bool HasExplicitInterface() const {
388 return !attrs.test(Attr::ImplicitInterface);
389 }
390 std::optional<int> FindPassIndex(std::optional<parser::CharBlock>) const;
391 bool CanBeCalledViaImplicitInterface(
392 std::string *whyNot = nullptr, bool checkCUDA = true) const;
393 bool CanOverride(const Procedure &, std::optional<int> passIndex) const;
394 bool IsCompatibleWith(const Procedure &, bool ignoreImplicitVsExplicit,
395 std::string *whyNot = nullptr, const SpecificIntrinsic * = nullptr,
396 std::optional<std::string> *warning = nullptr) const;
397
398 llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
399
400 std::optional<FunctionResult> functionResult;
401 DummyArguments dummyArguments;
402 Attrs attrs;
403 std::optional<common::CUDASubprogramAttrs> cudaSubprogramAttrs;
404 // Used only by CUDA semantic checks; this is not a CUDA procedure attribute.
405 bool hasOpenACCRoutine{false};
406 // Statement functions are inlined; CUDA data-attribute matching does not
407 // apply to their dummy arguments.
408 bool isStmtFunction{false};
409};
410
411} // namespace Fortran::evaluate::characteristics
412#endif // FORTRAN_EVALUATE_CHARACTERISTICS_H_
Definition enum-set.h:28
Definition indirection.h:31
Definition variable.h:386
Definition type.h:101
Definition common.h:222
Definition common.h:224
Definition call.h:352
Definition type.h:61
Definition message.h:399
Definition symbol.h:918
Definition expression.h:924
Definition characteristics.h:350