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