1 | //===-- lib/Semantics/expression.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/Semantics/expression.h" |
10 | #include "check-call.h" |
11 | #include "pointer-assignment.h" |
12 | #include "resolve-names-utils.h" |
13 | #include "resolve-names.h" |
14 | #include "flang/Common/idioms.h" |
15 | #include "flang/Common/type-kinds.h" |
16 | #include "flang/Evaluate/common.h" |
17 | #include "flang/Evaluate/fold.h" |
18 | #include "flang/Evaluate/tools.h" |
19 | #include "flang/Parser/characters.h" |
20 | #include "flang/Parser/dump-parse-tree.h" |
21 | #include "flang/Parser/parse-tree-visitor.h" |
22 | #include "flang/Parser/parse-tree.h" |
23 | #include "flang/Semantics/scope.h" |
24 | #include "flang/Semantics/semantics.h" |
25 | #include "flang/Semantics/symbol.h" |
26 | #include "flang/Semantics/tools.h" |
27 | #include "flang/Support/Fortran.h" |
28 | #include "llvm/Support/raw_ostream.h" |
29 | #include <algorithm> |
30 | #include <functional> |
31 | #include <optional> |
32 | #include <set> |
33 | #include <vector> |
34 | |
35 | // Typedef for optional generic expressions (ubiquitous in this file) |
36 | using MaybeExpr = |
37 | std::optional<Fortran::evaluate::Expr<Fortran::evaluate::SomeType>>; |
38 | |
39 | // Much of the code that implements semantic analysis of expressions is |
40 | // tightly coupled with their typed representations in lib/Evaluate, |
41 | // and appears here in namespace Fortran::evaluate for convenience. |
42 | namespace Fortran::evaluate { |
43 | |
44 | using common::LanguageFeature; |
45 | using common::NumericOperator; |
46 | using common::TypeCategory; |
47 | |
48 | static inline std::string ToUpperCase(std::string_view str) { |
49 | return parser::ToUpperCaseLetters(str); |
50 | } |
51 | |
52 | struct DynamicTypeWithLength : public DynamicType { |
53 | explicit DynamicTypeWithLength(const DynamicType &t) : DynamicType{t} {} |
54 | std::optional<Expr<SubscriptInteger>> LEN() const; |
55 | std::optional<Expr<SubscriptInteger>> length; |
56 | }; |
57 | |
58 | std::optional<Expr<SubscriptInteger>> DynamicTypeWithLength::LEN() const { |
59 | if (length) { |
60 | return length; |
61 | } else { |
62 | return GetCharLength(); |
63 | } |
64 | } |
65 | |
66 | static std::optional<DynamicTypeWithLength> AnalyzeTypeSpec( |
67 | const std::optional<parser::TypeSpec> &spec, FoldingContext &context) { |
68 | if (spec) { |
69 | if (const semantics::DeclTypeSpec *typeSpec{spec->declTypeSpec}) { |
70 | // Name resolution sets TypeSpec::declTypeSpec only when it's valid |
71 | // (viz., an intrinsic type with valid known kind or a non-polymorphic |
72 | // & non-ABSTRACT derived type). |
73 | if (const semantics::IntrinsicTypeSpec *intrinsic{ |
74 | typeSpec->AsIntrinsic()}) { |
75 | TypeCategory category{intrinsic->category()}; |
76 | if (auto optKind{ToInt64(intrinsic->kind())}) { |
77 | int kind{static_cast<int>(*optKind)}; |
78 | if (category == TypeCategory::Character) { |
79 | const semantics::CharacterTypeSpec &cts{ |
80 | typeSpec->characterTypeSpec()}; |
81 | const semantics::ParamValue &len{cts.length()}; |
82 | if (len.isAssumed() || len.isDeferred()) { |
83 | context.messages().Say( |
84 | "A length specifier of '*' or ':' may not appear in the type of an array constructor"_err_en_US); |
85 | } |
86 | DynamicTypeWithLength type{DynamicType{kind, len}}; |
87 | if (auto lenExpr{type.LEN()}) { |
88 | type.length = Fold(context, |
89 | AsExpr(Extremum<SubscriptInteger>{Ordering::Greater, |
90 | Expr<SubscriptInteger>{0}, std::move(*lenExpr)})); |
91 | } |
92 | return type; |
93 | } else { |
94 | return DynamicTypeWithLength{DynamicType{category, kind}}; |
95 | } |
96 | } |
97 | } else if (const semantics::DerivedTypeSpec *derived{ |
98 | typeSpec->AsDerived()}) { |
99 | return DynamicTypeWithLength{DynamicType{*derived}}; |
100 | } |
101 | } |
102 | } |
103 | return std::nullopt; |
104 | } |
105 | |
106 | // Utilities to set a source location, if we have one, on an actual argument, |
107 | // when it is statically present. |
108 | static void SetArgSourceLocation(ActualArgument &x, parser::CharBlock at) { |
109 | x.set_sourceLocation(at); |
110 | } |
111 | static void SetArgSourceLocation( |
112 | std::optional<ActualArgument> &x, parser::CharBlock at) { |
113 | if (x) { |
114 | x->set_sourceLocation(at); |
115 | } |
116 | } |
117 | static void SetArgSourceLocation( |
118 | std::optional<ActualArgument> &x, std::optional<parser::CharBlock> at) { |
119 | if (x && at) { |
120 | x->set_sourceLocation(*at); |
121 | } |
122 | } |
123 | |
124 | class ArgumentAnalyzer { |
125 | public: |
126 | explicit ArgumentAnalyzer(ExpressionAnalyzer &context) |
127 | : context_{context}, source_{context.GetContextualMessages().at()}, |
128 | isProcedureCall_{false} {} |
129 | ArgumentAnalyzer(ExpressionAnalyzer &context, parser::CharBlock source, |
130 | bool isProcedureCall = false) |
131 | : context_{context}, source_{source}, isProcedureCall_{isProcedureCall} {} |
132 | bool fatalErrors() const { return fatalErrors_; } |
133 | ActualArguments &&GetActuals() { |
134 | CHECK(!fatalErrors_); |
135 | return std::move(actuals_); |
136 | } |
137 | const Expr<SomeType> &GetExpr(std::size_t i) const { |
138 | return DEREF(actuals_.at(i).value().UnwrapExpr()); |
139 | } |
140 | Expr<SomeType> &&MoveExpr(std::size_t i) { |
141 | return std::move(DEREF(actuals_.at(i).value().UnwrapExpr())); |
142 | } |
143 | void Analyze(const common::Indirection<parser::Expr> &x) { |
144 | Analyze(x: x.value()); |
145 | } |
146 | void Analyze(const parser::Expr &x) { |
147 | actuals_.emplace_back(AnalyzeExpr(x)); |
148 | SetArgSourceLocation(actuals_.back(), x.source); |
149 | fatalErrors_ |= !actuals_.back(); |
150 | } |
151 | void Analyze(const parser::Variable &); |
152 | void Analyze(const parser::ActualArgSpec &, bool isSubroutine); |
153 | void ConvertBOZOperand(std::optional<DynamicType> *thisType, std::size_t, |
154 | std::optional<DynamicType> otherType); |
155 | void ConvertBOZAssignmentRHS(const DynamicType &lhsType); |
156 | |
157 | bool IsIntrinsicRelational( |
158 | RelationalOperator, const DynamicType &, const DynamicType &) const; |
159 | bool IsIntrinsicLogical() const; |
160 | bool IsIntrinsicNumeric(NumericOperator) const; |
161 | bool IsIntrinsicConcat() const; |
162 | |
163 | bool CheckConformance(); |
164 | bool CheckAssignmentConformance(); |
165 | bool CheckForNullPointer(const char *where = "as an operand here"); |
166 | bool CheckForAssumedRank(const char *where = "as an operand here"); |
167 | |
168 | // Find and return a user-defined operator or report an error. |
169 | // The provided message is used if there is no such operator. |
170 | // If a definedOpSymbolPtr is provided, the caller must check |
171 | // for its accessibility. |
172 | MaybeExpr TryDefinedOp( |
173 | const char *, parser::MessageFixedText, bool isUserOp = false); |
174 | template <typename E> |
175 | MaybeExpr TryDefinedOp(E opr, parser::MessageFixedText msg) { |
176 | return TryDefinedOp( |
177 | context_.context().languageFeatures().GetNames(opr), msg); |
178 | } |
179 | // Find and return a user-defined assignment |
180 | std::optional<ProcedureRef> TryDefinedAssignment(); |
181 | std::optional<ProcedureRef> GetDefinedAssignmentProc(); |
182 | std::optional<DynamicType> GetType(std::size_t) const; |
183 | void Dump(llvm::raw_ostream &); |
184 | |
185 | private: |
186 | MaybeExpr TryDefinedOp( |
187 | const std::vector<const char *> &, parser::MessageFixedText); |
188 | MaybeExpr TryBoundOp(const Symbol &, int passIndex); |
189 | std::optional<ActualArgument> AnalyzeExpr(const parser::Expr &); |
190 | std::optional<ActualArgument> AnalyzeVariable(const parser::Variable &); |
191 | MaybeExpr AnalyzeExprOrWholeAssumedSizeArray(const parser::Expr &); |
192 | bool AreConformable() const; |
193 | const Symbol *FindBoundOp(parser::CharBlock, int passIndex, |
194 | const Symbol *&generic, bool isSubroutine); |
195 | void AddAssignmentConversion( |
196 | const DynamicType &lhsType, const DynamicType &rhsType); |
197 | bool OkLogicalIntegerAssignment(TypeCategory lhs, TypeCategory rhs); |
198 | int GetRank(std::size_t) const; |
199 | bool IsBOZLiteral(std::size_t i) const { |
200 | return evaluate::IsBOZLiteral(GetExpr(i)); |
201 | } |
202 | void SayNoMatch(const std::string &, bool isAssignment = false); |
203 | std::string TypeAsFortran(std::size_t); |
204 | bool AnyUntypedOrMissingOperand(); |
205 | |
206 | ExpressionAnalyzer &context_; |
207 | ActualArguments actuals_; |
208 | parser::CharBlock source_; |
209 | bool fatalErrors_{false}; |
210 | const bool isProcedureCall_; // false for user-defined op or assignment |
211 | }; |
212 | |
213 | // Wraps a data reference in a typed Designator<>, and a procedure |
214 | // or procedure pointer reference in a ProcedureDesignator. |
215 | MaybeExpr ExpressionAnalyzer::Designate(DataRef &&ref) { |
216 | const Symbol &last{ref.GetLastSymbol()}; |
217 | const Symbol &specific{BypassGeneric(last)}; |
218 | const Symbol &symbol{specific.GetUltimate()}; |
219 | if (semantics::IsProcedure(symbol)) { |
220 | if (symbol.attrs().test(semantics::Attr::ABSTRACT)) { |
221 | Say("Abstract procedure interface '%s' may not be used as a designator"_err_en_US, |
222 | last.name()); |
223 | } |
224 | if (auto *component{std::get_if<Component>(&ref.u)}) { |
225 | if (!CheckDataRef(ref)) { |
226 | return std::nullopt; |
227 | } |
228 | return Expr<SomeType>{ProcedureDesignator{std::move(*component)}}; |
229 | } else if (!std::holds_alternative<SymbolRef>(ref.u)) { |
230 | DIE("unexpected alternative in DataRef"); |
231 | } else if (!symbol.attrs().test(semantics::Attr::INTRINSIC)) { |
232 | if (symbol.has<semantics::GenericDetails>()) { |
233 | Say("'%s' is not a specific procedure"_err_en_US, last.name()); |
234 | } else if (IsProcedurePointer(specific)) { |
235 | // For procedure pointers, retain associations so that data accesses |
236 | // from client modules will work. |
237 | return Expr<SomeType>{ProcedureDesignator{specific}}; |
238 | } else { |
239 | return Expr<SomeType>{ProcedureDesignator{symbol}}; |
240 | } |
241 | } else if (auto interface{context_.intrinsics().IsSpecificIntrinsicFunction( |
242 | symbol.name().ToString())}; |
243 | interface && !interface->isRestrictedSpecific) { |
244 | SpecificIntrinsic intrinsic{ |
245 | symbol.name().ToString(), std::move(*interface)}; |
246 | intrinsic.isRestrictedSpecific = interface->isRestrictedSpecific; |
247 | return Expr<SomeType>{ProcedureDesignator{std::move(intrinsic)}}; |
248 | } else { |
249 | Say("'%s' is not an unrestricted specific intrinsic procedure"_err_en_US, |
250 | last.name()); |
251 | } |
252 | return std::nullopt; |
253 | } else if (MaybeExpr result{AsGenericExpr(std::move(ref))}) { |
254 | return result; |
255 | } else if (semantics::HadUseError( |
256 | context_, GetContextualMessages().at(), &symbol)) { |
257 | return std::nullopt; |
258 | } else { |
259 | if (!context_.HasError(last) && !context_.HasError(symbol)) { |
260 | AttachDeclaration( |
261 | Say("'%s' is not an object that can appear in an expression"_err_en_US, |
262 | last.name()), |
263 | symbol); |
264 | context_.SetError(last); |
265 | } |
266 | return std::nullopt; |
267 | } |
268 | } |
269 | |
270 | // Returns false if any dimension could be empty (e.g. A(1:0)) or has an error |
271 | static bool FoldSubscripts(semantics::SemanticsContext &context, |
272 | const Symbol &arraySymbol, std::vector<Subscript> &subscripts, Shape &lb, |
273 | Shape &ub) { |
274 | FoldingContext &foldingContext{context.foldingContext()}; |
275 | lb = GetLBOUNDs(foldingContext, NamedEntity{arraySymbol}); |
276 | CHECK(lb.size() >= subscripts.size()); |
277 | ub = GetUBOUNDs(foldingContext, NamedEntity{arraySymbol}); |
278 | CHECK(ub.size() >= subscripts.size()); |
279 | bool anyPossiblyEmptyDim{false}; |
280 | int dim{0}; |
281 | for (Subscript &ss : subscripts) { |
282 | if (Triplet * triplet{std::get_if<Triplet>(&ss.u)}) { |
283 | auto expr{Fold(foldingContext, triplet->stride())}; |
284 | auto stride{ToInt64(expr)}; |
285 | triplet->set_stride(std::move(expr)); |
286 | std::optional<ConstantSubscript> lower, upper; |
287 | if (auto expr{triplet->lower()}) { |
288 | *expr = Fold(foldingContext, std::move(*expr)); |
289 | lower = ToInt64(*expr); |
290 | triplet->set_lower(std::move(*expr)); |
291 | } else { |
292 | lower = ToInt64(lb[dim]); |
293 | } |
294 | if (auto expr{triplet->upper()}) { |
295 | *expr = Fold(foldingContext, std::move(*expr)); |
296 | upper = ToInt64(*expr); |
297 | triplet->set_upper(std::move(*expr)); |
298 | } else { |
299 | upper = ToInt64(ub[dim]); |
300 | } |
301 | if (stride) { |
302 | if (*stride == 0) { |
303 | foldingContext.messages().Say( |
304 | "Stride of triplet must not be zero"_err_en_US); |
305 | return false; // error |
306 | } |
307 | if (lower && upper) { |
308 | if (*stride > 0) { |
309 | anyPossiblyEmptyDim |= *lower > *upper; |
310 | } else { |
311 | anyPossiblyEmptyDim |= *lower < *upper; |
312 | } |
313 | } else { |
314 | anyPossiblyEmptyDim = true; |
315 | } |
316 | } else { // non-constant stride |
317 | if (lower && upper && *lower == *upper) { |
318 | // stride is not relevant |
319 | } else { |
320 | anyPossiblyEmptyDim = true; |
321 | } |
322 | } |
323 | } else { // not triplet |
324 | auto &expr{std::get<IndirectSubscriptIntegerExpr>(ss.u).value()}; |
325 | expr = Fold(foldingContext, std::move(expr)); |
326 | anyPossiblyEmptyDim |= expr.Rank() > 0; // vector subscript |
327 | } |
328 | ++dim; |
329 | } |
330 | return !anyPossiblyEmptyDim; |
331 | } |
332 | |
333 | static void ValidateSubscriptValue(parser::ContextualMessages &messages, |
334 | const Symbol &symbol, ConstantSubscript val, |
335 | std::optional<ConstantSubscript> lb, std::optional<ConstantSubscript> ub, |
336 | int dim, const char *co = "") { |
337 | std::optional<parser::MessageFixedText> msg; |
338 | std::optional<ConstantSubscript> bound; |
339 | if (lb && val < *lb) { |
340 | msg = |
341 | "%ssubscript %jd is less than lower %sbound %jd for %sdimension %d of array"_err_en_US; |
342 | bound = *lb; |
343 | } else if (ub && val > *ub) { |
344 | msg = |
345 | "%ssubscript %jd is greater than upper %sbound %jd for %sdimension %d of array"_err_en_US; |
346 | bound = *ub; |
347 | if (dim + 1 == symbol.Rank() && IsDummy(symbol) && *bound == 1) { |
348 | // Old-school overindexing of a dummy array isn't fatal when |
349 | // it's on the last dimension and the extent is 1. |
350 | msg->set_severity(parser::Severity::Warning); |
351 | } |
352 | } |
353 | if (msg) { |
354 | AttachDeclaration( |
355 | messages.Say(std::move(*msg), co, static_cast<std::intmax_t>(val), co, |
356 | static_cast<std::intmax_t>(bound.value()), co, dim + 1), |
357 | symbol); |
358 | } |
359 | } |
360 | |
361 | static void ValidateSubscripts(semantics::SemanticsContext &context, |
362 | const Symbol &arraySymbol, const std::vector<Subscript> &subscripts, |
363 | const Shape &lb, const Shape &ub) { |
364 | int dim{0}; |
365 | for (const Subscript &ss : subscripts) { |
366 | auto dimLB{ToInt64(lb[dim])}; |
367 | auto dimUB{ToInt64(ub[dim])}; |
368 | if (dimUB && dimLB && *dimUB < *dimLB) { |
369 | AttachDeclaration( |
370 | context.Warn(common::UsageWarning::SubscriptedEmptyArray, |
371 | context.foldingContext().messages().at(), |
372 | "Empty array dimension %d should not be subscripted as an element or non-empty array section"_err_en_US, |
373 | dim + 1), |
374 | arraySymbol); |
375 | break; |
376 | } |
377 | std::optional<ConstantSubscript> val[2]; |
378 | int vals{0}; |
379 | if (auto *triplet{std::get_if<Triplet>(&ss.u)}) { |
380 | auto stride{ToInt64(triplet->stride())}; |
381 | std::optional<ConstantSubscript> lower, upper; |
382 | if (const auto *lowerExpr{triplet->GetLower()}) { |
383 | lower = ToInt64(*lowerExpr); |
384 | } else if (lb[dim]) { |
385 | lower = ToInt64(*lb[dim]); |
386 | } |
387 | if (const auto *upperExpr{triplet->GetUpper()}) { |
388 | upper = ToInt64(*upperExpr); |
389 | } else if (ub[dim]) { |
390 | upper = ToInt64(*ub[dim]); |
391 | } |
392 | if (lower) { |
393 | val[vals++] = *lower; |
394 | if (upper && *upper != lower && (stride && *stride != 0)) { |
395 | // Normalize upper bound for non-unit stride |
396 | // 1:10:2 -> 1:9:2, 10:1:-2 -> 10:2:-2 |
397 | val[vals++] = *lower + *stride * ((*upper - *lower) / *stride); |
398 | } |
399 | } |
400 | } else { |
401 | val[vals++] = |
402 | ToInt64(std::get<IndirectSubscriptIntegerExpr>(ss.u).value()); |
403 | } |
404 | for (int j{0}; j < vals; ++j) { |
405 | if (val[j]) { |
406 | ValidateSubscriptValue(context.foldingContext().messages(), arraySymbol, |
407 | *val[j], dimLB, dimUB, dim); |
408 | } |
409 | } |
410 | ++dim; |
411 | } |
412 | } |
413 | |
414 | static void CheckSubscripts( |
415 | semantics::SemanticsContext &context, ArrayRef &ref) { |
416 | const Symbol &arraySymbol{ref.base().GetLastSymbol()}; |
417 | Shape lb, ub; |
418 | if (FoldSubscripts(context, arraySymbol, ref.subscript(), lb, ub)) { |
419 | ValidateSubscripts(context, arraySymbol, ref.subscript(), lb, ub); |
420 | } |
421 | } |
422 | |
423 | static void CheckCosubscripts( |
424 | semantics::SemanticsContext &context, CoarrayRef &ref) { |
425 | const Symbol &coarraySymbol{ref.GetLastSymbol()}; |
426 | FoldingContext &foldingContext{context.foldingContext()}; |
427 | int dim{0}; |
428 | for (auto &expr : ref.cosubscript()) { |
429 | expr = Fold(foldingContext, std::move(expr)); |
430 | if (auto val{ToInt64(expr)}) { |
431 | ValidateSubscriptValue(foldingContext.messages(), coarraySymbol, *val, |
432 | ToInt64(GetLCOBOUND(coarraySymbol, dim)), |
433 | ToInt64(GetUCOBOUND(coarraySymbol, dim)), dim, "co"); |
434 | } |
435 | ++dim; |
436 | } |
437 | } |
438 | |
439 | // Some subscript semantic checks must be deferred until all of the |
440 | // subscripts are in hand. |
441 | MaybeExpr ExpressionAnalyzer::CompleteSubscripts(ArrayRef &&ref) { |
442 | const Symbol &symbol{ref.GetLastSymbol().GetUltimate()}; |
443 | int symbolRank{symbol.Rank()}; |
444 | int subscripts{static_cast<int>(ref.size())}; |
445 | if (subscripts == 0) { |
446 | return std::nullopt; // error recovery |
447 | } else if (subscripts != symbolRank) { |
448 | if (symbolRank != 0) { |
449 | Say("Reference to rank-%d object '%s' has %d subscripts"_err_en_US, |
450 | symbolRank, symbol.name(), subscripts); |
451 | } |
452 | return std::nullopt; |
453 | } else if (symbol.has<semantics::ObjectEntityDetails>() || |
454 | symbol.has<semantics::AssocEntityDetails>()) { |
455 | // C928 & C1002 |
456 | if (Triplet * last{std::get_if<Triplet>(&ref.subscript().back().u)}) { |
457 | if (!last->upper() && IsAssumedSizeArray(symbol)) { |
458 | Say("Assumed-size array '%s' must have explicit final subscript upper bound value"_err_en_US, |
459 | symbol.name()); |
460 | return std::nullopt; |
461 | } |
462 | } |
463 | } else { |
464 | // Shouldn't get here from Analyze(ArrayElement) without a valid base, |
465 | // which, if not an object, must be a construct entity from |
466 | // SELECT TYPE/RANK or ASSOCIATE. |
467 | CHECK(symbol.has<semantics::AssocEntityDetails>()); |
468 | } |
469 | if (!semantics::IsNamedConstant(symbol) && !inDataStmtObject_) { |
470 | // Subscripts of named constants are checked in folding. |
471 | // Subscripts of DATA statement objects are checked in data statement |
472 | // conversion to initializers. |
473 | CheckSubscripts(context_, ref); |
474 | } |
475 | return Designate(DataRef{std::move(ref)}); |
476 | } |
477 | |
478 | // Applies subscripts to a data reference. |
479 | MaybeExpr ExpressionAnalyzer::ApplySubscripts( |
480 | DataRef &&dataRef, std::vector<Subscript> &&subscripts) { |
481 | if (subscripts.empty()) { |
482 | return std::nullopt; // error recovery |
483 | } |
484 | return common::visit(common::visitors{ |
485 | [&](SymbolRef &&symbol) { |
486 | return CompleteSubscripts( |
487 | ArrayRef{symbol, std::move(subscripts)}); |
488 | }, |
489 | [&](Component &&c) { |
490 | return CompleteSubscripts( |
491 | ArrayRef{std::move(c), std::move(subscripts)}); |
492 | }, |
493 | [&](auto &&) -> MaybeExpr { |
494 | DIE("bad base for ArrayRef"); |
495 | return std::nullopt; |
496 | }, |
497 | }, |
498 | std::move(dataRef.u)); |
499 | } |
500 | |
501 | // C919a - only one part-ref of a data-ref may have rank > 0 |
502 | bool ExpressionAnalyzer::CheckRanks(const DataRef &dataRef) { |
503 | return common::visit( |
504 | common::visitors{ |
505 | [this](const Component &component) { |
506 | const Symbol &symbol{component.GetLastSymbol()}; |
507 | if (int componentRank{symbol.Rank()}; componentRank > 0) { |
508 | if (int baseRank{component.base().Rank()}; baseRank > 0) { |
509 | Say("Reference to whole rank-%d component '%s' of rank-%d array of derived type is not allowed"_err_en_US, |
510 | componentRank, symbol.name(), baseRank); |
511 | return false; |
512 | } |
513 | } else { |
514 | return CheckRanks(component.base()); |
515 | } |
516 | return true; |
517 | }, |
518 | [this](const ArrayRef &arrayRef) { |
519 | if (const auto *component{arrayRef.base().UnwrapComponent()}) { |
520 | int subscriptRank{0}; |
521 | for (const Subscript &subscript : arrayRef.subscript()) { |
522 | subscriptRank += subscript.Rank(); |
523 | } |
524 | if (subscriptRank > 0) { |
525 | if (int componentBaseRank{component->base().Rank()}; |
526 | componentBaseRank > 0) { |
527 | Say("Subscripts of component '%s' of rank-%d derived type array have rank %d but must all be scalar"_err_en_US, |
528 | component->GetLastSymbol().name(), componentBaseRank, |
529 | subscriptRank); |
530 | return false; |
531 | } |
532 | } else { |
533 | return CheckRanks(component->base()); |
534 | } |
535 | } |
536 | return true; |
537 | }, |
538 | [](const SymbolRef &) { return true; }, |
539 | [](const CoarrayRef &) { return true; }, |
540 | }, |
541 | dataRef.u); |
542 | } |
543 | |
544 | // C911 - if the last name in a data-ref has an abstract derived type, |
545 | // it must also be polymorphic. |
546 | bool ExpressionAnalyzer::CheckPolymorphic(const DataRef &dataRef) { |
547 | if (auto type{DynamicType::From(dataRef.GetLastSymbol())}) { |
548 | if (type->category() == TypeCategory::Derived && !type->IsPolymorphic()) { |
549 | const Symbol &typeSymbol{ |
550 | type->GetDerivedTypeSpec().typeSymbol().GetUltimate()}; |
551 | if (typeSymbol.attrs().test(semantics::Attr::ABSTRACT)) { |
552 | AttachDeclaration( |
553 | Say("Reference to object with abstract derived type '%s' must be polymorphic"_err_en_US, |
554 | typeSymbol.name()), |
555 | typeSymbol); |
556 | return false; |
557 | } |
558 | } |
559 | } |
560 | return true; |
561 | } |
562 | |
563 | bool ExpressionAnalyzer::CheckDataRef(const DataRef &dataRef) { |
564 | // Always check both, don't short-circuit |
565 | bool ranksOk{CheckRanks(dataRef)}; |
566 | bool polyOk{CheckPolymorphic(dataRef)}; |
567 | return ranksOk && polyOk; |
568 | } |
569 | |
570 | // Parse tree correction after a substring S(j:k) was misparsed as an |
571 | // array section. Fortran substrings must have a range, not a |
572 | // single index. |
573 | static std::optional<parser::Substring> FixMisparsedSubstringDataRef( |
574 | parser::DataRef &dataRef) { |
575 | if (auto *ae{ |
576 | std::get_if<common::Indirection<parser::ArrayElement>>(&dataRef.u)}) { |
577 | // ...%a(j:k) and "a" is a character scalar |
578 | parser::ArrayElement &arrElement{ae->value()}; |
579 | if (arrElement.subscripts.size() == 1) { |
580 | if (auto *triplet{std::get_if<parser::SubscriptTriplet>( |
581 | &arrElement.subscripts.front().u)}) { |
582 | if (!std::get<2 /*stride*/>(triplet->t).has_value()) { |
583 | if (const Symbol *symbol{ |
584 | parser::GetLastName(arrElement.base).symbol}) { |
585 | const Symbol &ultimate{symbol->GetUltimate()}; |
586 | if (const semantics::DeclTypeSpec *type{ultimate.GetType()}) { |
587 | if (ultimate.Rank() == 0 && |
588 | type->category() == semantics::DeclTypeSpec::Character) { |
589 | // The ambiguous S(j:k) was parsed as an array section |
590 | // reference, but it's now clear that it's a substring. |
591 | // Fix the parse tree in situ. |
592 | return arrElement.ConvertToSubstring(); |
593 | } |
594 | } |
595 | } |
596 | } |
597 | } |
598 | } |
599 | } |
600 | return std::nullopt; |
601 | } |
602 | |
603 | // When a designator is a misparsed type-param-inquiry of a misparsed |
604 | // substring -- it looks like a structure component reference of an array |
605 | // slice -- fix the substring and then convert to an intrinsic function |
606 | // call to KIND() or LEN(). And when the designator is a misparsed |
607 | // substring, convert it into a substring reference in place. |
608 | MaybeExpr ExpressionAnalyzer::FixMisparsedSubstring( |
609 | const parser::Designator &d) { |
610 | auto &mutate{const_cast<parser::Designator &>(d)}; |
611 | if (auto *dataRef{std::get_if<parser::DataRef>(&mutate.u)}) { |
612 | if (auto *sc{std::get_if<common::Indirection<parser::StructureComponent>>( |
613 | &dataRef->u)}) { |
614 | parser::StructureComponent &structComponent{sc->value()}; |
615 | parser::CharBlock which{structComponent.component.source}; |
616 | if (which == "kind"|| which == "len") { |
617 | if (auto substring{ |
618 | FixMisparsedSubstringDataRef(structComponent.base)}) { |
619 | // ...%a(j:k)%kind or %len and "a" is a character scalar |
620 | mutate.u = std::move(*substring); |
621 | if (MaybeExpr substringExpr{Analyze(d)}) { |
622 | return MakeFunctionRef(which, |
623 | ActualArguments{ActualArgument{std::move(*substringExpr)}}); |
624 | } |
625 | } |
626 | } |
627 | } else if (auto substring{FixMisparsedSubstringDataRef(*dataRef)}) { |
628 | mutate.u = std::move(*substring); |
629 | } |
630 | } |
631 | return std::nullopt; |
632 | } |
633 | |
634 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Designator &d) { |
635 | auto restorer{GetContextualMessages().SetLocation(d.source)}; |
636 | if (auto substringInquiry{FixMisparsedSubstring(d)}) { |
637 | return substringInquiry; |
638 | } |
639 | // These checks have to be deferred to these "top level" data-refs where |
640 | // we can be sure that there are no following subscripts (yet). |
641 | MaybeExpr result{Analyze(d.u)}; |
642 | if (result) { |
643 | std::optional<DataRef> dataRef{ExtractDataRef(std::move(result))}; |
644 | if (!dataRef) { |
645 | dataRef = ExtractDataRef(std::move(result), /*intoSubstring=*/true); |
646 | } |
647 | if (!dataRef) { |
648 | dataRef = ExtractDataRef(std::move(result), |
649 | /*intoSubstring=*/false, /*intoComplexPart=*/true); |
650 | } |
651 | if (dataRef) { |
652 | if (!CheckDataRef(*dataRef)) { |
653 | result.reset(); |
654 | } else if (ExtractCoarrayRef(*dataRef).has_value()) { |
655 | if (auto dyType{result->GetType()}; |
656 | dyType && dyType->category() == TypeCategory::Derived) { |
657 | if (!std::holds_alternative<CoarrayRef>(dataRef->u) && |
658 | dyType->IsPolymorphic()) { // F'2023 C918 |
659 | Say("The base of a polymorphic object may not be coindexed"_err_en_US); |
660 | } |
661 | if (const auto *derived{GetDerivedTypeSpec(*dyType)}) { |
662 | if (auto bad{FindPolymorphicAllocatablePotentialComponent( |
663 | *derived)}) { // F'2023 C917 |
664 | Say("A coindexed designator may not have a type with the polymorphic potential subobject component '%s'"_err_en_US, |
665 | bad.BuildResultDesignatorName()); |
666 | } |
667 | } |
668 | } |
669 | } |
670 | } |
671 | } |
672 | return result; |
673 | } |
674 | |
675 | // A utility subroutine to repackage optional expressions of various levels |
676 | // of type specificity as fully general MaybeExpr values. |
677 | template <typename A> common::IfNoLvalue<MaybeExpr, A> AsMaybeExpr(A &&x) { |
678 | return AsGenericExpr(std::move(x)); |
679 | } |
680 | template <typename A> MaybeExpr AsMaybeExpr(std::optional<A> &&x) { |
681 | if (x) { |
682 | return AsMaybeExpr(std::move(*x)); |
683 | } |
684 | return std::nullopt; |
685 | } |
686 | |
687 | // Type kind parameter values for literal constants. |
688 | int ExpressionAnalyzer::AnalyzeKindParam( |
689 | const std::optional<parser::KindParam> &kindParam, int defaultKind) { |
690 | if (!kindParam) { |
691 | return defaultKind; |
692 | } |
693 | std::int64_t kind{common::visit( |
694 | common::visitors{ |
695 | [](std::uint64_t k) { return static_cast<std::int64_t>(k); }, |
696 | [&](const parser::Scalar< |
697 | parser::Integer<parser::Constant<parser::Name>>> &n) { |
698 | if (MaybeExpr ie{Analyze(n)}) { |
699 | return ToInt64(*ie).value_or(defaultKind); |
700 | } |
701 | return static_cast<std::int64_t>(defaultKind); |
702 | }, |
703 | }, |
704 | kindParam->u)}; |
705 | if (kind != static_cast<int>(kind)) { |
706 | Say("Unsupported type kind value (%jd)"_err_en_US, |
707 | static_cast<std::intmax_t>(kind)); |
708 | kind = defaultKind; |
709 | } |
710 | return static_cast<int>(kind); |
711 | } |
712 | |
713 | // Common handling of parser::IntLiteralConstant, SignedIntLiteralConstant, |
714 | // and UnsignedLiteralConstant |
715 | template <typename TYPES, TypeCategory CAT> struct IntTypeVisitor { |
716 | using Result = MaybeExpr; |
717 | using Types = TYPES; |
718 | template <typename T> Result Test() { |
719 | if (T::kind >= kind) { |
720 | const char *p{digits.begin()}; |
721 | using Int = typename T::Scalar; |
722 | typename Int::ValueWithOverflow num{0, false}; |
723 | const char *typeName{ |
724 | CAT == TypeCategory::Integer ? "INTEGER": "UNSIGNED"}; |
725 | if (isNegated) { |
726 | auto unsignedNum{Int::Read(p, 10, false /*unsigned*/)}; |
727 | num.value = unsignedNum.value.Negate().value; |
728 | num.overflow = unsignedNum.overflow || |
729 | (CAT == TypeCategory::Integer && num.value > Int{0}); |
730 | if (!num.overflow && num.value.Negate().overflow) { |
731 | analyzer.Warn(LanguageFeature::BigIntLiterals, digits, |
732 | "negated maximum INTEGER(KIND=%d) literal"_port_en_US, T::kind); |
733 | } |
734 | } else { |
735 | num = Int::Read(p, 10, /*isSigned=*/CAT == TypeCategory::Integer); |
736 | } |
737 | if (num.overflow) { |
738 | if constexpr (CAT == TypeCategory::Unsigned) { |
739 | analyzer.Warn(common::UsageWarning::UnsignedLiteralTruncation, |
740 | "Unsigned literal too large for UNSIGNED(KIND=%d); truncated"_warn_en_US, |
741 | kind); |
742 | return Expr<SomeType>{ |
743 | Expr<SomeKind<CAT>>{Expr<T>{Constant<T>{std::move(num.value)}}}}; |
744 | } |
745 | } else { |
746 | if (T::kind > kind) { |
747 | if (!isDefaultKind || |
748 | !analyzer.context().IsEnabled(LanguageFeature::BigIntLiterals)) { |
749 | return std::nullopt; |
750 | } else { |
751 | analyzer.Warn(LanguageFeature::BigIntLiterals, digits, |
752 | "Integer literal is too large for default %s(KIND=%d); " |
753 | "assuming %s(KIND=%d)"_port_en_US, |
754 | typeName, kind, typeName, T::kind); |
755 | } |
756 | } |
757 | return Expr<SomeType>{ |
758 | Expr<SomeKind<CAT>>{Expr<T>{Constant<T>{std::move(num.value)}}}}; |
759 | } |
760 | } |
761 | return std::nullopt; |
762 | } |
763 | ExpressionAnalyzer &analyzer; |
764 | parser::CharBlock digits; |
765 | std::int64_t kind; |
766 | bool isDefaultKind; |
767 | bool isNegated; |
768 | }; |
769 | |
770 | template <typename TYPES, TypeCategory CAT, typename PARSED> |
771 | MaybeExpr ExpressionAnalyzer::IntLiteralConstant( |
772 | const PARSED &x, bool isNegated) { |
773 | const auto &kindParam{std::get<std::optional<parser::KindParam>>(x.t)}; |
774 | bool isDefaultKind{!kindParam}; |
775 | int kind{AnalyzeKindParam(kindParam, GetDefaultKind(CAT))}; |
776 | const char *typeName{CAT == TypeCategory::Integer ? "INTEGER": "UNSIGNED"}; |
777 | if (CheckIntrinsicKind(CAT, kind)) { |
778 | auto digits{std::get<parser::CharBlock>(x.t)}; |
779 | if (MaybeExpr result{common::SearchTypes(IntTypeVisitor<TYPES, CAT>{ |
780 | *this, digits, kind, isDefaultKind, isNegated})}) { |
781 | return result; |
782 | } else if (isDefaultKind) { |
783 | Say(digits, |
784 | "Integer literal is too large for any allowable kind of %s"_err_en_US, |
785 | typeName); |
786 | } else { |
787 | Say(digits, "Integer literal is too large for %s(KIND=%d)"_err_en_US, |
788 | typeName, kind); |
789 | } |
790 | } |
791 | return std::nullopt; |
792 | } |
793 | |
794 | MaybeExpr ExpressionAnalyzer::Analyze( |
795 | const parser::IntLiteralConstant &x, bool isNegated) { |
796 | auto restorer{ |
797 | GetContextualMessages().SetLocation(std::get<parser::CharBlock>(x.t))}; |
798 | return IntLiteralConstant<IntegerTypes, TypeCategory::Integer>(x, isNegated); |
799 | } |
800 | |
801 | MaybeExpr ExpressionAnalyzer::Analyze( |
802 | const parser::SignedIntLiteralConstant &x) { |
803 | auto restorer{GetContextualMessages().SetLocation(x.source)}; |
804 | return IntLiteralConstant<IntegerTypes, TypeCategory::Integer>(x); |
805 | } |
806 | |
807 | MaybeExpr ExpressionAnalyzer::Analyze( |
808 | const parser::UnsignedLiteralConstant &x) { |
809 | parser::CharBlock at{std::get<parser::CharBlock>(x.t)}; |
810 | auto restorer{GetContextualMessages().SetLocation(at)}; |
811 | if (!context().IsEnabled(common::LanguageFeature::Unsigned) && |
812 | !context().AnyFatalError()) { |
813 | context().Say( |
814 | at, "-funsigned is required to enable UNSIGNED constants"_err_en_US); |
815 | } |
816 | return IntLiteralConstant<UnsignedTypes, TypeCategory::Unsigned>(x); |
817 | } |
818 | |
819 | template <typename TYPE> |
820 | Constant<TYPE> ReadRealLiteral( |
821 | parser::CharBlock source, FoldingContext &context) { |
822 | const char *p{source.begin()}; |
823 | auto valWithFlags{ |
824 | Scalar<TYPE>::Read(p, context.targetCharacteristics().roundingMode())}; |
825 | CHECK(p == source.end()); |
826 | RealFlagWarnings(context, valWithFlags.flags, "conversion of REAL literal"); |
827 | auto value{valWithFlags.value}; |
828 | if (context.targetCharacteristics().areSubnormalsFlushedToZero()) { |
829 | value = value.FlushSubnormalToZero(); |
830 | } |
831 | return {value}; |
832 | } |
833 | |
834 | struct RealTypeVisitor { |
835 | using Result = std::optional<Expr<SomeReal>>; |
836 | using Types = RealTypes; |
837 | |
838 | RealTypeVisitor(int k, parser::CharBlock lit, FoldingContext &ctx) |
839 | : kind{k}, literal{lit}, context{ctx} {} |
840 | |
841 | template <typename T> Result Test() { |
842 | if (kind == T::kind) { |
843 | return {AsCategoryExpr(ReadRealLiteral<T>(literal, context))}; |
844 | } |
845 | return std::nullopt; |
846 | } |
847 | |
848 | int kind; |
849 | parser::CharBlock literal; |
850 | FoldingContext &context; |
851 | }; |
852 | |
853 | // Reads a real literal constant and encodes it with the right kind. |
854 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::RealLiteralConstant &x) { |
855 | // Use a local message context around the real literal for better |
856 | // provenance on any messages. |
857 | auto restorer{GetContextualMessages().SetLocation(x.real.source)}; |
858 | // If a kind parameter appears, it defines the kind of the literal and the |
859 | // letter used in an exponent part must be 'E' (e.g., the 'E' in |
860 | // "6.02214E+23"). In the absence of an explicit kind parameter, any |
861 | // exponent letter determines the kind. Otherwise, defaults apply. |
862 | auto &defaults{context_.defaultKinds()}; |
863 | int defaultKind{defaults.GetDefaultKind(TypeCategory::Real)}; |
864 | const char *end{x.real.source.end()}; |
865 | char expoLetter{' '}; |
866 | std::optional<int> letterKind; |
867 | for (const char *p{x.real.source.begin()}; p < end; ++p) { |
868 | if (parser::IsLetter(*p)) { |
869 | expoLetter = *p; |
870 | switch (expoLetter) { |
871 | case 'e': |
872 | letterKind = defaults.GetDefaultKind(TypeCategory::Real); |
873 | break; |
874 | case 'd': |
875 | letterKind = defaults.doublePrecisionKind(); |
876 | break; |
877 | case 'q': |
878 | letterKind = defaults.quadPrecisionKind(); |
879 | break; |
880 | default: |
881 | Say("Unknown exponent letter '%c'"_err_en_US, expoLetter); |
882 | } |
883 | break; |
884 | } |
885 | } |
886 | if (letterKind) { |
887 | defaultKind = *letterKind; |
888 | } |
889 | // C716 requires 'E' as an exponent. |
890 | // Extension: allow exponent-letter matching the kind-param. |
891 | auto kind{AnalyzeKindParam(x.kind, defaultKind)}; |
892 | if (letterKind && expoLetter != 'e') { |
893 | if (kind != *letterKind) { |
894 | Warn(common::LanguageFeature::ExponentMatchingKindParam, |
895 | "Explicit kind parameter on real constant disagrees with exponent letter '%c'"_warn_en_US, |
896 | expoLetter); |
897 | } else if (x.kind) { |
898 | Warn(common::LanguageFeature::ExponentMatchingKindParam, |
899 | "Explicit kind parameter together with non-'E' exponent letter is not standard"_port_en_US); |
900 | } |
901 | } |
902 | auto result{common::SearchTypes( |
903 | RealTypeVisitor{kind, x.real.source, GetFoldingContext()})}; |
904 | if (!result) { // C717 |
905 | Say("Unsupported REAL(KIND=%d)"_err_en_US, kind); |
906 | } |
907 | return AsMaybeExpr(std::move(result)); |
908 | } |
909 | |
910 | MaybeExpr ExpressionAnalyzer::Analyze( |
911 | const parser::SignedRealLiteralConstant &x) { |
912 | if (auto result{Analyze(std::get<parser::RealLiteralConstant>(x.t))}) { |
913 | auto &realExpr{std::get<Expr<SomeReal>>(result->u)}; |
914 | if (auto sign{std::get<std::optional<parser::Sign>>(x.t)}) { |
915 | if (sign == parser::Sign::Negative) { |
916 | return AsGenericExpr(-std::move(realExpr)); |
917 | } |
918 | } |
919 | return result; |
920 | } |
921 | return std::nullopt; |
922 | } |
923 | |
924 | MaybeExpr ExpressionAnalyzer::Analyze( |
925 | const parser::SignedComplexLiteralConstant &x) { |
926 | auto result{Analyze(std::get<parser::ComplexLiteralConstant>(x.t))}; |
927 | if (!result) { |
928 | return std::nullopt; |
929 | } else if (std::get<parser::Sign>(x.t) == parser::Sign::Negative) { |
930 | return AsGenericExpr(-std::move(std::get<Expr<SomeComplex>>(result->u))); |
931 | } else { |
932 | return result; |
933 | } |
934 | } |
935 | |
936 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::ComplexPart &x) { |
937 | return Analyze(x.u); |
938 | } |
939 | |
940 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::ComplexLiteralConstant &z) { |
941 | return AnalyzeComplex(Analyze(std::get<0>(z.t)), Analyze(std::get<1>(z.t)), |
942 | "complex literal constant"); |
943 | } |
944 | |
945 | // CHARACTER literal processing. |
946 | MaybeExpr ExpressionAnalyzer::AnalyzeString(std::string &&string, int kind) { |
947 | if (!CheckIntrinsicKind(TypeCategory::Character, kind)) { |
948 | return std::nullopt; |
949 | } |
950 | switch (kind) { |
951 | case 1: |
952 | return AsGenericExpr(Constant<Type<TypeCategory::Character, 1>>{ |
953 | parser::DecodeString<std::string, parser::Encoding::LATIN_1>( |
954 | string, true)}); |
955 | case 2: |
956 | return AsGenericExpr(Constant<Type<TypeCategory::Character, 2>>{ |
957 | parser::DecodeString<std::u16string, parser::Encoding::UTF_8>( |
958 | string, true)}); |
959 | case 4: |
960 | return AsGenericExpr(Constant<Type<TypeCategory::Character, 4>>{ |
961 | parser::DecodeString<std::u32string, parser::Encoding::UTF_8>( |
962 | string, true)}); |
963 | default: |
964 | CRASH_NO_CASE; |
965 | } |
966 | } |
967 | |
968 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::CharLiteralConstant &x) { |
969 | int kind{ |
970 | AnalyzeKindParam(std::get<std::optional<parser::KindParam>>(x.t), 1)}; |
971 | auto value{std::get<std::string>(x.t)}; |
972 | return AnalyzeString(std::move(value), kind); |
973 | } |
974 | |
975 | MaybeExpr ExpressionAnalyzer::Analyze( |
976 | const parser::HollerithLiteralConstant &x) { |
977 | int kind{GetDefaultKind(TypeCategory::Character)}; |
978 | auto result{AnalyzeString(std::string{x.v}, kind)}; |
979 | if (auto *constant{UnwrapConstantValue<Ascii>(result)}) { |
980 | constant->set_wasHollerith(true); |
981 | } |
982 | return result; |
983 | } |
984 | |
985 | // .TRUE. and .FALSE. of various kinds |
986 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::LogicalLiteralConstant &x) { |
987 | auto kind{AnalyzeKindParam(std::get<std::optional<parser::KindParam>>(x.t), |
988 | GetDefaultKind(TypeCategory::Logical))}; |
989 | bool value{std::get<bool>(x.t)}; |
990 | auto result{common::SearchTypes( |
991 | TypeKindVisitor<TypeCategory::Logical, Constant, bool>{ |
992 | kind, std::move(value)})}; |
993 | if (!result) { |
994 | Say("unsupported LOGICAL(KIND=%d)"_err_en_US, kind); // C728 |
995 | } |
996 | return result; |
997 | } |
998 | |
999 | // BOZ typeless literals |
1000 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::BOZLiteralConstant &x) { |
1001 | const char *p{x.v.c_str()}; |
1002 | std::uint64_t base{16}; |
1003 | switch (*p++) { |
1004 | case 'b': |
1005 | base = 2; |
1006 | break; |
1007 | case 'o': |
1008 | base = 8; |
1009 | break; |
1010 | case 'z': |
1011 | break; |
1012 | case 'x': |
1013 | break; |
1014 | default: |
1015 | CRASH_NO_CASE; |
1016 | } |
1017 | CHECK(*p == '"'); |
1018 | ++p; |
1019 | auto value{BOZLiteralConstant::Read(p, base, false /*unsigned*/)}; |
1020 | if (*p != '"') { |
1021 | Say("Invalid digit ('%c') in BOZ literal '%s'"_err_en_US, *p, |
1022 | x.v); // C7107, C7108 |
1023 | return std::nullopt; |
1024 | } |
1025 | if (value.overflow) { |
1026 | Say("BOZ literal '%s' too large"_err_en_US, x.v); |
1027 | return std::nullopt; |
1028 | } |
1029 | return AsGenericExpr(std::move(value.value)); |
1030 | } |
1031 | |
1032 | // Names and named constants |
1033 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Name &n) { |
1034 | auto restorer{GetContextualMessages().SetLocation(n.source)}; |
1035 | if (std::optional<int> kind{IsImpliedDo(n.source)}) { |
1036 | return AsMaybeExpr(ConvertToKind<TypeCategory::Integer>( |
1037 | *kind, AsExpr(ImpliedDoIndex{n.source}))); |
1038 | } |
1039 | if (context_.HasError(n.symbol)) { // includes case of no symbol |
1040 | return std::nullopt; |
1041 | } else { |
1042 | const Symbol &ultimate{n.symbol->GetUltimate()}; |
1043 | if (ultimate.has<semantics::TypeParamDetails>()) { |
1044 | // A bare reference to a derived type parameter within a parameterized |
1045 | // derived type definition. |
1046 | auto dyType{DynamicType::From(ultimate)}; |
1047 | if (!dyType) { |
1048 | // When the integer kind of this type parameter is not known now, |
1049 | // it's either an error or because it depends on earlier-declared kind |
1050 | // type parameters. So assume that it's a subscript integer for now |
1051 | // while processing other specification expressions in the PDT |
1052 | // definition; the right kind value will be used later in each of its |
1053 | // instantiations. |
1054 | int kind{SubscriptInteger::kind}; |
1055 | if (const auto *typeSpec{ultimate.GetType()}) { |
1056 | if (const semantics::IntrinsicTypeSpec * |
1057 | intrinType{typeSpec->AsIntrinsic()}) { |
1058 | if (auto k{ToInt64(Fold(semantics::KindExpr{intrinType->kind()}))}; |
1059 | k && |
1060 | common::IsValidKindOfIntrinsicType(TypeCategory::Integer, *k)) { |
1061 | kind = *k; |
1062 | } |
1063 | } |
1064 | } |
1065 | dyType = DynamicType{TypeCategory::Integer, kind}; |
1066 | } |
1067 | return Fold(ConvertToType( |
1068 | *dyType, AsGenericExpr(TypeParamInquiry{std::nullopt, ultimate}))); |
1069 | } else { |
1070 | if (n.symbol->attrs().test(semantics::Attr::VOLATILE)) { |
1071 | if (const semantics::Scope *pure{semantics::FindPureProcedureContaining( |
1072 | context_.FindScope(n.source))}) { |
1073 | SayAt(n, |
1074 | "VOLATILE variable '%s' may not be referenced in pure subprogram '%s'"_err_en_US, |
1075 | n.source, DEREF(pure->symbol()).name()); |
1076 | n.symbol->attrs().reset(semantics::Attr::VOLATILE); |
1077 | } |
1078 | } |
1079 | CheckForWholeAssumedSizeArray(n.source, n.symbol); |
1080 | return Designate(DataRef{*n.symbol}); |
1081 | } |
1082 | } |
1083 | } |
1084 | |
1085 | void ExpressionAnalyzer::CheckForWholeAssumedSizeArray( |
1086 | parser::CharBlock at, const Symbol *symbol) { |
1087 | if (!isWholeAssumedSizeArrayOk_ && symbol && |
1088 | semantics::IsAssumedSizeArray(ResolveAssociations(*symbol))) { |
1089 | AttachDeclaration( |
1090 | SayAt(at, |
1091 | "Whole assumed-size array '%s' may not appear here without subscripts"_err_en_US, |
1092 | symbol->name()), |
1093 | *symbol); |
1094 | } |
1095 | } |
1096 | |
1097 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::NamedConstant &n) { |
1098 | auto restorer{GetContextualMessages().SetLocation(n.v.source)}; |
1099 | if (MaybeExpr value{Analyze(n.v)}) { |
1100 | Expr<SomeType> folded{Fold(std::move(*value))}; |
1101 | if (IsConstantExpr(folded)) { |
1102 | return folded; |
1103 | } |
1104 | Say(n.v.source, "must be a constant"_err_en_US); // C718 |
1105 | } |
1106 | return std::nullopt; |
1107 | } |
1108 | |
1109 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::NullInit &n) { |
1110 | auto restorer{AllowNullPointer()}; |
1111 | if (MaybeExpr value{Analyze(n.v.value())}) { |
1112 | // Subtle: when the NullInit is a DataStmtConstant, it might |
1113 | // be a misparse of a structure constructor without parameters |
1114 | // or components (e.g., T()). Checking the result to ensure |
1115 | // that a "=>" data entity initializer actually resolved to |
1116 | // a null pointer has to be done by the caller. |
1117 | return Fold(std::move(*value)); |
1118 | } |
1119 | return std::nullopt; |
1120 | } |
1121 | |
1122 | MaybeExpr ExpressionAnalyzer::Analyze( |
1123 | const parser::StmtFunctionStmt &stmtFunc) { |
1124 | inStmtFunctionDefinition_ = true; |
1125 | return Analyze(std::get<parser::Scalar<parser::Expr>>(stmtFunc.t)); |
1126 | } |
1127 | |
1128 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::InitialDataTarget &x) { |
1129 | return Analyze(x.value()); |
1130 | } |
1131 | |
1132 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::DataStmtValue &x) { |
1133 | if (const auto &repeat{ |
1134 | std::get<std::optional<parser::DataStmtRepeat>>(x.t)}) { |
1135 | x.repetitions = -1; |
1136 | if (MaybeExpr expr{Analyze(repeat->u)}) { |
1137 | Expr<SomeType> folded{Fold(std::move(*expr))}; |
1138 | if (auto value{ToInt64(folded)}) { |
1139 | if (*value >= 0) { // C882 |
1140 | x.repetitions = *value; |
1141 | } else { |
1142 | Say(FindSourceLocation(repeat), |
1143 | "Repeat count (%jd) for data value must not be negative"_err_en_US, |
1144 | *value); |
1145 | } |
1146 | } |
1147 | } |
1148 | } |
1149 | return Analyze(std::get<parser::DataStmtConstant>(x.t)); |
1150 | } |
1151 | |
1152 | // Substring references |
1153 | std::optional<Expr<SubscriptInteger>> ExpressionAnalyzer::GetSubstringBound( |
1154 | const std::optional<parser::ScalarIntExpr> &bound) { |
1155 | if (bound) { |
1156 | if (MaybeExpr expr{Analyze(*bound)}) { |
1157 | if (expr->Rank() > 1) { |
1158 | Say("substring bound expression has rank %d"_err_en_US, expr->Rank()); |
1159 | } |
1160 | if (auto *intExpr{std::get_if<Expr<SomeInteger>>(&expr->u)}) { |
1161 | if (auto *ssIntExpr{std::get_if<Expr<SubscriptInteger>>(&intExpr->u)}) { |
1162 | return {std::move(*ssIntExpr)}; |
1163 | } |
1164 | return {Expr<SubscriptInteger>{ |
1165 | Convert<SubscriptInteger, TypeCategory::Integer>{ |
1166 | std::move(*intExpr)}}}; |
1167 | } else { |
1168 | Say("substring bound expression is not INTEGER"_err_en_US); |
1169 | } |
1170 | } |
1171 | } |
1172 | return std::nullopt; |
1173 | } |
1174 | |
1175 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Substring &ss) { |
1176 | if (MaybeExpr baseExpr{Analyze(std::get<parser::DataRef>(ss.t))}) { |
1177 | if (std::optional<DataRef> dataRef{ExtractDataRef(std::move(*baseExpr))}) { |
1178 | if (MaybeExpr newBaseExpr{Designate(std::move(*dataRef))}) { |
1179 | if (std::optional<DataRef> checked{ |
1180 | ExtractDataRef(std::move(*newBaseExpr))}) { |
1181 | const parser::SubstringRange &range{ |
1182 | std::get<parser::SubstringRange>(ss.t)}; |
1183 | std::optional<Expr<SubscriptInteger>> first{ |
1184 | Fold(GetSubstringBound(std::get<0>(range.t)))}; |
1185 | std::optional<Expr<SubscriptInteger>> last{ |
1186 | Fold(GetSubstringBound(std::get<1>(range.t)))}; |
1187 | const Symbol &symbol{checked->GetLastSymbol()}; |
1188 | if (std::optional<DynamicType> dynamicType{ |
1189 | DynamicType::From(symbol)}) { |
1190 | if (dynamicType->category() == TypeCategory::Character) { |
1191 | auto lbValue{ToInt64(first)}; |
1192 | if (!lbValue) { |
1193 | lbValue = 1; |
1194 | } |
1195 | auto ubValue{ToInt64(last)}; |
1196 | auto len{dynamicType->knownLength()}; |
1197 | if (!ubValue) { |
1198 | ubValue = len; |
1199 | } |
1200 | if (lbValue && ubValue && *lbValue > *ubValue) { |
1201 | // valid, substring is empty |
1202 | } else if (lbValue && *lbValue < 1 && (ubValue || !last)) { |
1203 | Say("Substring must begin at 1 or later, not %jd"_err_en_US, |
1204 | static_cast<std::intmax_t>(*lbValue)); |
1205 | return std::nullopt; |
1206 | } else if (ubValue && len && *ubValue > *len && |
1207 | (lbValue || !first)) { |
1208 | Say("Substring must end at %zd or earlier, not %jd"_err_en_US, |
1209 | static_cast<std::intmax_t>(*len), |
1210 | static_cast<std::intmax_t>(*ubValue)); |
1211 | return std::nullopt; |
1212 | } |
1213 | return WrapperHelper<TypeCategory::Character, Designator, |
1214 | Substring>(dynamicType->kind(), |
1215 | Substring{std::move(checked.value()), std::move(first), |
1216 | std::move(last)}); |
1217 | } |
1218 | } |
1219 | Say("substring may apply only to CHARACTER"_err_en_US); |
1220 | } |
1221 | } |
1222 | } |
1223 | } |
1224 | return std::nullopt; |
1225 | } |
1226 | |
1227 | // CHARACTER literal substrings |
1228 | MaybeExpr ExpressionAnalyzer::Analyze( |
1229 | const parser::CharLiteralConstantSubstring &x) { |
1230 | const parser::SubstringRange &range{std::get<parser::SubstringRange>(x.t)}; |
1231 | std::optional<Expr<SubscriptInteger>> lower{ |
1232 | GetSubstringBound(std::get<0>(range.t))}; |
1233 | std::optional<Expr<SubscriptInteger>> upper{ |
1234 | GetSubstringBound(std::get<1>(range.t))}; |
1235 | if (MaybeExpr string{Analyze(std::get<parser::CharLiteralConstant>(x.t))}) { |
1236 | if (auto *charExpr{std::get_if<Expr<SomeCharacter>>(&string->u)}) { |
1237 | Expr<SubscriptInteger> length{ |
1238 | common::visit([](const auto &ckExpr) { return ckExpr.LEN().value(); }, |
1239 | charExpr->u)}; |
1240 | if (!lower) { |
1241 | lower = Expr<SubscriptInteger>{1}; |
1242 | } |
1243 | if (!upper) { |
1244 | upper = Expr<SubscriptInteger>{ |
1245 | static_cast<std::int64_t>(ToInt64(length).value())}; |
1246 | } |
1247 | return common::visit( |
1248 | [&](auto &&ckExpr) -> MaybeExpr { |
1249 | using Result = ResultType<decltype(ckExpr)>; |
1250 | auto *cp{std::get_if<Constant<Result>>(&ckExpr.u)}; |
1251 | CHECK(DEREF(cp).size() == 1); |
1252 | StaticDataObject::Pointer staticData{StaticDataObject::Create()}; |
1253 | staticData->set_alignment(Result::kind) |
1254 | .set_itemBytes(Result::kind) |
1255 | .Push(cp->GetScalarValue().value(), |
1256 | foldingContext_.targetCharacteristics().isBigEndian()); |
1257 | Substring substring{std::move(staticData), std::move(lower.value()), |
1258 | std::move(upper.value())}; |
1259 | return AsGenericExpr( |
1260 | Expr<Result>{Designator<Result>{std::move(substring)}}); |
1261 | }, |
1262 | std::move(charExpr->u)); |
1263 | } |
1264 | } |
1265 | return std::nullopt; |
1266 | } |
1267 | |
1268 | // substring%KIND/LEN |
1269 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::SubstringInquiry &x) { |
1270 | if (MaybeExpr substring{Analyze(x.v)}) { |
1271 | CHECK(x.source.size() >= 8); |
1272 | int nameLen{x.source.end()[-1] == 'n' ? 3 /*LEN*/ : 4 /*KIND*/}; |
1273 | parser::CharBlock name{ |
1274 | x.source.end() - nameLen, static_cast<std::size_t>(nameLen)}; |
1275 | CHECK(name == "len"|| name == "kind"); |
1276 | return MakeFunctionRef( |
1277 | name, ActualArguments{ActualArgument{std::move(*substring)}}); |
1278 | } else { |
1279 | return std::nullopt; |
1280 | } |
1281 | } |
1282 | |
1283 | // Subscripted array references |
1284 | std::optional<Expr<SubscriptInteger>> ExpressionAnalyzer::AsSubscript( |
1285 | MaybeExpr &&expr) { |
1286 | if (expr) { |
1287 | if (expr->Rank() > 1) { |
1288 | Say("Subscript expression has rank %d greater than 1"_err_en_US, |
1289 | expr->Rank()); |
1290 | } |
1291 | if (auto *intExpr{std::get_if<Expr<SomeInteger>>(&expr->u)}) { |
1292 | if (auto *ssIntExpr{std::get_if<Expr<SubscriptInteger>>(&intExpr->u)}) { |
1293 | return std::move(*ssIntExpr); |
1294 | } else { |
1295 | return Expr<SubscriptInteger>{ |
1296 | Convert<SubscriptInteger, TypeCategory::Integer>{ |
1297 | std::move(*intExpr)}}; |
1298 | } |
1299 | } else { |
1300 | Say("Subscript expression is not INTEGER"_err_en_US); |
1301 | } |
1302 | } |
1303 | return std::nullopt; |
1304 | } |
1305 | |
1306 | std::optional<Expr<SubscriptInteger>> ExpressionAnalyzer::TripletPart( |
1307 | const std::optional<parser::Subscript> &s) { |
1308 | if (s) { |
1309 | return AsSubscript(Analyze(*s)); |
1310 | } else { |
1311 | return std::nullopt; |
1312 | } |
1313 | } |
1314 | |
1315 | std::optional<Subscript> ExpressionAnalyzer::AnalyzeSectionSubscript( |
1316 | const parser::SectionSubscript &ss) { |
1317 | return common::visit( |
1318 | common::visitors{ |
1319 | [&](const parser::SubscriptTriplet &t) -> std::optional<Subscript> { |
1320 | const auto &lower{std::get<0>(t.t)}; |
1321 | const auto &upper{std::get<1>(t.t)}; |
1322 | const auto &stride{std::get<2>(t.t)}; |
1323 | auto result{Triplet{ |
1324 | TripletPart(lower), TripletPart(upper), TripletPart(stride)}}; |
1325 | if ((lower && !result.lower()) || (upper && !result.upper())) { |
1326 | return std::nullopt; |
1327 | } else { |
1328 | return std::make_optional<Subscript>(result); |
1329 | } |
1330 | }, |
1331 | [&](const auto &s) -> std::optional<Subscript> { |
1332 | if (auto subscriptExpr{AsSubscript(Analyze(s))}) { |
1333 | return Subscript{std::move(*subscriptExpr)}; |
1334 | } else { |
1335 | return std::nullopt; |
1336 | } |
1337 | }, |
1338 | }, |
1339 | ss.u); |
1340 | } |
1341 | |
1342 | // Empty result means an error occurred |
1343 | std::vector<Subscript> ExpressionAnalyzer::AnalyzeSectionSubscripts( |
1344 | const std::list<parser::SectionSubscript> &sss) { |
1345 | bool error{false}; |
1346 | std::vector<Subscript> subscripts; |
1347 | for (const auto &s : sss) { |
1348 | if (auto subscript{AnalyzeSectionSubscript(s)}) { |
1349 | subscripts.emplace_back(std::move(*subscript)); |
1350 | } else { |
1351 | error = true; |
1352 | } |
1353 | } |
1354 | return !error ? subscripts : std::vector<Subscript>{}; |
1355 | } |
1356 | |
1357 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::ArrayElement &ae) { |
1358 | MaybeExpr baseExpr; |
1359 | { |
1360 | auto restorer{AllowWholeAssumedSizeArray()}; |
1361 | baseExpr = Analyze(ae.base); |
1362 | } |
1363 | if (baseExpr) { |
1364 | if (ae.subscripts.empty()) { |
1365 | // will be converted to function call later or error reported |
1366 | } else if (baseExpr->Rank() == 0) { |
1367 | if (const Symbol *symbol{GetLastSymbol(*baseExpr)}) { |
1368 | if (!context_.HasError(symbol)) { |
1369 | if (inDataStmtConstant_) { |
1370 | // Better error for NULL(X) with a MOLD= argument |
1371 | Say("'%s' must be an array or structure constructor if used with non-empty parentheses as a DATA statement constant"_err_en_US, |
1372 | symbol->name()); |
1373 | } else { |
1374 | Say("'%s' is not an array"_err_en_US, symbol->name()); |
1375 | } |
1376 | context_.SetError(*symbol); |
1377 | } |
1378 | } |
1379 | } else if (std::optional<DataRef> dataRef{ |
1380 | ExtractDataRef(std::move(*baseExpr))}) { |
1381 | return ApplySubscripts( |
1382 | std::move(*dataRef), AnalyzeSectionSubscripts(ae.subscripts)); |
1383 | } else { |
1384 | Say("Subscripts may be applied only to an object, component, or array constant"_err_en_US); |
1385 | } |
1386 | } |
1387 | // error was reported: analyze subscripts without reporting more errors |
1388 | auto restorer{GetContextualMessages().DiscardMessages()}; |
1389 | AnalyzeSectionSubscripts(ae.subscripts); |
1390 | return std::nullopt; |
1391 | } |
1392 | |
1393 | // Type parameter inquiries apply to data references, but don't depend |
1394 | // on any trailing (co)subscripts. |
1395 | static NamedEntity IgnoreAnySubscripts(Designator<SomeDerived> &&designator) { |
1396 | return common::visit( |
1397 | common::visitors{ |
1398 | [](SymbolRef &&symbol) { return NamedEntity{symbol}; }, |
1399 | [](Component &&component) { |
1400 | return NamedEntity{std::move(component)}; |
1401 | }, |
1402 | [](ArrayRef &&arrayRef) { return std::move(arrayRef.base()); }, |
1403 | [](CoarrayRef &&coarrayRef) { |
1404 | return NamedEntity{coarrayRef.GetLastSymbol()}; |
1405 | }, |
1406 | }, |
1407 | std::move(designator.u)); |
1408 | } |
1409 | |
1410 | // Components, but not bindings, of parent derived types are explicitly |
1411 | // represented as such. |
1412 | std::optional<Component> ExpressionAnalyzer::CreateComponent(DataRef &&base, |
1413 | const Symbol &component, const semantics::Scope &scope, |
1414 | bool C919bAlreadyEnforced) { |
1415 | if (!C919bAlreadyEnforced && IsAllocatableOrPointer(component) && |
1416 | base.Rank() > 0) { // C919b |
1417 | Say("An allocatable or pointer component reference must be applied to a scalar base"_err_en_US); |
1418 | } |
1419 | if (&component.owner() == &scope || |
1420 | component.has<semantics::ProcBindingDetails>()) { |
1421 | return Component{std::move(base), component}; |
1422 | } |
1423 | if (const Symbol *typeSymbol{scope.GetSymbol()}) { |
1424 | if (const Symbol *parentComponent{typeSymbol->GetParentComponent(&scope)}) { |
1425 | if (const auto *object{ |
1426 | parentComponent->detailsIf<semantics::ObjectEntityDetails>()}) { |
1427 | if (const auto *parentType{object->type()}) { |
1428 | if (const semantics::Scope *parentScope{ |
1429 | parentType->derivedTypeSpec().scope()}) { |
1430 | return CreateComponent( |
1431 | DataRef{Component{std::move(base), *parentComponent}}, |
1432 | component, *parentScope, C919bAlreadyEnforced); |
1433 | } |
1434 | } |
1435 | } |
1436 | } |
1437 | } |
1438 | return std::nullopt; |
1439 | } |
1440 | |
1441 | // Derived type component references and type parameter inquiries |
1442 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::StructureComponent &sc) { |
1443 | Symbol *sym{sc.component.symbol}; |
1444 | if (context_.HasError(sym)) { |
1445 | return std::nullopt; |
1446 | } |
1447 | const auto *misc{sym->detailsIf<semantics::MiscDetails>()}; |
1448 | bool isTypeParamInquiry{sym->has<semantics::TypeParamDetails>() || |
1449 | (misc && |
1450 | (misc->kind() == semantics::MiscDetails::Kind::KindParamInquiry || |
1451 | misc->kind() == semantics::MiscDetails::Kind::LenParamInquiry))}; |
1452 | MaybeExpr base; |
1453 | if (isTypeParamInquiry) { |
1454 | auto restorer{AllowWholeAssumedSizeArray()}; |
1455 | base = Analyze(sc.base); |
1456 | } else { |
1457 | base = Analyze(sc.base); |
1458 | } |
1459 | if (!base) { |
1460 | return std::nullopt; |
1461 | } |
1462 | const auto &name{sc.component.source}; |
1463 | if (auto *dtExpr{UnwrapExpr<Expr<SomeDerived>>(*base)}) { |
1464 | const auto *dtSpec{GetDerivedTypeSpec(dtExpr->GetType())}; |
1465 | if (isTypeParamInquiry) { |
1466 | if (auto *designator{UnwrapExpr<Designator<SomeDerived>>(*dtExpr)}) { |
1467 | if (std::optional<DynamicType> dyType{DynamicType::From(*sym)}) { |
1468 | if (dyType->category() == TypeCategory::Integer) { |
1469 | auto restorer{GetContextualMessages().SetLocation(name)}; |
1470 | return Fold(ConvertToType(*dyType, |
1471 | AsGenericExpr(TypeParamInquiry{ |
1472 | IgnoreAnySubscripts(std::move(*designator)), *sym}))); |
1473 | } |
1474 | } |
1475 | Say(name, "Type parameter is not INTEGER"_err_en_US); |
1476 | } else { |
1477 | Say(name, |
1478 | "A type parameter inquiry must be applied to a designator"_err_en_US); |
1479 | } |
1480 | } else if (!dtSpec || !dtSpec->scope()) { |
1481 | CHECK(context_.AnyFatalError() || !foldingContext_.messages().empty()); |
1482 | return std::nullopt; |
1483 | } else if (std::optional<DataRef> dataRef{ |
1484 | ExtractDataRef(std::move(*dtExpr))}) { |
1485 | auto restorer{GetContextualMessages().SetLocation(name)}; |
1486 | if (auto component{ |
1487 | CreateComponent(std::move(*dataRef), *sym, *dtSpec->scope())}) { |
1488 | return Designate(DataRef{std::move(*component)}); |
1489 | } else { |
1490 | Say(name, "Component is not in scope of derived TYPE(%s)"_err_en_US, |
1491 | dtSpec->typeSymbol().name()); |
1492 | } |
1493 | } else { |
1494 | Say(name, |
1495 | "Base of component reference must be a data reference"_err_en_US); |
1496 | } |
1497 | } else if (auto *details{sym->detailsIf<semantics::MiscDetails>()}) { |
1498 | // special part-ref: %re, %im, %kind, %len |
1499 | // Type errors on the base of %re/%im/%len are detected and |
1500 | // reported in name resolution. |
1501 | using MiscKind = semantics::MiscDetails::Kind; |
1502 | MiscKind kind{details->kind()}; |
1503 | if (kind == MiscKind::ComplexPartRe || kind == MiscKind::ComplexPartIm) { |
1504 | if (auto *zExpr{std::get_if<Expr<SomeComplex>>(&base->u)}) { |
1505 | if (std::optional<DataRef> dataRef{ExtractDataRef(*zExpr)}) { |
1506 | // Represent %RE/%IM as a designator |
1507 | Expr<SomeReal> realExpr{common::visit( |
1508 | [&](const auto &z) { |
1509 | using PartType = typename ResultType<decltype(z)>::Part; |
1510 | auto part{kind == MiscKind::ComplexPartRe |
1511 | ? ComplexPart::Part::RE |
1512 | : ComplexPart::Part::IM}; |
1513 | return AsCategoryExpr(Designator<PartType>{ |
1514 | ComplexPart{std::move(*dataRef), part}}); |
1515 | }, |
1516 | zExpr->u)}; |
1517 | return AsGenericExpr(std::move(realExpr)); |
1518 | } |
1519 | } |
1520 | } else if (isTypeParamInquiry) { // %kind or %len |
1521 | ActualArgument arg{std::move(*base)}; |
1522 | SetArgSourceLocation(arg, name); |
1523 | return MakeFunctionRef(name, ActualArguments{std::move(arg)}); |
1524 | } else { |
1525 | DIE("unexpected MiscDetails::Kind"); |
1526 | } |
1527 | } else { |
1528 | Say(name, "derived type required before component reference"_err_en_US); |
1529 | } |
1530 | return std::nullopt; |
1531 | } |
1532 | |
1533 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::CoindexedNamedObject &x) { |
1534 | if (auto dataRef{ExtractDataRef(Analyze(x.base))}) { |
1535 | if (!std::holds_alternative<ArrayRef>(dataRef->u) && |
1536 | dataRef->GetLastSymbol().Rank() > 0) { // F'2023 C916 |
1537 | Say("Subscripts must appear in a coindexed reference when its base is an array"_err_en_US); |
1538 | } |
1539 | std::vector<Expr<SubscriptInteger>> cosubscripts; |
1540 | bool cosubsOk{true}; |
1541 | for (const auto &cosub : |
1542 | std::get<std::list<parser::Cosubscript>>(x.imageSelector.t)) { |
1543 | MaybeExpr coex{Analyze(cosub)}; |
1544 | if (auto *intExpr{UnwrapExpr<Expr<SomeInteger>>(coex)}) { |
1545 | cosubscripts.push_back( |
1546 | ConvertToType<SubscriptInteger>(std::move(*intExpr))); |
1547 | } else { |
1548 | cosubsOk = false; |
1549 | } |
1550 | } |
1551 | if (cosubsOk) { |
1552 | int numCosubscripts{static_cast<int>(cosubscripts.size())}; |
1553 | const Symbol &symbol{dataRef->GetLastSymbol()}; |
1554 | if (numCosubscripts != GetCorank(symbol)) { |
1555 | Say("'%s' has corank %d, but coindexed reference has %d cosubscripts"_err_en_US, |
1556 | symbol.name(), GetCorank(symbol), numCosubscripts); |
1557 | } |
1558 | } |
1559 | CoarrayRef coarrayRef{std::move(*dataRef), std::move(cosubscripts)}; |
1560 | for (const auto &imageSelSpec : |
1561 | std::get<std::list<parser::ImageSelectorSpec>>(x.imageSelector.t)) { |
1562 | common::visit( |
1563 | common::visitors{ |
1564 | [&](const parser::ImageSelectorSpec::Stat &x) { |
1565 | Analyze(x.v); |
1566 | if (const auto *expr{GetExpr(context_, x.v)}) { |
1567 | if (const auto *intExpr{ |
1568 | std::get_if<Expr<SomeInteger>>(&expr->u)}) { |
1569 | if (coarrayRef.stat()) { |
1570 | Say("coindexed reference has multiple STAT= specifiers"_err_en_US); |
1571 | } else { |
1572 | coarrayRef.set_stat(Expr<SomeInteger>{*intExpr}); |
1573 | } |
1574 | } |
1575 | } |
1576 | }, |
1577 | [&](const parser::TeamValue &x) { |
1578 | Analyze(x.v); |
1579 | if (const auto *expr{GetExpr(context_, x.v)}) { |
1580 | if (coarrayRef.team()) { |
1581 | Say("coindexed reference has multiple TEAM= or TEAM_NUMBER= specifiers"_err_en_US); |
1582 | } else if (auto dyType{expr->GetType()}; |
1583 | dyType && IsTeamType(GetDerivedTypeSpec(*dyType))) { |
1584 | coarrayRef.set_team(Expr<SomeType>{*expr}); |
1585 | } else { |
1586 | Say("TEAM= specifier must have type TEAM_TYPE from ISO_FORTRAN_ENV"_err_en_US); |
1587 | } |
1588 | } |
1589 | }, |
1590 | [&](const parser::ImageSelectorSpec::Team_Number &x) { |
1591 | Analyze(x.v); |
1592 | if (const auto *expr{GetExpr(context_, x.v)}) { |
1593 | if (coarrayRef.team()) { |
1594 | Say("coindexed reference has multiple TEAM= or TEAM_NUMBER= specifiers"_err_en_US); |
1595 | } else { |
1596 | coarrayRef.set_team(Expr<SomeType>{*expr}); |
1597 | } |
1598 | } |
1599 | }}, |
1600 | imageSelSpec.u); |
1601 | } |
1602 | CheckCosubscripts(context_, coarrayRef); |
1603 | return Designate(DataRef{std::move(coarrayRef)}); |
1604 | } |
1605 | return std::nullopt; |
1606 | } |
1607 | |
1608 | int ExpressionAnalyzer::IntegerTypeSpecKind( |
1609 | const parser::IntegerTypeSpec &spec) { |
1610 | Expr<SubscriptInteger> value{ |
1611 | AnalyzeKindSelector(TypeCategory::Integer, spec.v)}; |
1612 | if (auto kind{ToInt64(value)}) { |
1613 | return static_cast<int>(*kind); |
1614 | } |
1615 | SayAt(spec, "Constant INTEGER kind value required here"_err_en_US); |
1616 | return GetDefaultKind(TypeCategory::Integer); |
1617 | } |
1618 | |
1619 | // Array constructors |
1620 | |
1621 | // Inverts a collection of generic ArrayConstructorValues<SomeType> that |
1622 | // all happen to have the same actual type T into one ArrayConstructor<T>. |
1623 | template <typename T> |
1624 | ArrayConstructorValues<T> MakeSpecific( |
1625 | ArrayConstructorValues<SomeType> &&from) { |
1626 | ArrayConstructorValues<T> to; |
1627 | for (ArrayConstructorValue<SomeType> &x : from) { |
1628 | common::visit( |
1629 | common::visitors{ |
1630 | [&](common::CopyableIndirection<Expr<SomeType>> &&expr) { |
1631 | auto *typed{UnwrapExpr<Expr<T>>(expr.value())}; |
1632 | to.Push(std::move(DEREF(typed))); |
1633 | }, |
1634 | [&](ImpliedDo<SomeType> &&impliedDo) { |
1635 | to.Push(ImpliedDo<T>{impliedDo.name(), |
1636 | std::move(impliedDo.lower()), std::move(impliedDo.upper()), |
1637 | std::move(impliedDo.stride()), |
1638 | MakeSpecific<T>(std::move(impliedDo.values()))}); |
1639 | }, |
1640 | }, |
1641 | std::move(x.u)); |
1642 | } |
1643 | return to; |
1644 | } |
1645 | |
1646 | class ArrayConstructorContext { |
1647 | public: |
1648 | ArrayConstructorContext( |
1649 | ExpressionAnalyzer &c, std::optional<DynamicTypeWithLength> &&t) |
1650 | : exprAnalyzer_{c}, type_{std::move(t)} {} |
1651 | |
1652 | void Add(const parser::AcValue &); |
1653 | MaybeExpr ToExpr(); |
1654 | |
1655 | // These interfaces allow *this to be used as a type visitor argument to |
1656 | // common::SearchTypes() to convert the array constructor to a typed |
1657 | // expression in ToExpr(). |
1658 | using Result = MaybeExpr; |
1659 | using Types = AllTypes; |
1660 | template <typename T> Result Test() { |
1661 | if (type_ && type_->category() == T::category) { |
1662 | if constexpr (T::category == TypeCategory::Derived) { |
1663 | if (!type_->IsUnlimitedPolymorphic()) { |
1664 | return AsMaybeExpr(ArrayConstructor<T>{type_->GetDerivedTypeSpec(), |
1665 | MakeSpecific<T>(std::move(values_))}); |
1666 | } |
1667 | } else if (type_->kind() == T::kind) { |
1668 | ArrayConstructor<T> result{MakeSpecific<T>(std::move(values_))}; |
1669 | if constexpr (T::category == TypeCategory::Character) { |
1670 | if (auto len{LengthIfGood()}) { |
1671 | // The ac-do-variables may be treated as constant expressions, |
1672 | // if some conditions on ac-implied-do-control hold (10.1.12 (12)). |
1673 | // At the same time, they may be treated as constant expressions |
1674 | // only in the context of the ac-implied-do, but setting |
1675 | // the character length here may result in complete elimination |
1676 | // of the ac-implied-do. For example: |
1677 | // character(10) :: c |
1678 | // ... len([(c(i:i), integer(8)::i = 1,4)]) |
1679 | // would be evaulated into: |
1680 | // ... int(max(0_8,i-i+1_8),kind=4) |
1681 | // with a dangling reference to the ac-do-variable. |
1682 | // Prevent this by checking for the ac-do-variable references |
1683 | // in the 'len' expression. |
1684 | result.set_LEN(std::move(*len)); |
1685 | } |
1686 | } |
1687 | return AsMaybeExpr(std::move(result)); |
1688 | } |
1689 | } |
1690 | return std::nullopt; |
1691 | } |
1692 | |
1693 | private: |
1694 | using ImpliedDoIntType = ResultType<ImpliedDoIndex>; |
1695 | |
1696 | std::optional<Expr<SubscriptInteger>> LengthIfGood() const { |
1697 | if (type_) { |
1698 | auto len{type_->LEN()}; |
1699 | if (explicitType_ || |
1700 | (len && IsConstantExpr(*len) && !ContainsAnyImpliedDoIndex(*len))) { |
1701 | return len; |
1702 | } |
1703 | } |
1704 | return std::nullopt; |
1705 | } |
1706 | bool NeedLength() const { |
1707 | return type_ && type_->category() == TypeCategory::Character && |
1708 | !LengthIfGood(); |
1709 | } |
1710 | void Push(MaybeExpr &&); |
1711 | void Add(const parser::AcValue::Triplet &); |
1712 | void Add(const parser::Expr &); |
1713 | void Add(const parser::AcImpliedDo &); |
1714 | void UnrollConstantImpliedDo(const parser::AcImpliedDo &, |
1715 | parser::CharBlock name, std::int64_t lower, std::int64_t upper, |
1716 | std::int64_t stride); |
1717 | |
1718 | template <int KIND> |
1719 | std::optional<Expr<Type<TypeCategory::Integer, KIND>>> ToSpecificInt( |
1720 | MaybeExpr &&y) { |
1721 | if (y) { |
1722 | Expr<SomeInteger> *intExpr{UnwrapExpr<Expr<SomeInteger>>(*y)}; |
1723 | return Fold(exprAnalyzer_.GetFoldingContext(), |
1724 | ConvertToType<Type<TypeCategory::Integer, KIND>>( |
1725 | std::move(DEREF(intExpr)))); |
1726 | } else { |
1727 | return std::nullopt; |
1728 | } |
1729 | } |
1730 | |
1731 | template <int KIND, typename A> |
1732 | std::optional<Expr<Type<TypeCategory::Integer, KIND>>> GetSpecificIntExpr( |
1733 | const A &x) { |
1734 | return ToSpecificInt<KIND>(exprAnalyzer_.Analyze(x)); |
1735 | } |
1736 | |
1737 | // Nested array constructors all reference the same ExpressionAnalyzer, |
1738 | // which represents the nest of active implied DO loop indices. |
1739 | ExpressionAnalyzer &exprAnalyzer_; |
1740 | std::optional<DynamicTypeWithLength> type_; |
1741 | bool explicitType_{type_.has_value()}; |
1742 | std::optional<std::int64_t> constantLength_; |
1743 | ArrayConstructorValues<SomeType> values_; |
1744 | std::uint64_t messageDisplayedSet_{0}; |
1745 | }; |
1746 | |
1747 | void ArrayConstructorContext::Push(MaybeExpr &&x) { |
1748 | if (!x) { |
1749 | return; |
1750 | } |
1751 | if (!type_) { |
1752 | if (auto *boz{std::get_if<BOZLiteralConstant>(&x->u)}) { |
1753 | // Treat an array constructor of BOZ as if default integer. |
1754 | exprAnalyzer_.Warn(common::LanguageFeature::BOZAsDefaultInteger, |
1755 | "BOZ literal in array constructor without explicit type is assumed to be default INTEGER"_port_en_US); |
1756 | x = AsGenericExpr(ConvertToKind<TypeCategory::Integer>( |
1757 | exprAnalyzer_.GetDefaultKind(TypeCategory::Integer), |
1758 | std::move(*boz))); |
1759 | } |
1760 | } |
1761 | std::optional<DynamicType> dyType{x->GetType()}; |
1762 | if (!dyType) { |
1763 | if (auto *boz{std::get_if<BOZLiteralConstant>(&x->u)}) { |
1764 | if (!type_) { |
1765 | // Treat an array constructor of BOZ as if default integer. |
1766 | exprAnalyzer_.Warn(common::LanguageFeature::BOZAsDefaultInteger, |
1767 | "BOZ literal in array constructor without explicit type is assumed to be default INTEGER"_port_en_US); |
1768 | x = AsGenericExpr(ConvertToKind<TypeCategory::Integer>( |
1769 | exprAnalyzer_.GetDefaultKind(TypeCategory::Integer), |
1770 | std::move(*boz))); |
1771 | dyType = x.value().GetType(); |
1772 | } else if (auto cast{ConvertToType(*type_, std::move(*x))}) { |
1773 | x = std::move(cast); |
1774 | dyType = *type_; |
1775 | } else { |
1776 | if (!(messageDisplayedSet_ & 0x80)) { |
1777 | exprAnalyzer_.Say( |
1778 | "BOZ literal is not suitable for use in this array constructor"_err_en_US); |
1779 | messageDisplayedSet_ |= 0x80; |
1780 | } |
1781 | return; |
1782 | } |
1783 | } else { // procedure name, &c. |
1784 | if (!(messageDisplayedSet_ & 0x40)) { |
1785 | exprAnalyzer_.Say( |
1786 | "Item is not suitable for use in an array constructor"_err_en_US); |
1787 | messageDisplayedSet_ |= 0x40; |
1788 | } |
1789 | return; |
1790 | } |
1791 | } else if (dyType->IsUnlimitedPolymorphic()) { |
1792 | if (!(messageDisplayedSet_ & 8)) { |
1793 | exprAnalyzer_.Say("Cannot have an unlimited polymorphic value in an " |
1794 | "array constructor"_err_en_US); // C7113 |
1795 | messageDisplayedSet_ |= 8; |
1796 | } |
1797 | return; |
1798 | } else if (dyType->category() == TypeCategory::Derived && |
1799 | dyType->GetDerivedTypeSpec().typeSymbol().attrs().test( |
1800 | semantics::Attr::ABSTRACT)) { // F'2023 C7125 |
1801 | if (!(messageDisplayedSet_ & 0x200)) { |
1802 | exprAnalyzer_.Say( |
1803 | "An item whose declared type is ABSTRACT may not appear in an array constructor"_err_en_US); |
1804 | messageDisplayedSet_ |= 0x200; |
1805 | } |
1806 | } |
1807 | DynamicTypeWithLength xType{dyType.value()}; |
1808 | if (Expr<SomeCharacter> * charExpr{UnwrapExpr<Expr<SomeCharacter>>(*x)}) { |
1809 | CHECK(xType.category() == TypeCategory::Character); |
1810 | xType.length = |
1811 | common::visit([](const auto &kc) { return kc.LEN(); }, charExpr->u); |
1812 | } |
1813 | if (!type_) { |
1814 | // If there is no explicit type-spec in an array constructor, the type |
1815 | // of the array is the declared type of all of the elements, which must |
1816 | // be well-defined and all match. |
1817 | // TODO: Possible language extension: use the most general type of |
1818 | // the values as the type of a numeric constructed array, convert all |
1819 | // of the other values to that type. Alternative: let the first value |
1820 | // determine the type, and convert the others to that type. |
1821 | CHECK(!explicitType_); |
1822 | type_ = std::move(xType); |
1823 | constantLength_ = ToInt64(type_->length); |
1824 | values_.Push(std::move(*x)); |
1825 | } else if (!explicitType_) { |
1826 | if (type_->IsTkCompatibleWith(xType) && xType.IsTkCompatibleWith(*type_)) { |
1827 | values_.Push(std::move(*x)); |
1828 | auto xLen{xType.LEN()}; |
1829 | if (auto thisLen{ToInt64(xLen)}) { |
1830 | if (constantLength_) { |
1831 | if (*thisLen != *constantLength_ && !(messageDisplayedSet_ & 1)) { |
1832 | exprAnalyzer_.Warn( |
1833 | common::LanguageFeature::DistinctArrayConstructorLengths, |
1834 | "Character literal in array constructor without explicit " |
1835 | "type has different length than earlier elements"_port_en_US); |
1836 | messageDisplayedSet_ |= 1; |
1837 | } |
1838 | if (*thisLen > *constantLength_) { |
1839 | // Language extension: use the longest literal to determine the |
1840 | // length of the array constructor's character elements, not the |
1841 | // first, when there is no explicit type. |
1842 | *constantLength_ = *thisLen; |
1843 | type_->length = std::move(xLen); |
1844 | } |
1845 | } else { |
1846 | constantLength_ = *thisLen; |
1847 | type_->length = std::move(xLen); |
1848 | } |
1849 | } else if (xLen && NeedLength()) { |
1850 | type_->length = std::move(xLen); |
1851 | } |
1852 | } else { |
1853 | if (!(messageDisplayedSet_ & 2)) { |
1854 | exprAnalyzer_.Say( |
1855 | "Values in array constructor must have the same declared type " |
1856 | "when no explicit type appears"_err_en_US); // C7110 |
1857 | messageDisplayedSet_ |= 2; |
1858 | } |
1859 | } |
1860 | } else { |
1861 | if (auto cast{ConvertToType(*type_, std::move(*x))}) { |
1862 | values_.Push(std::move(*cast)); |
1863 | } else if (!(messageDisplayedSet_ & 4)) { |
1864 | exprAnalyzer_.Say("Value in array constructor of type '%s' could not " |
1865 | "be converted to the type of the array '%s'"_err_en_US, |
1866 | x->GetType()->AsFortran(), type_->AsFortran()); // C7111, C7112 |
1867 | messageDisplayedSet_ |= 4; |
1868 | } |
1869 | } |
1870 | } |
1871 | |
1872 | void ArrayConstructorContext::Add(const parser::AcValue &x) { |
1873 | common::visit( |
1874 | common::visitors{ |
1875 | [&](const parser::AcValue::Triplet &triplet) { Add(triplet); }, |
1876 | [&](const common::Indirection<parser::Expr> &expr) { |
1877 | Add(expr.value()); |
1878 | }, |
1879 | [&](const common::Indirection<parser::AcImpliedDo> &impliedDo) { |
1880 | Add(impliedDo.value()); |
1881 | }, |
1882 | }, |
1883 | x.u); |
1884 | } |
1885 | |
1886 | // Transforms l:u(:s) into (_,_=l,u(,s)) with an anonymous index '_' |
1887 | void ArrayConstructorContext::Add(const parser::AcValue::Triplet &triplet) { |
1888 | MaybeExpr lowerExpr{exprAnalyzer_.Analyze(std::get<0>(triplet.t))}; |
1889 | MaybeExpr upperExpr{exprAnalyzer_.Analyze(std::get<1>(triplet.t))}; |
1890 | MaybeExpr strideExpr{exprAnalyzer_.Analyze(std::get<2>(triplet.t))}; |
1891 | if (lowerExpr && upperExpr) { |
1892 | auto lowerType{lowerExpr->GetType()}; |
1893 | auto upperType{upperExpr->GetType()}; |
1894 | auto strideType{strideExpr ? strideExpr->GetType() : lowerType}; |
1895 | if (lowerType && upperType && strideType) { |
1896 | int kind{lowerType->kind()}; |
1897 | if (upperType->kind() > kind) { |
1898 | kind = upperType->kind(); |
1899 | } |
1900 | if (strideType->kind() > kind) { |
1901 | kind = strideType->kind(); |
1902 | } |
1903 | auto lower{ToSpecificInt<ImpliedDoIntType::kind>(std::move(lowerExpr))}; |
1904 | auto upper{ToSpecificInt<ImpliedDoIntType::kind>(std::move(upperExpr))}; |
1905 | if (lower && upper) { |
1906 | auto stride{ |
1907 | ToSpecificInt<ImpliedDoIntType::kind>(std::move(strideExpr))}; |
1908 | if (!stride) { |
1909 | stride = Expr<ImpliedDoIntType>{1}; |
1910 | } |
1911 | DynamicType type{TypeCategory::Integer, kind}; |
1912 | if (!type_) { |
1913 | type_ = DynamicTypeWithLength{type}; |
1914 | } |
1915 | parser::CharBlock anonymous; |
1916 | if (auto converted{ConvertToType(type, |
1917 | AsGenericExpr( |
1918 | Expr<ImpliedDoIntType>{ImpliedDoIndex{anonymous}}))}) { |
1919 | auto v{std::move(values_)}; |
1920 | Push(std::move(converted)); |
1921 | std::swap(v, values_); |
1922 | values_.Push(ImpliedDo<SomeType>{anonymous, std::move(*lower), |
1923 | std::move(*upper), std::move(*stride), std::move(v)}); |
1924 | } |
1925 | } |
1926 | } |
1927 | } |
1928 | } |
1929 | |
1930 | void ArrayConstructorContext::Add(const parser::Expr &expr) { |
1931 | auto restorer1{ |
1932 | exprAnalyzer_.GetContextualMessages().SetLocation(expr.source)}; |
1933 | auto restorer2{exprAnalyzer_.AllowWholeAssumedSizeArray(false)}; |
1934 | Push(exprAnalyzer_.Analyze(expr)); |
1935 | } |
1936 | |
1937 | void ArrayConstructorContext::Add(const parser::AcImpliedDo &impliedDo) { |
1938 | const auto &control{std::get<parser::AcImpliedDoControl>(impliedDo.t)}; |
1939 | const auto &bounds{std::get<parser::AcImpliedDoControl::Bounds>(control.t)}; |
1940 | exprAnalyzer_.Analyze(bounds.name); |
1941 | parser::CharBlock name{bounds.name.thing.thing.source}; |
1942 | int kind{ImpliedDoIntType::kind}; |
1943 | if (const Symbol * symbol{bounds.name.thing.thing.symbol}) { |
1944 | if (auto dynamicType{DynamicType::From(symbol)}) { |
1945 | if (dynamicType->category() == TypeCategory::Integer) { |
1946 | kind = dynamicType->kind(); |
1947 | } |
1948 | } |
1949 | } |
1950 | std::optional<Expr<ImpliedDoIntType>> lower{ |
1951 | GetSpecificIntExpr<ImpliedDoIntType::kind>(bounds.lower)}; |
1952 | std::optional<Expr<ImpliedDoIntType>> upper{ |
1953 | GetSpecificIntExpr<ImpliedDoIntType::kind>(bounds.upper)}; |
1954 | if (lower && upper) { |
1955 | std::optional<Expr<ImpliedDoIntType>> stride{ |
1956 | GetSpecificIntExpr<ImpliedDoIntType::kind>(bounds.step)}; |
1957 | if (!stride) { |
1958 | stride = Expr<ImpliedDoIntType>{1}; |
1959 | } |
1960 | if (exprAnalyzer_.AddImpliedDo(name, kind)) { |
1961 | // Check for constant bounds; the loop may require complete unrolling |
1962 | // of the parse tree if all bounds are constant in order to allow the |
1963 | // implied DO loop index to qualify as a constant expression. |
1964 | auto cLower{ToInt64(lower)}; |
1965 | auto cUpper{ToInt64(upper)}; |
1966 | auto cStride{ToInt64(stride)}; |
1967 | if (!(messageDisplayedSet_ & 0x10) && cStride && *cStride == 0) { |
1968 | exprAnalyzer_.SayAt(bounds.step.value().thing.thing.value().source, |
1969 | "The stride of an implied DO loop must not be zero"_err_en_US); |
1970 | messageDisplayedSet_ |= 0x10; |
1971 | } |
1972 | bool isConstant{cLower && cUpper && cStride && *cStride != 0}; |
1973 | bool isNonemptyConstant{isConstant && |
1974 | ((*cStride > 0 && *cLower <= *cUpper) || |
1975 | (*cStride < 0 && *cLower >= *cUpper))}; |
1976 | bool isEmpty{isConstant && !isNonemptyConstant}; |
1977 | bool unrollConstantLoop{false}; |
1978 | parser::Messages buffer; |
1979 | auto saveMessagesDisplayed{messageDisplayedSet_}; |
1980 | { |
1981 | auto messageRestorer{ |
1982 | exprAnalyzer_.GetContextualMessages().SetMessages(buffer)}; |
1983 | auto v{std::move(values_)}; |
1984 | for (const auto &value : |
1985 | std::get<std::list<parser::AcValue>>(impliedDo.t)) { |
1986 | Add(value); |
1987 | } |
1988 | std::swap(v, values_); |
1989 | if (isNonemptyConstant && buffer.AnyFatalError()) { |
1990 | unrollConstantLoop = true; |
1991 | } else { |
1992 | values_.Push(ImpliedDo<SomeType>{name, std::move(*lower), |
1993 | std::move(*upper), std::move(*stride), std::move(v)}); |
1994 | } |
1995 | } |
1996 | // F'2023 7.8 p5 |
1997 | if (!(messageDisplayedSet_ & 0x100) && isEmpty && NeedLength()) { |
1998 | exprAnalyzer_.SayAt(name, |
1999 | "Array constructor implied DO loop has no iterations and indeterminate character length"_err_en_US); |
2000 | messageDisplayedSet_ |= 0x100; |
2001 | } |
2002 | if (unrollConstantLoop) { |
2003 | messageDisplayedSet_ = saveMessagesDisplayed; |
2004 | UnrollConstantImpliedDo(impliedDo, name, *cLower, *cUpper, *cStride); |
2005 | } else if (auto *messages{ |
2006 | exprAnalyzer_.GetContextualMessages().messages()}) { |
2007 | messages->Annex(std::move(buffer)); |
2008 | } |
2009 | exprAnalyzer_.RemoveImpliedDo(name); |
2010 | } else if (!(messageDisplayedSet_ & 0x20)) { |
2011 | exprAnalyzer_.SayAt(name, |
2012 | "Implied DO index '%s' is active in a surrounding implied DO loop " |
2013 | "and may not have the same name"_err_en_US, |
2014 | name); // C7115 |
2015 | messageDisplayedSet_ |= 0x20; |
2016 | } |
2017 | } |
2018 | } |
2019 | |
2020 | // Fortran considers an implied DO index of an array constructor to be |
2021 | // a constant expression if the bounds of the implied DO loop are constant. |
2022 | // Usually this doesn't matter, but if we emitted spurious messages as a |
2023 | // result of not using constant values for the index while analyzing the |
2024 | // items, we need to do it again the "hard" way with multiple iterations over |
2025 | // the parse tree. |
2026 | void ArrayConstructorContext::UnrollConstantImpliedDo( |
2027 | const parser::AcImpliedDo &impliedDo, parser::CharBlock name, |
2028 | std::int64_t lower, std::int64_t upper, std::int64_t stride) { |
2029 | auto &foldingContext{exprAnalyzer_.GetFoldingContext()}; |
2030 | auto restorer{exprAnalyzer_.DoNotUseSavedTypedExprs()}; |
2031 | for (auto &at{foldingContext.StartImpliedDo(name, lower)}; |
2032 | (stride > 0 && at <= upper) || (stride < 0 && at >= upper); |
2033 | at += stride) { |
2034 | for (const auto &value : |
2035 | std::get<std::list<parser::AcValue>>(impliedDo.t)) { |
2036 | Add(value); |
2037 | } |
2038 | } |
2039 | foldingContext.EndImpliedDo(name); |
2040 | } |
2041 | |
2042 | MaybeExpr ArrayConstructorContext::ToExpr() { |
2043 | return common::SearchTypes(std::move(*this)); |
2044 | } |
2045 | |
2046 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::ArrayConstructor &array) { |
2047 | const parser::AcSpec &acSpec{array.v}; |
2048 | ArrayConstructorContext acContext{ |
2049 | *this, AnalyzeTypeSpec(acSpec.type, GetFoldingContext())}; |
2050 | for (const parser::AcValue &value : acSpec.values) { |
2051 | acContext.Add(value); |
2052 | } |
2053 | return acContext.ToExpr(); |
2054 | } |
2055 | |
2056 | // Check if implicit conversion of expr to the symbol type is legal (if needed), |
2057 | // and make it explicit if requested. |
2058 | static MaybeExpr ImplicitConvertTo(const semantics::Symbol &sym, |
2059 | Expr<SomeType> &&expr, bool keepConvertImplicit) { |
2060 | if (!keepConvertImplicit) { |
2061 | return ConvertToType(sym, std::move(expr)); |
2062 | } else { |
2063 | // Test if a convert could be inserted, but do not make it explicit to |
2064 | // preserve the information that expr is a variable. |
2065 | if (ConvertToType(sym, common::Clone(expr))) { |
2066 | return MaybeExpr{std::move(expr)}; |
2067 | } |
2068 | } |
2069 | // Illegal implicit convert. |
2070 | return std::nullopt; |
2071 | } |
2072 | |
2073 | MaybeExpr ExpressionAnalyzer::CheckStructureConstructor( |
2074 | parser::CharBlock typeName, const semantics::DerivedTypeSpec &spec, |
2075 | std::list<ComponentSpec> &&componentSpecs) { |
2076 | const Symbol &typeSymbol{spec.typeSymbol()}; |
2077 | if (!spec.scope() || !typeSymbol.has<semantics::DerivedTypeDetails>()) { |
2078 | return std::nullopt; // error recovery |
2079 | } |
2080 | const semantics::Scope &scope{context_.FindScope(typeName)}; |
2081 | const semantics::Scope *pureContext{FindPureProcedureContaining(scope)}; |
2082 | const auto &typeDetails{typeSymbol.get<semantics::DerivedTypeDetails>()}; |
2083 | const Symbol *parentComponent{typeDetails.GetParentComponent(*spec.scope())}; |
2084 | |
2085 | if (typeSymbol.attrs().test(semantics::Attr::ABSTRACT)) { // C796 |
2086 | AttachDeclaration( |
2087 | Say(typeName, |
2088 | "ABSTRACT derived type '%s' may not be used in a structure constructor"_err_en_US, |
2089 | typeName), |
2090 | typeSymbol); // C7114 |
2091 | } |
2092 | |
2093 | // This iterator traverses all of the components in the derived type and its |
2094 | // parents. The symbols for whole parent components appear after their |
2095 | // own components and before the components of the types that extend them. |
2096 | // E.g., TYPE :: A; REAL X; END TYPE |
2097 | // TYPE, EXTENDS(A) :: B; REAL Y; END TYPE |
2098 | // produces the component list X, A, Y. |
2099 | // The order is important below because a structure constructor can |
2100 | // initialize X or A by name, but not both. |
2101 | auto components{semantics::OrderedComponentIterator{spec}}; |
2102 | auto nextAnonymous{components.begin()}; |
2103 | auto afterLastParentComponentIter{components.end()}; |
2104 | if (parentComponent) { |
2105 | for (auto iter{components.begin()}; iter != components.end(); ++iter) { |
2106 | if (iter->test(Symbol::Flag::ParentComp)) { |
2107 | afterLastParentComponentIter = iter; |
2108 | ++afterLastParentComponentIter; |
2109 | } |
2110 | } |
2111 | } |
2112 | |
2113 | std::set<parser::CharBlock> unavailable; |
2114 | bool anyKeyword{false}; |
2115 | StructureConstructor result{spec}; |
2116 | bool checkConflicts{true}; // until we hit one |
2117 | auto &messages{GetContextualMessages()}; |
2118 | |
2119 | for (ComponentSpec &componentSpec : componentSpecs) { |
2120 | parser::CharBlock source{componentSpec.source}; |
2121 | parser::CharBlock exprSource{componentSpec.exprSource}; |
2122 | auto restorer{messages.SetLocation(source)}; |
2123 | const Symbol *symbol{componentSpec.keywordSymbol}; |
2124 | MaybeExpr &maybeValue{componentSpec.expr}; |
2125 | if (!maybeValue.has_value()) { |
2126 | return std::nullopt; |
2127 | } |
2128 | Expr<SomeType> &value{*maybeValue}; |
2129 | std::optional<DynamicType> valueType{DynamicType::From(value)}; |
2130 | if (componentSpec.hasKeyword) { |
2131 | anyKeyword = true; |
2132 | if (!symbol) { |
2133 | // Skip overridden inaccessible parent components in favor of |
2134 | // their later overrides. |
2135 | for (const Symbol &sym : components) { |
2136 | if (sym.name() == source) { |
2137 | symbol = &sym; |
2138 | } |
2139 | } |
2140 | } |
2141 | if (!symbol) { // C7101 |
2142 | Say(source, |
2143 | "Keyword '%s=' does not name a component of derived type '%s'"_err_en_US, |
2144 | source, typeName); |
2145 | } |
2146 | } else { |
2147 | if (anyKeyword) { // C7100 |
2148 | Say(source, |
2149 | "Value in structure constructor lacks a component name"_err_en_US); |
2150 | checkConflicts = false; // stem cascade |
2151 | } |
2152 | // Here's a regrettably common extension of the standard: anonymous |
2153 | // initialization of parent components, e.g., T(PT(1)) rather than |
2154 | // T(1) or T(PT=PT(1)). There may be multiple parent components. |
2155 | if (nextAnonymous == components.begin() && parentComponent && valueType && |
2156 | context().IsEnabled(LanguageFeature::AnonymousParents)) { |
2157 | for (auto parent{components.begin()}; |
2158 | parent != afterLastParentComponentIter; ++parent) { |
2159 | if (auto parentType{DynamicType::From(*parent)}; parentType && |
2160 | parent->test(Symbol::Flag::ParentComp) && |
2161 | valueType->IsEquivalentTo(*parentType)) { |
2162 | symbol = &*parent; |
2163 | nextAnonymous = ++parent; |
2164 | Warn(LanguageFeature::AnonymousParents, source, |
2165 | "Whole parent component '%s' in structure constructor should not be anonymous"_port_en_US, |
2166 | symbol->name()); |
2167 | break; |
2168 | } |
2169 | } |
2170 | } |
2171 | while (!symbol && nextAnonymous != components.end()) { |
2172 | const Symbol &next{*nextAnonymous}; |
2173 | ++nextAnonymous; |
2174 | if (!next.test(Symbol::Flag::ParentComp)) { |
2175 | symbol = &next; |
2176 | } |
2177 | } |
2178 | if (!symbol) { |
2179 | Say(source, "Unexpected value in structure constructor"_err_en_US); |
2180 | } |
2181 | } |
2182 | if (symbol) { |
2183 | const semantics::Scope &innermost{context_.FindScope(exprSource)}; |
2184 | if (auto msg{CheckAccessibleSymbol(innermost, *symbol)}) { |
2185 | Say(exprSource, std::move(*msg)); |
2186 | } |
2187 | if (checkConflicts) { |
2188 | auto componentIter{ |
2189 | std::find(components.begin(), components.end(), *symbol)}; |
2190 | if (unavailable.find(symbol->name()) != unavailable.cend()) { |
2191 | // C797, C798 |
2192 | Say(source, |
2193 | "Component '%s' conflicts with another component earlier in this structure constructor"_err_en_US, |
2194 | symbol->name()); |
2195 | } else if (symbol->test(Symbol::Flag::ParentComp)) { |
2196 | // Make earlier components unavailable once a whole parent appears. |
2197 | for (auto it{components.begin()}; it != componentIter; ++it) { |
2198 | unavailable.insert(it->name()); |
2199 | } |
2200 | } else { |
2201 | // Make whole parent components unavailable after any of their |
2202 | // constituents appear. |
2203 | for (auto it{componentIter}; it != components.end(); ++it) { |
2204 | if (it->test(Symbol::Flag::ParentComp)) { |
2205 | unavailable.insert(it->name()); |
2206 | } |
2207 | } |
2208 | } |
2209 | } |
2210 | unavailable.insert(symbol->name()); |
2211 | if (symbol->has<semantics::TypeParamDetails>()) { |
2212 | Say(exprSource, |
2213 | "Type parameter '%s' may not appear as a component of a structure constructor"_err_en_US, |
2214 | symbol->name()); |
2215 | } |
2216 | if (!(symbol->has<semantics::ProcEntityDetails>() || |
2217 | symbol->has<semantics::ObjectEntityDetails>())) { |
2218 | continue; // recovery |
2219 | } |
2220 | if (IsPointer(*symbol)) { // C7104, C7105, C1594(4) |
2221 | semantics::CheckStructConstructorPointerComponent( |
2222 | context_, *symbol, value, innermost); |
2223 | result.Add(*symbol, Fold(std::move(value))); |
2224 | continue; |
2225 | } |
2226 | if (IsNullPointer(&value)) { |
2227 | if (IsAllocatable(*symbol)) { |
2228 | if (IsBareNullPointer(&value)) { |
2229 | // NULL() with no arguments allowed by 7.5.10 para 6 for |
2230 | // ALLOCATABLE. |
2231 | result.Add(*symbol, Expr<SomeType>{NullPointer{}}); |
2232 | continue; |
2233 | } |
2234 | if (IsNullObjectPointer(&value)) { |
2235 | AttachDeclaration( |
2236 | Warn(common::LanguageFeature::NullMoldAllocatableComponentValue, |
2237 | exprSource, |
2238 | "NULL() with arguments is not standard conforming as the value for allocatable component '%s'"_port_en_US, |
2239 | symbol->name()), |
2240 | *symbol); |
2241 | // proceed to check type & shape |
2242 | } else { |
2243 | AttachDeclaration( |
2244 | Say(exprSource, |
2245 | "A NULL procedure pointer may not be used as the value for component '%s'"_err_en_US, |
2246 | symbol->name()), |
2247 | *symbol); |
2248 | continue; |
2249 | } |
2250 | } else { |
2251 | AttachDeclaration( |
2252 | Say(exprSource, |
2253 | "A NULL pointer may not be used as the value for component '%s'"_err_en_US, |
2254 | symbol->name()), |
2255 | *symbol); |
2256 | continue; |
2257 | } |
2258 | } else if (IsNullAllocatable(&value) && IsAllocatable(*symbol)) { |
2259 | result.Add(*symbol, Expr<SomeType>{NullPointer{}}); |
2260 | continue; |
2261 | } else if (auto *derived{evaluate::GetDerivedTypeSpec( |
2262 | evaluate::DynamicType::From(*symbol))}) { |
2263 | if (auto iter{FindPointerPotentialComponent(*derived)}; |
2264 | iter && pureContext) { // F'2023 C15104(4) |
2265 | if (const Symbol * |
2266 | visible{semantics::FindExternallyVisibleObject( |
2267 | value, *pureContext)}) { |
2268 | Say(exprSource, |
2269 | "The externally visible object '%s' may not be used in a pure procedure as the value for component '%s' which has the pointer component '%s'"_err_en_US, |
2270 | visible->name(), symbol->name(), |
2271 | iter.BuildResultDesignatorName()); |
2272 | } else if (ExtractCoarrayRef(value)) { |
2273 | Say(exprSource, |
2274 | "A coindexed object may not be used in a pure procedure as the value for component '%s' which has the pointer component '%s'"_err_en_US, |
2275 | symbol->name(), iter.BuildResultDesignatorName()); |
2276 | } |
2277 | } |
2278 | } |
2279 | // Make implicit conversion explicit to allow folding of the structure |
2280 | // constructors and help semantic checking, unless the component is |
2281 | // allocatable, in which case the value could be an unallocated |
2282 | // allocatable (see Fortran 2018 7.5.10 point 7). The explicit |
2283 | // convert would cause a segfault. Lowering will deal with |
2284 | // conditionally converting and preserving the lower bounds in this |
2285 | // case. |
2286 | if (MaybeExpr converted{ImplicitConvertTo( |
2287 | *symbol, std::move(value), IsAllocatable(*symbol))}) { |
2288 | if (auto componentShape{GetShape(GetFoldingContext(), *symbol)}) { |
2289 | if (auto valueShape{GetShape(GetFoldingContext(), *converted)}) { |
2290 | if (GetRank(*componentShape) == 0 && GetRank(*valueShape) > 0) { |
2291 | AttachDeclaration( |
2292 | Say(exprSource, |
2293 | "Rank-%d array value is not compatible with scalar component '%s'"_err_en_US, |
2294 | GetRank(*valueShape), symbol->name()), |
2295 | *symbol); |
2296 | } else { |
2297 | auto checked{CheckConformance(messages, *componentShape, |
2298 | *valueShape, CheckConformanceFlags::RightIsExpandableDeferred, |
2299 | "component", "value")}; |
2300 | if (checked && *checked && GetRank(*componentShape) > 0 && |
2301 | GetRank(*valueShape) == 0 && |
2302 | (IsDeferredShape(*symbol) || |
2303 | !IsExpandableScalar(*converted, GetFoldingContext(), |
2304 | *componentShape, true /*admit PURE call*/))) { |
2305 | AttachDeclaration( |
2306 | Say(exprSource, |
2307 | "Scalar value cannot be expanded to shape of array component '%s'"_err_en_US, |
2308 | symbol->name()), |
2309 | *symbol); |
2310 | } |
2311 | if (checked.value_or(true)) { |
2312 | result.Add(*symbol, std::move(*converted)); |
2313 | } |
2314 | } |
2315 | } else { |
2316 | Say(exprSource, "Shape of value cannot be determined"_err_en_US); |
2317 | } |
2318 | } else { |
2319 | AttachDeclaration( |
2320 | Say(exprSource, |
2321 | "Shape of component '%s' cannot be determined"_err_en_US, |
2322 | symbol->name()), |
2323 | *symbol); |
2324 | } |
2325 | } else if (auto symType{DynamicType::From(symbol)}) { |
2326 | if (IsAllocatable(*symbol) && symType->IsUnlimitedPolymorphic() && |
2327 | valueType) { |
2328 | // ok |
2329 | } else if (valueType) { |
2330 | AttachDeclaration( |
2331 | Say(exprSource, |
2332 | "Value in structure constructor of type '%s' is incompatible with component '%s' of type '%s'"_err_en_US, |
2333 | valueType->AsFortran(), symbol->name(), symType->AsFortran()), |
2334 | *symbol); |
2335 | } else { |
2336 | AttachDeclaration( |
2337 | Say(exprSource, |
2338 | "Value in structure constructor is incompatible with component '%s' of type %s"_err_en_US, |
2339 | symbol->name(), symType->AsFortran()), |
2340 | *symbol); |
2341 | } |
2342 | } |
2343 | } |
2344 | } |
2345 | |
2346 | // Ensure that unmentioned component objects have default initializers. |
2347 | for (const Symbol &symbol : components) { |
2348 | if (!symbol.test(Symbol::Flag::ParentComp) && |
2349 | unavailable.find(symbol.name()) == unavailable.cend()) { |
2350 | if (IsAllocatable(symbol)) { |
2351 | // Set all remaining allocatables to explicit NULL(). |
2352 | result.Add(symbol, Expr<SomeType>{NullPointer{}}); |
2353 | } else { |
2354 | const auto *object{symbol.detailsIf<semantics::ObjectEntityDetails>()}; |
2355 | if (object && object->init()) { |
2356 | result.Add(symbol, common::Clone(*object->init())); |
2357 | } else if (IsPointer(symbol)) { |
2358 | result.Add(symbol, Expr<SomeType>{NullPointer{}}); |
2359 | } else if (object) { // C799 |
2360 | AttachDeclaration( |
2361 | Say(typeName, |
2362 | "Structure constructor lacks a value for component '%s'"_err_en_US, |
2363 | symbol.name()), |
2364 | symbol); |
2365 | } |
2366 | } |
2367 | } |
2368 | } |
2369 | |
2370 | return AsMaybeExpr(Expr<SomeDerived>{std::move(result)}); |
2371 | } |
2372 | |
2373 | MaybeExpr ExpressionAnalyzer::Analyze( |
2374 | const parser::StructureConstructor &structure) { |
2375 | const auto &parsedType{std::get<parser::DerivedTypeSpec>(structure.t)}; |
2376 | parser::Name structureType{std::get<parser::Name>(parsedType.t)}; |
2377 | parser::CharBlock &typeName{structureType.source}; |
2378 | if (semantics::Symbol * typeSymbol{structureType.symbol}) { |
2379 | if (typeSymbol->has<semantics::DerivedTypeDetails>()) { |
2380 | semantics::DerivedTypeSpec dtSpec{typeName, typeSymbol->GetUltimate()}; |
2381 | if (!CheckIsValidForwardReference(dtSpec)) { |
2382 | return std::nullopt; |
2383 | } |
2384 | } |
2385 | } |
2386 | if (!parsedType.derivedTypeSpec) { |
2387 | return std::nullopt; |
2388 | } |
2389 | auto restorer{AllowNullPointer()}; // NULL() can be a valid component |
2390 | std::list<ComponentSpec> componentSpecs; |
2391 | for (const auto &component : |
2392 | std::get<std::list<parser::ComponentSpec>>(structure.t)) { |
2393 | const parser::Expr &expr{ |
2394 | std::get<parser::ComponentDataSource>(component.t).v.value()}; |
2395 | auto restorer{GetContextualMessages().SetLocation(expr.source)}; |
2396 | ComponentSpec compSpec; |
2397 | compSpec.exprSource = expr.source; |
2398 | compSpec.expr = Analyze(expr); |
2399 | if (const auto &kw{std::get<std::optional<parser::Keyword>>(component.t)}) { |
2400 | compSpec.source = kw->v.source; |
2401 | compSpec.hasKeyword = true; |
2402 | compSpec.keywordSymbol = kw->v.symbol; |
2403 | } else { |
2404 | compSpec.source = expr.source; |
2405 | } |
2406 | componentSpecs.emplace_back(std::move(compSpec)); |
2407 | } |
2408 | return CheckStructureConstructor( |
2409 | typeName, DEREF(parsedType.derivedTypeSpec), std::move(componentSpecs)); |
2410 | } |
2411 | |
2412 | static std::optional<parser::CharBlock> GetPassName( |
2413 | const semantics::Symbol &proc) { |
2414 | return common::visit( |
2415 | [](const auto &details) { |
2416 | if constexpr (std::is_base_of_v<semantics::WithPassArg, |
2417 | std::decay_t<decltype(details)>>) { |
2418 | return details.passName(); |
2419 | } else { |
2420 | return std::optional<parser::CharBlock>{}; |
2421 | } |
2422 | }, |
2423 | proc.details()); |
2424 | } |
2425 | |
2426 | static std::optional<int> GetPassIndex(const Symbol &proc) { |
2427 | CHECK(!proc.attrs().test(semantics::Attr::NOPASS)); |
2428 | std::optional<parser::CharBlock> passName{GetPassName(proc)}; |
2429 | const auto *interface { |
2430 | semantics::FindInterface(proc) |
2431 | }; |
2432 | if (!passName || !interface) { |
2433 | return 0; // first argument is passed-object |
2434 | } |
2435 | const auto &subp{interface->get<semantics::SubprogramDetails>()}; |
2436 | int index{0}; |
2437 | for (const auto *arg : subp.dummyArgs()) { |
2438 | if (arg && arg->name() == passName) { |
2439 | return index; |
2440 | } |
2441 | ++index; |
2442 | } |
2443 | return std::nullopt; |
2444 | } |
2445 | |
2446 | // Injects an expression into an actual argument list as the "passed object" |
2447 | // for a type-bound procedure reference that is not NOPASS. Adds an |
2448 | // argument keyword if possible, but not when the passed object goes |
2449 | // before a positional argument. |
2450 | // e.g., obj%tbp(x) -> tbp(obj,x). |
2451 | static void AddPassArg(ActualArguments &actuals, const Expr<SomeDerived> &expr, |
2452 | const Symbol &component, bool isPassedObject = true) { |
2453 | if (component.attrs().test(semantics::Attr::NOPASS)) { |
2454 | return; |
2455 | } |
2456 | std::optional<int> passIndex{GetPassIndex(component)}; |
2457 | if (!passIndex) { |
2458 | return; // error recovery |
2459 | } |
2460 | auto iter{actuals.begin()}; |
2461 | int at{0}; |
2462 | while (iter < actuals.end() && at < *passIndex) { |
2463 | if (*iter && (*iter)->keyword()) { |
2464 | iter = actuals.end(); |
2465 | break; |
2466 | } |
2467 | ++iter; |
2468 | ++at; |
2469 | } |
2470 | ActualArgument passed{AsGenericExpr(common::Clone(expr))}; |
2471 | passed.set_isPassedObject(isPassedObject); |
2472 | if (iter == actuals.end()) { |
2473 | if (auto passName{GetPassName(component)}) { |
2474 | passed.set_keyword(*passName); |
2475 | } |
2476 | } |
2477 | actuals.emplace(iter, std::move(passed)); |
2478 | } |
2479 | |
2480 | // Return the compile-time resolution of a procedure binding, if possible. |
2481 | static const Symbol *GetBindingResolution( |
2482 | const std::optional<DynamicType> &baseType, const Symbol &component) { |
2483 | const auto *binding{component.detailsIf<semantics::ProcBindingDetails>()}; |
2484 | if (!binding) { |
2485 | return nullptr; |
2486 | } |
2487 | if (!component.attrs().test(semantics::Attr::NON_OVERRIDABLE) && |
2488 | (!baseType || baseType->IsPolymorphic())) { |
2489 | return nullptr; |
2490 | } |
2491 | return &binding->symbol(); |
2492 | } |
2493 | |
2494 | auto ExpressionAnalyzer::AnalyzeProcedureComponentRef( |
2495 | const parser::ProcComponentRef &pcr, ActualArguments &&arguments, |
2496 | bool isSubroutine) -> std::optional<CalleeAndArguments> { |
2497 | const parser::StructureComponent &sc{pcr.v.thing}; |
2498 | if (MaybeExpr base{Analyze(sc.base)}) { |
2499 | if (const Symbol *sym{sc.component.symbol}) { |
2500 | if (context_.HasError(sym)) { |
2501 | return std::nullopt; |
2502 | } |
2503 | if (!IsProcedure(*sym)) { |
2504 | AttachDeclaration( |
2505 | Say(sc.component.source, "'%s' is not a procedure"_err_en_US, |
2506 | sc.component.source), |
2507 | *sym); |
2508 | return std::nullopt; |
2509 | } |
2510 | if (auto *dtExpr{UnwrapExpr<Expr<SomeDerived>>(*base)}) { |
2511 | if (sym->has<semantics::GenericDetails>()) { |
2512 | const Symbol &generic{*sym}; |
2513 | auto dyType{dtExpr->GetType()}; |
2514 | AdjustActuals adjustment{ |
2515 | [&](const Symbol &proc, ActualArguments &actuals) { |
2516 | if (!proc.attrs().test(semantics::Attr::NOPASS)) { |
2517 | AddPassArg(actuals, std::move(*dtExpr), proc); |
2518 | } |
2519 | return true; |
2520 | }}; |
2521 | auto pair{ |
2522 | ResolveGeneric(generic, arguments, adjustment, isSubroutine)}; |
2523 | sym = pair.first; |
2524 | if (!sym) { |
2525 | EmitGenericResolutionError(generic, pair.second, isSubroutine); |
2526 | return std::nullopt; |
2527 | } |
2528 | // re-resolve the name to the specific binding |
2529 | CHECK(sym->has<semantics::ProcBindingDetails>()); |
2530 | // Use the most recent override of a binding, respecting |
2531 | // the rule that inaccessible bindings may not be overridden |
2532 | // outside their module. Fortran doesn't allow a PUBLIC |
2533 | // binding to be overridden by a PRIVATE one. |
2534 | CHECK(dyType && dyType->category() == TypeCategory::Derived && |
2535 | !dyType->IsUnlimitedPolymorphic()); |
2536 | if (const Symbol * |
2537 | latest{DEREF(dyType->GetDerivedTypeSpec().typeSymbol().scope()) |
2538 | .FindComponent(sym->name())}) { |
2539 | if (sym->attrs().test(semantics::Attr::PRIVATE)) { |
2540 | const auto *bindingModule{FindModuleContaining(generic.owner())}; |
2541 | const Symbol *s{latest}; |
2542 | while (s && FindModuleContaining(s->owner()) != bindingModule) { |
2543 | if (const auto *parent{s->owner().GetDerivedTypeParent()}) { |
2544 | s = parent->FindComponent(sym->name()); |
2545 | } else { |
2546 | s = nullptr; |
2547 | } |
2548 | } |
2549 | if (s && !s->attrs().test(semantics::Attr::PRIVATE)) { |
2550 | // The latest override in the same module as the binding |
2551 | // is public, so it can be overridden. |
2552 | } else { |
2553 | latest = s; |
2554 | } |
2555 | } |
2556 | if (latest) { |
2557 | sym = latest; |
2558 | } |
2559 | } |
2560 | sc.component.symbol = const_cast<Symbol *>(sym); |
2561 | } |
2562 | std::optional<DataRef> dataRef{ExtractDataRef(std::move(*dtExpr))}; |
2563 | if (dataRef && !CheckDataRef(*dataRef)) { |
2564 | return std::nullopt; |
2565 | } |
2566 | if (dataRef && dataRef->Rank() > 0) { |
2567 | if (sym->has<semantics::ProcBindingDetails>() && |
2568 | sym->attrs().test(semantics::Attr::NOPASS)) { |
2569 | // F'2023 C1529 seems unnecessary and most compilers don't |
2570 | // enforce it. |
2571 | AttachDeclaration( |
2572 | Warn(common::LanguageFeature::NopassScalarBase, |
2573 | sc.component.source, |
2574 | "Base of NOPASS type-bound procedure reference should be scalar"_port_en_US), |
2575 | *sym); |
2576 | } else if (IsProcedurePointer(*sym)) { // C919 |
2577 | Say(sc.component.source, |
2578 | "Base of procedure component reference must be scalar"_err_en_US); |
2579 | } |
2580 | } |
2581 | if (const Symbol *resolution{ |
2582 | GetBindingResolution(dtExpr->GetType(), *sym)}) { |
2583 | AddPassArg(arguments, std::move(*dtExpr), *sym, false); |
2584 | return CalleeAndArguments{ |
2585 | ProcedureDesignator{*resolution}, std::move(arguments)}; |
2586 | } else if (dataRef.has_value()) { |
2587 | if (ExtractCoarrayRef(*dataRef)) { |
2588 | if (IsProcedurePointer(*sym)) { |
2589 | Say(sc.component.source, |
2590 | "Base of procedure component reference may not be coindexed"_err_en_US); |
2591 | } else { |
2592 | Say(sc.component.source, |
2593 | "A procedure binding may not be coindexed unless it can be resolved at compilation time"_err_en_US); |
2594 | } |
2595 | } |
2596 | if (sym->attrs().test(semantics::Attr::NOPASS)) { |
2597 | const auto *dtSpec{GetDerivedTypeSpec(dtExpr->GetType())}; |
2598 | if (dtSpec && dtSpec->scope()) { |
2599 | if (auto component{CreateComponent(std::move(*dataRef), *sym, |
2600 | *dtSpec->scope(), /*C919bAlreadyEnforced=*/true)}) { |
2601 | return CalleeAndArguments{ |
2602 | ProcedureDesignator{std::move(*component)}, |
2603 | std::move(arguments)}; |
2604 | } |
2605 | } |
2606 | Say(sc.component.source, |
2607 | "Component is not in scope of base derived type"_err_en_US); |
2608 | return std::nullopt; |
2609 | } else { |
2610 | AddPassArg(arguments, |
2611 | Expr<SomeDerived>{Designator<SomeDerived>{std::move(*dataRef)}}, |
2612 | *sym); |
2613 | return CalleeAndArguments{ |
2614 | ProcedureDesignator{*sym}, std::move(arguments)}; |
2615 | } |
2616 | } |
2617 | } |
2618 | Say(sc.component.source, |
2619 | "Base of procedure component reference is not a derived-type object"_err_en_US); |
2620 | } |
2621 | } |
2622 | CHECK(context_.AnyFatalError()); |
2623 | return std::nullopt; |
2624 | } |
2625 | |
2626 | // Can actual be argument associated with dummy? |
2627 | static bool CheckCompatibleArgument(bool isElemental, |
2628 | const ActualArgument &actual, const characteristics::DummyArgument &dummy, |
2629 | FoldingContext &foldingContext) { |
2630 | const auto *expr{actual.UnwrapExpr()}; |
2631 | return common::visit( |
2632 | common::visitors{ |
2633 | [&](const characteristics::DummyDataObject &x) { |
2634 | if ((x.attrs.test( |
2635 | characteristics::DummyDataObject::Attr::Pointer) || |
2636 | x.attrs.test( |
2637 | characteristics::DummyDataObject::Attr::Allocatable)) && |
2638 | IsBareNullPointer(expr)) { |
2639 | // NULL() without MOLD= is compatible with any dummy data pointer |
2640 | // or allocatable, but cannot be allowed to lead to ambiguity. |
2641 | return true; |
2642 | } else if (!isElemental && actual.Rank() != x.type.Rank() && |
2643 | !x.type.attrs().test( |
2644 | characteristics::TypeAndShape::Attr::AssumedRank) && |
2645 | !x.ignoreTKR.test(common::IgnoreTKR::Rank)) { |
2646 | return false; |
2647 | } else if (auto actualType{actual.GetType()}) { |
2648 | return x.type.type().IsTkCompatibleWith(*actualType, x.ignoreTKR); |
2649 | } |
2650 | return false; |
2651 | }, |
2652 | [&](const characteristics::DummyProcedure &dummy) { |
2653 | if ((dummy.attrs.test( |
2654 | characteristics::DummyProcedure::Attr::Optional) || |
2655 | dummy.attrs.test( |
2656 | characteristics::DummyProcedure::Attr::Pointer)) && |
2657 | IsBareNullPointer(expr)) { |
2658 | // NULL() is compatible with any dummy pointer |
2659 | // or optional dummy procedure. |
2660 | return true; |
2661 | } |
2662 | if (!expr || !IsProcedurePointerTarget(*expr)) { |
2663 | return false; |
2664 | } |
2665 | if (auto actualProc{characteristics::Procedure::Characterize( |
2666 | *expr, foldingContext)}) { |
2667 | const auto &dummyResult{dummy.procedure.value().functionResult}; |
2668 | const auto *dummyTypeAndShape{ |
2669 | dummyResult ? dummyResult->GetTypeAndShape() : nullptr}; |
2670 | const auto &actualResult{actualProc->functionResult}; |
2671 | const auto *actualTypeAndShape{ |
2672 | actualResult ? actualResult->GetTypeAndShape() : nullptr}; |
2673 | if (dummyTypeAndShape && actualTypeAndShape) { |
2674 | // Return false when the function results' types are both |
2675 | // known and not compatible. |
2676 | return actualTypeAndShape->type().IsTkCompatibleWith( |
2677 | dummyTypeAndShape->type()); |
2678 | } |
2679 | } |
2680 | return true; |
2681 | }, |
2682 | [&](const characteristics::AlternateReturn &) { |
2683 | return actual.isAlternateReturn(); |
2684 | }, |
2685 | }, |
2686 | dummy.u); |
2687 | } |
2688 | |
2689 | // Are the actual arguments compatible with the dummy arguments of procedure? |
2690 | static bool CheckCompatibleArguments( |
2691 | const characteristics::Procedure &procedure, const ActualArguments &actuals, |
2692 | FoldingContext &foldingContext) { |
2693 | bool isElemental{procedure.IsElemental()}; |
2694 | const auto &dummies{procedure.dummyArguments}; |
2695 | CHECK(dummies.size() == actuals.size()); |
2696 | for (std::size_t i{0}; i < dummies.size(); ++i) { |
2697 | const characteristics::DummyArgument &dummy{dummies[i]}; |
2698 | const std::optional<ActualArgument> &actual{actuals[i]}; |
2699 | if (actual && |
2700 | !CheckCompatibleArgument(isElemental, *actual, dummy, foldingContext)) { |
2701 | return false; |
2702 | } |
2703 | } |
2704 | return true; |
2705 | } |
2706 | |
2707 | static constexpr int cudaInfMatchingValue{std::numeric_limits<int>::max()}; |
2708 | |
2709 | // Compute the matching distance as described in section 3.2.3 of the CUDA |
2710 | // Fortran references. |
2711 | static int GetMatchingDistance(const common::LanguageFeatureControl &features, |
2712 | const characteristics::DummyArgument &dummy, |
2713 | const std::optional<ActualArgument> &actual) { |
2714 | bool isCudaManaged{features.IsEnabled(common::LanguageFeature::CudaManaged)}; |
2715 | bool isCudaUnified{features.IsEnabled(common::LanguageFeature::CudaUnified)}; |
2716 | CHECK(!(isCudaUnified && isCudaManaged) && "expect only one enabled."); |
2717 | |
2718 | std::optional<common::CUDADataAttr> actualDataAttr, dummyDataAttr; |
2719 | if (actual) { |
2720 | if (auto *expr{actual->UnwrapExpr()}) { |
2721 | const auto *actualLastSymbol{evaluate::GetLastSymbol(*expr)}; |
2722 | if (actualLastSymbol) { |
2723 | actualLastSymbol = &semantics::ResolveAssociations(*actualLastSymbol); |
2724 | if (const auto *actualObject{actualLastSymbol |
2725 | ? actualLastSymbol |
2726 | ->detailsIf<semantics::ObjectEntityDetails>() |
2727 | : nullptr}) { |
2728 | actualDataAttr = actualObject->cudaDataAttr(); |
2729 | } |
2730 | } |
2731 | } |
2732 | } |
2733 | |
2734 | common::visit(common::visitors{ |
2735 | [&](const characteristics::DummyDataObject &object) { |
2736 | dummyDataAttr = object.cudaDataAttr; |
2737 | }, |
2738 | [&](const auto &) {}, |
2739 | }, |
2740 | dummy.u); |
2741 | |
2742 | if (!dummyDataAttr) { |
2743 | if (!actualDataAttr) { |
2744 | if (isCudaUnified || isCudaManaged) { |
2745 | return 3; |
2746 | } |
2747 | return 0; |
2748 | } else if (*actualDataAttr == common::CUDADataAttr::Device) { |
2749 | return cudaInfMatchingValue; |
2750 | } else if (*actualDataAttr == common::CUDADataAttr::Managed || |
2751 | *actualDataAttr == common::CUDADataAttr::Unified) { |
2752 | return 3; |
2753 | } |
2754 | } else if (*dummyDataAttr == common::CUDADataAttr::Device) { |
2755 | if (!actualDataAttr) { |
2756 | if (isCudaUnified || isCudaManaged) { |
2757 | return 2; |
2758 | } |
2759 | return cudaInfMatchingValue; |
2760 | } else if (*actualDataAttr == common::CUDADataAttr::Device) { |
2761 | return 0; |
2762 | } else if (*actualDataAttr == common::CUDADataAttr::Managed || |
2763 | *actualDataAttr == common::CUDADataAttr::Unified) { |
2764 | return 2; |
2765 | } |
2766 | } else if (*dummyDataAttr == common::CUDADataAttr::Managed) { |
2767 | if (!actualDataAttr) { |
2768 | return isCudaUnified ? 1 : isCudaManaged ? 0 : cudaInfMatchingValue; |
2769 | } |
2770 | if (*actualDataAttr == common::CUDADataAttr::Device) { |
2771 | return cudaInfMatchingValue; |
2772 | } else if (*actualDataAttr == common::CUDADataAttr::Managed) { |
2773 | return 0; |
2774 | } else if (*actualDataAttr == common::CUDADataAttr::Unified) { |
2775 | return 1; |
2776 | } |
2777 | } else if (*dummyDataAttr == common::CUDADataAttr::Unified) { |
2778 | if (!actualDataAttr) { |
2779 | return isCudaUnified ? 0 : isCudaManaged ? 1 : cudaInfMatchingValue; |
2780 | } |
2781 | if (*actualDataAttr == common::CUDADataAttr::Device) { |
2782 | return cudaInfMatchingValue; |
2783 | } else if (*actualDataAttr == common::CUDADataAttr::Managed) { |
2784 | return 1; |
2785 | } else if (*actualDataAttr == common::CUDADataAttr::Unified) { |
2786 | return 0; |
2787 | } |
2788 | } |
2789 | return cudaInfMatchingValue; |
2790 | } |
2791 | |
2792 | static int ComputeCudaMatchingDistance( |
2793 | const common::LanguageFeatureControl &features, |
2794 | const characteristics::Procedure &procedure, |
2795 | const ActualArguments &actuals) { |
2796 | const auto &dummies{procedure.dummyArguments}; |
2797 | CHECK(dummies.size() == actuals.size()); |
2798 | int distance{0}; |
2799 | for (std::size_t i{0}; i < dummies.size(); ++i) { |
2800 | const characteristics::DummyArgument &dummy{dummies[i]}; |
2801 | const std::optional<ActualArgument> &actual{actuals[i]}; |
2802 | int d{GetMatchingDistance(features, dummy, actual)}; |
2803 | if (d == cudaInfMatchingValue) |
2804 | return d; |
2805 | distance += d; |
2806 | } |
2807 | return distance; |
2808 | } |
2809 | |
2810 | // Handles a forward reference to a module function from what must |
2811 | // be a specification expression. Return false if the symbol is |
2812 | // an invalid forward reference. |
2813 | const Symbol *ExpressionAnalyzer::ResolveForward(const Symbol &symbol) { |
2814 | if (context_.HasError(symbol)) { |
2815 | return nullptr; |
2816 | } |
2817 | if (const auto *details{ |
2818 | symbol.detailsIf<semantics::SubprogramNameDetails>()}) { |
2819 | if (details->kind() == semantics::SubprogramKind::Module) { |
2820 | // If this symbol is still a SubprogramNameDetails, we must be |
2821 | // checking a specification expression in a sibling module |
2822 | // procedure. Resolve its names now so that its interface |
2823 | // is known. |
2824 | const semantics::Scope &scope{symbol.owner()}; |
2825 | semantics::ResolveSpecificationParts(context_, symbol); |
2826 | const Symbol *resolved{nullptr}; |
2827 | if (auto iter{scope.find(symbol.name())}; iter != scope.cend()) { |
2828 | resolved = &*iter->second; |
2829 | } |
2830 | if (!resolved || resolved->has<semantics::SubprogramNameDetails>()) { |
2831 | // When the symbol hasn't had its details updated, we must have |
2832 | // already been in the process of resolving the function's |
2833 | // specification part; but recursive function calls are not |
2834 | // allowed in specification parts (10.1.11 para 5). |
2835 | Say("The module function '%s' may not be referenced recursively in a specification expression"_err_en_US, |
2836 | symbol.name()); |
2837 | context_.SetError(symbol); |
2838 | } |
2839 | return resolved; |
2840 | } else if (inStmtFunctionDefinition_) { |
2841 | semantics::ResolveSpecificationParts(context_, symbol); |
2842 | CHECK(symbol.has<semantics::SubprogramDetails>()); |
2843 | } else { // 10.1.11 para 4 |
2844 | Say("The internal function '%s' may not be referenced in a specification expression"_err_en_US, |
2845 | symbol.name()); |
2846 | context_.SetError(symbol); |
2847 | return nullptr; |
2848 | } |
2849 | } |
2850 | return &symbol; |
2851 | } |
2852 | |
2853 | // Resolve a call to a generic procedure with given actual arguments. |
2854 | // adjustActuals is called on procedure bindings to handle pass arg. |
2855 | std::pair<const Symbol *, bool> ExpressionAnalyzer::ResolveGeneric( |
2856 | const Symbol &symbol, const ActualArguments &actuals, |
2857 | const AdjustActuals &adjustActuals, bool isSubroutine, |
2858 | bool mightBeStructureConstructor) { |
2859 | const Symbol &ultimate{symbol.GetUltimate()}; |
2860 | // Check for a match with an explicit INTRINSIC |
2861 | const Symbol *explicitIntrinsic{nullptr}; |
2862 | if (ultimate.attrs().test(semantics::Attr::INTRINSIC)) { |
2863 | parser::Messages buffer; |
2864 | auto restorer{GetContextualMessages().SetMessages(buffer)}; |
2865 | ActualArguments localActuals{actuals}; |
2866 | if (context_.intrinsics().Probe( |
2867 | CallCharacteristics{ultimate.name().ToString(), isSubroutine}, |
2868 | localActuals, foldingContext_) && |
2869 | !buffer.AnyFatalError()) { |
2870 | explicitIntrinsic = &ultimate; |
2871 | } |
2872 | } |
2873 | const Symbol *elemental{nullptr}; // matching elemental specific proc |
2874 | const Symbol *nonElemental{nullptr}; // matching non-elemental specific |
2875 | const auto *genericDetails{ultimate.detailsIf<semantics::GenericDetails>()}; |
2876 | if (genericDetails && !explicitIntrinsic) { |
2877 | int crtMatchingDistance{cudaInfMatchingValue}; |
2878 | for (const Symbol &specific0 : genericDetails->specificProcs()) { |
2879 | const Symbol &specific1{BypassGeneric(specific0)}; |
2880 | if (isSubroutine != !IsFunction(specific1)) { |
2881 | continue; |
2882 | } |
2883 | const Symbol *specific{ResolveForward(specific1)}; |
2884 | if (!specific) { |
2885 | continue; |
2886 | } |
2887 | if (std::optional<characteristics::Procedure> procedure{ |
2888 | characteristics::Procedure::Characterize( |
2889 | ProcedureDesignator{*specific}, context_.foldingContext(), |
2890 | /*emitError=*/false)}) { |
2891 | ActualArguments localActuals{actuals}; |
2892 | if (specific->has<semantics::ProcBindingDetails>()) { |
2893 | if (!adjustActuals.value()(*specific, localActuals)) { |
2894 | continue; |
2895 | } |
2896 | } |
2897 | if (semantics::CheckInterfaceForGeneric(*procedure, localActuals, |
2898 | context_, false /* no integer conversions */) && |
2899 | CheckCompatibleArguments( |
2900 | *procedure, localActuals, foldingContext_)) { |
2901 | if ((procedure->IsElemental() && elemental) || |
2902 | (!procedure->IsElemental() && nonElemental)) { |
2903 | int d{ComputeCudaMatchingDistance( |
2904 | context_.languageFeatures(), *procedure, localActuals)}; |
2905 | if (d != crtMatchingDistance) { |
2906 | if (d > crtMatchingDistance) { |
2907 | continue; |
2908 | } |
2909 | // Matching distance is smaller than the previously matched |
2910 | // specific. Let it go through so the current procedure is picked. |
2911 | } else { |
2912 | // 16.9.144(6): a bare NULL() is not allowed as an actual |
2913 | // argument to a generic procedure if the specific procedure |
2914 | // cannot be unambiguously distinguished |
2915 | // Underspecified external procedure actual arguments can |
2916 | // also lead to ambiguity. |
2917 | return {nullptr, true /* due to ambiguity */}; |
2918 | } |
2919 | } |
2920 | if (!procedure->IsElemental()) { |
2921 | // takes priority over elemental match |
2922 | nonElemental = specific; |
2923 | } else { |
2924 | elemental = specific; |
2925 | } |
2926 | crtMatchingDistance = ComputeCudaMatchingDistance( |
2927 | context_.languageFeatures(), *procedure, localActuals); |
2928 | } |
2929 | } |
2930 | } |
2931 | } |
2932 | // Is there a derived type of the same name? |
2933 | const Symbol *derivedType{nullptr}; |
2934 | if (mightBeStructureConstructor && !isSubroutine && genericDetails) { |
2935 | if (const Symbol * dt{genericDetails->derivedType()}) { |
2936 | const Symbol &ultimate{dt->GetUltimate()}; |
2937 | if (ultimate.has<semantics::DerivedTypeDetails>()) { |
2938 | derivedType = &ultimate; |
2939 | } |
2940 | } |
2941 | } |
2942 | // F'2023 C7108 checking. No Fortran compiler actually enforces this |
2943 | // constraint, so it's just a portability warning here. |
2944 | if (derivedType && (explicitIntrinsic || nonElemental || elemental) && |
2945 | context_.ShouldWarn( |
2946 | common::LanguageFeature::AmbiguousStructureConstructor)) { |
2947 | // See whethr there's ambiguity with a structure constructor. |
2948 | bool possiblyAmbiguous{true}; |
2949 | if (const semantics::Scope * dtScope{derivedType->scope()}) { |
2950 | parser::Messages buffer; |
2951 | auto restorer{GetContextualMessages().SetMessages(buffer)}; |
2952 | std::list<ComponentSpec> componentSpecs; |
2953 | for (const auto &actual : actuals) { |
2954 | if (actual) { |
2955 | ComponentSpec compSpec; |
2956 | if (const Expr<SomeType> *expr{actual->UnwrapExpr()}) { |
2957 | compSpec.expr = *expr; |
2958 | } else { |
2959 | possiblyAmbiguous = false; |
2960 | } |
2961 | if (auto loc{actual->sourceLocation()}) { |
2962 | compSpec.source = compSpec.exprSource = *loc; |
2963 | } |
2964 | if (auto kw{actual->keyword()}) { |
2965 | compSpec.hasKeyword = true; |
2966 | compSpec.keywordSymbol = dtScope->FindComponent(*kw); |
2967 | } |
2968 | componentSpecs.emplace_back(std::move(compSpec)); |
2969 | } else { |
2970 | possiblyAmbiguous = false; |
2971 | } |
2972 | } |
2973 | semantics::DerivedTypeSpec dtSpec{derivedType->name(), *derivedType}; |
2974 | dtSpec.set_scope(*dtScope); |
2975 | possiblyAmbiguous = possiblyAmbiguous && |
2976 | CheckStructureConstructor( |
2977 | derivedType->name(), dtSpec, std::move(componentSpecs)) |
2978 | .has_value() && |
2979 | !buffer.AnyFatalError(); |
2980 | } |
2981 | if (possiblyAmbiguous) { |
2982 | if (explicitIntrinsic) { |
2983 | Warn(common::LanguageFeature::AmbiguousStructureConstructor, |
2984 | "Reference to the intrinsic function '%s' is ambiguous with a structure constructor of the same name"_port_en_US, |
2985 | symbol.name()); |
2986 | } else { |
2987 | Warn(common::LanguageFeature::AmbiguousStructureConstructor, |
2988 | "Reference to generic function '%s' (resolving to specific '%s') is ambiguous with a structure constructor of the same name"_port_en_US, |
2989 | symbol.name(), |
2990 | nonElemental ? nonElemental->name() : elemental->name()); |
2991 | } |
2992 | } |
2993 | } |
2994 | // Return the right resolution, if there is one. Explicit intrinsics |
2995 | // are preferred, then non-elements specifics, then elementals, and |
2996 | // lastly structure constructors. |
2997 | if (explicitIntrinsic) { |
2998 | return {explicitIntrinsic, false}; |
2999 | } else if (nonElemental) { |
3000 | return {&AccessSpecific(symbol, *nonElemental), false}; |
3001 | } else if (elemental) { |
3002 | return {&AccessSpecific(symbol, *elemental), false}; |
3003 | } |
3004 | // Check parent derived type |
3005 | if (const auto *parentScope{symbol.owner().GetDerivedTypeParent()}) { |
3006 | if (const Symbol * extended{parentScope->FindComponent(symbol.name())}) { |
3007 | auto pair{ResolveGeneric( |
3008 | *extended, actuals, adjustActuals, isSubroutine, false)}; |
3009 | if (pair.first) { |
3010 | return pair; |
3011 | } |
3012 | } |
3013 | } |
3014 | // Structure constructor? |
3015 | if (derivedType) { |
3016 | return {derivedType, false}; |
3017 | } |
3018 | // Check for generic or explicit INTRINSIC of the same name in outer scopes. |
3019 | // See 15.5.5.2 for details. |
3020 | if (!symbol.owner().IsGlobal() && !symbol.owner().IsDerivedType()) { |
3021 | if (const Symbol * |
3022 | outer{symbol.owner().parent().FindSymbol(symbol.name())}) { |
3023 | auto pair{ResolveGeneric(*outer, actuals, adjustActuals, isSubroutine, |
3024 | mightBeStructureConstructor)}; |
3025 | if (pair.first) { |
3026 | return pair; |
3027 | } |
3028 | } |
3029 | } |
3030 | return {nullptr, false}; |
3031 | } |
3032 | |
3033 | const Symbol &ExpressionAnalyzer::AccessSpecific( |
3034 | const Symbol &originalGeneric, const Symbol &specific) { |
3035 | if (const auto *hosted{ |
3036 | originalGeneric.detailsIf<semantics::HostAssocDetails>()}) { |
3037 | return AccessSpecific(hosted->symbol(), specific); |
3038 | } else if (const auto *used{ |
3039 | originalGeneric.detailsIf<semantics::UseDetails>()}) { |
3040 | const auto &scope{originalGeneric.owner()}; |
3041 | if (auto iter{scope.find(specific.name())}; iter != scope.end()) { |
3042 | if (const auto *useDetails{ |
3043 | iter->second->detailsIf<semantics::UseDetails>()}) { |
3044 | const Symbol &usedSymbol{useDetails->symbol()}; |
3045 | const auto *usedGeneric{ |
3046 | usedSymbol.detailsIf<semantics::GenericDetails>()}; |
3047 | if (&usedSymbol == &specific || |
3048 | (usedGeneric && usedGeneric->specific() == &specific)) { |
3049 | return specific; |
3050 | } |
3051 | } |
3052 | } |
3053 | // Create a renaming USE of the specific procedure. |
3054 | auto rename{context_.SaveTempName( |
3055 | used->symbol().owner().GetName().value().ToString() + "$"+ |
3056 | specific.owner().GetName().value().ToString() + "$"+ |
3057 | specific.name().ToString())}; |
3058 | return *const_cast<semantics::Scope &>(scope) |
3059 | .try_emplace(rename, specific.attrs(), |
3060 | semantics::UseDetails{rename, specific}) |
3061 | .first->second; |
3062 | } else { |
3063 | return specific; |
3064 | } |
3065 | } |
3066 | |
3067 | void ExpressionAnalyzer::EmitGenericResolutionError( |
3068 | const Symbol &symbol, bool dueToAmbiguity, bool isSubroutine) { |
3069 | Say(dueToAmbiguity |
3070 | ? "The actual arguments to the generic procedure '%s' matched multiple specific procedures, perhaps due to use of NULL() without MOLD= or an actual procedure with an implicit interface"_err_en_US |
3071 | : semantics::IsGenericDefinedOp(symbol) |
3072 | ? "No specific procedure of generic operator '%s' matches the actual arguments"_err_en_US |
3073 | : isSubroutine |
3074 | ? "No specific subroutine of generic '%s' matches the actual arguments"_err_en_US |
3075 | : "No specific function of generic '%s' matches the actual arguments"_err_en_US, |
3076 | symbol.name()); |
3077 | } |
3078 | |
3079 | auto ExpressionAnalyzer::GetCalleeAndArguments( |
3080 | const parser::ProcedureDesignator &pd, ActualArguments &&arguments, |
3081 | bool isSubroutine, bool mightBeStructureConstructor) |
3082 | -> std::optional<CalleeAndArguments> { |
3083 | return common::visit(common::visitors{ |
3084 | [&](const parser::Name &name) { |
3085 | return GetCalleeAndArguments(name, |
3086 | std::move(arguments), isSubroutine, |
3087 | mightBeStructureConstructor); |
3088 | }, |
3089 | [&](const parser::ProcComponentRef &pcr) { |
3090 | return AnalyzeProcedureComponentRef( |
3091 | pcr, std::move(arguments), isSubroutine); |
3092 | }, |
3093 | }, |
3094 | pd.u); |
3095 | } |
3096 | |
3097 | auto ExpressionAnalyzer::GetCalleeAndArguments(const parser::Name &name, |
3098 | ActualArguments &&arguments, bool isSubroutine, |
3099 | bool mightBeStructureConstructor) -> std::optional<CalleeAndArguments> { |
3100 | const Symbol *symbol{name.symbol}; |
3101 | if (context_.HasError(symbol)) { |
3102 | return std::nullopt; // also handles null symbol |
3103 | } |
3104 | symbol = ResolveForward(*symbol); |
3105 | if (!symbol) { |
3106 | return std::nullopt; |
3107 | } |
3108 | name.symbol = const_cast<Symbol *>(symbol); |
3109 | const Symbol &ultimate{symbol->GetUltimate()}; |
3110 | CheckForBadRecursion(name.source, ultimate); |
3111 | bool dueToAmbiguity{false}; |
3112 | bool isGenericInterface{ultimate.has<semantics::GenericDetails>()}; |
3113 | bool isExplicitIntrinsic{ultimate.attrs().test(semantics::Attr::INTRINSIC)}; |
3114 | const Symbol *resolution{nullptr}; |
3115 | if (isGenericInterface || isExplicitIntrinsic) { |
3116 | ExpressionAnalyzer::AdjustActuals noAdjustment; |
3117 | auto pair{ResolveGeneric(*symbol, arguments, noAdjustment, isSubroutine, |
3118 | mightBeStructureConstructor)}; |
3119 | resolution = pair.first; |
3120 | dueToAmbiguity = pair.second; |
3121 | if (resolution) { |
3122 | if (context_.GetPPCBuiltinsScope() && |
3123 | resolution->name().ToString().rfind("__ppc_", 0) == 0) { |
3124 | semantics::CheckPPCIntrinsic( |
3125 | *symbol, *resolution, arguments, GetFoldingContext()); |
3126 | } |
3127 | // re-resolve name to the specific procedure |
3128 | name.symbol = const_cast<Symbol *>(resolution); |
3129 | } |
3130 | } else if (IsProcedure(ultimate) && |
3131 | ultimate.attrs().test(semantics::Attr::ABSTRACT)) { |
3132 | Say("Abstract procedure interface '%s' may not be referenced"_err_en_US, |
3133 | name.source); |
3134 | } else { |
3135 | resolution = symbol; |
3136 | } |
3137 | if (resolution && context_.targetCharacteristics().isOSWindows()) { |
3138 | semantics::CheckWindowsIntrinsic(*resolution, GetFoldingContext()); |
3139 | } |
3140 | if (!resolution || resolution->attrs().test(semantics::Attr::INTRINSIC)) { |
3141 | auto name{resolution ? resolution->name() : ultimate.name()}; |
3142 | if (std::optional<SpecificCall> specificCall{context_.intrinsics().Probe( |
3143 | CallCharacteristics{name.ToString(), isSubroutine}, arguments, |
3144 | GetFoldingContext())}) { |
3145 | CheckBadExplicitType(*specificCall, *symbol); |
3146 | return CalleeAndArguments{ |
3147 | ProcedureDesignator{std::move(specificCall->specificIntrinsic)}, |
3148 | std::move(specificCall->arguments)}; |
3149 | } else { |
3150 | if (isGenericInterface) { |
3151 | EmitGenericResolutionError(*symbol, dueToAmbiguity, isSubroutine); |
3152 | } |
3153 | return std::nullopt; |
3154 | } |
3155 | } |
3156 | if (resolution->GetUltimate().has<semantics::DerivedTypeDetails>()) { |
3157 | if (mightBeStructureConstructor) { |
3158 | return CalleeAndArguments{ |
3159 | semantics::SymbolRef{*resolution}, std::move(arguments)}; |
3160 | } |
3161 | } else if (IsProcedure(*resolution)) { |
3162 | return CalleeAndArguments{ |
3163 | ProcedureDesignator{*resolution}, std::move(arguments)}; |
3164 | } |
3165 | if (!context_.HasError(*resolution)) { |
3166 | AttachDeclaration( |
3167 | Say(name.source, "'%s' is not a callable procedure"_err_en_US, |
3168 | name.source), |
3169 | *resolution); |
3170 | } |
3171 | return std::nullopt; |
3172 | } |
3173 | |
3174 | // Fortran 2018 expressly states (8.2 p3) that any declared type for a |
3175 | // generic intrinsic function "has no effect" on the result type of a |
3176 | // call to that intrinsic. So one can declare "character*8 cos" and |
3177 | // still get a real result from "cos(1.)". This is a dangerous feature, |
3178 | // especially since implementations are free to extend their sets of |
3179 | // intrinsics, and in doing so might clash with a name in a program. |
3180 | // So we emit a warning in this situation, and perhaps it should be an |
3181 | // error -- any correctly working program can silence the message by |
3182 | // simply deleting the pointless type declaration. |
3183 | void ExpressionAnalyzer::CheckBadExplicitType( |
3184 | const SpecificCall &call, const Symbol &intrinsic) { |
3185 | if (intrinsic.GetUltimate().GetType()) { |
3186 | const auto &procedure{call.specificIntrinsic.characteristics.value()}; |
3187 | if (const auto &result{procedure.functionResult}) { |
3188 | if (const auto *typeAndShape{result->GetTypeAndShape()}) { |
3189 | if (auto declared{ |
3190 | typeAndShape->Characterize(intrinsic, GetFoldingContext())}) { |
3191 | if (!declared->type().IsTkCompatibleWith(typeAndShape->type())) { |
3192 | if (auto *msg{Warn( |
3193 | common::UsageWarning::IgnoredIntrinsicFunctionType, |
3194 | "The result type '%s' of the intrinsic function '%s' is not the explicit declared type '%s'"_warn_en_US, |
3195 | typeAndShape->AsFortran(), intrinsic.name(), |
3196 | declared->AsFortran())}) { |
3197 | msg->Attach(intrinsic.name(), |
3198 | "Ignored declaration of intrinsic function '%s'"_en_US, |
3199 | intrinsic.name()); |
3200 | } |
3201 | } |
3202 | } |
3203 | } |
3204 | } |
3205 | } |
3206 | } |
3207 | |
3208 | void ExpressionAnalyzer::CheckForBadRecursion( |
3209 | parser::CharBlock callSite, const semantics::Symbol &proc) { |
3210 | if (const auto *scope{proc.scope()}) { |
3211 | if (scope->sourceRange().Contains(callSite)) { |
3212 | parser::Message *msg{nullptr}; |
3213 | if (proc.attrs().test(semantics::Attr::NON_RECURSIVE)) { // 15.6.2.1(3) |
3214 | msg = Say("NON_RECURSIVE procedure '%s' cannot call itself"_err_en_US, |
3215 | callSite); |
3216 | } else if (IsAssumedLengthCharacter(proc) && IsExternal(proc)) { |
3217 | // TODO: Also catch assumed PDT type parameters |
3218 | msg = Say( // 15.6.2.1(3) |
3219 | "Assumed-length CHARACTER(*) function '%s' cannot call itself"_err_en_US, |
3220 | callSite); |
3221 | } else if (FindCUDADeviceContext(scope)) { |
3222 | msg = Say( |
3223 | "Device subprogram '%s' cannot call itself"_err_en_US, callSite); |
3224 | } |
3225 | AttachDeclaration(msg, proc); |
3226 | } |
3227 | } |
3228 | } |
3229 | |
3230 | template <typename A> static const Symbol *AssumedTypeDummy(const A &x) { |
3231 | if (const auto *designator{ |
3232 | std::get_if<common::Indirection<parser::Designator>>(&x.u)}) { |
3233 | if (const auto *dataRef{ |
3234 | std::get_if<parser::DataRef>(&designator->value().u)}) { |
3235 | if (const auto *name{std::get_if<parser::Name>(&dataRef->u)}) { |
3236 | return AssumedTypeDummy(*name); |
3237 | } |
3238 | } |
3239 | } |
3240 | return nullptr; |
3241 | } |
3242 | template <> |
3243 | const Symbol *AssumedTypeDummy<parser::Name>(const parser::Name &name) { |
3244 | if (const Symbol *symbol{name.symbol}) { |
3245 | if (const auto *type{symbol->GetType()}) { |
3246 | if (type->category() == semantics::DeclTypeSpec::TypeStar) { |
3247 | return symbol; |
3248 | } |
3249 | } |
3250 | } |
3251 | return nullptr; |
3252 | } |
3253 | template <typename A> |
3254 | static const Symbol *AssumedTypePointerOrAllocatableDummy(const A &object) { |
3255 | // It is illegal for allocatable of pointer objects to be TYPE(*), but at that |
3256 | // point it is not guaranteed that it has been checked the object has |
3257 | // POINTER or ALLOCATABLE attribute, so do not assume nullptr can be directly |
3258 | // returned. |
3259 | return common::visit( |
3260 | common::visitors{ |
3261 | [&](const parser::StructureComponent &x) { |
3262 | return AssumedTypeDummy(x.component); |
3263 | }, |
3264 | [&](const parser::Name &x) { return AssumedTypeDummy(x); }, |
3265 | }, |
3266 | object.u); |
3267 | } |
3268 | template <> |
3269 | const Symbol *AssumedTypeDummy<parser::AllocateObject>( |
3270 | const parser::AllocateObject &x) { |
3271 | return AssumedTypePointerOrAllocatableDummy(x); |
3272 | } |
3273 | template <> |
3274 | const Symbol *AssumedTypeDummy<parser::PointerObject>( |
3275 | const parser::PointerObject &x) { |
3276 | return AssumedTypePointerOrAllocatableDummy(x); |
3277 | } |
3278 | |
3279 | bool ExpressionAnalyzer::CheckIsValidForwardReference( |
3280 | const semantics::DerivedTypeSpec &dtSpec) { |
3281 | if (dtSpec.IsForwardReferenced()) { |
3282 | Say("Cannot construct value for derived type '%s' before it is defined"_err_en_US, |
3283 | dtSpec.name()); |
3284 | return false; |
3285 | } |
3286 | return true; |
3287 | } |
3288 | |
3289 | std::optional<Chevrons> ExpressionAnalyzer::AnalyzeChevrons( |
3290 | const parser::CallStmt &call) { |
3291 | Chevrons result; |
3292 | auto checkLaunchArg{[&](const Expr<SomeType> &expr, const char *which) { |
3293 | if (auto dyType{expr.GetType()}) { |
3294 | if (dyType->category() == TypeCategory::Integer) { |
3295 | return true; |
3296 | } |
3297 | if (dyType->category() == TypeCategory::Derived && |
3298 | !dyType->IsPolymorphic() && |
3299 | IsBuiltinDerivedType(&dyType->GetDerivedTypeSpec(), "dim3")) { |
3300 | return true; |
3301 | } |
3302 | } |
3303 | Say("Kernel launch %s parameter must be either integer or TYPE(dim3)"_err_en_US, |
3304 | which); |
3305 | return false; |
3306 | }}; |
3307 | if (const auto &chevrons{call.chevrons}) { |
3308 | auto &starOrExpr{std::get<0>(chevrons->t)}; |
3309 | if (starOrExpr.v) { |
3310 | if (auto expr{Analyze(*starOrExpr.v)}; |
3311 | expr && checkLaunchArg(*expr, "grid")) { |
3312 | result.emplace_back(*expr); |
3313 | } else { |
3314 | return std::nullopt; |
3315 | } |
3316 | } else { |
3317 | result.emplace_back( |
3318 | AsGenericExpr(evaluate::Constant<evaluate::CInteger>{-1})); |
3319 | } |
3320 | if (auto expr{Analyze(std::get<1>(chevrons->t))}; |
3321 | expr && checkLaunchArg(*expr, "block")) { |
3322 | result.emplace_back(*expr); |
3323 | } else { |
3324 | return std::nullopt; |
3325 | } |
3326 | if (const auto &maybeExpr{std::get<2>(chevrons->t)}) { |
3327 | if (auto expr{Analyze(*maybeExpr)}) { |
3328 | result.emplace_back(*expr); |
3329 | } else { |
3330 | return std::nullopt; |
3331 | } |
3332 | } |
3333 | if (const auto &maybeExpr{std::get<3>(chevrons->t)}) { |
3334 | if (auto expr{Analyze(*maybeExpr)}) { |
3335 | result.emplace_back(*expr); |
3336 | } else { |
3337 | return std::nullopt; |
3338 | } |
3339 | } |
3340 | } |
3341 | return std::move(result); |
3342 | } |
3343 | |
3344 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::FunctionReference &funcRef, |
3345 | std::optional<parser::StructureConstructor> *structureConstructor) { |
3346 | const parser::Call &call{funcRef.v}; |
3347 | auto restorer{GetContextualMessages().SetLocation(funcRef.source)}; |
3348 | ArgumentAnalyzer analyzer{*this, funcRef.source, true /* isProcedureCall */}; |
3349 | for (const auto &arg : std::get<std::list<parser::ActualArgSpec>>(call.t)) { |
3350 | analyzer.Analyze(arg, false /* not subroutine call */); |
3351 | } |
3352 | if (analyzer.fatalErrors()) { |
3353 | return std::nullopt; |
3354 | } |
3355 | bool mightBeStructureConstructor{structureConstructor != nullptr}; |
3356 | if (std::optional<CalleeAndArguments> callee{GetCalleeAndArguments( |
3357 | std::get<parser::ProcedureDesignator>(call.t), analyzer.GetActuals(), |
3358 | false /* not subroutine */, mightBeStructureConstructor)}) { |
3359 | if (auto *proc{std::get_if<ProcedureDesignator>(&callee->u)}) { |
3360 | return MakeFunctionRef( |
3361 | funcRef.source, std::move(*proc), std::move(callee->arguments)); |
3362 | } |
3363 | CHECK(std::holds_alternative<semantics::SymbolRef>(callee->u)); |
3364 | const Symbol &symbol{*std::get<semantics::SymbolRef>(callee->u)}; |
3365 | if (mightBeStructureConstructor) { |
3366 | // Structure constructor misparsed as function reference? |
3367 | const auto &designator{std::get<parser::ProcedureDesignator>(call.t)}; |
3368 | if (const auto *name{std::get_if<parser::Name>(&designator.u)}) { |
3369 | semantics::Scope &scope{context_.FindScope(name->source)}; |
3370 | semantics::DerivedTypeSpec dtSpec{name->source, symbol}; |
3371 | if (!CheckIsValidForwardReference(dtSpec)) { |
3372 | return std::nullopt; |
3373 | } |
3374 | const semantics::DeclTypeSpec &type{ |
3375 | semantics::FindOrInstantiateDerivedType(scope, std::move(dtSpec))}; |
3376 | auto &mutableRef{const_cast<parser::FunctionReference &>(funcRef)}; |
3377 | *structureConstructor = |
3378 | mutableRef.ConvertToStructureConstructor(type.derivedTypeSpec()); |
3379 | // Don't use saved typed expressions left over from argument |
3380 | // analysis; they might not be valid structure components |
3381 | // (e.g., a TYPE(*) argument) |
3382 | auto restorer{DoNotUseSavedTypedExprs()}; |
3383 | return Analyze(structureConstructor->value()); |
3384 | } |
3385 | } |
3386 | if (!context_.HasError(symbol)) { |
3387 | AttachDeclaration( |
3388 | Say("'%s' is called like a function but is not a procedure"_err_en_US, |
3389 | symbol.name()), |
3390 | symbol); |
3391 | context_.SetError(symbol); |
3392 | } |
3393 | } |
3394 | return std::nullopt; |
3395 | } |
3396 | |
3397 | static bool HasAlternateReturns(const evaluate::ActualArguments &args) { |
3398 | for (const auto &arg : args) { |
3399 | if (arg && arg->isAlternateReturn()) { |
3400 | return true; |
3401 | } |
3402 | } |
3403 | return false; |
3404 | } |
3405 | |
3406 | void ExpressionAnalyzer::Analyze(const parser::CallStmt &callStmt) { |
3407 | const parser::Call &call{callStmt.call}; |
3408 | auto restorer{GetContextualMessages().SetLocation(callStmt.source)}; |
3409 | ArgumentAnalyzer analyzer{*this, callStmt.source, true /* isProcedureCall */}; |
3410 | const auto &actualArgList{std::get<std::list<parser::ActualArgSpec>>(call.t)}; |
3411 | for (const auto &arg : actualArgList) { |
3412 | analyzer.Analyze(arg, true /* is subroutine call */); |
3413 | } |
3414 | if (auto chevrons{AnalyzeChevrons(callStmt)}; |
3415 | chevrons && !analyzer.fatalErrors()) { |
3416 | if (std::optional<CalleeAndArguments> callee{ |
3417 | GetCalleeAndArguments(std::get<parser::ProcedureDesignator>(call.t), |
3418 | analyzer.GetActuals(), true /* subroutine */)}) { |
3419 | ProcedureDesignator *proc{std::get_if<ProcedureDesignator>(&callee->u)}; |
3420 | CHECK(proc); |
3421 | bool isKernel{false}; |
3422 | if (const Symbol * procSym{proc->GetSymbol()}) { |
3423 | const Symbol &ultimate{procSym->GetUltimate()}; |
3424 | if (const auto *subpDetails{ |
3425 | ultimate.detailsIf<semantics::SubprogramDetails>()}) { |
3426 | if (auto attrs{subpDetails->cudaSubprogramAttrs()}) { |
3427 | isKernel = *attrs == common::CUDASubprogramAttrs::Global || |
3428 | *attrs == common::CUDASubprogramAttrs::Grid_Global; |
3429 | } |
3430 | } else if (const auto *procDetails{ |
3431 | ultimate.detailsIf<semantics::ProcEntityDetails>()}) { |
3432 | isKernel = procDetails->isCUDAKernel(); |
3433 | } |
3434 | if (isKernel && chevrons->empty()) { |
3435 | Say("'%s' is a kernel subroutine and must be called with kernel launch parameters in chevrons"_err_en_US, |
3436 | procSym->name()); |
3437 | } |
3438 | } |
3439 | if (!isKernel && !chevrons->empty()) { |
3440 | Say("Kernel launch parameters in chevrons may not be used unless calling a kernel subroutine"_err_en_US); |
3441 | } |
3442 | if (CheckCall(callStmt.source, *proc, callee->arguments)) { |
3443 | callStmt.typedCall.Reset( |
3444 | new ProcedureRef{std::move(*proc), std::move(callee->arguments), |
3445 | HasAlternateReturns(callee->arguments)}, |
3446 | ProcedureRef::Deleter); |
3447 | DEREF(callStmt.typedCall.get()).set_chevrons(std::move(*chevrons)); |
3448 | return; |
3449 | } |
3450 | } |
3451 | if (!context_.AnyFatalError()) { |
3452 | std::string buf; |
3453 | llvm::raw_string_ostream dump{buf}; |
3454 | parser::DumpTree(dump, callStmt); |
3455 | Say("Internal error: Expression analysis failed on CALL statement: %s"_err_en_US, |
3456 | buf); |
3457 | } |
3458 | } |
3459 | } |
3460 | |
3461 | const Assignment *ExpressionAnalyzer::Analyze(const parser::AssignmentStmt &x) { |
3462 | if (!x.typedAssignment) { |
3463 | ArgumentAnalyzer analyzer{*this}; |
3464 | const auto &variable{std::get<parser::Variable>(x.t)}; |
3465 | analyzer.Analyze(variable); |
3466 | const auto &rhsExpr{std::get<parser::Expr>(x.t)}; |
3467 | analyzer.Analyze(rhsExpr); |
3468 | std::optional<Assignment> assignment; |
3469 | if (!analyzer.fatalErrors()) { |
3470 | auto restorer{GetContextualMessages().SetLocation(variable.GetSource())}; |
3471 | std::optional<ProcedureRef> procRef{analyzer.TryDefinedAssignment()}; |
3472 | if (!procRef) { |
3473 | analyzer.CheckForNullPointer( |
3474 | "in a non-pointer intrinsic assignment statement"); |
3475 | analyzer.CheckForAssumedRank("in an assignment statement"); |
3476 | const Expr<SomeType> &lhs{analyzer.GetExpr(0)}; |
3477 | if (auto dyType{lhs.GetType()}) { |
3478 | if (dyType->IsPolymorphic()) { // 10.2.1.2p1(1) |
3479 | const Symbol *lastWhole0{UnwrapWholeSymbolOrComponentDataRef(lhs)}; |
3480 | const Symbol *lastWhole{ |
3481 | lastWhole0 ? &ResolveAssociations(*lastWhole0) : nullptr}; |
3482 | if (!lastWhole || !IsAllocatable(*lastWhole)) { |
3483 | Say("Left-hand side of intrinsic assignment may not be polymorphic unless assignment is to an entire allocatable"_err_en_US); |
3484 | } else if (evaluate::IsCoarray(*lastWhole)) { |
3485 | Say("Left-hand side of intrinsic assignment may not be polymorphic if it is a coarray"_err_en_US); |
3486 | } |
3487 | } |
3488 | if (auto *derived{GetDerivedTypeSpec(*dyType)}) { |
3489 | if (auto iter{FindAllocatableUltimateComponent(*derived)}) { |
3490 | if (ExtractCoarrayRef(lhs)) { |
3491 | Say("Left-hand side of intrinsic assignment must not be coindexed due to allocatable ultimate component '%s'"_err_en_US, |
3492 | iter.BuildResultDesignatorName()); |
3493 | } |
3494 | } |
3495 | } |
3496 | } |
3497 | CheckForWholeAssumedSizeArray( |
3498 | rhsExpr.source, UnwrapWholeSymbolDataRef(analyzer.GetExpr(1))); |
3499 | } |
3500 | assignment.emplace(analyzer.MoveExpr(0), analyzer.MoveExpr(1)); |
3501 | if (procRef) { |
3502 | assignment->u = std::move(*procRef); |
3503 | } |
3504 | } |
3505 | x.typedAssignment.Reset(new GenericAssignmentWrapper{std::move(assignment)}, |
3506 | GenericAssignmentWrapper::Deleter); |
3507 | } |
3508 | return common::GetPtrFromOptional(x.typedAssignment->v); |
3509 | } |
3510 | |
3511 | const Assignment *ExpressionAnalyzer::Analyze( |
3512 | const parser::PointerAssignmentStmt &x) { |
3513 | if (!x.typedAssignment) { |
3514 | MaybeExpr lhs{Analyze(std::get<parser::DataRef>(x.t))}; |
3515 | MaybeExpr rhs; |
3516 | { |
3517 | auto restorer{AllowNullPointer()}; |
3518 | rhs = Analyze(std::get<parser::Expr>(x.t)); |
3519 | } |
3520 | if (!lhs || !rhs) { |
3521 | x.typedAssignment.Reset( |
3522 | new GenericAssignmentWrapper{}, GenericAssignmentWrapper::Deleter); |
3523 | } else { |
3524 | Assignment assignment{std::move(*lhs), std::move(*rhs)}; |
3525 | common::visit( |
3526 | common::visitors{ |
3527 | [&](const std::list<parser::BoundsRemapping> &list) { |
3528 | Assignment::BoundsRemapping bounds; |
3529 | for (const auto &elem : list) { |
3530 | auto lower{AsSubscript(Analyze(std::get<0>(elem.t)))}; |
3531 | auto upper{AsSubscript(Analyze(std::get<1>(elem.t)))}; |
3532 | if (lower && upper) { |
3533 | bounds.emplace_back( |
3534 | Fold(std::move(*lower)), Fold(std::move(*upper))); |
3535 | } |
3536 | } |
3537 | assignment.u = std::move(bounds); |
3538 | }, |
3539 | [&](const std::list<parser::BoundsSpec> &list) { |
3540 | Assignment::BoundsSpec bounds; |
3541 | for (const auto &bound : list) { |
3542 | if (auto lower{AsSubscript(Analyze(bound.v))}) { |
3543 | bounds.emplace_back(Fold(std::move(*lower))); |
3544 | } |
3545 | } |
3546 | assignment.u = std::move(bounds); |
3547 | }, |
3548 | }, |
3549 | std::get<parser::PointerAssignmentStmt::Bounds>(x.t).u); |
3550 | x.typedAssignment.Reset( |
3551 | new GenericAssignmentWrapper{std::move(assignment)}, |
3552 | GenericAssignmentWrapper::Deleter); |
3553 | } |
3554 | } |
3555 | return common::GetPtrFromOptional(x.typedAssignment->v); |
3556 | } |
3557 | |
3558 | static bool IsExternalCalledImplicitly( |
3559 | parser::CharBlock callSite, const Symbol *symbol) { |
3560 | return symbol && symbol->owner().IsGlobal() && |
3561 | symbol->has<semantics::SubprogramDetails>() && |
3562 | (!symbol->scope() /*ENTRY*/ || |
3563 | !symbol->scope()->sourceRange().Contains(callSite)); |
3564 | } |
3565 | |
3566 | std::optional<characteristics::Procedure> ExpressionAnalyzer::CheckCall( |
3567 | parser::CharBlock callSite, const ProcedureDesignator &proc, |
3568 | ActualArguments &arguments) { |
3569 | bool treatExternalAsImplicit{ |
3570 | IsExternalCalledImplicitly(callSite, proc.GetSymbol())}; |
3571 | const Symbol *procSymbol{proc.GetSymbol()}; |
3572 | std::optional<characteristics::Procedure> chars; |
3573 | if (procSymbol && procSymbol->has<semantics::ProcEntityDetails>() && |
3574 | procSymbol->owner().IsGlobal()) { |
3575 | // Unknown global external, implicit interface; assume |
3576 | // characteristics from the actual arguments, and check |
3577 | // for consistency with other references. |
3578 | chars = characteristics::Procedure::FromActuals( |
3579 | proc, arguments, context_.foldingContext()); |
3580 | if (chars && procSymbol) { |
3581 | // Ensure calls over implicit interfaces are consistent |
3582 | auto name{procSymbol->name()}; |
3583 | if (auto iter{implicitInterfaces_.find(name)}; |
3584 | iter != implicitInterfaces_.end()) { |
3585 | std::string whyNot; |
3586 | if (!chars->IsCompatibleWith(iter->second.second, |
3587 | /*ignoreImplicitVsExplicit=*/false, &whyNot)) { |
3588 | if (auto *msg{Warn( |
3589 | common::UsageWarning::IncompatibleImplicitInterfaces, |
3590 | callSite, |
3591 | "Reference to the procedure '%s' has an implicit interface that is distinct from another reference: %s"_warn_en_US, |
3592 | name, whyNot)}) { |
3593 | msg->Attach( |
3594 | iter->second.first, "previous reference to '%s'"_en_US, name); |
3595 | } |
3596 | } |
3597 | } else { |
3598 | implicitInterfaces_.insert( |
3599 | std::make_pair(name, std::make_pair(callSite, *chars))); |
3600 | } |
3601 | } |
3602 | } |
3603 | if (!chars) { |
3604 | chars = characteristics::Procedure::Characterize( |
3605 | proc, context_.foldingContext(), /*emitError=*/true); |
3606 | } |
3607 | bool ok{true}; |
3608 | if (chars) { |
3609 | std::string whyNot; |
3610 | if (treatExternalAsImplicit && |
3611 | !chars->CanBeCalledViaImplicitInterface(&whyNot)) { |
3612 | if (auto *msg{Say(callSite, |
3613 | "References to the procedure '%s' require an explicit interface"_err_en_US, |
3614 | DEREF(procSymbol).name())}; |
3615 | msg && !whyNot.empty()) { |
3616 | msg->Attach(callSite, "%s"_because_en_US, whyNot); |
3617 | } |
3618 | } |
3619 | const SpecificIntrinsic *specificIntrinsic{proc.GetSpecificIntrinsic()}; |
3620 | bool procIsDummy{procSymbol && IsDummy(*procSymbol)}; |
3621 | if (chars->functionResult && |
3622 | chars->functionResult->IsAssumedLengthCharacter() && |
3623 | !specificIntrinsic && !procIsDummy) { |
3624 | Say(callSite, |
3625 | "Assumed-length character function must be defined with a length to be called"_err_en_US); |
3626 | } |
3627 | ok &= semantics::CheckArguments(*chars, arguments, context_, |
3628 | context_.FindScope(callSite), treatExternalAsImplicit, |
3629 | /*ignoreImplicitVsExplicit=*/false, specificIntrinsic); |
3630 | } |
3631 | if (procSymbol && !IsPureProcedure(*procSymbol)) { |
3632 | if (const semantics::Scope * |
3633 | pure{semantics::FindPureProcedureContaining( |
3634 | context_.FindScope(callSite))}) { |
3635 | Say(callSite, |
3636 | "Procedure '%s' referenced in pure subprogram '%s' must be pure too"_err_en_US, |
3637 | procSymbol->name(), DEREF(pure->symbol()).name()); |
3638 | } |
3639 | } |
3640 | if (ok && !treatExternalAsImplicit && procSymbol && |
3641 | !(chars && chars->HasExplicitInterface())) { |
3642 | if (const Symbol *global{FindGlobal(*procSymbol)}; |
3643 | global && global != procSymbol && IsProcedure(*global)) { |
3644 | // Check a known global definition behind a local interface |
3645 | if (auto globalChars{characteristics::Procedure::Characterize( |
3646 | *global, context_.foldingContext())}) { |
3647 | semantics::CheckArguments(*globalChars, arguments, context_, |
3648 | context_.FindScope(callSite), /*treatExternalAsImplicit=*/true, |
3649 | /*ignoreImplicitVsExplicit=*/false, |
3650 | nullptr /*not specific intrinsic*/); |
3651 | } |
3652 | } |
3653 | } |
3654 | return chars; |
3655 | } |
3656 | |
3657 | // Unary operations |
3658 | |
3659 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Parentheses &x) { |
3660 | if (MaybeExpr operand{Analyze(x.v.value())}) { |
3661 | if (const semantics::Symbol *symbol{GetLastSymbol(*operand)}) { |
3662 | if (const semantics::Symbol *result{FindFunctionResult(*symbol)}) { |
3663 | if (semantics::IsProcedurePointer(*result)) { |
3664 | Say("A function reference that returns a procedure " |
3665 | "pointer may not be parenthesized"_err_en_US); // C1003 |
3666 | } |
3667 | } |
3668 | } |
3669 | return Parenthesize(std::move(*operand)); |
3670 | } |
3671 | return std::nullopt; |
3672 | } |
3673 | |
3674 | static MaybeExpr NumericUnaryHelper(ExpressionAnalyzer &context, |
3675 | NumericOperator opr, const parser::Expr::IntrinsicUnary &x) { |
3676 | ArgumentAnalyzer analyzer{context}; |
3677 | analyzer.Analyze(x: x.v); |
3678 | if (!analyzer.fatalErrors()) { |
3679 | if (analyzer.IsIntrinsicNumeric(opr)) { |
3680 | analyzer.CheckForNullPointer(); |
3681 | analyzer.CheckForAssumedRank(); |
3682 | if (opr == NumericOperator::Add) { |
3683 | return analyzer.MoveExpr(0); |
3684 | } else { |
3685 | return Negation(context.GetContextualMessages(), analyzer.MoveExpr(0)); |
3686 | } |
3687 | } else { |
3688 | return analyzer.TryDefinedOp(AsFortran(opr), |
3689 | "Operand of unary %s must be numeric; have %s"_err_en_US); |
3690 | } |
3691 | } |
3692 | return std::nullopt; |
3693 | } |
3694 | |
3695 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::UnaryPlus &x) { |
3696 | return NumericUnaryHelper(*this, NumericOperator::Add, x); |
3697 | } |
3698 | |
3699 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Negate &x) { |
3700 | if (const auto *litConst{ |
3701 | std::get_if<parser::LiteralConstant>(&x.v.value().u)}) { |
3702 | if (const auto *intConst{ |
3703 | std::get_if<parser::IntLiteralConstant>(&litConst->u)}) { |
3704 | return Analyze(*intConst, true); |
3705 | } |
3706 | } |
3707 | return NumericUnaryHelper(*this, NumericOperator::Subtract, x); |
3708 | } |
3709 | |
3710 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::NOT &x) { |
3711 | ArgumentAnalyzer analyzer{*this}; |
3712 | analyzer.Analyze(x.v); |
3713 | if (!analyzer.fatalErrors()) { |
3714 | if (analyzer.IsIntrinsicLogical()) { |
3715 | analyzer.CheckForNullPointer(); |
3716 | analyzer.CheckForAssumedRank(); |
3717 | return AsGenericExpr( |
3718 | LogicalNegation(std::get<Expr<SomeLogical>>(analyzer.MoveExpr(0).u))); |
3719 | } else { |
3720 | return analyzer.TryDefinedOp(LogicalOperator::Not, |
3721 | "Operand of %s must be LOGICAL; have %s"_err_en_US); |
3722 | } |
3723 | } |
3724 | return std::nullopt; |
3725 | } |
3726 | |
3727 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::PercentLoc &x) { |
3728 | // Represent %LOC() exactly as if it had been a call to the LOC() extension |
3729 | // intrinsic function. |
3730 | // Use the actual source for the name of the call for error reporting. |
3731 | std::optional<ActualArgument> arg; |
3732 | if (const Symbol *assumedTypeDummy{AssumedTypeDummy(x.v.value())}) { |
3733 | arg = ActualArgument{ActualArgument::AssumedType{*assumedTypeDummy}}; |
3734 | } else if (MaybeExpr argExpr{Analyze(x.v.value())}) { |
3735 | arg = ActualArgument{std::move(*argExpr)}; |
3736 | } else { |
3737 | return std::nullopt; |
3738 | } |
3739 | parser::CharBlock at{GetContextualMessages().at()}; |
3740 | CHECK(at.size() >= 4); |
3741 | parser::CharBlock loc{at.begin() + 1, 3}; |
3742 | CHECK(loc == "loc"); |
3743 | return MakeFunctionRef(loc, ActualArguments{std::move(*arg)}); |
3744 | } |
3745 | |
3746 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::DefinedUnary &x) { |
3747 | const auto &name{std::get<parser::DefinedOpName>(x.t).v}; |
3748 | ArgumentAnalyzer analyzer{*this, name.source}; |
3749 | analyzer.Analyze(std::get<1>(x.t)); |
3750 | return analyzer.TryDefinedOp(name.source.ToString().c_str(), |
3751 | "No operator %s defined for %s"_err_en_US, true); |
3752 | } |
3753 | |
3754 | // Binary (dyadic) operations |
3755 | |
3756 | template <template <typename> class OPR, NumericOperator opr> |
3757 | MaybeExpr NumericBinaryHelper( |
3758 | ExpressionAnalyzer &context, const parser::Expr::IntrinsicBinary &x) { |
3759 | ArgumentAnalyzer analyzer{context}; |
3760 | analyzer.Analyze(x: std::get<0>(x.t)); |
3761 | analyzer.Analyze(x: std::get<1>(x.t)); |
3762 | if (!analyzer.fatalErrors()) { |
3763 | if (analyzer.IsIntrinsicNumeric(opr)) { |
3764 | analyzer.CheckForNullPointer(); |
3765 | analyzer.CheckForAssumedRank(); |
3766 | analyzer.CheckConformance(); |
3767 | constexpr bool canBeUnsigned{opr != NumericOperator::Power}; |
3768 | return NumericOperation<OPR, canBeUnsigned>( |
3769 | context.GetContextualMessages(), analyzer.MoveExpr(0), |
3770 | analyzer.MoveExpr(1), context.GetDefaultKind(TypeCategory::Real)); |
3771 | } else { |
3772 | return analyzer.TryDefinedOp(AsFortran(opr), |
3773 | "Operands of %s must be numeric; have %s and %s"_err_en_US); |
3774 | } |
3775 | } |
3776 | return std::nullopt; |
3777 | } |
3778 | |
3779 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Power &x) { |
3780 | return NumericBinaryHelper<Power, NumericOperator::Power>(*this, x); |
3781 | } |
3782 | |
3783 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Multiply &x) { |
3784 | return NumericBinaryHelper<Multiply, NumericOperator::Multiply>(*this, x); |
3785 | } |
3786 | |
3787 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Divide &x) { |
3788 | return NumericBinaryHelper<Divide, NumericOperator::Divide>(*this, x); |
3789 | } |
3790 | |
3791 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Add &x) { |
3792 | return NumericBinaryHelper<Add, NumericOperator::Add>(*this, x); |
3793 | } |
3794 | |
3795 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Subtract &x) { |
3796 | return NumericBinaryHelper<Subtract, NumericOperator::Subtract>(*this, x); |
3797 | } |
3798 | |
3799 | MaybeExpr ExpressionAnalyzer::Analyze( |
3800 | const parser::Expr::ComplexConstructor &z) { |
3801 | Warn(common::LanguageFeature::ComplexConstructor, |
3802 | "nonstandard usage: generalized COMPLEX constructor"_port_en_US); |
3803 | return AnalyzeComplex(Analyze(std::get<0>(z.t).value()), |
3804 | Analyze(std::get<1>(z.t).value()), "complex constructor"); |
3805 | } |
3806 | |
3807 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Concat &x) { |
3808 | ArgumentAnalyzer analyzer{*this}; |
3809 | analyzer.Analyze(std::get<0>(x.t)); |
3810 | analyzer.Analyze(std::get<1>(x.t)); |
3811 | if (!analyzer.fatalErrors()) { |
3812 | if (analyzer.IsIntrinsicConcat()) { |
3813 | analyzer.CheckForNullPointer(); |
3814 | analyzer.CheckForAssumedRank(); |
3815 | return common::visit( |
3816 | [&](auto &&x, auto &&y) -> MaybeExpr { |
3817 | using T = ResultType<decltype(x)>; |
3818 | if constexpr (std::is_same_v<T, ResultType<decltype(y)>>) { |
3819 | return AsGenericExpr(Concat<T::kind>{std::move(x), std::move(y)}); |
3820 | } else { |
3821 | DIE("different types for intrinsic concat"); |
3822 | } |
3823 | }, |
3824 | std::move(std::get<Expr<SomeCharacter>>(analyzer.MoveExpr(0).u).u), |
3825 | std::move(std::get<Expr<SomeCharacter>>(analyzer.MoveExpr(1).u).u)); |
3826 | } else { |
3827 | return analyzer.TryDefinedOp("//", |
3828 | "Operands of %s must be CHARACTER with the same kind; have %s and %s"_err_en_US); |
3829 | } |
3830 | } |
3831 | return std::nullopt; |
3832 | } |
3833 | |
3834 | // The Name represents a user-defined intrinsic operator. |
3835 | // If the actuals match one of the specific procedures, return a function ref. |
3836 | // Otherwise report the error in messages. |
3837 | MaybeExpr ExpressionAnalyzer::AnalyzeDefinedOp(const parser::Name &name, |
3838 | ActualArguments &&actuals, const Symbol *&symbol) { |
3839 | if (auto callee{GetCalleeAndArguments(name, std::move(actuals))}) { |
3840 | auto &proc{std::get<evaluate::ProcedureDesignator>(callee->u)}; |
3841 | symbol = proc.GetSymbol(); |
3842 | return MakeFunctionRef( |
3843 | name.source, std::move(proc), std::move(callee->arguments)); |
3844 | } else { |
3845 | return std::nullopt; |
3846 | } |
3847 | } |
3848 | |
3849 | MaybeExpr RelationHelper(ExpressionAnalyzer &context, RelationalOperator opr, |
3850 | const parser::Expr::IntrinsicBinary &x) { |
3851 | ArgumentAnalyzer analyzer{context}; |
3852 | analyzer.Analyze(x: std::get<0>(x.t)); |
3853 | analyzer.Analyze(x: std::get<1>(x.t)); |
3854 | if (!analyzer.fatalErrors()) { |
3855 | std::optional<DynamicType> leftType{analyzer.GetType(0)}; |
3856 | std::optional<DynamicType> rightType{analyzer.GetType(1)}; |
3857 | analyzer.ConvertBOZOperand(&leftType, 0, rightType); |
3858 | analyzer.ConvertBOZOperand(&rightType, 1, leftType); |
3859 | if (leftType && rightType && |
3860 | analyzer.IsIntrinsicRelational(opr, *leftType, *rightType)) { |
3861 | analyzer.CheckForNullPointer(where: "as a relational operand"); |
3862 | analyzer.CheckForAssumedRank(where: "as a relational operand"); |
3863 | if (auto cmp{Relate(context.GetContextualMessages(), opr, |
3864 | analyzer.MoveExpr(0), analyzer.MoveExpr(1))}) { |
3865 | return AsMaybeExpr(ConvertToKind<TypeCategory::Logical>( |
3866 | context.GetDefaultKind(TypeCategory::Logical), |
3867 | AsExpr(std::move(*cmp)))); |
3868 | } |
3869 | } else { |
3870 | return analyzer.TryDefinedOp(opr, |
3871 | leftType && leftType->category() == TypeCategory::Logical && |
3872 | rightType && rightType->category() == TypeCategory::Logical |
3873 | ? "LOGICAL operands must be compared using .EQV. or .NEQV."_err_en_US |
3874 | : "Operands of %s must have comparable types; have %s and %s"_err_en_US); |
3875 | } |
3876 | } |
3877 | return std::nullopt; |
3878 | } |
3879 | |
3880 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::LT &x) { |
3881 | return RelationHelper(*this, RelationalOperator::LT, x); |
3882 | } |
3883 | |
3884 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::LE &x) { |
3885 | return RelationHelper(*this, RelationalOperator::LE, x); |
3886 | } |
3887 | |
3888 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::EQ &x) { |
3889 | return RelationHelper(*this, RelationalOperator::EQ, x); |
3890 | } |
3891 | |
3892 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::NE &x) { |
3893 | return RelationHelper(*this, RelationalOperator::NE, x); |
3894 | } |
3895 | |
3896 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::GE &x) { |
3897 | return RelationHelper(*this, RelationalOperator::GE, x); |
3898 | } |
3899 | |
3900 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::GT &x) { |
3901 | return RelationHelper(*this, RelationalOperator::GT, x); |
3902 | } |
3903 | |
3904 | MaybeExpr LogicalBinaryHelper(ExpressionAnalyzer &context, LogicalOperator opr, |
3905 | const parser::Expr::IntrinsicBinary &x) { |
3906 | ArgumentAnalyzer analyzer{context}; |
3907 | analyzer.Analyze(x: std::get<0>(x.t)); |
3908 | analyzer.Analyze(x: std::get<1>(x.t)); |
3909 | if (!analyzer.fatalErrors()) { |
3910 | if (analyzer.IsIntrinsicLogical()) { |
3911 | analyzer.CheckForNullPointer(where: "as a logical operand"); |
3912 | analyzer.CheckForAssumedRank(where: "as a logical operand"); |
3913 | return AsGenericExpr(BinaryLogicalOperation(opr, |
3914 | std::get<Expr<SomeLogical>>(analyzer.MoveExpr(0).u), |
3915 | std::get<Expr<SomeLogical>>(analyzer.MoveExpr(1).u))); |
3916 | } else { |
3917 | return analyzer.TryDefinedOp( |
3918 | opr, "Operands of %s must be LOGICAL; have %s and %s"_err_en_US); |
3919 | } |
3920 | } |
3921 | return std::nullopt; |
3922 | } |
3923 | |
3924 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::AND &x) { |
3925 | return LogicalBinaryHelper(*this, LogicalOperator::And, x); |
3926 | } |
3927 | |
3928 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::OR &x) { |
3929 | return LogicalBinaryHelper(*this, LogicalOperator::Or, x); |
3930 | } |
3931 | |
3932 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::EQV &x) { |
3933 | return LogicalBinaryHelper(*this, LogicalOperator::Eqv, x); |
3934 | } |
3935 | |
3936 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::NEQV &x) { |
3937 | return LogicalBinaryHelper(*this, LogicalOperator::Neqv, x); |
3938 | } |
3939 | |
3940 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::DefinedBinary &x) { |
3941 | const auto &name{std::get<parser::DefinedOpName>(x.t).v}; |
3942 | ArgumentAnalyzer analyzer{*this, name.source}; |
3943 | analyzer.Analyze(std::get<1>(x.t)); |
3944 | analyzer.Analyze(std::get<2>(x.t)); |
3945 | return analyzer.TryDefinedOp(name.source.ToString().c_str(), |
3946 | "No operator %s defined for %s and %s"_err_en_US, true); |
3947 | } |
3948 | |
3949 | // Returns true if a parsed function reference should be converted |
3950 | // into an array element reference. |
3951 | static bool CheckFuncRefToArrayElement(semantics::SemanticsContext &context, |
3952 | const parser::FunctionReference &funcRef) { |
3953 | // Emit message if the function reference fix will end up an array element |
3954 | // reference with no subscripts, or subscripts on a scalar, because it will |
3955 | // not be possible to later distinguish in expressions between an empty |
3956 | // subscript list due to bad subscripts error recovery or because the |
3957 | // user did not put any. |
3958 | auto &proc{std::get<parser::ProcedureDesignator>(funcRef.v.t)}; |
3959 | const auto *name{std::get_if<parser::Name>(&proc.u)}; |
3960 | if (!name) { |
3961 | name = &std::get<parser::ProcComponentRef>(proc.u).v.thing.component; |
3962 | } |
3963 | if (!name->symbol) { |
3964 | return false; |
3965 | } else if (name->symbol->Rank() == 0) { |
3966 | if (const Symbol *function{ |
3967 | semantics::IsFunctionResultWithSameNameAsFunction(*name->symbol)}) { |
3968 | auto &msg{context.Say(funcRef.source, |
3969 | function->flags().test(Symbol::Flag::StmtFunction) |
3970 | ? "Recursive call to statement function '%s' is not allowed"_err_en_US |
3971 | : "Recursive call to '%s' requires a distinct RESULT in its declaration"_err_en_US, |
3972 | name->source)}; |
3973 | AttachDeclaration(&msg, *function); |
3974 | name->symbol = const_cast<Symbol *>(function); |
3975 | } |
3976 | return false; |
3977 | } else { |
3978 | if (std::get<std::list<parser::ActualArgSpec>>(funcRef.v.t).empty()) { |
3979 | auto &msg{context.Say(funcRef.source, |
3980 | "Reference to array '%s' with empty subscript list"_err_en_US, |
3981 | name->source)}; |
3982 | if (name->symbol) { |
3983 | AttachDeclaration(&msg, *name->symbol); |
3984 | } |
3985 | } |
3986 | return true; |
3987 | } |
3988 | } |
3989 | |
3990 | // Converts, if appropriate, an original misparse of ambiguous syntax like |
3991 | // A(1) as a function reference into an array reference. |
3992 | // Misparsed structure constructors are detected elsewhere after generic |
3993 | // function call resolution fails. |
3994 | template <typename... A> |
3995 | static void FixMisparsedFunctionReference( |
3996 | semantics::SemanticsContext &context, const std::variant<A...> &constU) { |
3997 | // The parse tree is updated in situ when resolving an ambiguous parse. |
3998 | using uType = std::decay_t<decltype(constU)>; |
3999 | auto &u{const_cast<uType &>(constU)}; |
4000 | if (auto *func{ |
4001 | std::get_if<common::Indirection<parser::FunctionReference>>(&u)}) { |
4002 | parser::FunctionReference &funcRef{func->value()}; |
4003 | // Ensure that there are no argument keywords |
4004 | for (const auto &arg : |
4005 | std::get<std::list<parser::ActualArgSpec>>(funcRef.v.t)) { |
4006 | if (std::get<std::optional<parser::Keyword>>(arg.t)) { |
4007 | return; |
4008 | } |
4009 | } |
4010 | auto &proc{std::get<parser::ProcedureDesignator>(funcRef.v.t)}; |
4011 | if (Symbol *origSymbol{ |
4012 | common::visit(common::visitors{ |
4013 | [&](parser::Name &name) { return name.symbol; }, |
4014 | [&](parser::ProcComponentRef &pcr) { |
4015 | return pcr.v.thing.component.symbol; |
4016 | }, |
4017 | }, |
4018 | proc.u)}) { |
4019 | Symbol &symbol{origSymbol->GetUltimate()}; |
4020 | if (symbol.has<semantics::ObjectEntityDetails>() || |
4021 | symbol.has<semantics::AssocEntityDetails>()) { |
4022 | // Note that expression in AssocEntityDetails cannot be a procedure |
4023 | // pointer as per C1105 so this cannot be a function reference. |
4024 | if constexpr (common::HasMember<common::Indirection<parser::Designator>, |
4025 | uType>) { |
4026 | if (CheckFuncRefToArrayElement(context, funcRef)) { |
4027 | u = common::Indirection{funcRef.ConvertToArrayElementRef()}; |
4028 | } |
4029 | } else { |
4030 | DIE("can't fix misparsed function as array reference"); |
4031 | } |
4032 | } |
4033 | } |
4034 | } |
4035 | } |
4036 | |
4037 | // Common handling of parse tree node types that retain the |
4038 | // representation of the analyzed expression. |
4039 | template <typename PARSED> |
4040 | MaybeExpr ExpressionAnalyzer::ExprOrVariable( |
4041 | const PARSED &x, parser::CharBlock source) { |
4042 | auto restorer{GetContextualMessages().SetLocation(source)}; |
4043 | if constexpr (std::is_same_v<PARSED, parser::Expr> || |
4044 | std::is_same_v<PARSED, parser::Variable>) { |
4045 | FixMisparsedFunctionReference(context_, x.u); |
4046 | } |
4047 | if (AssumedTypeDummy(x)) { // C710 |
4048 | Say("TYPE(*) dummy argument may only be used as an actual argument"_err_en_US); |
4049 | ResetExpr(x); |
4050 | return std::nullopt; |
4051 | } |
4052 | MaybeExpr result; |
4053 | if constexpr (common::HasMember<parser::StructureConstructor, |
4054 | std::decay_t<decltype(x.u)>> && |
4055 | common::HasMember<common::Indirection<parser::FunctionReference>, |
4056 | std::decay_t<decltype(x.u)>>) { |
4057 | if (const auto *funcRef{ |
4058 | std::get_if<common::Indirection<parser::FunctionReference>>( |
4059 | &x.u)}) { |
4060 | // Function references in Exprs might turn out to be misparsed structure |
4061 | // constructors; we have to try generic procedure resolution |
4062 | // first to be sure. |
4063 | std::optional<parser::StructureConstructor> ctor; |
4064 | result = Analyze(funcRef->value(), &ctor); |
4065 | if (ctor) { |
4066 | // A misparsed function reference is really a structure |
4067 | // constructor. Repair the parse tree in situ. |
4068 | const_cast<PARSED &>(x).u = std::move(*ctor); |
4069 | } |
4070 | } else { |
4071 | result = Analyze(x.u); |
4072 | } |
4073 | } else { |
4074 | result = Analyze(x.u); |
4075 | } |
4076 | if (result) { |
4077 | if constexpr (std::is_same_v<PARSED, parser::Expr>) { |
4078 | if (!isNullPointerOk_ && IsNullPointerOrAllocatable(&*result)) { |
4079 | Say(source, |
4080 | "NULL() may not be used as an expression in this context"_err_en_US); |
4081 | } |
4082 | } |
4083 | SetExpr(x, Fold(std::move(*result))); |
4084 | return x.typedExpr->v; |
4085 | } else { |
4086 | ResetExpr(x); |
4087 | if (!context_.AnyFatalError()) { |
4088 | std::string buf; |
4089 | llvm::raw_string_ostream dump{buf}; |
4090 | parser::DumpTree(dump, x); |
4091 | Say("Internal error: Expression analysis failed on: %s"_err_en_US, buf); |
4092 | } |
4093 | return std::nullopt; |
4094 | } |
4095 | } |
4096 | |
4097 | // This is an optional preliminary pass over parser::Expr subtrees. |
4098 | // Given an expression tree, iteratively traverse it in a bottom-up order |
4099 | // to analyze all of its subexpressions. A later normal top-down analysis |
4100 | // will then be able to use the results that will have been saved in the |
4101 | // parse tree without having to recurse deeply. This technique keeps |
4102 | // absurdly deep expression parse trees from causing the analyzer to overflow |
4103 | // its stack. |
4104 | MaybeExpr ExpressionAnalyzer::IterativelyAnalyzeSubexpressions( |
4105 | const parser::Expr &top) { |
4106 | std::vector<const parser::Expr *> queue, finish; |
4107 | queue.push_back(&top); |
4108 | do { |
4109 | const parser::Expr &expr{*queue.back()}; |
4110 | queue.pop_back(); |
4111 | if (!expr.typedExpr) { |
4112 | const parser::Expr::IntrinsicUnary *unary{nullptr}; |
4113 | const parser::Expr::IntrinsicBinary *binary{nullptr}; |
4114 | common::visit( |
4115 | [&unary, &binary](auto &y) { |
4116 | if constexpr (std::is_convertible_v<decltype(&y), |
4117 | decltype(unary)>) { |
4118 | // Don't evaluate a constant operand to Negate |
4119 | if (!std::holds_alternative<parser::LiteralConstant>( |
4120 | y.v.value().u)) { |
4121 | unary = &y; |
4122 | } |
4123 | } else if constexpr (std::is_convertible_v<decltype(&y), |
4124 | decltype(binary)>) { |
4125 | binary = &y; |
4126 | } |
4127 | }, |
4128 | expr.u); |
4129 | if (unary) { |
4130 | queue.push_back(&unary->v.value()); |
4131 | } else if (binary) { |
4132 | queue.push_back(&std::get<0>(binary->t).value()); |
4133 | queue.push_back(&std::get<1>(binary->t).value()); |
4134 | } |
4135 | finish.push_back(&expr); |
4136 | } |
4137 | } while (!queue.empty()); |
4138 | // Analyze the collected subexpressions in bottom-up order. |
4139 | // On an error, bail out and leave partial results in place. |
4140 | MaybeExpr result; |
4141 | for (auto riter{finish.rbegin()}; riter != finish.rend(); ++riter) { |
4142 | const parser::Expr &expr{**riter}; |
4143 | result = ExprOrVariable(expr, expr.source); |
4144 | if (!result) { |
4145 | return result; |
4146 | } |
4147 | } |
4148 | return result; // last value was from analysis of "top" |
4149 | } |
4150 | |
4151 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr &expr) { |
4152 | bool wasIterativelyAnalyzing{iterativelyAnalyzingSubexpressions_}; |
4153 | MaybeExpr result; |
4154 | if (useSavedTypedExprs_) { |
4155 | if (expr.typedExpr) { |
4156 | return expr.typedExpr->v; |
4157 | } |
4158 | if (!wasIterativelyAnalyzing) { |
4159 | iterativelyAnalyzingSubexpressions_ = true; |
4160 | result = IterativelyAnalyzeSubexpressions(expr); |
4161 | } |
4162 | } |
4163 | if (!result) { |
4164 | result = ExprOrVariable(expr, expr.source); |
4165 | } |
4166 | iterativelyAnalyzingSubexpressions_ = wasIterativelyAnalyzing; |
4167 | return result; |
4168 | } |
4169 | |
4170 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Variable &variable) { |
4171 | if (useSavedTypedExprs_ && variable.typedExpr) { |
4172 | return variable.typedExpr->v; |
4173 | } |
4174 | return ExprOrVariable(variable, variable.GetSource()); |
4175 | } |
4176 | |
4177 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::Selector &selector) { |
4178 | if (const auto *var{std::get_if<parser::Variable>(&selector.u)}) { |
4179 | if (!useSavedTypedExprs_ || !var->typedExpr) { |
4180 | parser::CharBlock source{var->GetSource()}; |
4181 | auto restorer{GetContextualMessages().SetLocation(source)}; |
4182 | FixMisparsedFunctionReference(context_, var->u); |
4183 | if (const auto *funcRef{ |
4184 | std::get_if<common::Indirection<parser::FunctionReference>>( |
4185 | &var->u)}) { |
4186 | // A Selector that parsed as a Variable might turn out during analysis |
4187 | // to actually be a structure constructor. In that case, repair the |
4188 | // Variable parse tree node into an Expr |
4189 | std::optional<parser::StructureConstructor> ctor; |
4190 | if (MaybeExpr result{Analyze(funcRef->value(), &ctor)}) { |
4191 | if (ctor) { |
4192 | auto &writable{const_cast<parser::Selector &>(selector)}; |
4193 | writable.u = parser::Expr{std::move(*ctor)}; |
4194 | auto &expr{std::get<parser::Expr>(writable.u)}; |
4195 | expr.source = source; |
4196 | SetExpr(expr, Fold(std::move(*result))); |
4197 | return expr.typedExpr->v; |
4198 | } else { |
4199 | SetExpr(*var, Fold(std::move(*result))); |
4200 | return var->typedExpr->v; |
4201 | } |
4202 | } else { |
4203 | ResetExpr(*var); |
4204 | if (context_.AnyFatalError()) { |
4205 | return std::nullopt; |
4206 | } |
4207 | } |
4208 | } |
4209 | } |
4210 | // Not a Variable -> FunctionReference |
4211 | auto restorer{AllowWholeAssumedSizeArray()}; |
4212 | return Analyze(selector.u); |
4213 | } else { // Expr |
4214 | return Analyze(selector.u); |
4215 | } |
4216 | } |
4217 | |
4218 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::DataStmtConstant &x) { |
4219 | auto restorer{common::ScopedSet(inDataStmtConstant_, true)}; |
4220 | return ExprOrVariable(x, x.source); |
4221 | } |
4222 | |
4223 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::AllocateObject &x) { |
4224 | return ExprOrVariable(x, parser::FindSourceLocation(x)); |
4225 | } |
4226 | |
4227 | MaybeExpr ExpressionAnalyzer::Analyze(const parser::PointerObject &x) { |
4228 | return ExprOrVariable(x, parser::FindSourceLocation(x)); |
4229 | } |
4230 | |
4231 | Expr<SubscriptInteger> ExpressionAnalyzer::AnalyzeKindSelector( |
4232 | TypeCategory category, |
4233 | const std::optional<parser::KindSelector> &selector) { |
4234 | int defaultKind{GetDefaultKind(category)}; |
4235 | if (!selector) { |
4236 | return Expr<SubscriptInteger>{defaultKind}; |
4237 | } |
4238 | return common::visit( |
4239 | common::visitors{ |
4240 | [&](const parser::ScalarIntConstantExpr &x) { |
4241 | if (MaybeExpr kind{Analyze(x)}) { |
4242 | if (std::optional<std::int64_t> code{ToInt64(*kind)}) { |
4243 | if (CheckIntrinsicKind(category, *code)) { |
4244 | return Expr<SubscriptInteger>{*code}; |
4245 | } |
4246 | } else if (auto *intExpr{UnwrapExpr<Expr<SomeInteger>>(*kind)}) { |
4247 | return ConvertToType<SubscriptInteger>(std::move(*intExpr)); |
4248 | } |
4249 | } |
4250 | return Expr<SubscriptInteger>{defaultKind}; |
4251 | }, |
4252 | [&](const parser::KindSelector::StarSize &x) { |
4253 | std::intmax_t size = x.v; |
4254 | if (!CheckIntrinsicSize(category, size)) { |
4255 | size = defaultKind; |
4256 | } else if (category == TypeCategory::Complex) { |
4257 | size /= 2; |
4258 | } |
4259 | return Expr<SubscriptInteger>{size}; |
4260 | }, |
4261 | }, |
4262 | selector->u); |
4263 | } |
4264 | |
4265 | int ExpressionAnalyzer::GetDefaultKind(common::TypeCategory category) { |
4266 | return context_.GetDefaultKind(category); |
4267 | } |
4268 | |
4269 | DynamicType ExpressionAnalyzer::GetDefaultKindOfType( |
4270 | common::TypeCategory category) { |
4271 | return {category, GetDefaultKind(category)}; |
4272 | } |
4273 | |
4274 | bool ExpressionAnalyzer::CheckIntrinsicKind( |
4275 | TypeCategory category, std::int64_t kind) { |
4276 | if (foldingContext_.targetCharacteristics().IsTypeEnabled( |
4277 | category, kind)) { // C712, C714, C715, C727 |
4278 | return true; |
4279 | } else if (foldingContext_.targetCharacteristics().CanSupportType( |
4280 | category, kind)) { |
4281 | Say("%s(KIND=%jd) is not an enabled type for this target"_err_en_US, |
4282 | ToUpperCase(EnumToString(category)), kind); |
4283 | return true; |
4284 | } else { |
4285 | Say("%s(KIND=%jd) is not a supported type"_err_en_US, |
4286 | ToUpperCase(EnumToString(category)), kind); |
4287 | return false; |
4288 | } |
4289 | } |
4290 | |
4291 | bool ExpressionAnalyzer::CheckIntrinsicSize( |
4292 | TypeCategory category, std::int64_t size) { |
4293 | std::int64_t kind{size}; |
4294 | if (category == TypeCategory::Complex) { |
4295 | // COMPLEX*16 == COMPLEX(KIND=8) |
4296 | if (size % 2 == 0) { |
4297 | kind = size / 2; |
4298 | } else { |
4299 | Say("COMPLEX*%jd is not a supported type"_err_en_US, size); |
4300 | return false; |
4301 | } |
4302 | } |
4303 | return CheckIntrinsicKind(category, kind); |
4304 | } |
4305 | |
4306 | bool ExpressionAnalyzer::AddImpliedDo(parser::CharBlock name, int kind) { |
4307 | return impliedDos_.insert(std::make_pair(name, kind)).second; |
4308 | } |
4309 | |
4310 | void ExpressionAnalyzer::RemoveImpliedDo(parser::CharBlock name) { |
4311 | auto iter{impliedDos_.find(name)}; |
4312 | if (iter != impliedDos_.end()) { |
4313 | impliedDos_.erase(iter); |
4314 | } |
4315 | } |
4316 | |
4317 | std::optional<int> ExpressionAnalyzer::IsImpliedDo( |
4318 | parser::CharBlock name) const { |
4319 | auto iter{impliedDos_.find(name)}; |
4320 | if (iter != impliedDos_.cend()) { |
4321 | return {iter->second}; |
4322 | } else { |
4323 | return std::nullopt; |
4324 | } |
4325 | } |
4326 | |
4327 | bool ExpressionAnalyzer::EnforceTypeConstraint(parser::CharBlock at, |
4328 | const MaybeExpr &result, TypeCategory category, bool defaultKind) { |
4329 | if (result) { |
4330 | if (auto type{result->GetType()}) { |
4331 | if (type->category() != category) { // C885 |
4332 | Say(at, "Must have %s type, but is %s"_err_en_US, |
4333 | ToUpperCase(EnumToString(category)), |
4334 | ToUpperCase(type->AsFortran())); |
4335 | return false; |
4336 | } else if (defaultKind) { |
4337 | int kind{context_.GetDefaultKind(category)}; |
4338 | if (type->kind() != kind) { |
4339 | Say(at, "Must have default kind(%d) of %s type, but is %s"_err_en_US, |
4340 | kind, ToUpperCase(EnumToString(category)), |
4341 | ToUpperCase(type->AsFortran())); |
4342 | return false; |
4343 | } |
4344 | } |
4345 | } else { |
4346 | Say(at, "Must have %s type, but is typeless"_err_en_US, |
4347 | ToUpperCase(EnumToString(category))); |
4348 | return false; |
4349 | } |
4350 | } |
4351 | return true; |
4352 | } |
4353 | |
4354 | MaybeExpr ExpressionAnalyzer::MakeFunctionRef(parser::CharBlock callSite, |
4355 | ProcedureDesignator &&proc, ActualArguments &&arguments) { |
4356 | if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&proc.u)}) { |
4357 | if (intrinsic->characteristics.value().attrs.test( |
4358 | characteristics::Procedure::Attr::NullPointer) && |
4359 | arguments.empty()) { |
4360 | return Expr<SomeType>{NullPointer{}}; |
4361 | } |
4362 | } |
4363 | if (const Symbol *symbol{proc.GetSymbol()}) { |
4364 | if (!ResolveForward(*symbol)) { |
4365 | return std::nullopt; |
4366 | } |
4367 | } |
4368 | if (auto chars{CheckCall(callSite, proc, arguments)}) { |
4369 | if (chars->functionResult) { |
4370 | const auto &result{*chars->functionResult}; |
4371 | ProcedureRef procRef{std::move(proc), std::move(arguments)}; |
4372 | if (result.IsProcedurePointer()) { |
4373 | return Expr<SomeType>{std::move(procRef)}; |
4374 | } else { |
4375 | // Not a procedure pointer, so type and shape are known. |
4376 | return TypedWrapper<FunctionRef, ProcedureRef>( |
4377 | DEREF(result.GetTypeAndShape()).type(), std::move(procRef)); |
4378 | } |
4379 | } else { |
4380 | Say("Function result characteristics are not known"_err_en_US); |
4381 | } |
4382 | } |
4383 | return std::nullopt; |
4384 | } |
4385 | |
4386 | MaybeExpr ExpressionAnalyzer::MakeFunctionRef( |
4387 | parser::CharBlock intrinsic, ActualArguments &&arguments) { |
4388 | if (std::optional<SpecificCall> specificCall{ |
4389 | context_.intrinsics().Probe(CallCharacteristics{intrinsic.ToString()}, |
4390 | arguments, GetFoldingContext())}) { |
4391 | return MakeFunctionRef(intrinsic, |
4392 | ProcedureDesignator{std::move(specificCall->specificIntrinsic)}, |
4393 | std::move(specificCall->arguments)); |
4394 | } else { |
4395 | return std::nullopt; |
4396 | } |
4397 | } |
4398 | |
4399 | MaybeExpr ExpressionAnalyzer::AnalyzeComplex( |
4400 | MaybeExpr &&re, MaybeExpr &&im, const char *what) { |
4401 | if (re && re->Rank() > 0) { |
4402 | Warn(common::LanguageFeature::ComplexConstructor, |
4403 | "Real part of %s is not scalar"_port_en_US, what); |
4404 | } |
4405 | if (im && im->Rank() > 0) { |
4406 | Warn(common::LanguageFeature::ComplexConstructor, |
4407 | "Imaginary part of %s is not scalar"_port_en_US, what); |
4408 | } |
4409 | if (re && im) { |
4410 | ConformabilityCheck(GetContextualMessages(), *re, *im); |
4411 | } |
4412 | return AsMaybeExpr(ConstructComplex(GetContextualMessages(), std::move(re), |
4413 | std::move(im), GetDefaultKind(TypeCategory::Real))); |
4414 | } |
4415 | |
4416 | std::optional<ActualArgument> ArgumentAnalyzer::AnalyzeVariable( |
4417 | const parser::Variable &x) { |
4418 | source_.ExtendToCover(x.GetSource()); |
4419 | if (MaybeExpr expr{context_.Analyze(x)}) { |
4420 | if (!IsConstantExpr(*expr)) { |
4421 | ActualArgument actual{std::move(*expr)}; |
4422 | SetArgSourceLocation(actual, x.GetSource()); |
4423 | return actual; |
4424 | } |
4425 | const Symbol *symbol{GetLastSymbol(*expr)}; |
4426 | if (!symbol) { |
4427 | context_.SayAt(x, "Assignment to constant '%s' is not allowed"_err_en_US, |
4428 | x.GetSource()); |
4429 | } else if (IsProcedure(*symbol)) { |
4430 | if (auto *msg{context_.SayAt(x, |
4431 | "Assignment to procedure '%s' is not allowed"_err_en_US, |
4432 | symbol->name())}) { |
4433 | if (auto *subp{symbol->detailsIf<semantics::SubprogramDetails>()}) { |
4434 | if (subp->isFunction()) { |
4435 | const auto &result{subp->result().name()}; |
4436 | msg->Attach(result, "Function result is '%s'"_en_US, result); |
4437 | } |
4438 | } |
4439 | } |
4440 | } else { |
4441 | context_.SayAt( |
4442 | x, "Assignment to '%s' is not allowed"_err_en_US, symbol->name()); |
4443 | } |
4444 | } |
4445 | fatalErrors_ = true; |
4446 | return std::nullopt; |
4447 | } |
4448 | |
4449 | void ArgumentAnalyzer::Analyze(const parser::Variable &x) { |
4450 | if (auto actual = AnalyzeVariable(x)) { |
4451 | actuals_.emplace_back(std::move(actual)); |
4452 | } |
4453 | } |
4454 | |
4455 | void ArgumentAnalyzer::Analyze( |
4456 | const parser::ActualArgSpec &arg, bool isSubroutine) { |
4457 | // TODO: C1534: Don't allow a "restricted" specific intrinsic to be passed. |
4458 | std::optional<ActualArgument> actual; |
4459 | auto restorer{context_.AllowWholeAssumedSizeArray()}; |
4460 | common::visit( |
4461 | common::visitors{ |
4462 | [&](const common::Indirection<parser::Expr> &x) { |
4463 | actual = AnalyzeExpr(x.value()); |
4464 | }, |
4465 | [&](const parser::AltReturnSpec &label) { |
4466 | if (!isSubroutine) { |
4467 | context_.Say( |
4468 | "alternate return specification may not appear on function reference"_err_en_US); |
4469 | } |
4470 | actual = ActualArgument(label.v); |
4471 | }, |
4472 | [&](const parser::ActualArg::PercentRef &percentRef) { |
4473 | actual = AnalyzeExpr(percentRef.v); |
4474 | if (actual.has_value()) { |
4475 | actual->set_isPercentRef(); |
4476 | } |
4477 | }, |
4478 | [&](const parser::ActualArg::PercentVal &percentVal) { |
4479 | actual = AnalyzeExpr(percentVal.v); |
4480 | if (actual.has_value()) { |
4481 | actual->set_isPercentVal(); |
4482 | } |
4483 | }, |
4484 | }, |
4485 | std::get<parser::ActualArg>(arg.t).u); |
4486 | if (actual) { |
4487 | if (const auto &argKW{std::get<std::optional<parser::Keyword>>(arg.t)}) { |
4488 | actual->set_keyword(argKW->v.source); |
4489 | } |
4490 | actuals_.emplace_back(std::move(*actual)); |
4491 | } else { |
4492 | fatalErrors_ = true; |
4493 | } |
4494 | } |
4495 | |
4496 | bool ArgumentAnalyzer::IsIntrinsicRelational(RelationalOperator opr, |
4497 | const DynamicType &leftType, const DynamicType &rightType) const { |
4498 | CHECK(actuals_.size() == 2); |
4499 | return semantics::IsIntrinsicRelational( |
4500 | opr, leftType, GetRank(0), rightType, GetRank(1)); |
4501 | } |
4502 | |
4503 | bool ArgumentAnalyzer::IsIntrinsicNumeric(NumericOperator opr) const { |
4504 | std::optional<DynamicType> leftType{GetType(0)}; |
4505 | if (actuals_.size() == 1) { |
4506 | if (IsBOZLiteral(i: 0)) { |
4507 | return opr == NumericOperator::Add; // unary '+' |
4508 | } else { |
4509 | return leftType && semantics::IsIntrinsicNumeric(*leftType); |
4510 | } |
4511 | } else { |
4512 | std::optional<DynamicType> rightType{GetType(1)}; |
4513 | if (IsBOZLiteral(i: 0) && rightType) { // BOZ opr Integer/Unsigned/Real |
4514 | auto cat1{rightType->category()}; |
4515 | return cat1 == TypeCategory::Integer || cat1 == TypeCategory::Unsigned || |
4516 | cat1 == TypeCategory::Real; |
4517 | } else if (IsBOZLiteral(i: 1) && leftType) { // Integer/Unsigned/Real opr BOZ |
4518 | auto cat0{leftType->category()}; |
4519 | return cat0 == TypeCategory::Integer || cat0 == TypeCategory::Unsigned || |
4520 | cat0 == TypeCategory::Real; |
4521 | } else { |
4522 | return leftType && rightType && |
4523 | semantics::IsIntrinsicNumeric( |
4524 | *leftType, GetRank(0), *rightType, GetRank(1)); |
4525 | } |
4526 | } |
4527 | } |
4528 | |
4529 | bool ArgumentAnalyzer::IsIntrinsicLogical() const { |
4530 | if (std::optional<DynamicType> leftType{GetType(0)}) { |
4531 | if (actuals_.size() == 1) { |
4532 | return semantics::IsIntrinsicLogical(*leftType); |
4533 | } else if (std::optional<DynamicType> rightType{GetType(1)}) { |
4534 | return semantics::IsIntrinsicLogical( |
4535 | *leftType, GetRank(0), *rightType, GetRank(1)); |
4536 | } |
4537 | } |
4538 | return false; |
4539 | } |
4540 | |
4541 | bool ArgumentAnalyzer::IsIntrinsicConcat() const { |
4542 | if (std::optional<DynamicType> leftType{GetType(0)}) { |
4543 | if (std::optional<DynamicType> rightType{GetType(1)}) { |
4544 | return semantics::IsIntrinsicConcat( |
4545 | *leftType, GetRank(0), *rightType, GetRank(1)); |
4546 | } |
4547 | } |
4548 | return false; |
4549 | } |
4550 | |
4551 | bool ArgumentAnalyzer::CheckConformance() { |
4552 | if (actuals_.size() == 2) { |
4553 | const auto *lhs{actuals_.at(0).value().UnwrapExpr()}; |
4554 | const auto *rhs{actuals_.at(1).value().UnwrapExpr()}; |
4555 | if (lhs && rhs) { |
4556 | auto &foldingContext{context_.GetFoldingContext()}; |
4557 | auto lhShape{GetShape(foldingContext, *lhs)}; |
4558 | auto rhShape{GetShape(foldingContext, *rhs)}; |
4559 | if (lhShape && rhShape) { |
4560 | if (!evaluate::CheckConformance(foldingContext.messages(), *lhShape, |
4561 | *rhShape, CheckConformanceFlags::EitherScalarExpandable, |
4562 | "left operand", "right operand") |
4563 | .value_or(false /*fail when conformance is not known now*/)) { |
4564 | fatalErrors_ = true; |
4565 | return false; |
4566 | } |
4567 | } |
4568 | } |
4569 | } |
4570 | return true; // no proven problem |
4571 | } |
4572 | |
4573 | bool ArgumentAnalyzer::CheckAssignmentConformance() { |
4574 | if (actuals_.size() == 2 && actuals_[0] && actuals_[1]) { |
4575 | const auto *lhs{actuals_[0]->UnwrapExpr()}; |
4576 | const auto *rhs{actuals_[1]->UnwrapExpr()}; |
4577 | if (lhs && rhs) { |
4578 | auto &foldingContext{context_.GetFoldingContext()}; |
4579 | auto lhShape{GetShape(foldingContext, *lhs)}; |
4580 | auto rhShape{GetShape(foldingContext, *rhs)}; |
4581 | if (lhShape && rhShape) { |
4582 | if (!evaluate::CheckConformance(foldingContext.messages(), *lhShape, |
4583 | *rhShape, CheckConformanceFlags::RightScalarExpandable, |
4584 | "left-hand side", "right-hand side") |
4585 | .value_or(true /*ok when conformance is not known now*/)) { |
4586 | fatalErrors_ = true; |
4587 | return false; |
4588 | } |
4589 | } |
4590 | } |
4591 | } |
4592 | return true; // no proven problem |
4593 | } |
4594 | |
4595 | bool ArgumentAnalyzer::CheckForNullPointer(const char *where) { |
4596 | for (const std::optional<ActualArgument> &arg : actuals_) { |
4597 | if (arg && IsNullPointerOrAllocatable(arg->UnwrapExpr())) { |
4598 | context_.Say( |
4599 | source_, "A NULL() pointer is not allowed %s"_err_en_US, where); |
4600 | fatalErrors_ = true; |
4601 | return false; |
4602 | } |
4603 | } |
4604 | return true; |
4605 | } |
4606 | |
4607 | bool ArgumentAnalyzer::CheckForAssumedRank(const char *where) { |
4608 | for (const std::optional<ActualArgument> &arg : actuals_) { |
4609 | if (arg && IsAssumedRank(arg->UnwrapExpr())) { |
4610 | context_.Say(source_, |
4611 | "An assumed-rank dummy argument is not allowed %s"_err_en_US, where); |
4612 | fatalErrors_ = true; |
4613 | return false; |
4614 | } |
4615 | } |
4616 | return true; |
4617 | } |
4618 | |
4619 | MaybeExpr ArgumentAnalyzer::TryDefinedOp( |
4620 | const char *opr, parser::MessageFixedText error, bool isUserOp) { |
4621 | if (AnyUntypedOrMissingOperand()) { |
4622 | context_.Say(error, ToUpperCase(opr), TypeAsFortran(0), TypeAsFortran(1)); |
4623 | return std::nullopt; |
4624 | } |
4625 | MaybeExpr result; |
4626 | bool anyPossibilities{false}; |
4627 | std::optional<parser::MessageFormattedText> inaccessible; |
4628 | std::vector<const Symbol *> hit; |
4629 | std::string oprNameString{ |
4630 | isUserOp ? std::string{opr} : "operator("s+ opr + ')'}; |
4631 | parser::CharBlock oprName{oprNameString}; |
4632 | parser::Messages hitBuffer; |
4633 | { |
4634 | parser::Messages buffer; |
4635 | auto restorer{context_.GetContextualMessages().SetMessages(buffer)}; |
4636 | const auto &scope{context_.context().FindScope(source_)}; |
4637 | |
4638 | auto FoundOne{[&](MaybeExpr &&thisResult, const Symbol &generic, |
4639 | const Symbol *resolution) { |
4640 | anyPossibilities = true; |
4641 | if (thisResult) { |
4642 | if (auto thisInaccessible{CheckAccessibleSymbol(scope, generic)}) { |
4643 | inaccessible = thisInaccessible; |
4644 | } else { |
4645 | bool isElemental{IsElementalProcedure(DEREF(resolution))}; |
4646 | bool hitsAreNonElemental{ |
4647 | !hit.empty() && !IsElementalProcedure(DEREF(hit[0]))}; |
4648 | if (isElemental && hitsAreNonElemental) { |
4649 | // ignore elemental resolutions in favor of a non-elemental one |
4650 | } else { |
4651 | if (!isElemental && !hitsAreNonElemental) { |
4652 | hit.clear(); |
4653 | } |
4654 | result = std::move(thisResult); |
4655 | hit.push_back(resolution); |
4656 | hitBuffer = std::move(buffer); |
4657 | } |
4658 | } |
4659 | } |
4660 | }}; |
4661 | |
4662 | if (Symbol * generic{scope.FindSymbol(oprName)}; generic && !fatalErrors_) { |
4663 | parser::Name name{generic->name(), generic}; |
4664 | const Symbol *resultSymbol{nullptr}; |
4665 | MaybeExpr possibleResult{context_.AnalyzeDefinedOp( |
4666 | name, ActualArguments{actuals_}, resultSymbol)}; |
4667 | FoundOne(std::move(possibleResult), *generic, resultSymbol); |
4668 | } |
4669 | for (std::size_t passIndex{0}; passIndex < actuals_.size(); ++passIndex) { |
4670 | buffer.clear(); |
4671 | const Symbol *generic{nullptr}; |
4672 | if (const Symbol * |
4673 | binding{FindBoundOp( |
4674 | oprName, passIndex, generic, /*isSubroutine=*/false)}) { |
4675 | FoundOne(TryBoundOp(*binding, passIndex), DEREF(generic), binding); |
4676 | } |
4677 | } |
4678 | } |
4679 | if (result) { |
4680 | if (hit.size() > 1) { |
4681 | if (auto *msg{context_.Say( |
4682 | "%zd matching accessible generic interfaces for %s were found"_err_en_US, |
4683 | hit.size(), ToUpperCase(opr))}) { |
4684 | for (const Symbol *symbol : hit) { |
4685 | AttachDeclaration(*msg, *symbol); |
4686 | } |
4687 | } |
4688 | } |
4689 | if (auto *msgs{context_.GetContextualMessages().messages()}) { |
4690 | msgs->Annex(std::move(hitBuffer)); |
4691 | } |
4692 | } else if (inaccessible) { |
4693 | context_.Say(source_, std::move(*inaccessible)); |
4694 | } else if (anyPossibilities) { |
4695 | SayNoMatch(ToUpperCase(str: oprNameString), isAssignment: false); |
4696 | } else if (actuals_.size() == 2 && !AreConformable()) { |
4697 | context_.Say( |
4698 | "Operands of %s are not conformable; have rank %d and rank %d"_err_en_US, |
4699 | ToUpperCase(opr), actuals_[0]->Rank(), actuals_[1]->Rank()); |
4700 | } else if (CheckForNullPointer() && CheckForAssumedRank()) { |
4701 | context_.Say(error, ToUpperCase(opr), TypeAsFortran(0), TypeAsFortran(1)); |
4702 | } |
4703 | return result; |
4704 | } |
4705 | |
4706 | MaybeExpr ArgumentAnalyzer::TryDefinedOp( |
4707 | const std::vector<const char *> &oprs, parser::MessageFixedText error) { |
4708 | if (oprs.size() == 1) { |
4709 | return TryDefinedOp(oprs[0], error); |
4710 | } |
4711 | MaybeExpr result; |
4712 | std::vector<const char *> hit; |
4713 | parser::Messages hitBuffer; |
4714 | { |
4715 | for (std::size_t i{0}; i < oprs.size(); ++i) { |
4716 | parser::Messages buffer; |
4717 | auto restorer{context_.GetContextualMessages().SetMessages(buffer)}; |
4718 | if (MaybeExpr thisResult{TryDefinedOp(oprs[i], error)}) { |
4719 | result = std::move(thisResult); |
4720 | hit.push_back(x: oprs[i]); |
4721 | hitBuffer = std::move(buffer); |
4722 | } |
4723 | } |
4724 | } |
4725 | if (hit.empty()) { // for the error |
4726 | result = TryDefinedOp(oprs[0], error); |
4727 | } else if (hit.size() > 1) { |
4728 | context_.Say( |
4729 | "Matching accessible definitions were found with %zd variant spellings of the generic operator ('%s', '%s')"_err_en_US, |
4730 | hit.size(), ToUpperCase(hit[0]), ToUpperCase(hit[1])); |
4731 | } else { // one hit; preserve errors |
4732 | context_.context().messages().Annex(std::move(hitBuffer)); |
4733 | } |
4734 | return result; |
4735 | } |
4736 | |
4737 | MaybeExpr ArgumentAnalyzer::TryBoundOp(const Symbol &symbol, int passIndex) { |
4738 | ActualArguments localActuals{actuals_}; |
4739 | const Symbol *proc{GetBindingResolution(GetType(passIndex), symbol)}; |
4740 | if (!proc) { |
4741 | proc = &symbol; |
4742 | localActuals.at(passIndex).value().set_isPassedObject(); |
4743 | } |
4744 | CheckConformance(); |
4745 | return context_.MakeFunctionRef( |
4746 | source_, ProcedureDesignator{*proc}, std::move(localActuals)); |
4747 | } |
4748 | |
4749 | std::optional<ProcedureRef> ArgumentAnalyzer::TryDefinedAssignment() { |
4750 | using semantics::Tristate; |
4751 | const Expr<SomeType> &lhs{GetExpr(0)}; |
4752 | const Expr<SomeType> &rhs{GetExpr(1)}; |
4753 | std::optional<DynamicType> lhsType{lhs.GetType()}; |
4754 | std::optional<DynamicType> rhsType{rhs.GetType()}; |
4755 | int lhsRank{lhs.Rank()}; |
4756 | int rhsRank{rhs.Rank()}; |
4757 | Tristate isDefined{ |
4758 | semantics::IsDefinedAssignment(lhsType, lhsRank, rhsType, rhsRank)}; |
4759 | if (isDefined == Tristate::No) { |
4760 | // Make implicit conversion explicit, unless it is an assignment to a whole |
4761 | // allocatable (the explicit conversion would prevent the propagation of the |
4762 | // right hand side if it is a variable). Lowering will deal with the |
4763 | // conversion in this case. |
4764 | if (lhsType) { |
4765 | if (rhsType) { |
4766 | if (!IsAllocatableDesignator(lhs) || context_.inWhereBody()) { |
4767 | AddAssignmentConversion(*lhsType, *rhsType); |
4768 | } |
4769 | } else if (IsBOZLiteral(i: 1)) { |
4770 | ConvertBOZAssignmentRHS(*lhsType); |
4771 | if (IsBOZLiteral(i: 1)) { |
4772 | context_.Say( |
4773 | "Right-hand side of this assignment may not be BOZ"_err_en_US); |
4774 | fatalErrors_ = true; |
4775 | } |
4776 | } |
4777 | } |
4778 | if (!fatalErrors_) { |
4779 | CheckAssignmentConformance(); |
4780 | } |
4781 | return std::nullopt; // user-defined assignment not allowed for these args |
4782 | } |
4783 | auto restorer{context_.GetContextualMessages().SetLocation(source_)}; |
4784 | if (std::optional<ProcedureRef> procRef{GetDefinedAssignmentProc()}) { |
4785 | if (context_.inWhereBody() && !procRef->proc().IsElemental()) { // C1032 |
4786 | context_.Say( |
4787 | "Defined assignment in WHERE must be elemental, but '%s' is not"_err_en_US, |
4788 | DEREF(procRef->proc().GetSymbol()).name()); |
4789 | } |
4790 | context_.CheckCall(source_, procRef->proc(), procRef->arguments()); |
4791 | return std::move(*procRef); |
4792 | } |
4793 | if (isDefined == Tristate::Yes) { |
4794 | if (!lhsType || !rhsType || (lhsRank != rhsRank && rhsRank != 0) || |
4795 | !OkLogicalIntegerAssignment(lhsType->category(), rhsType->category())) { |
4796 | SayNoMatch("ASSIGNMENT(=)", isAssignment: true); |
4797 | } |
4798 | } else if (!fatalErrors_) { |
4799 | CheckAssignmentConformance(); |
4800 | } |
4801 | return std::nullopt; |
4802 | } |
4803 | |
4804 | bool ArgumentAnalyzer::OkLogicalIntegerAssignment( |
4805 | TypeCategory lhs, TypeCategory rhs) { |
4806 | if (!context_.context().languageFeatures().IsEnabled( |
4807 | common::LanguageFeature::LogicalIntegerAssignment)) { |
4808 | return false; |
4809 | } |
4810 | std::optional<parser::MessageFixedText> msg; |
4811 | if (lhs == TypeCategory::Integer && rhs == TypeCategory::Logical) { |
4812 | // allow assignment to LOGICAL from INTEGER as a legacy extension |
4813 | msg = "assignment of LOGICAL to INTEGER"_port_en_US; |
4814 | } else if (lhs == TypeCategory::Logical && rhs == TypeCategory::Integer) { |
4815 | // ... and assignment to LOGICAL from INTEGER |
4816 | msg = "assignment of INTEGER to LOGICAL"_port_en_US; |
4817 | } else { |
4818 | return false; |
4819 | } |
4820 | context_.Warn( |
4821 | common::LanguageFeature::LogicalIntegerAssignment, std::move(*msg)); |
4822 | return true; |
4823 | } |
4824 | |
4825 | std::optional<ProcedureRef> ArgumentAnalyzer::GetDefinedAssignmentProc() { |
4826 | const Symbol *proc{nullptr}; |
4827 | bool isProcElemental{false}; |
4828 | std::optional<int> passedObjectIndex; |
4829 | std::string oprNameString{"assignment(=)"}; |
4830 | parser::CharBlock oprName{oprNameString}; |
4831 | const auto &scope{context_.context().FindScope(source_)}; |
4832 | { |
4833 | auto restorer{context_.GetContextualMessages().DiscardMessages()}; |
4834 | if (const Symbol *symbol{scope.FindSymbol(oprName)}) { |
4835 | ExpressionAnalyzer::AdjustActuals noAdjustment; |
4836 | proc = |
4837 | context_.ResolveGeneric(*symbol, actuals_, noAdjustment, true).first; |
4838 | if (proc) { |
4839 | isProcElemental = IsElementalProcedure(*proc); |
4840 | } |
4841 | } |
4842 | for (std::size_t i{0}; (!proc || isProcElemental) && i < actuals_.size(); |
4843 | ++i) { |
4844 | const Symbol *generic{nullptr}; |
4845 | if (const Symbol * |
4846 | binding{FindBoundOp(oprName, i, generic, /*isSubroutine=*/true)}) { |
4847 | // ignore inaccessible type-bound ASSIGNMENT(=) generic |
4848 | if (!CheckAccessibleSymbol(scope, DEREF(generic))) { |
4849 | const Symbol *resolution{GetBindingResolution(GetType(i), *binding)}; |
4850 | const Symbol &newProc{*(resolution ? resolution : binding)}; |
4851 | bool isElemental{IsElementalProcedure(newProc)}; |
4852 | if (!proc || !isElemental) { |
4853 | // Non-elemental resolution overrides elemental |
4854 | proc = &newProc; |
4855 | isProcElemental = isElemental; |
4856 | if (resolution) { |
4857 | passedObjectIndex.reset(); |
4858 | } else { |
4859 | passedObjectIndex = i; |
4860 | } |
4861 | } |
4862 | } |
4863 | } |
4864 | } |
4865 | } |
4866 | if (!proc) { |
4867 | return std::nullopt; |
4868 | } |
4869 | ActualArguments actualsCopy{actuals_}; |
4870 | // Ensure that the RHS argument is not passed as a variable unless |
4871 | // the dummy argument has the VALUE attribute. |
4872 | if (evaluate::IsVariable(actualsCopy.at(1).value().UnwrapExpr())) { |
4873 | auto chars{evaluate::characteristics::Procedure::Characterize( |
4874 | *proc, context_.GetFoldingContext())}; |
4875 | const auto *rhsDummy{chars && chars->dummyArguments.size() == 2 |
4876 | ? std::get_if<evaluate::characteristics::DummyDataObject>( |
4877 | &chars->dummyArguments.at(1).u) |
4878 | : nullptr}; |
4879 | if (!rhsDummy || |
4880 | !rhsDummy->attrs.test( |
4881 | evaluate::characteristics::DummyDataObject::Attr::Value)) { |
4882 | actualsCopy.at(1).value().Parenthesize(); |
4883 | } |
4884 | } |
4885 | if (passedObjectIndex) { |
4886 | actualsCopy[*passedObjectIndex]->set_isPassedObject(); |
4887 | } |
4888 | return ProcedureRef{ProcedureDesignator{*proc}, std::move(actualsCopy)}; |
4889 | } |
4890 | |
4891 | void ArgumentAnalyzer::Dump(llvm::raw_ostream &os) { |
4892 | os << "source_: "<< source_.ToString() << " fatalErrors_ = "<< fatalErrors_ |
4893 | << '\n'; |
4894 | for (const auto &actual : actuals_) { |
4895 | if (!actual.has_value()) { |
4896 | os << "- error\n"; |
4897 | } else if (const Symbol *symbol{actual->GetAssumedTypeDummy()}) { |
4898 | os << "- assumed type: "<< symbol->name().ToString() << '\n'; |
4899 | } else if (const Expr<SomeType> *expr{actual->UnwrapExpr()}) { |
4900 | expr->AsFortran(os << "- expr: ") << '\n'; |
4901 | } else { |
4902 | DIE("bad ActualArgument"); |
4903 | } |
4904 | } |
4905 | } |
4906 | |
4907 | std::optional<ActualArgument> ArgumentAnalyzer::AnalyzeExpr( |
4908 | const parser::Expr &expr) { |
4909 | source_.ExtendToCover(expr.source); |
4910 | if (const Symbol *assumedTypeDummy{AssumedTypeDummy(expr)}) { |
4911 | ResetExpr(expr); |
4912 | if (isProcedureCall_) { |
4913 | ActualArgument arg{ActualArgument::AssumedType{*assumedTypeDummy}}; |
4914 | SetArgSourceLocation(arg, expr.source); |
4915 | return std::move(arg); |
4916 | } |
4917 | context_.SayAt(expr.source, |
4918 | "TYPE(*) dummy argument may only be used as an actual argument"_err_en_US); |
4919 | } else if (MaybeExpr argExpr{AnalyzeExprOrWholeAssumedSizeArray(expr)}) { |
4920 | if (isProcedureCall_ || !IsProcedureDesignator(*argExpr)) { |
4921 | // Pad Hollerith actual argument with spaces up to a multiple of 8 |
4922 | // bytes, in case the data are interpreted as double precision |
4923 | // (or a smaller numeric type) by legacy code. |
4924 | if (auto hollerith{UnwrapExpr<Constant<Ascii>>(*argExpr)}; |
4925 | hollerith && hollerith->wasHollerith()) { |
4926 | std::string bytes{hollerith->values()}; |
4927 | while ((bytes.size() % 8) != 0) { |
4928 | bytes += ' '; |
4929 | } |
4930 | Constant<Ascii> c{std::move(bytes)}; |
4931 | c.set_wasHollerith(true); |
4932 | argExpr = AsGenericExpr(std::move(c)); |
4933 | } |
4934 | ActualArgument arg{std::move(*argExpr)}; |
4935 | SetArgSourceLocation(arg, expr.source); |
4936 | return std::move(arg); |
4937 | } |
4938 | context_.SayAt(expr.source, |
4939 | IsFunctionDesignator(*argExpr) |
4940 | ? "Function call must have argument list"_err_en_US |
4941 | : "Subroutine name is not allowed here"_err_en_US); |
4942 | } |
4943 | return std::nullopt; |
4944 | } |
4945 | |
4946 | MaybeExpr ArgumentAnalyzer::AnalyzeExprOrWholeAssumedSizeArray( |
4947 | const parser::Expr &expr) { |
4948 | // If an expression's parse tree is a whole assumed-size array: |
4949 | // Expr -> Designator -> DataRef -> Name |
4950 | // treat it as a special case for argument passing and bypass |
4951 | // the C1002/C1014 constraint checking in expression semantics. |
4952 | if (const auto *name{parser::Unwrap<parser::Name>(expr)}) { |
4953 | if (name->symbol && semantics::IsAssumedSizeArray(*name->symbol)) { |
4954 | auto restorer{context_.AllowWholeAssumedSizeArray()}; |
4955 | return context_.Analyze(expr); |
4956 | } |
4957 | } |
4958 | auto restorer{context_.AllowNullPointer()}; |
4959 | return context_.Analyze(expr); |
4960 | } |
4961 | |
4962 | bool ArgumentAnalyzer::AreConformable() const { |
4963 | CHECK(actuals_.size() == 2); |
4964 | return actuals_[0] && actuals_[1] && |
4965 | evaluate::AreConformable(*actuals_[0], *actuals_[1]); |
4966 | } |
4967 | |
4968 | // Look for a type-bound operator in the type of arg number passIndex. |
4969 | const Symbol *ArgumentAnalyzer::FindBoundOp(parser::CharBlock oprName, |
4970 | int passIndex, const Symbol *&generic, bool isSubroutine) { |
4971 | const auto *type{GetDerivedTypeSpec(GetType(passIndex))}; |
4972 | const semantics::Scope *scope{type ? type->scope() : nullptr}; |
4973 | if (scope) { |
4974 | // Use the original type definition's scope, since PDT |
4975 | // instantiations don't have redundant copies of bindings or |
4976 | // generics. |
4977 | scope = DEREF(scope->derivedTypeSpec()).typeSymbol().scope(); |
4978 | } |
4979 | generic = scope ? scope->FindComponent(oprName) : nullptr; |
4980 | if (generic) { |
4981 | ExpressionAnalyzer::AdjustActuals adjustment{ |
4982 | [&](const Symbol &proc, ActualArguments &) { |
4983 | return passIndex == GetPassIndex(proc).value_or(-1); |
4984 | }}; |
4985 | auto pair{ |
4986 | context_.ResolveGeneric(*generic, actuals_, adjustment, isSubroutine)}; |
4987 | if (const Symbol *binding{pair.first}) { |
4988 | CHECK(binding->has<semantics::ProcBindingDetails>()); |
4989 | // Use the most recent override of the binding, if any |
4990 | return scope->FindComponent(binding->name()); |
4991 | } else { |
4992 | context_.EmitGenericResolutionError(*generic, pair.second, isSubroutine); |
4993 | } |
4994 | } |
4995 | return nullptr; |
4996 | } |
4997 | |
4998 | // If there is an implicit conversion between intrinsic types, make it explicit |
4999 | void ArgumentAnalyzer::AddAssignmentConversion( |
5000 | const DynamicType &lhsType, const DynamicType &rhsType) { |
5001 | if (lhsType.category() == rhsType.category() && |
5002 | (lhsType.category() == TypeCategory::Derived || |
5003 | lhsType.kind() == rhsType.kind())) { |
5004 | // no conversion necessary |
5005 | } else if (auto rhsExpr{evaluate::Fold(context_.GetFoldingContext(), |
5006 | evaluate::ConvertToType(lhsType, MoveExpr(1)))}) { |
5007 | std::optional<parser::CharBlock> source; |
5008 | if (actuals_[1]) { |
5009 | source = actuals_[1]->sourceLocation(); |
5010 | } |
5011 | actuals_[1] = ActualArgument{*rhsExpr}; |
5012 | SetArgSourceLocation(actuals_[1], source); |
5013 | } else { |
5014 | actuals_[1] = std::nullopt; |
5015 | } |
5016 | } |
5017 | |
5018 | std::optional<DynamicType> ArgumentAnalyzer::GetType(std::size_t i) const { |
5019 | return i < actuals_.size() ? actuals_[i].value().GetType() : std::nullopt; |
5020 | } |
5021 | int ArgumentAnalyzer::GetRank(std::size_t i) const { |
5022 | return i < actuals_.size() ? actuals_[i].value().Rank() : 0; |
5023 | } |
5024 | |
5025 | // If the argument at index i is a BOZ literal, convert its type to match the |
5026 | // otherType. If it's REAL, convert to REAL; if it's UNSIGNED, convert to |
5027 | // UNSIGNED; otherwise, convert to INTEGER. |
5028 | // Note that IBM supports comparing BOZ literals to CHARACTER operands. That |
5029 | // is not currently supported. |
5030 | void ArgumentAnalyzer::ConvertBOZOperand(std::optional<DynamicType> *thisType, |
5031 | std::size_t i, std::optional<DynamicType> otherType) { |
5032 | if (IsBOZLiteral(i)) { |
5033 | Expr<SomeType> &&argExpr{MoveExpr(i)}; |
5034 | auto *boz{std::get_if<BOZLiteralConstant>(&argExpr.u)}; |
5035 | if (otherType && otherType->category() == TypeCategory::Real) { |
5036 | int kind{context_.context().GetDefaultKind(TypeCategory::Real)}; |
5037 | MaybeExpr realExpr{ |
5038 | ConvertToKind<TypeCategory::Real>(kind, std::move(*boz))}; |
5039 | actuals_[i] = std::move(realExpr.value()); |
5040 | if (thisType) { |
5041 | thisType->emplace(TypeCategory::Real, kind); |
5042 | } |
5043 | } else if (otherType && otherType->category() == TypeCategory::Unsigned) { |
5044 | int kind{context_.context().GetDefaultKind(TypeCategory::Unsigned)}; |
5045 | MaybeExpr unsignedExpr{ |
5046 | ConvertToKind<TypeCategory::Unsigned>(kind, std::move(*boz))}; |
5047 | actuals_[i] = std::move(unsignedExpr.value()); |
5048 | if (thisType) { |
5049 | thisType->emplace(TypeCategory::Unsigned, kind); |
5050 | } |
5051 | } else { |
5052 | int kind{context_.context().GetDefaultKind(TypeCategory::Integer)}; |
5053 | MaybeExpr intExpr{ |
5054 | ConvertToKind<TypeCategory::Integer>(kind, std::move(*boz))}; |
5055 | actuals_[i] = std::move(*intExpr); |
5056 | if (thisType) { |
5057 | thisType->emplace(TypeCategory::Integer, kind); |
5058 | } |
5059 | } |
5060 | } |
5061 | } |
5062 | |
5063 | void ArgumentAnalyzer::ConvertBOZAssignmentRHS(const DynamicType &lhsType) { |
5064 | if (lhsType.category() == TypeCategory::Integer || |
5065 | lhsType.category() == TypeCategory::Unsigned || |
5066 | lhsType.category() == TypeCategory::Real) { |
5067 | Expr<SomeType> rhs{MoveExpr(1)}; |
5068 | if (MaybeExpr converted{ConvertToType(lhsType, std::move(rhs))}) { |
5069 | actuals_[1] = std::move(*converted); |
5070 | } |
5071 | } |
5072 | } |
5073 | |
5074 | // Report error resolving opr when there is a user-defined one available |
5075 | void ArgumentAnalyzer::SayNoMatch(const std::string &opr, bool isAssignment) { |
5076 | std::string type0{TypeAsFortran(0)}; |
5077 | auto rank0{actuals_[0]->Rank()}; |
5078 | if (actuals_.size() == 1) { |
5079 | if (rank0 > 0) { |
5080 | context_.Say("No intrinsic or user-defined %s matches " |
5081 | "rank %d array of %s"_err_en_US, |
5082 | opr, rank0, type0); |
5083 | } else { |
5084 | context_.Say("No intrinsic or user-defined %s matches " |
5085 | "operand type %s"_err_en_US, |
5086 | opr, type0); |
5087 | } |
5088 | } else { |
5089 | std::string type1{TypeAsFortran(1)}; |
5090 | auto rank1{actuals_[1]->Rank()}; |
5091 | if (rank0 > 0 && rank1 > 0 && rank0 != rank1) { |
5092 | context_.Say("No intrinsic or user-defined %s matches " |
5093 | "rank %d array of %s and rank %d array of %s"_err_en_US, |
5094 | opr, rank0, type0, rank1, type1); |
5095 | } else if (isAssignment && rank0 != rank1) { |
5096 | if (rank0 == 0) { |
5097 | context_.Say("No intrinsic or user-defined %s matches " |
5098 | "scalar %s and rank %d array of %s"_err_en_US, |
5099 | opr, type0, rank1, type1); |
5100 | } else { |
5101 | context_.Say("No intrinsic or user-defined %s matches " |
5102 | "rank %d array of %s and scalar %s"_err_en_US, |
5103 | opr, rank0, type0, type1); |
5104 | } |
5105 | } else { |
5106 | context_.Say("No intrinsic or user-defined %s matches " |
5107 | "operand types %s and %s"_err_en_US, |
5108 | opr, type0, type1); |
5109 | } |
5110 | } |
5111 | } |
5112 | |
5113 | std::string ArgumentAnalyzer::TypeAsFortran(std::size_t i) { |
5114 | if (i >= actuals_.size() || !actuals_[i]) { |
5115 | return "missing argument"; |
5116 | } else if (std::optional<DynamicType> type{GetType(i)}) { |
5117 | return type->IsAssumedType() ? "TYPE(*)"s |
5118 | : type->IsUnlimitedPolymorphic() ? "CLASS(*)"s |
5119 | : type->IsPolymorphic() ? type->AsFortran() |
5120 | : type->category() == TypeCategory::Derived |
5121 | ? "TYPE("s+ type->AsFortran() + ')' |
5122 | : type->category() == TypeCategory::Character |
5123 | ? "CHARACTER(KIND="s+ std::to_string(type->kind()) + ')' |
5124 | : ToUpperCase(type->AsFortran()); |
5125 | } else { |
5126 | return "untyped"; |
5127 | } |
5128 | } |
5129 | |
5130 | bool ArgumentAnalyzer::AnyUntypedOrMissingOperand() { |
5131 | for (const auto &actual : actuals_) { |
5132 | if (!actual || |
5133 | (!actual->GetType() && !IsBareNullPointer(actual->UnwrapExpr()))) { |
5134 | return true; |
5135 | } |
5136 | } |
5137 | return false; |
5138 | } |
5139 | } // namespace Fortran::evaluate |
5140 | |
5141 | namespace Fortran::semantics { |
5142 | evaluate::Expr<evaluate::SubscriptInteger> AnalyzeKindSelector( |
5143 | SemanticsContext &context, common::TypeCategory category, |
5144 | const std::optional<parser::KindSelector> &selector) { |
5145 | evaluate::ExpressionAnalyzer analyzer{context}; |
5146 | CHECK(context.location().has_value()); |
5147 | auto restorer{ |
5148 | analyzer.GetContextualMessages().SetLocation(*context.location())}; |
5149 | return analyzer.AnalyzeKindSelector(category, selector); |
5150 | } |
5151 | |
5152 | ExprChecker::ExprChecker(SemanticsContext &context) : context_{context} {} |
5153 | |
5154 | bool ExprChecker::Pre(const parser::DataStmtObject &obj) { |
5155 | exprAnalyzer_.set_inDataStmtObject(true); |
5156 | return true; |
5157 | } |
5158 | |
5159 | void ExprChecker::Post(const parser::DataStmtObject &obj) { |
5160 | exprAnalyzer_.set_inDataStmtObject(false); |
5161 | } |
5162 | |
5163 | bool ExprChecker::Pre(const parser::DataImpliedDo &ido) { |
5164 | parser::Walk(std::get<parser::DataImpliedDo::Bounds>(ido.t), *this); |
5165 | const auto &bounds{std::get<parser::DataImpliedDo::Bounds>(ido.t)}; |
5166 | auto name{bounds.name.thing.thing}; |
5167 | int kind{evaluate::ResultType<evaluate::ImpliedDoIndex>::kind}; |
5168 | if (const auto dynamicType{evaluate::DynamicType::From(*name.symbol)}) { |
5169 | if (dynamicType->category() == TypeCategory::Integer) { |
5170 | kind = dynamicType->kind(); |
5171 | } |
5172 | } |
5173 | exprAnalyzer_.AddImpliedDo(name.source, kind); |
5174 | parser::Walk(std::get<std::list<parser::DataIDoObject>>(ido.t), *this); |
5175 | exprAnalyzer_.RemoveImpliedDo(name.source); |
5176 | return false; |
5177 | } |
5178 | |
5179 | bool ExprChecker::Walk(const parser::Program &program) { |
5180 | parser::Walk(program, *this); |
5181 | return !context_.AnyFatalError(); |
5182 | } |
5183 | } // namespace Fortran::semantics |
5184 |
Definitions
- ToUpperCase
- DynamicTypeWithLength
- DynamicTypeWithLength
- LEN
- AnalyzeTypeSpec
- SetArgSourceLocation
- SetArgSourceLocation
- SetArgSourceLocation
- ArgumentAnalyzer
- ArgumentAnalyzer
- ArgumentAnalyzer
- fatalErrors
- GetActuals
- GetExpr
- MoveExpr
- Analyze
- Analyze
- TryDefinedOp
- IsBOZLiteral
- FoldSubscripts
- ValidateSubscriptValue
- ValidateSubscripts
- CheckSubscripts
- CheckCosubscripts
- FixMisparsedSubstringDataRef
- AsMaybeExpr
- AsMaybeExpr
- IntTypeVisitor
- Test
- ReadRealLiteral
- RealTypeVisitor
- RealTypeVisitor
- Test
- IgnoreAnySubscripts
- MakeSpecific
- ArrayConstructorContext
- ArrayConstructorContext
- Test
- LengthIfGood
- NeedLength
- ToSpecificInt
- GetSpecificIntExpr
- Push
- Add
- Add
- Add
- Add
- UnrollConstantImpliedDo
- ToExpr
- ImplicitConvertTo
- GetPassName
- GetPassIndex
- AddPassArg
- GetBindingResolution
- CheckCompatibleArgument
- CheckCompatibleArguments
- cudaInfMatchingValue
- GetMatchingDistance
- ComputeCudaMatchingDistance
- AssumedTypeDummy
- AssumedTypeDummy
- AssumedTypePointerOrAllocatableDummy
- HasAlternateReturns
- IsExternalCalledImplicitly
- NumericUnaryHelper
- NumericBinaryHelper
- RelationHelper
- LogicalBinaryHelper
- CheckFuncRefToArrayElement
- FixMisparsedFunctionReference
- AnalyzeVariable
- Analyze
- Analyze
- IsIntrinsicRelational
- IsIntrinsicNumeric
- IsIntrinsicLogical
- IsIntrinsicConcat
- CheckConformance
- CheckAssignmentConformance
- CheckForNullPointer
- CheckForAssumedRank
- TryDefinedOp
- TryDefinedOp
- TryBoundOp
- TryDefinedAssignment
- OkLogicalIntegerAssignment
- GetDefinedAssignmentProc
- Dump
- AnalyzeExpr
- AnalyzeExprOrWholeAssumedSizeArray
- AreConformable
- FindBoundOp
- AddAssignmentConversion
- GetType
- GetRank
- ConvertBOZOperand
- ConvertBOZAssignmentRHS
- SayNoMatch
- TypeAsFortran
- AnyUntypedOrMissingOperand
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more