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