1 //===-- lib/Semantics/check-call.cpp --------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "check-call.h"
10 #include "pointer-assignment.h"
11 #include "flang/Evaluate/characteristics.h"
12 #include "flang/Evaluate/check-expression.h"
13 #include "flang/Evaluate/shape.h"
14 #include "flang/Evaluate/tools.h"
15 #include "flang/Parser/characters.h"
16 #include "flang/Parser/message.h"
17 #include "flang/Semantics/scope.h"
18 #include "flang/Semantics/tools.h"
19 #include <map>
20 #include <string>
21
22 using namespace Fortran::parser::literals;
23 namespace characteristics = Fortran::evaluate::characteristics;
24
25 namespace Fortran::semantics {
26
CheckImplicitInterfaceArg(evaluate::ActualArgument & arg,parser::ContextualMessages & messages)27 static void CheckImplicitInterfaceArg(
28 evaluate::ActualArgument &arg, parser::ContextualMessages &messages) {
29 if (auto kw{arg.keyword()}) {
30 messages.Say(*kw,
31 "Keyword '%s=' may not appear in a reference to a procedure with an implicit interface"_err_en_US,
32 *kw);
33 }
34 if (auto type{arg.GetType()}) {
35 if (type->IsAssumedType()) {
36 messages.Say(
37 "Assumed type argument requires an explicit interface"_err_en_US);
38 } else if (type->IsPolymorphic()) {
39 messages.Say(
40 "Polymorphic argument requires an explicit interface"_err_en_US);
41 } else if (const DerivedTypeSpec * derived{GetDerivedTypeSpec(type)}) {
42 if (!derived->parameters().empty()) {
43 messages.Say(
44 "Parameterized derived type argument requires an explicit interface"_err_en_US);
45 }
46 }
47 }
48 if (const auto *expr{arg.UnwrapExpr()}) {
49 if (auto named{evaluate::ExtractNamedEntity(*expr)}) {
50 const Symbol &symbol{named->GetLastSymbol()};
51 if (symbol.Corank() > 0) {
52 messages.Say(
53 "Coarray argument requires an explicit interface"_err_en_US);
54 }
55 if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
56 if (details->IsAssumedRank()) {
57 messages.Say(
58 "Assumed rank argument requires an explicit interface"_err_en_US);
59 }
60 }
61 if (symbol.attrs().test(Attr::ASYNCHRONOUS)) {
62 messages.Say(
63 "ASYNCHRONOUS argument requires an explicit interface"_err_en_US);
64 }
65 if (symbol.attrs().test(Attr::VOLATILE)) {
66 messages.Say(
67 "VOLATILE argument requires an explicit interface"_err_en_US);
68 }
69 }
70 }
71 }
72
73 // When scalar CHARACTER actual arguments are known to be short,
74 // we extend them on the right with spaces and a warning.
PadShortCharacterActual(evaluate::Expr<evaluate::SomeType> & actual,const characteristics::TypeAndShape & dummyType,characteristics::TypeAndShape & actualType,evaluate::FoldingContext & context,parser::ContextualMessages & messages)75 static void PadShortCharacterActual(evaluate::Expr<evaluate::SomeType> &actual,
76 const characteristics::TypeAndShape &dummyType,
77 characteristics::TypeAndShape &actualType,
78 evaluate::FoldingContext &context, parser::ContextualMessages &messages) {
79 if (dummyType.type().category() == TypeCategory::Character &&
80 actualType.type().category() == TypeCategory::Character &&
81 dummyType.type().kind() == actualType.type().kind() &&
82 GetRank(actualType.shape()) == 0) {
83 if (dummyType.LEN() && actualType.LEN()) {
84 auto dummyLength{ToInt64(Fold(context, common::Clone(*dummyType.LEN())))};
85 auto actualLength{
86 ToInt64(Fold(context, common::Clone(*actualType.LEN())))};
87 if (dummyLength && actualLength && *actualLength < *dummyLength) {
88 messages.Say(
89 "Actual length '%jd' is less than expected length '%jd'"_en_US,
90 *actualLength, *dummyLength);
91 auto converted{ConvertToType(dummyType.type(), std::move(actual))};
92 CHECK(converted);
93 actual = std::move(*converted);
94 actualType.set_LEN(SubscriptIntExpr{*dummyLength});
95 }
96 }
97 }
98 }
99
100 // Automatic conversion of different-kind INTEGER scalar actual
101 // argument expressions (not variables) to INTEGER scalar dummies.
102 // We return nonstandard INTEGER(8) results from intrinsic functions
103 // like SIZE() by default in order to facilitate the use of large
104 // arrays. Emit a warning when downconverting.
ConvertIntegerActual(evaluate::Expr<evaluate::SomeType> & actual,const characteristics::TypeAndShape & dummyType,characteristics::TypeAndShape & actualType,parser::ContextualMessages & messages)105 static void ConvertIntegerActual(evaluate::Expr<evaluate::SomeType> &actual,
106 const characteristics::TypeAndShape &dummyType,
107 characteristics::TypeAndShape &actualType,
108 parser::ContextualMessages &messages) {
109 if (dummyType.type().category() == TypeCategory::Integer &&
110 actualType.type().category() == TypeCategory::Integer &&
111 dummyType.type().kind() != actualType.type().kind() &&
112 GetRank(dummyType.shape()) == 0 && GetRank(actualType.shape()) == 0 &&
113 !evaluate::IsVariable(actual)) {
114 auto converted{
115 evaluate::ConvertToType(dummyType.type(), std::move(actual))};
116 CHECK(converted);
117 actual = std::move(*converted);
118 if (dummyType.type().kind() < actualType.type().kind()) {
119 messages.Say(
120 "Actual argument scalar expression of type INTEGER(%d) was converted to smaller dummy argument type INTEGER(%d)"_en_US,
121 actualType.type().kind(), dummyType.type().kind());
122 }
123 actualType = dummyType;
124 }
125 }
126
DefersSameTypeParameters(const DerivedTypeSpec & actual,const DerivedTypeSpec & dummy)127 static bool DefersSameTypeParameters(
128 const DerivedTypeSpec &actual, const DerivedTypeSpec &dummy) {
129 for (const auto &pair : actual.parameters()) {
130 const ParamValue &actualValue{pair.second};
131 const ParamValue *dummyValue{dummy.FindParameter(pair.first)};
132 if (!dummyValue || (actualValue.isDeferred() != dummyValue->isDeferred())) {
133 return false;
134 }
135 }
136 return true;
137 }
138
CheckExplicitDataArg(const characteristics::DummyDataObject & dummy,const std::string & dummyName,evaluate::Expr<evaluate::SomeType> & actual,characteristics::TypeAndShape & actualType,bool isElemental,bool actualIsArrayElement,evaluate::FoldingContext & context,const Scope * scope,const evaluate::SpecificIntrinsic * intrinsic)139 static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
140 const std::string &dummyName, evaluate::Expr<evaluate::SomeType> &actual,
141 characteristics::TypeAndShape &actualType, bool isElemental,
142 bool actualIsArrayElement, evaluate::FoldingContext &context,
143 const Scope *scope, const evaluate::SpecificIntrinsic *intrinsic) {
144
145 // Basic type & rank checking
146 parser::ContextualMessages &messages{context.messages()};
147 PadShortCharacterActual(actual, dummy.type, actualType, context, messages);
148 ConvertIntegerActual(actual, dummy.type, actualType, messages);
149 bool typesCompatible{dummy.type.type().IsTkCompatibleWith(actualType.type())};
150 if (typesCompatible) {
151 if (isElemental) {
152 } else if (dummy.type.attrs().test(
153 characteristics::TypeAndShape::Attr::AssumedRank)) {
154 } else if (!dummy.type.attrs().test(
155 characteristics::TypeAndShape::Attr::AssumedShape) &&
156 (actualType.Rank() > 0 || actualIsArrayElement)) {
157 // Sequence association (15.5.2.11) applies -- rank need not match
158 // if the actual argument is an array or array element designator.
159 } else {
160 // Let CheckConformance accept scalars; storage association
161 // cases are checked here below.
162 CheckConformance(messages, dummy.type.shape(), actualType.shape(),
163 "dummy argument", "actual argument", true, true);
164 }
165 } else {
166 const auto &len{actualType.LEN()};
167 messages.Say(
168 "Actual argument type '%s' is not compatible with dummy argument type '%s'"_err_en_US,
169 actualType.type().AsFortran(len ? len->AsFortran() : ""),
170 dummy.type.type().AsFortran());
171 }
172
173 bool actualIsPolymorphic{actualType.type().IsPolymorphic()};
174 bool dummyIsPolymorphic{dummy.type.type().IsPolymorphic()};
175 bool actualIsCoindexed{ExtractCoarrayRef(actual).has_value()};
176 bool actualIsAssumedSize{actualType.attrs().test(
177 characteristics::TypeAndShape::Attr::AssumedSize)};
178 bool dummyIsAssumedSize{dummy.type.attrs().test(
179 characteristics::TypeAndShape::Attr::AssumedSize)};
180 bool dummyIsAsynchronous{
181 dummy.attrs.test(characteristics::DummyDataObject::Attr::Asynchronous)};
182 bool dummyIsVolatile{
183 dummy.attrs.test(characteristics::DummyDataObject::Attr::Volatile)};
184 bool dummyIsValue{
185 dummy.attrs.test(characteristics::DummyDataObject::Attr::Value)};
186
187 if (actualIsPolymorphic && dummyIsPolymorphic &&
188 actualIsCoindexed) { // 15.5.2.4(2)
189 messages.Say(
190 "Coindexed polymorphic object may not be associated with a polymorphic %s"_err_en_US,
191 dummyName);
192 }
193 if (actualIsPolymorphic && !dummyIsPolymorphic &&
194 actualIsAssumedSize) { // 15.5.2.4(2)
195 messages.Say(
196 "Assumed-size polymorphic array may not be associated with a monomorphic %s"_err_en_US,
197 dummyName);
198 }
199
200 // Derived type actual argument checks
201 const Symbol *actualFirstSymbol{evaluate::GetFirstSymbol(actual)};
202 bool actualIsAsynchronous{
203 actualFirstSymbol && actualFirstSymbol->attrs().test(Attr::ASYNCHRONOUS)};
204 bool actualIsVolatile{
205 actualFirstSymbol && actualFirstSymbol->attrs().test(Attr::VOLATILE)};
206 if (const auto *derived{evaluate::GetDerivedTypeSpec(actualType.type())}) {
207 if (dummy.type.type().IsAssumedType()) {
208 if (!derived->parameters().empty()) { // 15.5.2.4(2)
209 messages.Say(
210 "Actual argument associated with TYPE(*) %s may not have a parameterized derived type"_err_en_US,
211 dummyName);
212 }
213 if (const Symbol *
214 tbp{FindImmediateComponent(*derived, [](const Symbol &symbol) {
215 return symbol.has<ProcBindingDetails>();
216 })}) { // 15.5.2.4(2)
217 evaluate::SayWithDeclaration(messages, *tbp,
218 "Actual argument associated with TYPE(*) %s may not have type-bound procedure '%s'"_err_en_US,
219 dummyName, tbp->name());
220 }
221 const auto &finals{
222 derived->typeSymbol().get<DerivedTypeDetails>().finals()};
223 if (!finals.empty()) { // 15.5.2.4(2)
224 if (auto *msg{messages.Say(
225 "Actual argument associated with TYPE(*) %s may not have derived type '%s' with FINAL subroutine '%s'"_err_en_US,
226 dummyName, derived->typeSymbol().name(),
227 finals.begin()->first)}) {
228 msg->Attach(finals.begin()->first,
229 "FINAL subroutine '%s' in derived type '%s'"_en_US,
230 finals.begin()->first, derived->typeSymbol().name());
231 }
232 }
233 }
234 if (actualIsCoindexed) {
235 if (dummy.intent != common::Intent::In && !dummyIsValue) {
236 if (auto bad{
237 FindAllocatableUltimateComponent(*derived)}) { // 15.5.2.4(6)
238 evaluate::SayWithDeclaration(messages, *bad,
239 "Coindexed actual argument with ALLOCATABLE ultimate component '%s' must be associated with a %s with VALUE or INTENT(IN) attributes"_err_en_US,
240 bad.BuildResultDesignatorName(), dummyName);
241 }
242 }
243 if (auto coarrayRef{evaluate::ExtractCoarrayRef(actual)}) { // C1537
244 const Symbol &coarray{coarrayRef->GetLastSymbol()};
245 if (const DeclTypeSpec * type{coarray.GetType()}) {
246 if (const DerivedTypeSpec * derived{type->AsDerived()}) {
247 if (auto bad{semantics::FindPointerUltimateComponent(*derived)}) {
248 evaluate::SayWithDeclaration(messages, coarray,
249 "Coindexed object '%s' with POINTER ultimate component '%s' cannot be associated with %s"_err_en_US,
250 coarray.name(), bad.BuildResultDesignatorName(), dummyName);
251 }
252 }
253 }
254 }
255 }
256 if (actualIsVolatile != dummyIsVolatile) { // 15.5.2.4(22)
257 if (auto bad{semantics::FindCoarrayUltimateComponent(*derived)}) {
258 evaluate::SayWithDeclaration(messages, *bad,
259 "VOLATILE attribute must match for %s when actual argument has a coarray ultimate component '%s'"_err_en_US,
260 dummyName, bad.BuildResultDesignatorName());
261 }
262 }
263 }
264
265 // Rank and shape checks
266 const auto *actualLastSymbol{evaluate::GetLastSymbol(actual)};
267 if (actualLastSymbol) {
268 actualLastSymbol = GetAssociationRoot(*actualLastSymbol);
269 }
270 const ObjectEntityDetails *actualLastObject{actualLastSymbol
271 ? actualLastSymbol->GetUltimate().detailsIf<ObjectEntityDetails>()
272 : nullptr};
273 int actualRank{evaluate::GetRank(actualType.shape())};
274 bool actualIsPointer{(actualLastSymbol && IsPointer(*actualLastSymbol)) ||
275 evaluate::IsNullPointer(actual)};
276 if (dummy.type.attrs().test(
277 characteristics::TypeAndShape::Attr::AssumedShape)) {
278 // 15.5.2.4(16)
279 if (actualRank == 0) {
280 messages.Say(
281 "Scalar actual argument may not be associated with assumed-shape %s"_err_en_US,
282 dummyName);
283 }
284 if (actualIsAssumedSize && actualLastSymbol) {
285 evaluate::SayWithDeclaration(messages, *actualLastSymbol,
286 "Assumed-size array may not be associated with assumed-shape %s"_err_en_US,
287 dummyName);
288 }
289 } else if (actualRank == 0 && dummy.type.Rank() > 0) {
290 // Actual is scalar, dummy is an array. 15.5.2.4(14), 15.5.2.11
291 if (actualIsCoindexed) {
292 messages.Say(
293 "Coindexed scalar actual argument must be associated with a scalar %s"_err_en_US,
294 dummyName);
295 }
296 if (actualLastSymbol && actualLastSymbol->Rank() == 0 &&
297 !(dummy.type.type().IsAssumedType() && dummyIsAssumedSize)) {
298 messages.Say(
299 "Whole scalar actual argument may not be associated with a %s array"_err_en_US,
300 dummyName);
301 }
302 if (actualIsPolymorphic) {
303 messages.Say(
304 "Polymorphic scalar may not be associated with a %s array"_err_en_US,
305 dummyName);
306 }
307 if (actualIsPointer) {
308 messages.Say(
309 "Scalar POINTER target may not be associated with a %s array"_err_en_US,
310 dummyName);
311 }
312 if (actualLastObject && actualLastObject->IsAssumedShape()) {
313 messages.Say(
314 "Element of assumed-shape array may not be associated with a %s array"_err_en_US,
315 dummyName);
316 }
317 }
318 if (actualLastObject && actualLastObject->IsCoarray() &&
319 IsAllocatable(*actualLastSymbol) && dummy.intent == common::Intent::Out &&
320 !(intrinsic &&
321 evaluate::AcceptsIntentOutAllocatableCoarray(
322 intrinsic->name))) { // C846
323 messages.Say(
324 "ALLOCATABLE coarray '%s' may not be associated with INTENT(OUT) %s"_err_en_US,
325 actualLastSymbol->name(), dummyName);
326 }
327
328 // Definability
329 const char *reason{nullptr};
330 if (dummy.intent == common::Intent::Out) {
331 reason = "INTENT(OUT)";
332 } else if (dummy.intent == common::Intent::InOut) {
333 reason = "INTENT(IN OUT)";
334 } else if (dummyIsAsynchronous) {
335 reason = "ASYNCHRONOUS";
336 } else if (dummyIsVolatile) {
337 reason = "VOLATILE";
338 }
339 if (reason && scope) {
340 bool vectorSubscriptIsOk{isElemental || dummyIsValue}; // 15.5.2.4(21)
341 if (auto why{WhyNotModifiable(
342 messages.at(), actual, *scope, vectorSubscriptIsOk)}) {
343 if (auto *msg{messages.Say(
344 "Actual argument associated with %s %s must be definable"_err_en_US, // C1158
345 reason, dummyName)}) {
346 msg->Attach(*why);
347 }
348 }
349 }
350
351 // Cases when temporaries might be needed but must not be permitted.
352 bool dummyIsPointer{
353 dummy.attrs.test(characteristics::DummyDataObject::Attr::Pointer)};
354 bool dummyIsContiguous{
355 dummy.attrs.test(characteristics::DummyDataObject::Attr::Contiguous)};
356 bool actualIsContiguous{IsSimplyContiguous(actual, context)};
357 bool dummyIsAssumedRank{dummy.type.attrs().test(
358 characteristics::TypeAndShape::Attr::AssumedRank)};
359 bool dummyIsAssumedShape{dummy.type.attrs().test(
360 characteristics::TypeAndShape::Attr::AssumedShape)};
361 if ((actualIsAsynchronous || actualIsVolatile) &&
362 (dummyIsAsynchronous || dummyIsVolatile) && !dummyIsValue) {
363 if (actualIsCoindexed) { // C1538
364 messages.Say(
365 "Coindexed ASYNCHRONOUS or VOLATILE actual argument may not be associated with %s with ASYNCHRONOUS or VOLATILE attributes unless VALUE"_err_en_US,
366 dummyName);
367 }
368 if (actualRank > 0 && !actualIsContiguous) {
369 if (dummyIsContiguous ||
370 !(dummyIsAssumedShape || dummyIsAssumedRank ||
371 (actualIsPointer && dummyIsPointer))) { // C1539 & C1540
372 messages.Say(
373 "ASYNCHRONOUS or VOLATILE actual argument that is not simply contiguous may not be associated with a contiguous %s"_err_en_US,
374 dummyName);
375 }
376 }
377 }
378
379 // 15.5.2.6 -- dummy is ALLOCATABLE
380 bool dummyIsAllocatable{
381 dummy.attrs.test(characteristics::DummyDataObject::Attr::Allocatable)};
382 bool actualIsAllocatable{
383 actualLastSymbol && IsAllocatable(*actualLastSymbol)};
384 if (dummyIsAllocatable) {
385 if (!actualIsAllocatable) {
386 messages.Say(
387 "ALLOCATABLE %s must be associated with an ALLOCATABLE actual argument"_err_en_US,
388 dummyName);
389 }
390 if (actualIsAllocatable && actualIsCoindexed &&
391 dummy.intent != common::Intent::In) {
392 messages.Say(
393 "ALLOCATABLE %s must have INTENT(IN) to be associated with a coindexed actual argument"_err_en_US,
394 dummyName);
395 }
396 if (!actualIsCoindexed && actualLastSymbol &&
397 actualLastSymbol->Corank() != dummy.type.corank()) {
398 messages.Say(
399 "ALLOCATABLE %s has corank %d but actual argument has corank %d"_err_en_US,
400 dummyName, dummy.type.corank(), actualLastSymbol->Corank());
401 }
402 }
403
404 // 15.5.2.7 -- dummy is POINTER
405 if (dummyIsPointer) {
406 if (dummyIsContiguous && !actualIsContiguous) {
407 messages.Say(
408 "Actual argument associated with CONTIGUOUS POINTER %s must be simply contiguous"_err_en_US,
409 dummyName);
410 }
411 if (!actualIsPointer) {
412 if (dummy.intent == common::Intent::In) {
413 semantics::CheckPointerAssignment(
414 context, parser::CharBlock{}, dummyName, dummy, actual);
415 } else {
416 messages.Say(
417 "Actual argument associated with POINTER %s must also be POINTER unless INTENT(IN)"_err_en_US,
418 dummyName);
419 }
420 }
421 }
422
423 // 15.5.2.5 -- actual & dummy are both POINTER or both ALLOCATABLE
424 if ((actualIsPointer && dummyIsPointer) ||
425 (actualIsAllocatable && dummyIsAllocatable)) {
426 bool actualIsUnlimited{actualType.type().IsUnlimitedPolymorphic()};
427 bool dummyIsUnlimited{dummy.type.type().IsUnlimitedPolymorphic()};
428 if (actualIsUnlimited != dummyIsUnlimited) {
429 if (typesCompatible) {
430 messages.Say(
431 "If a POINTER or ALLOCATABLE dummy or actual argument is unlimited polymorphic, both must be so"_err_en_US);
432 }
433 } else if (dummyIsPolymorphic != actualIsPolymorphic) {
434 if (dummy.intent == common::Intent::In && typesCompatible) {
435 // extension: allow with warning, rule is only relevant for definables
436 messages.Say(
437 "If a POINTER or ALLOCATABLE dummy or actual argument is polymorphic, both should be so"_en_US);
438 } else {
439 messages.Say(
440 "If a POINTER or ALLOCATABLE dummy or actual argument is polymorphic, both must be so"_err_en_US);
441 }
442 } else if (!actualIsUnlimited && typesCompatible) {
443 if (!actualType.type().IsTkCompatibleWith(dummy.type.type())) {
444 if (dummy.intent == common::Intent::In) {
445 // extension: allow with warning, rule is only relevant for definables
446 messages.Say(
447 "POINTER or ALLOCATABLE dummy and actual arguments should have the same declared type and kind"_en_US);
448 } else {
449 messages.Say(
450 "POINTER or ALLOCATABLE dummy and actual arguments must have the same declared type and kind"_err_en_US);
451 }
452 }
453 if (const auto *derived{
454 evaluate::GetDerivedTypeSpec(actualType.type())}) {
455 if (!DefersSameTypeParameters(
456 *derived, *evaluate::GetDerivedTypeSpec(dummy.type.type()))) {
457 messages.Say(
458 "Dummy and actual arguments must defer the same type parameters when POINTER or ALLOCATABLE"_err_en_US);
459 }
460 }
461 }
462 }
463
464 // 15.5.2.8 -- coarray dummy arguments
465 if (dummy.type.corank() > 0) {
466 if (actualType.corank() == 0) {
467 messages.Say(
468 "Actual argument associated with coarray %s must be a coarray"_err_en_US,
469 dummyName);
470 }
471 if (dummyIsVolatile) {
472 if (!actualIsVolatile) {
473 messages.Say(
474 "non-VOLATILE coarray may not be associated with VOLATILE coarray %s"_err_en_US,
475 dummyName);
476 }
477 } else {
478 if (actualIsVolatile) {
479 messages.Say(
480 "VOLATILE coarray may not be associated with non-VOLATILE coarray %s"_err_en_US,
481 dummyName);
482 }
483 }
484 if (actualRank == dummy.type.Rank() && !actualIsContiguous) {
485 if (dummyIsContiguous) {
486 messages.Say(
487 "Actual argument associated with a CONTIGUOUS coarray %s must be simply contiguous"_err_en_US,
488 dummyName);
489 } else if (!dummyIsAssumedShape && !dummyIsAssumedRank) {
490 messages.Say(
491 "Actual argument associated with coarray %s (not assumed shape or rank) must be simply contiguous"_err_en_US,
492 dummyName);
493 }
494 }
495 }
496 }
497
CheckProcedureArg(evaluate::ActualArgument & arg,const characteristics::DummyProcedure & proc,const std::string & dummyName,evaluate::FoldingContext & context)498 static void CheckProcedureArg(evaluate::ActualArgument &arg,
499 const characteristics::DummyProcedure &proc, const std::string &dummyName,
500 evaluate::FoldingContext &context) {
501 parser::ContextualMessages &messages{context.messages()};
502 const characteristics::Procedure &interface{proc.procedure.value()};
503 if (const auto *expr{arg.UnwrapExpr()}) {
504 bool dummyIsPointer{
505 proc.attrs.test(characteristics::DummyProcedure::Attr::Pointer)};
506 const auto *argProcDesignator{
507 std::get_if<evaluate::ProcedureDesignator>(&expr->u)};
508 const auto *argProcSymbol{
509 argProcDesignator ? argProcDesignator->GetSymbol() : nullptr};
510 if (auto argChars{characteristics::DummyArgument::FromActual(
511 "actual argument", *expr, context)}) {
512 if (!argChars->IsTypelessIntrinsicDummy()) {
513 if (auto *argProc{
514 std::get_if<characteristics::DummyProcedure>(&argChars->u)}) {
515 characteristics::Procedure &argInterface{argProc->procedure.value()};
516 argInterface.attrs.reset(
517 characteristics::Procedure::Attr::NullPointer);
518 if (!argProcSymbol || argProcSymbol->attrs().test(Attr::INTRINSIC)) {
519 // It's ok to pass ELEMENTAL unrestricted intrinsic functions.
520 argInterface.attrs.reset(
521 characteristics::Procedure::Attr::Elemental);
522 } else if (argInterface.attrs.test(
523 characteristics::Procedure::Attr::Elemental)) {
524 if (argProcSymbol) { // C1533
525 evaluate::SayWithDeclaration(messages, *argProcSymbol,
526 "Non-intrinsic ELEMENTAL procedure '%s' may not be passed as an actual argument"_err_en_US,
527 argProcSymbol->name());
528 return; // avoid piling on with checks below
529 } else {
530 argInterface.attrs.reset(
531 characteristics::Procedure::Attr::NullPointer);
532 }
533 }
534 if (!interface.IsPure()) {
535 // 15.5.2.9(1): if dummy is not pure, actual need not be.
536 argInterface.attrs.reset(characteristics::Procedure::Attr::Pure);
537 }
538 if (interface.HasExplicitInterface()) {
539 if (interface != argInterface) {
540 messages.Say(
541 "Actual argument procedure has interface incompatible with %s"_err_en_US,
542 dummyName);
543 }
544 } else { // 15.5.2.9(2,3)
545 if (interface.IsSubroutine() && argInterface.IsFunction()) {
546 messages.Say(
547 "Actual argument associated with procedure %s is a function but must be a subroutine"_err_en_US,
548 dummyName);
549 } else if (interface.IsFunction()) {
550 if (argInterface.IsFunction()) {
551 if (interface.functionResult != argInterface.functionResult) {
552 messages.Say(
553 "Actual argument function associated with procedure %s has incompatible result type"_err_en_US,
554 dummyName);
555 }
556 } else if (argInterface.IsSubroutine()) {
557 messages.Say(
558 "Actual argument associated with procedure %s is a subroutine but must be a function"_err_en_US,
559 dummyName);
560 }
561 }
562 }
563 } else {
564 messages.Say(
565 "Actual argument associated with procedure %s is not a procedure"_err_en_US,
566 dummyName);
567 }
568 } else if (!(dummyIsPointer && IsNullPointer(*expr))) {
569 messages.Say(
570 "Actual argument associated with procedure %s is not a procedure"_err_en_US,
571 dummyName);
572 }
573 }
574 if (interface.HasExplicitInterface()) {
575 if (dummyIsPointer) {
576 // 15.5.2.9(5) -- dummy procedure POINTER
577 // Interface compatibility has already been checked above by comparison.
578 if (proc.intent != common::Intent::In && !IsVariable(*expr)) {
579 messages.Say(
580 "Actual argument associated with procedure pointer %s must be a POINTER unless INTENT(IN)"_err_en_US,
581 dummyName);
582 }
583 } else { // 15.5.2.9(4) -- dummy procedure is not POINTER
584 if (!argProcDesignator) {
585 messages.Say(
586 "Actual argument associated with non-POINTER procedure %s must be a procedure (and not a procedure pointer)"_err_en_US,
587 dummyName);
588 }
589 }
590 }
591 } else {
592 messages.Say(
593 "Assumed-type argument may not be forwarded as procedure %s"_err_en_US,
594 dummyName);
595 }
596 }
597
CheckExplicitInterfaceArg(evaluate::ActualArgument & arg,const characteristics::DummyArgument & dummy,const characteristics::Procedure & proc,evaluate::FoldingContext & context,const Scope * scope,const evaluate::SpecificIntrinsic * intrinsic)598 static void CheckExplicitInterfaceArg(evaluate::ActualArgument &arg,
599 const characteristics::DummyArgument &dummy,
600 const characteristics::Procedure &proc, evaluate::FoldingContext &context,
601 const Scope *scope, const evaluate::SpecificIntrinsic *intrinsic) {
602 auto &messages{context.messages()};
603 std::string dummyName{"dummy argument"};
604 if (!dummy.name.empty()) {
605 dummyName += " '"s + parser::ToLowerCaseLetters(dummy.name) + "='";
606 }
607 std::visit(
608 common::visitors{
609 [&](const characteristics::DummyDataObject &object) {
610 if (auto *expr{arg.UnwrapExpr()}) {
611 if (auto type{characteristics::TypeAndShape::Characterize(
612 *expr, context)}) {
613 arg.set_dummyIntent(object.intent);
614 bool isElemental{object.type.Rank() == 0 && proc.IsElemental()};
615 CheckExplicitDataArg(object, dummyName, *expr, *type,
616 isElemental, IsArrayElement(*expr), context, scope,
617 intrinsic);
618 } else if (object.type.type().IsTypelessIntrinsicArgument() &&
619 std::holds_alternative<evaluate::BOZLiteralConstant>(
620 expr->u)) {
621 // ok
622 } else if (object.type.type().IsTypelessIntrinsicArgument() &&
623 evaluate::IsNullPointer(*expr)) {
624 // ok, calling ASSOCIATED(NULL())
625 } else {
626 messages.Say(
627 "Actual argument '%s' associated with %s is not a variable or typed expression"_err_en_US,
628 expr->AsFortran(), dummyName);
629 }
630 } else {
631 const Symbol &assumed{DEREF(arg.GetAssumedTypeDummy())};
632 if (!object.type.type().IsAssumedType()) {
633 messages.Say(
634 "Assumed-type '%s' may be associated only with an assumed-type %s"_err_en_US,
635 assumed.name(), dummyName);
636 } else if (const auto *details{
637 assumed.detailsIf<ObjectEntityDetails>()}) {
638 if (!(details->IsAssumedShape() || details->IsAssumedRank())) {
639 messages.Say( // C711
640 "Assumed-type '%s' must be either assumed shape or assumed rank to be associated with assumed-type %s"_err_en_US,
641 assumed.name(), dummyName);
642 }
643 }
644 }
645 },
646 [&](const characteristics::DummyProcedure &proc) {
647 CheckProcedureArg(arg, proc, dummyName, context);
648 },
649 [&](const characteristics::AlternateReturn &) {
650 // TODO check alternate return
651 },
652 },
653 dummy.u);
654 }
655
RearrangeArguments(const characteristics::Procedure & proc,evaluate::ActualArguments & actuals,parser::ContextualMessages & messages)656 static void RearrangeArguments(const characteristics::Procedure &proc,
657 evaluate::ActualArguments &actuals, parser::ContextualMessages &messages) {
658 CHECK(proc.HasExplicitInterface());
659 if (actuals.size() < proc.dummyArguments.size()) {
660 actuals.resize(proc.dummyArguments.size());
661 } else if (actuals.size() > proc.dummyArguments.size()) {
662 messages.Say(
663 "Too many actual arguments (%zd) passed to procedure that expects only %zd"_err_en_US,
664 actuals.size(), proc.dummyArguments.size());
665 }
666 std::map<std::string, evaluate::ActualArgument> kwArgs;
667 for (auto &x : actuals) {
668 if (x && x->keyword()) {
669 auto emplaced{
670 kwArgs.try_emplace(x->keyword()->ToString(), std::move(*x))};
671 if (!emplaced.second) {
672 messages.Say(*x->keyword(),
673 "Argument keyword '%s=' appears on more than one effective argument in this procedure reference"_err_en_US,
674 *x->keyword());
675 }
676 x.reset();
677 }
678 }
679 if (!kwArgs.empty()) {
680 int index{0};
681 for (const auto &dummy : proc.dummyArguments) {
682 if (!dummy.name.empty()) {
683 auto iter{kwArgs.find(dummy.name)};
684 if (iter != kwArgs.end()) {
685 evaluate::ActualArgument &x{iter->second};
686 if (actuals[index]) {
687 messages.Say(*x.keyword(),
688 "Keyword argument '%s=' has already been specified positionally (#%d) in this procedure reference"_err_en_US,
689 *x.keyword(), index + 1);
690 } else {
691 actuals[index] = std::move(x);
692 }
693 kwArgs.erase(iter);
694 }
695 }
696 ++index;
697 }
698 for (auto &bad : kwArgs) {
699 evaluate::ActualArgument &x{bad.second};
700 messages.Say(*x.keyword(),
701 "Argument keyword '%s=' is not recognized for this procedure reference"_err_en_US,
702 *x.keyword());
703 }
704 }
705 }
706
CheckExplicitInterface(const characteristics::Procedure & proc,evaluate::ActualArguments & actuals,const evaluate::FoldingContext & context,const Scope * scope,const evaluate::SpecificIntrinsic * intrinsic)707 static parser::Messages CheckExplicitInterface(
708 const characteristics::Procedure &proc, evaluate::ActualArguments &actuals,
709 const evaluate::FoldingContext &context, const Scope *scope,
710 const evaluate::SpecificIntrinsic *intrinsic) {
711 parser::Messages buffer;
712 parser::ContextualMessages messages{context.messages().at(), &buffer};
713 RearrangeArguments(proc, actuals, messages);
714 if (buffer.empty()) {
715 int index{0};
716 evaluate::FoldingContext localContext{context, messages};
717 for (auto &actual : actuals) {
718 const auto &dummy{proc.dummyArguments.at(index++)};
719 if (actual) {
720 CheckExplicitInterfaceArg(
721 *actual, dummy, proc, localContext, scope, intrinsic);
722 } else if (!dummy.IsOptional()) {
723 if (dummy.name.empty()) {
724 messages.Say(
725 "Dummy argument #%d is not OPTIONAL and is not associated with "
726 "an actual argument in this procedure reference"_err_en_US,
727 index);
728 } else {
729 messages.Say("Dummy argument '%s=' (#%d) is not OPTIONAL and is not "
730 "associated with an actual argument in this procedure "
731 "reference"_err_en_US,
732 dummy.name, index);
733 }
734 }
735 }
736 }
737 return buffer;
738 }
739
CheckExplicitInterface(const characteristics::Procedure & proc,evaluate::ActualArguments & actuals,const evaluate::FoldingContext & context,const Scope & scope,const evaluate::SpecificIntrinsic * intrinsic)740 parser::Messages CheckExplicitInterface(const characteristics::Procedure &proc,
741 evaluate::ActualArguments &actuals, const evaluate::FoldingContext &context,
742 const Scope &scope, const evaluate::SpecificIntrinsic *intrinsic) {
743 return CheckExplicitInterface(proc, actuals, context, &scope, intrinsic);
744 }
745
CheckInterfaceForGeneric(const characteristics::Procedure & proc,evaluate::ActualArguments & actuals,const evaluate::FoldingContext & context)746 bool CheckInterfaceForGeneric(const characteristics::Procedure &proc,
747 evaluate::ActualArguments &actuals,
748 const evaluate::FoldingContext &context) {
749 return CheckExplicitInterface(proc, actuals, context, nullptr, nullptr)
750 .empty();
751 }
752
CheckArguments(const characteristics::Procedure & proc,evaluate::ActualArguments & actuals,evaluate::FoldingContext & context,const Scope & scope,bool treatingExternalAsImplicit,const evaluate::SpecificIntrinsic * intrinsic)753 void CheckArguments(const characteristics::Procedure &proc,
754 evaluate::ActualArguments &actuals, evaluate::FoldingContext &context,
755 const Scope &scope, bool treatingExternalAsImplicit,
756 const evaluate::SpecificIntrinsic *intrinsic) {
757 bool explicitInterface{proc.HasExplicitInterface()};
758 if (explicitInterface) {
759 auto buffer{
760 CheckExplicitInterface(proc, actuals, context, scope, intrinsic)};
761 if (treatingExternalAsImplicit && !buffer.empty()) {
762 if (auto *msg{context.messages().Say(
763 "Warning: if the procedure's interface were explicit, this reference would be in error:"_en_US)}) {
764 buffer.AttachTo(*msg);
765 }
766 }
767 if (auto *msgs{context.messages().messages()}) {
768 msgs->Merge(std::move(buffer));
769 }
770 }
771 if (!explicitInterface || treatingExternalAsImplicit) {
772 for (auto &actual : actuals) {
773 if (actual) {
774 CheckImplicitInterfaceArg(*actual, context.messages());
775 }
776 }
777 }
778 }
779 } // namespace Fortran::semantics
780