1 //===-- lib/Evaluate/call.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/call.h"
10 #include "flang/Common/Fortran.h"
11 #include "flang/Common/idioms.h"
12 #include "flang/Evaluate/characteristics.h"
13 #include "flang/Evaluate/check-expression.h"
14 #include "flang/Evaluate/expression.h"
15 #include "flang/Evaluate/tools.h"
16 #include "flang/Semantics/symbol.h"
17
18 namespace Fortran::evaluate {
19
DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(ActualArgument)20 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(ActualArgument)
21 ActualArgument::ActualArgument(Expr<SomeType> &&x) : u_{std::move(x)} {}
ActualArgument(common::CopyableIndirection<Expr<SomeType>> && v)22 ActualArgument::ActualArgument(common::CopyableIndirection<Expr<SomeType>> &&v)
23 : u_{std::move(v)} {}
ActualArgument(AssumedType x)24 ActualArgument::ActualArgument(AssumedType x) : u_{x} {}
ActualArgument(common::Label x)25 ActualArgument::ActualArgument(common::Label x) : u_{x} {}
~ActualArgument()26 ActualArgument::~ActualArgument() {}
27
AssumedType(const Symbol & symbol)28 ActualArgument::AssumedType::AssumedType(const Symbol &symbol)
29 : symbol_{symbol} {
30 const semantics::DeclTypeSpec *type{symbol.GetType()};
31 CHECK(type && type->category() == semantics::DeclTypeSpec::TypeStar);
32 }
33
Rank() const34 int ActualArgument::AssumedType::Rank() const { return symbol_->Rank(); }
35
operator =(Expr<SomeType> && expr)36 ActualArgument &ActualArgument::operator=(Expr<SomeType> &&expr) {
37 u_ = std::move(expr);
38 return *this;
39 }
40
GetType() const41 std::optional<DynamicType> ActualArgument::GetType() const {
42 if (const Expr<SomeType> *expr{UnwrapExpr()}) {
43 return expr->GetType();
44 } else if (std::holds_alternative<AssumedType>(u_)) {
45 return DynamicType::AssumedType();
46 } else {
47 return std::nullopt;
48 }
49 }
50
Rank() const51 int ActualArgument::Rank() const {
52 if (const Expr<SomeType> *expr{UnwrapExpr()}) {
53 return expr->Rank();
54 } else {
55 return std::get<AssumedType>(u_).Rank();
56 }
57 }
58
operator ==(const ActualArgument & that) const59 bool ActualArgument::operator==(const ActualArgument &that) const {
60 return keyword_ == that.keyword_ && isPassedObject_ == that.isPassedObject_ &&
61 u_ == that.u_;
62 }
63
Parenthesize()64 void ActualArgument::Parenthesize() {
65 u_ = evaluate::Parenthesize(std::move(DEREF(UnwrapExpr())));
66 }
67
SpecificIntrinsic(IntrinsicProcedure n,characteristics::Procedure && chars)68 SpecificIntrinsic::SpecificIntrinsic(
69 IntrinsicProcedure n, characteristics::Procedure &&chars)
70 : name{n}, characteristics{
71 new characteristics::Procedure{std::move(chars)}} {}
72
DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(SpecificIntrinsic)73 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(SpecificIntrinsic)
74
75 SpecificIntrinsic::~SpecificIntrinsic() {}
76
operator ==(const SpecificIntrinsic & that) const77 bool SpecificIntrinsic::operator==(const SpecificIntrinsic &that) const {
78 return name == that.name && characteristics == that.characteristics;
79 }
80
ProcedureDesignator(Component && c)81 ProcedureDesignator::ProcedureDesignator(Component &&c)
82 : u{common::CopyableIndirection<Component>::Make(std::move(c))} {}
83
operator ==(const ProcedureDesignator & that) const84 bool ProcedureDesignator::operator==(const ProcedureDesignator &that) const {
85 return u == that.u;
86 }
87
GetType() const88 std::optional<DynamicType> ProcedureDesignator::GetType() const {
89 if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&u)}) {
90 if (const auto &result{intrinsic->characteristics.value().functionResult}) {
91 if (const auto *typeAndShape{result->GetTypeAndShape()}) {
92 return typeAndShape->type();
93 }
94 }
95 } else {
96 return DynamicType::From(GetSymbol());
97 }
98 return std::nullopt;
99 }
100
Rank() const101 int ProcedureDesignator::Rank() const {
102 if (const Symbol * symbol{GetSymbol()}) {
103 // Subtle: will be zero for functions returning procedure pointers
104 return symbol->Rank();
105 }
106 if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&u)}) {
107 if (const auto &result{intrinsic->characteristics.value().functionResult}) {
108 if (const auto *typeAndShape{result->GetTypeAndShape()}) {
109 CHECK(!typeAndShape->attrs().test(
110 characteristics::TypeAndShape::Attr::AssumedRank));
111 return typeAndShape->Rank();
112 }
113 // Otherwise, intrinsic returns a procedure pointer (e.g. NULL(MOLD=pptr))
114 }
115 }
116 return 0;
117 }
118
GetInterfaceSymbol() const119 const Symbol *ProcedureDesignator::GetInterfaceSymbol() const {
120 if (const Symbol * symbol{GetSymbol()}) {
121 const Symbol &ultimate{symbol->GetUltimate()};
122 if (const auto *proc{ultimate.detailsIf<semantics::ProcEntityDetails>()}) {
123 return proc->interface().symbol();
124 } else if (const auto *binding{
125 ultimate.detailsIf<semantics::ProcBindingDetails>()}) {
126 return &binding->symbol();
127 } else if (ultimate.has<semantics::SubprogramDetails>()) {
128 return &ultimate;
129 }
130 }
131 return nullptr;
132 }
133
IsElemental() const134 bool ProcedureDesignator::IsElemental() const {
135 if (const Symbol * interface{GetInterfaceSymbol()}) {
136 return IsElementalProcedure(*interface);
137 } else if (const Symbol * symbol{GetSymbol()}) {
138 return IsElementalProcedure(*symbol);
139 } else if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&u)}) {
140 return intrinsic->characteristics.value().attrs.test(
141 characteristics::Procedure::Attr::Elemental);
142 } else {
143 DIE("ProcedureDesignator::IsElemental(): no case");
144 }
145 return false;
146 }
147
GetSpecificIntrinsic() const148 const SpecificIntrinsic *ProcedureDesignator::GetSpecificIntrinsic() const {
149 return std::get_if<SpecificIntrinsic>(&u);
150 }
151
GetComponent() const152 const Component *ProcedureDesignator::GetComponent() const {
153 if (auto *c{std::get_if<common::CopyableIndirection<Component>>(&u)}) {
154 return &c->value();
155 } else {
156 return nullptr;
157 }
158 }
159
GetSymbol() const160 const Symbol *ProcedureDesignator::GetSymbol() const {
161 return common::visit(
162 common::visitors{
163 [](SymbolRef symbol) { return &*symbol; },
164 [](const common::CopyableIndirection<Component> &c) {
165 return &c.value().GetLastSymbol();
166 },
167 [](const auto &) -> const Symbol * { return nullptr; },
168 },
169 u);
170 }
171
GetName() const172 std::string ProcedureDesignator::GetName() const {
173 return common::visit(
174 common::visitors{
175 [](const SpecificIntrinsic &i) { return i.name; },
176 [](const Symbol &symbol) { return symbol.name().ToString(); },
177 [](const common::CopyableIndirection<Component> &c) {
178 return c.value().GetLastSymbol().name().ToString();
179 },
180 },
181 u);
182 }
183
LEN() const184 std::optional<Expr<SubscriptInteger>> ProcedureRef::LEN() const {
185 if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&proc_.u)}) {
186 if (intrinsic->name == "repeat") {
187 // LEN(REPEAT(ch,n)) == LEN(ch) * n
188 CHECK(arguments_.size() == 2);
189 const auto *stringArg{
190 UnwrapExpr<Expr<SomeCharacter>>(arguments_[0].value())};
191 const auto *nCopiesArg{
192 UnwrapExpr<Expr<SomeInteger>>(arguments_[1].value())};
193 CHECK(stringArg && nCopiesArg);
194 if (auto stringLen{stringArg->LEN()}) {
195 auto converted{ConvertTo(*stringLen, common::Clone(*nCopiesArg))};
196 return *std::move(stringLen) * std::move(converted);
197 }
198 }
199 // Some other cases (e.g., LEN(CHAR(...))) are handled in
200 // ProcedureDesignator::LEN() because they're independent of the
201 // lengths of the actual arguments.
202 }
203 if (auto len{proc_.LEN()}) {
204 if (IsActuallyConstant(*len)) {
205 return len;
206 }
207 // TODO: Handle cases where the length of a function result is a
208 // safe expression in terms of actual argument values, after substituting
209 // actual argument expressions for INTENT(IN)/VALUE dummy arguments.
210 }
211 return std::nullopt;
212 }
213
Rank() const214 int ProcedureRef::Rank() const {
215 if (IsElemental()) {
216 for (const auto &arg : arguments_) {
217 if (arg) {
218 if (int rank{arg->Rank()}; rank > 0) {
219 return rank;
220 }
221 }
222 }
223 return 0;
224 } else {
225 return proc_.Rank();
226 }
227 }
228
~ProcedureRef()229 ProcedureRef::~ProcedureRef() {}
230
Deleter(ProcedureRef * p)231 void ProcedureRef::Deleter(ProcedureRef *p) { delete p; }
232
233 } // namespace Fortran::evaluate
234