File: | build/source/flang/lib/Semantics/check-declarations.cpp |
Warning: | line 1305, column 11 Forming reference to null pointer |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===-- lib/Semantics/check-declarations.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 | // Static declaration checking | |||
10 | ||||
11 | #include "check-declarations.h" | |||
12 | #include "definable.h" | |||
13 | #include "pointer-assignment.h" | |||
14 | #include "flang/Evaluate/check-expression.h" | |||
15 | #include "flang/Evaluate/fold.h" | |||
16 | #include "flang/Evaluate/tools.h" | |||
17 | #include "flang/Parser/characters.h" | |||
18 | #include "flang/Semantics/scope.h" | |||
19 | #include "flang/Semantics/semantics.h" | |||
20 | #include "flang/Semantics/symbol.h" | |||
21 | #include "flang/Semantics/tools.h" | |||
22 | #include "flang/Semantics/type.h" | |||
23 | #include <algorithm> | |||
24 | #include <map> | |||
25 | #include <string> | |||
26 | ||||
27 | namespace Fortran::semantics { | |||
28 | ||||
29 | namespace characteristics = evaluate::characteristics; | |||
30 | using characteristics::DummyArgument; | |||
31 | using characteristics::DummyDataObject; | |||
32 | using characteristics::DummyProcedure; | |||
33 | using characteristics::FunctionResult; | |||
34 | using characteristics::Procedure; | |||
35 | ||||
36 | class CheckHelper { | |||
37 | public: | |||
38 | explicit CheckHelper(SemanticsContext &c) : context_{c} {} | |||
39 | ||||
40 | SemanticsContext &context() { return context_; } | |||
41 | void Check() { Check(context_.globalScope()); } | |||
42 | void Check(const ParamValue &, bool canBeAssumed); | |||
43 | void Check(const Bound &bound) { CheckSpecExpr(bound.GetExplicit()); } | |||
44 | void Check(const ShapeSpec &spec) { | |||
45 | Check(spec.lbound()); | |||
46 | Check(spec.ubound()); | |||
47 | } | |||
48 | void Check(const ArraySpec &); | |||
49 | void Check(const DeclTypeSpec &, bool canHaveAssumedTypeParameters); | |||
50 | void Check(const Symbol &); | |||
51 | void CheckCommonBlock(const Symbol &); | |||
52 | void Check(const Scope &); | |||
53 | const Procedure *Characterize(const Symbol &); | |||
54 | ||||
55 | private: | |||
56 | template <typename A> void CheckSpecExpr(const A &x) { | |||
57 | evaluate::CheckSpecificationExpr(x, DEREF(scope_)Fortran::common::Deref(scope_, "flang/lib/Semantics/check-declarations.cpp" , 57), foldingContext_); | |||
58 | } | |||
59 | void CheckValue(const Symbol &, const DerivedTypeSpec *); | |||
60 | void CheckVolatile(const Symbol &, const DerivedTypeSpec *); | |||
61 | void CheckPointer(const Symbol &); | |||
62 | void CheckPassArg( | |||
63 | const Symbol &proc, const Symbol *interface, const WithPassArg &); | |||
64 | void CheckProcBinding(const Symbol &, const ProcBindingDetails &); | |||
65 | void CheckObjectEntity(const Symbol &, const ObjectEntityDetails &); | |||
66 | void CheckPointerInitialization(const Symbol &); | |||
67 | void CheckArraySpec(const Symbol &, const ArraySpec &); | |||
68 | void CheckProcEntity(const Symbol &, const ProcEntityDetails &); | |||
69 | void CheckSubprogram(const Symbol &, const SubprogramDetails &); | |||
70 | void CheckExternal(const Symbol &); | |||
71 | void CheckAssumedTypeEntity(const Symbol &, const ObjectEntityDetails &); | |||
72 | void CheckDerivedType(const Symbol &, const DerivedTypeDetails &); | |||
73 | bool CheckFinal( | |||
74 | const Symbol &subroutine, SourceName, const Symbol &derivedType); | |||
75 | bool CheckDistinguishableFinals(const Symbol &f1, SourceName f1name, | |||
76 | const Symbol &f2, SourceName f2name, const Symbol &derivedType); | |||
77 | void CheckGeneric(const Symbol &, const GenericDetails &); | |||
78 | void CheckHostAssoc(const Symbol &, const HostAssocDetails &); | |||
79 | bool CheckDefinedOperator( | |||
80 | SourceName, GenericKind, const Symbol &, const Procedure &); | |||
81 | std::optional<parser::MessageFixedText> CheckNumberOfArgs( | |||
82 | const GenericKind &, std::size_t); | |||
83 | bool CheckDefinedOperatorArg( | |||
84 | const SourceName &, const Symbol &, const Procedure &, std::size_t); | |||
85 | bool CheckDefinedAssignment(const Symbol &, const Procedure &); | |||
86 | bool CheckDefinedAssignmentArg(const Symbol &, const DummyArgument &, int); | |||
87 | void CheckSpecifics(const Symbol &, const GenericDetails &); | |||
88 | void CheckEquivalenceSet(const EquivalenceSet &); | |||
89 | void CheckBlockData(const Scope &); | |||
90 | void CheckGenericOps(const Scope &); | |||
91 | bool CheckConflicting(const Symbol &, Attr, Attr); | |||
92 | void WarnMissingFinal(const Symbol &); | |||
93 | void CheckSymbolType(const Symbol &); // C702 | |||
94 | bool InPure() const { | |||
95 | return innermostSymbol_ && IsPureProcedure(*innermostSymbol_); | |||
96 | } | |||
97 | bool InElemental() const { | |||
98 | return innermostSymbol_ && IsElementalProcedure(*innermostSymbol_); | |||
99 | } | |||
100 | bool InFunction() const { | |||
101 | return innermostSymbol_ && IsFunction(*innermostSymbol_); | |||
102 | } | |||
103 | bool InInterface() const { | |||
104 | const SubprogramDetails *subp{innermostSymbol_ | |||
105 | ? innermostSymbol_->detailsIf<SubprogramDetails>() | |||
106 | : nullptr}; | |||
107 | return subp && subp->isInterface(); | |||
108 | } | |||
109 | template <typename... A> | |||
110 | parser::Message *SayWithDeclaration(const Symbol &symbol, A &&...x) { | |||
111 | parser::Message *msg{messages_.Say(std::forward<A>(x)...)}; | |||
112 | if (msg && messages_.at().begin() != symbol.name().begin()) { | |||
113 | evaluate::AttachDeclaration(*msg, symbol); | |||
114 | } | |||
115 | return msg; | |||
116 | } | |||
117 | bool IsResultOkToDiffer(const FunctionResult &); | |||
118 | void CheckGlobalName(const Symbol &); | |||
119 | void CheckExplicitSave(const Symbol &); | |||
120 | void CheckBindC(const Symbol &); | |||
121 | void CheckBindCFunctionResult(const Symbol &); | |||
122 | // Check functions for defined I/O procedures | |||
123 | void CheckDefinedIoProc( | |||
124 | const Symbol &, const GenericDetails &, common::DefinedIo); | |||
125 | bool CheckDioDummyIsData(const Symbol &, const Symbol *, std::size_t); | |||
126 | void CheckDioDummyIsDerived( | |||
127 | const Symbol &, const Symbol &, common::DefinedIo ioKind, const Symbol &); | |||
128 | void CheckDioDummyIsDefaultInteger(const Symbol &, const Symbol &); | |||
129 | void CheckDioDummyIsScalar(const Symbol &, const Symbol &); | |||
130 | void CheckDioDummyAttrs(const Symbol &, const Symbol &, Attr); | |||
131 | void CheckDioDtvArg( | |||
132 | const Symbol &, const Symbol *, common::DefinedIo, const Symbol &); | |||
133 | void CheckGenericVsIntrinsic(const Symbol &, const GenericDetails &); | |||
134 | void CheckDefaultIntegerArg(const Symbol &, const Symbol *, Attr); | |||
135 | void CheckDioAssumedLenCharacterArg( | |||
136 | const Symbol &, const Symbol *, std::size_t, Attr); | |||
137 | void CheckDioVlistArg(const Symbol &, const Symbol *, std::size_t); | |||
138 | void CheckDioArgCount(const Symbol &, common::DefinedIo ioKind, std::size_t); | |||
139 | struct TypeWithDefinedIo { | |||
140 | const DerivedTypeSpec &type; | |||
141 | common::DefinedIo ioKind; | |||
142 | const Symbol &proc; | |||
143 | const Symbol &generic; | |||
144 | }; | |||
145 | void CheckAlreadySeenDefinedIo(const DerivedTypeSpec &, common::DefinedIo, | |||
146 | const Symbol &, const Symbol &generic); | |||
147 | void CheckModuleProcedureDef(const Symbol &); | |||
148 | ||||
149 | SemanticsContext &context_; | |||
150 | evaluate::FoldingContext &foldingContext_{context_.foldingContext()}; | |||
151 | parser::ContextualMessages &messages_{foldingContext_.messages()}; | |||
152 | const Scope *scope_{nullptr}; | |||
153 | bool scopeIsUninstantiatedPDT_{false}; | |||
154 | // This symbol is the one attached to the innermost enclosing scope | |||
155 | // that has a symbol. | |||
156 | const Symbol *innermostSymbol_{nullptr}; | |||
157 | // Cache of calls to Procedure::Characterize(Symbol) | |||
158 | std::map<SymbolRef, std::optional<Procedure>, SymbolAddressCompare> | |||
159 | characterizeCache_; | |||
160 | // Collection of module procedure symbols with non-BIND(C) | |||
161 | // global names, qualified by their module. | |||
162 | std::map<std::pair<SourceName, const Symbol *>, SymbolRef> moduleProcs_; | |||
163 | // Collection of symbols with global names, BIND(C) or otherwise | |||
164 | std::map<std::string, SymbolRef> globalNames_; | |||
165 | // Collection of external procedures without global definitions | |||
166 | std::map<std::string, SymbolRef> externalNames_; | |||
167 | }; | |||
168 | ||||
169 | class DistinguishabilityHelper { | |||
170 | public: | |||
171 | DistinguishabilityHelper(SemanticsContext &context) : context_{context} {} | |||
172 | void Add(const Symbol &, GenericKind, const Symbol &, const Procedure &); | |||
173 | void Check(const Scope &); | |||
174 | ||||
175 | private: | |||
176 | void SayNotDistinguishable(const Scope &, const SourceName &, GenericKind, | |||
177 | const Symbol &, const Symbol &); | |||
178 | void AttachDeclaration(parser::Message &, const Scope &, const Symbol &); | |||
179 | ||||
180 | SemanticsContext &context_; | |||
181 | struct ProcedureInfo { | |||
182 | GenericKind kind; | |||
183 | const Symbol &symbol; | |||
184 | const Procedure &procedure; | |||
185 | }; | |||
186 | std::map<SourceName, std::vector<ProcedureInfo>> nameToInfo_; | |||
187 | }; | |||
188 | ||||
189 | void CheckHelper::Check(const ParamValue &value, bool canBeAssumed) { | |||
190 | if (value.isAssumed()) { | |||
191 | if (!canBeAssumed) { // C795, C721, C726 | |||
192 | messages_.Say( | |||
193 | "An assumed (*) type parameter may be used only for a (non-statement function) dummy argument, associate name, character named constant, or external function result"_err_en_US); | |||
194 | } | |||
195 | } else { | |||
196 | CheckSpecExpr(value.GetExplicit()); | |||
197 | } | |||
198 | } | |||
199 | ||||
200 | void CheckHelper::Check(const ArraySpec &shape) { | |||
201 | for (const auto &spec : shape) { | |||
202 | Check(spec); | |||
203 | } | |||
204 | } | |||
205 | ||||
206 | void CheckHelper::Check( | |||
207 | const DeclTypeSpec &type, bool canHaveAssumedTypeParameters) { | |||
208 | if (type.category() == DeclTypeSpec::Character) { | |||
209 | Check(type.characterTypeSpec().length(), canHaveAssumedTypeParameters); | |||
210 | } else if (const DerivedTypeSpec *derived{type.AsDerived()}) { | |||
211 | for (auto &parm : derived->parameters()) { | |||
212 | Check(parm.second, canHaveAssumedTypeParameters); | |||
213 | } | |||
214 | } | |||
215 | } | |||
216 | ||||
217 | void CheckHelper::Check(const Symbol &symbol) { | |||
218 | if (symbol.name().size() > common::maxNameLen && | |||
219 | &symbol == &symbol.GetUltimate() && | |||
220 | !FindModuleFileContaining(symbol.owner())) { | |||
221 | messages_.Say(symbol.name(), | |||
222 | "%s has length %d, which is greater than the maximum name length " | |||
223 | "%d"_port_en_US, | |||
224 | symbol.name(), symbol.name().size(), common::maxNameLen); | |||
225 | } | |||
226 | if (context_.HasError(symbol)) { | |||
227 | return; | |||
228 | } | |||
229 | auto restorer{messages_.SetLocation(symbol.name())}; | |||
230 | context_.set_location(symbol.name()); | |||
231 | const DeclTypeSpec *type{symbol.GetType()}; | |||
232 | const DerivedTypeSpec *derived{type ? type->AsDerived() : nullptr}; | |||
233 | bool isDone{false}; | |||
234 | common::visit( | |||
235 | common::visitors{ | |||
236 | [&](const UseDetails &x) { isDone = true; }, | |||
237 | [&](const HostAssocDetails &x) { | |||
238 | CheckHostAssoc(symbol, x); | |||
239 | isDone = true; | |||
240 | }, | |||
241 | [&](const ProcBindingDetails &x) { | |||
242 | CheckProcBinding(symbol, x); | |||
243 | isDone = true; | |||
244 | }, | |||
245 | [&](const ObjectEntityDetails &x) { CheckObjectEntity(symbol, x); }, | |||
246 | [&](const ProcEntityDetails &x) { CheckProcEntity(symbol, x); }, | |||
247 | [&](const SubprogramDetails &x) { CheckSubprogram(symbol, x); }, | |||
248 | [&](const DerivedTypeDetails &x) { CheckDerivedType(symbol, x); }, | |||
| ||||
249 | [&](const GenericDetails &x) { CheckGeneric(symbol, x); }, | |||
250 | [](const auto &) {}, | |||
251 | }, | |||
252 | symbol.details()); | |||
253 | if (symbol.attrs().test(Attr::VOLATILE)) { | |||
254 | CheckVolatile(symbol, derived); | |||
255 | } | |||
256 | if (symbol.attrs().test(Attr::BIND_C)) { | |||
257 | CheckBindC(symbol); | |||
258 | } | |||
259 | if (symbol.attrs().test(Attr::SAVE) && | |||
260 | !symbol.implicitAttrs().test(Attr::SAVE)) { | |||
261 | CheckExplicitSave(symbol); | |||
262 | } | |||
263 | const auto *object{symbol.detailsIf<ObjectEntityDetails>()}; | |||
264 | if (symbol.attrs().test(Attr::CONTIGUOUS)) { | |||
265 | if ((!object && !symbol.has<UseDetails>()) || | |||
266 | !((IsPointer(symbol) && symbol.Rank() > 0) || IsAssumedShape(symbol) || | |||
267 | evaluate::IsAssumedRank(symbol))) { | |||
268 | if (symbol.owner().IsDerivedType()) { // C752 | |||
269 | messages_.Say( | |||
270 | "A CONTIGUOUS component must be an array with the POINTER attribute"_err_en_US); | |||
271 | } else { // C830 | |||
272 | messages_.Say( | |||
273 | "CONTIGUOUS entity must be an array pointer, assumed-shape, or assumed-rank"_err_en_US); | |||
274 | } | |||
275 | } | |||
276 | } | |||
277 | CheckGlobalName(symbol); | |||
278 | if (isDone) { | |||
279 | return; // following checks do not apply | |||
280 | } | |||
281 | ||||
282 | if (symbol.attrs().test(Attr::PROTECTED)) { | |||
283 | if (symbol.owner().kind() != Scope::Kind::Module) { // C854 | |||
284 | messages_.Say( | |||
285 | "A PROTECTED entity must be in the specification part of a module"_err_en_US); | |||
286 | } | |||
287 | if (!evaluate::IsVariable(symbol) && !IsProcedurePointer(symbol)) { // C855 | |||
288 | messages_.Say( | |||
289 | "A PROTECTED entity must be a variable or pointer"_err_en_US); | |||
290 | } | |||
291 | if (FindCommonBlockContaining(symbol)) { // C856 | |||
292 | messages_.Say( | |||
293 | "A PROTECTED entity may not be in a common block"_err_en_US); | |||
294 | } | |||
295 | } | |||
296 | if (IsPointer(symbol)) { | |||
297 | CheckPointer(symbol); | |||
298 | } | |||
299 | if (InPure()) { | |||
300 | if (InInterface()) { | |||
301 | // Declarations in interface definitions "have no effect" if they | |||
302 | // are not pertinent to the characteristics of the procedure. | |||
303 | // Restrictions on entities in pure procedure interfaces don't need | |||
304 | // enforcement. | |||
305 | } else { | |||
306 | if (IsSaved(symbol)) { | |||
307 | if (IsInitialized(symbol)) { | |||
308 | messages_.Say( | |||
309 | "A pure subprogram may not initialize a variable"_err_en_US); | |||
310 | } else { | |||
311 | messages_.Say( | |||
312 | "A pure subprogram may not have a variable with the SAVE attribute"_err_en_US); | |||
313 | } | |||
314 | } | |||
315 | } | |||
316 | if (symbol.attrs().test(Attr::VOLATILE) && | |||
317 | (IsDummy(symbol) || !InInterface())) { | |||
318 | messages_.Say( | |||
319 | "A pure subprogram may not have a variable with the VOLATILE attribute"_err_en_US); | |||
320 | } | |||
321 | if (IsProcedure(symbol) && !IsPureProcedure(symbol) && IsDummy(symbol)) { | |||
322 | messages_.Say( | |||
323 | "A dummy procedure of a pure subprogram must be pure"_err_en_US); | |||
324 | } | |||
325 | } | |||
326 | if (type) { // Section 7.2, paragraph 7; C795 | |||
327 | bool isChar{type->category() == DeclTypeSpec::Character}; | |||
328 | bool canHaveAssumedParameter{(isChar && IsNamedConstant(symbol)) || | |||
329 | (IsAssumedLengthCharacter(symbol) && // C722 | |||
330 | (IsExternal(symbol) || | |||
331 | ClassifyProcedure(symbol) == | |||
332 | ProcedureDefinitionClass::Dummy)) || | |||
333 | symbol.test(Symbol::Flag::ParentComp)}; | |||
334 | if (!IsStmtFunctionDummy(symbol)) { // C726 | |||
335 | if (object) { | |||
336 | canHaveAssumedParameter |= object->isDummy() || | |||
337 | (isChar && object->isFuncResult()) || | |||
338 | IsStmtFunctionResult(symbol); // Avoids multiple messages | |||
339 | } else { | |||
340 | canHaveAssumedParameter |= symbol.has<AssocEntityDetails>(); | |||
341 | } | |||
342 | } | |||
343 | if (IsProcedurePointer(symbol) && symbol.HasExplicitInterface()) { | |||
344 | // Don't check function result types here | |||
345 | } else { | |||
346 | Check(*type, canHaveAssumedParameter); | |||
347 | } | |||
348 | if (InPure() && InFunction() && IsFunctionResult(symbol)) { | |||
349 | if (type->IsPolymorphic() && IsAllocatable(symbol)) { // C1585 | |||
350 | messages_.Say( | |||
351 | "Result of pure function may not be both polymorphic and ALLOCATABLE"_err_en_US); | |||
352 | } | |||
353 | if (derived) { | |||
354 | // These cases would be caught be the general validation of local | |||
355 | // variables in a pure context, but these messages are more specific. | |||
356 | if (HasImpureFinal(symbol)) { // C1584 | |||
357 | messages_.Say( | |||
358 | "Result of pure function may not have an impure FINAL subroutine"_err_en_US); | |||
359 | } | |||
360 | if (auto bad{FindPolymorphicAllocatableUltimateComponent(*derived)}) { | |||
361 | SayWithDeclaration(*bad, | |||
362 | "Result of pure function may not have polymorphic ALLOCATABLE ultimate component '%s'"_err_en_US, | |||
363 | bad.BuildResultDesignatorName()); | |||
364 | } | |||
365 | } | |||
366 | } | |||
367 | } | |||
368 | if (IsAssumedLengthCharacter(symbol) && IsFunction(symbol)) { // C723 | |||
369 | if (symbol.attrs().test(Attr::RECURSIVE)) { | |||
370 | messages_.Say( | |||
371 | "An assumed-length CHARACTER(*) function cannot be RECURSIVE"_err_en_US); | |||
372 | } | |||
373 | if (symbol.Rank() > 0) { | |||
374 | messages_.Say( | |||
375 | "An assumed-length CHARACTER(*) function cannot return an array"_err_en_US); | |||
376 | } | |||
377 | if (!IsStmtFunction(symbol)) { | |||
378 | if (IsElementalProcedure(symbol)) { | |||
379 | messages_.Say( | |||
380 | "An assumed-length CHARACTER(*) function cannot be ELEMENTAL"_err_en_US); | |||
381 | } else if (IsPureProcedure(symbol)) { | |||
382 | messages_.Say( | |||
383 | "An assumed-length CHARACTER(*) function cannot be PURE"_err_en_US); | |||
384 | } | |||
385 | } | |||
386 | if (const Symbol *result{FindFunctionResult(symbol)}) { | |||
387 | if (IsPointer(*result)) { | |||
388 | messages_.Say( | |||
389 | "An assumed-length CHARACTER(*) function cannot return a POINTER"_err_en_US); | |||
390 | } | |||
391 | } else if (IsProcedurePointer(symbol) && IsDummy(symbol)) { | |||
392 | messages_.Say( | |||
393 | "A dummy procedure pointer should not have assumed-length CHARACTER(*) result type"_port_en_US); | |||
394 | // The non-dummy case is a hard error that's caught elsewhere. | |||
395 | } | |||
396 | } | |||
397 | if (symbol.attrs().test(Attr::VALUE)) { | |||
398 | CheckValue(symbol, derived); | |||
399 | } | |||
400 | if (IsDummy(symbol)) { | |||
401 | if (IsNamedConstant(symbol)) { | |||
402 | messages_.Say( | |||
403 | "A dummy argument may not also be a named constant"_err_en_US); | |||
404 | } | |||
405 | } else if (IsFunctionResult(symbol)) { | |||
406 | if (IsNamedConstant(symbol)) { | |||
407 | messages_.Say( | |||
408 | "A function result may not also be a named constant"_err_en_US); | |||
409 | } | |||
410 | CheckBindCFunctionResult(symbol); | |||
411 | } | |||
412 | if (symbol.owner().IsModule() && IsAutomatic(symbol)) { | |||
413 | messages_.Say( | |||
414 | "Automatic data object '%s' may not appear in the specification part" | |||
415 | " of a module"_err_en_US, | |||
416 | symbol.name()); | |||
417 | } | |||
418 | if (IsProcedure(symbol) && !symbol.HasExplicitInterface()) { | |||
419 | if (IsAllocatable(symbol)) { | |||
420 | messages_.Say( | |||
421 | "Procedure '%s' may not be ALLOCATABLE without an explicit interface"_err_en_US, | |||
422 | symbol.name()); | |||
423 | } else if (symbol.Rank() > 0) { | |||
424 | messages_.Say( | |||
425 | "Procedure '%s' may not be an array without an explicit interface"_err_en_US, | |||
426 | symbol.name()); | |||
427 | } | |||
428 | } | |||
429 | if (symbol.attrs().test(Attr::ASYNCHRONOUS) && | |||
430 | !evaluate::IsVariable(symbol)) { | |||
431 | messages_.Say( | |||
432 | "An entity may not have the ASYNCHRONOUS attribute unless it is a variable"_err_en_US); | |||
433 | } | |||
434 | } | |||
435 | ||||
436 | void CheckHelper::CheckCommonBlock(const Symbol &symbol) { | |||
437 | CheckGlobalName(symbol); | |||
438 | if (symbol.attrs().test(Attr::BIND_C)) { | |||
439 | CheckBindC(symbol); | |||
440 | } | |||
441 | } | |||
442 | ||||
443 | // C859, C860 | |||
444 | void CheckHelper::CheckExplicitSave(const Symbol &symbol) { | |||
445 | const Symbol &ultimate{symbol.GetUltimate()}; | |||
446 | if (ultimate.test(Symbol::Flag::InDataStmt)) { | |||
447 | // checked elsewhere | |||
448 | } else if (symbol.has<UseDetails>()) { | |||
449 | messages_.Say( | |||
450 | "The USE-associated name '%s' may not have an explicit SAVE attribute"_err_en_US, | |||
451 | symbol.name()); | |||
452 | } else if (IsDummy(ultimate)) { | |||
453 | messages_.Say( | |||
454 | "The dummy argument '%s' may not have an explicit SAVE attribute"_err_en_US, | |||
455 | symbol.name()); | |||
456 | } else if (IsFunctionResult(ultimate)) { | |||
457 | messages_.Say( | |||
458 | "The function result variable '%s' may not have an explicit SAVE attribute"_err_en_US, | |||
459 | symbol.name()); | |||
460 | } else if (const Symbol * common{FindCommonBlockContaining(ultimate)}) { | |||
461 | messages_.Say( | |||
462 | "The entity '%s' in COMMON block /%s/ may not have an explicit SAVE attribute"_err_en_US, | |||
463 | symbol.name(), common->name()); | |||
464 | } else if (IsAutomatic(ultimate)) { | |||
465 | messages_.Say( | |||
466 | "The automatic object '%s' may not have an explicit SAVE attribute"_err_en_US, | |||
467 | symbol.name()); | |||
468 | } else if (!evaluate::IsVariable(ultimate) && !IsProcedurePointer(ultimate)) { | |||
469 | messages_.Say( | |||
470 | "The entity '%s' with an explicit SAVE attribute must be a variable, procedure pointer, or COMMON block"_err_en_US, | |||
471 | symbol.name()); | |||
472 | } | |||
473 | } | |||
474 | ||||
475 | void CheckHelper::CheckBindCFunctionResult(const Symbol &symbol) { // C1553 | |||
476 | if (!innermostSymbol_ || !IsBindCProcedure(*innermostSymbol_)) { | |||
477 | return; | |||
478 | } | |||
479 | if (IsPointer(symbol) || IsAllocatable(symbol)) { | |||
480 | messages_.Say( | |||
481 | "BIND(C) function result cannot have ALLOCATABLE or POINTER attribute"_err_en_US); | |||
482 | } | |||
483 | if (const DeclTypeSpec * type{symbol.GetType()}; | |||
484 | type && type->category() == DeclTypeSpec::Character) { | |||
485 | bool isConstOne{false}; // 18.3.1(1) | |||
486 | if (const auto &len{type->characterTypeSpec().length().GetExplicit()}) { | |||
487 | if (auto constLen{evaluate::ToInt64(*len)}) { | |||
488 | isConstOne = constLen == 1; | |||
489 | } | |||
490 | } | |||
491 | if (!isConstOne) { | |||
492 | messages_.Say( | |||
493 | "BIND(C) character function result must have length one"_err_en_US); | |||
494 | } | |||
495 | } | |||
496 | if (symbol.Rank() > 0) { | |||
497 | messages_.Say("BIND(C) function result must be scalar"_err_en_US); | |||
498 | } | |||
499 | if (symbol.Corank()) { | |||
500 | messages_.Say("BIND(C) function result cannot be a coarray"_err_en_US); | |||
501 | } | |||
502 | } | |||
503 | ||||
504 | void CheckHelper::CheckValue( | |||
505 | const Symbol &symbol, const DerivedTypeSpec *derived) { // C863 - C865 | |||
506 | if (!IsDummy(symbol)) { | |||
507 | messages_.Say( | |||
508 | "VALUE attribute may apply only to a dummy argument"_err_en_US); | |||
509 | } | |||
510 | if (IsProcedure(symbol)) { | |||
511 | messages_.Say( | |||
512 | "VALUE attribute may apply only to a dummy data object"_err_en_US); | |||
513 | } | |||
514 | if (IsAssumedSizeArray(symbol)) { | |||
515 | messages_.Say( | |||
516 | "VALUE attribute may not apply to an assumed-size array"_err_en_US); | |||
517 | } | |||
518 | if (evaluate::IsCoarray(symbol)) { | |||
519 | messages_.Say("VALUE attribute may not apply to a coarray"_err_en_US); | |||
520 | } | |||
521 | if (IsAllocatable(symbol)) { | |||
522 | messages_.Say("VALUE attribute may not apply to an ALLOCATABLE"_err_en_US); | |||
523 | } else if (IsPointer(symbol)) { | |||
524 | messages_.Say("VALUE attribute may not apply to a POINTER"_err_en_US); | |||
525 | } | |||
526 | if (IsIntentInOut(symbol)) { | |||
527 | messages_.Say( | |||
528 | "VALUE attribute may not apply to an INTENT(IN OUT) argument"_err_en_US); | |||
529 | } else if (IsIntentOut(symbol)) { | |||
530 | messages_.Say( | |||
531 | "VALUE attribute may not apply to an INTENT(OUT) argument"_err_en_US); | |||
532 | } | |||
533 | if (symbol.attrs().test(Attr::VOLATILE)) { | |||
534 | messages_.Say("VALUE attribute may not apply to a VOLATILE"_err_en_US); | |||
535 | } | |||
536 | if (innermostSymbol_ && IsBindCProcedure(*innermostSymbol_)) { | |||
537 | if (IsOptional(symbol)) { | |||
538 | messages_.Say( | |||
539 | "VALUE attribute may not apply to an OPTIONAL in a BIND(C) procedure"_err_en_US); | |||
540 | } | |||
541 | if (symbol.Rank() > 0) { | |||
542 | messages_.Say( | |||
543 | "VALUE attribute may not apply to an array in a BIND(C) procedure"_err_en_US); | |||
544 | } | |||
545 | } | |||
546 | if (derived) { | |||
547 | if (FindCoarrayUltimateComponent(*derived)) { | |||
548 | messages_.Say( | |||
549 | "VALUE attribute may not apply to a type with a coarray ultimate component"_err_en_US); | |||
550 | } | |||
551 | } | |||
552 | } | |||
553 | ||||
554 | void CheckHelper::CheckAssumedTypeEntity( // C709 | |||
555 | const Symbol &symbol, const ObjectEntityDetails &details) { | |||
556 | if (const DeclTypeSpec *type{symbol.GetType()}; | |||
557 | type && type->category() == DeclTypeSpec::TypeStar) { | |||
558 | if (!IsDummy(symbol)) { | |||
559 | messages_.Say( | |||
560 | "Assumed-type entity '%s' must be a dummy argument"_err_en_US, | |||
561 | symbol.name()); | |||
562 | } else { | |||
563 | if (symbol.attrs().test(Attr::ALLOCATABLE)) { | |||
564 | messages_.Say("Assumed-type argument '%s' cannot have the ALLOCATABLE" | |||
565 | " attribute"_err_en_US, | |||
566 | symbol.name()); | |||
567 | } | |||
568 | if (symbol.attrs().test(Attr::POINTER)) { | |||
569 | messages_.Say("Assumed-type argument '%s' cannot have the POINTER" | |||
570 | " attribute"_err_en_US, | |||
571 | symbol.name()); | |||
572 | } | |||
573 | if (symbol.attrs().test(Attr::VALUE)) { | |||
574 | messages_.Say("Assumed-type argument '%s' cannot have the VALUE" | |||
575 | " attribute"_err_en_US, | |||
576 | symbol.name()); | |||
577 | } | |||
578 | if (symbol.attrs().test(Attr::INTENT_OUT)) { | |||
579 | messages_.Say( | |||
580 | "Assumed-type argument '%s' cannot be INTENT(OUT)"_err_en_US, | |||
581 | symbol.name()); | |||
582 | } | |||
583 | if (evaluate::IsCoarray(symbol)) { | |||
584 | messages_.Say( | |||
585 | "Assumed-type argument '%s' cannot be a coarray"_err_en_US, | |||
586 | symbol.name()); | |||
587 | } | |||
588 | if (details.IsArray() && details.shape().IsExplicitShape()) { | |||
589 | messages_.Say("Assumed-type array argument '%s' must be assumed shape," | |||
590 | " assumed size, or assumed rank"_err_en_US, | |||
591 | symbol.name()); | |||
592 | } | |||
593 | } | |||
594 | } | |||
595 | } | |||
596 | ||||
597 | void CheckHelper::CheckObjectEntity( | |||
598 | const Symbol &symbol, const ObjectEntityDetails &details) { | |||
599 | CheckSymbolType(symbol); | |||
600 | CheckArraySpec(symbol, details.shape()); | |||
601 | Check(details.shape()); | |||
602 | Check(details.coshape()); | |||
603 | if (details.shape().Rank() > common::maxRank) { | |||
604 | messages_.Say( | |||
605 | "'%s' has rank %d, which is greater than the maximum supported rank %d"_err_en_US, | |||
606 | symbol.name(), details.shape().Rank(), common::maxRank); | |||
607 | } else if (details.shape().Rank() + details.coshape().Rank() > | |||
608 | common::maxRank) { | |||
609 | messages_.Say( | |||
610 | "'%s' has rank %d and corank %d, whose sum is greater than the maximum supported rank %d"_err_en_US, | |||
611 | symbol.name(), details.shape().Rank(), details.coshape().Rank(), | |||
612 | common::maxRank); | |||
613 | } | |||
614 | CheckAssumedTypeEntity(symbol, details); | |||
615 | WarnMissingFinal(symbol); | |||
616 | const DeclTypeSpec *type{details.type()}; | |||
617 | const DerivedTypeSpec *derived{type ? type->AsDerived() : nullptr}; | |||
618 | if (!details.coshape().empty()) { | |||
619 | bool isDeferredCoshape{details.coshape().CanBeDeferredShape()}; | |||
620 | if (IsAllocatable(symbol)) { | |||
621 | if (!isDeferredCoshape) { // C827 | |||
622 | messages_.Say("'%s' is an ALLOCATABLE coarray and must have a deferred" | |||
623 | " coshape"_err_en_US, | |||
624 | symbol.name()); | |||
625 | } | |||
626 | } else if (symbol.owner().IsDerivedType()) { // C746 | |||
627 | std::string deferredMsg{ | |||
628 | isDeferredCoshape ? "" : " and have a deferred coshape"}; | |||
629 | messages_.Say("Component '%s' is a coarray and must have the ALLOCATABLE" | |||
630 | " attribute%s"_err_en_US, | |||
631 | symbol.name(), deferredMsg); | |||
632 | } else { | |||
633 | if (!details.coshape().CanBeAssumedSize()) { // C828 | |||
634 | messages_.Say( | |||
635 | "'%s' is a non-ALLOCATABLE coarray and must have an explicit coshape"_err_en_US, | |||
636 | symbol.name()); | |||
637 | } | |||
638 | } | |||
639 | if (IsBadCoarrayType(derived)) { // C747 & C824 | |||
640 | messages_.Say( | |||
641 | "Coarray '%s' may not have type TEAM_TYPE, C_PTR, or C_FUNPTR"_err_en_US, | |||
642 | symbol.name()); | |||
643 | } | |||
644 | } | |||
645 | if (details.isDummy()) { | |||
646 | if (IsIntentOut(symbol)) { | |||
647 | // Some of these errors would also be caught by the general check | |||
648 | // for definability of automatically deallocated local variables, | |||
649 | // but these messages are more specific. | |||
650 | if (FindUltimateComponent(symbol, [](const Symbol &x) { | |||
651 | return evaluate::IsCoarray(x) && IsAllocatable(x); | |||
652 | })) { // C846 | |||
653 | messages_.Say( | |||
654 | "An INTENT(OUT) dummy argument may not be, or contain, an ALLOCATABLE coarray"_err_en_US); | |||
655 | } | |||
656 | if (IsOrContainsEventOrLockComponent(symbol)) { // C847 | |||
657 | messages_.Say( | |||
658 | "An INTENT(OUT) dummy argument may not be, or contain, EVENT_TYPE or LOCK_TYPE"_err_en_US); | |||
659 | } | |||
660 | if (details.IsAssumedSize()) { // C834 | |||
661 | if (type && type->IsPolymorphic()) { | |||
662 | messages_.Say( | |||
663 | "An INTENT(OUT) assumed-size dummy argument array may not be polymorphic"_err_en_US); | |||
664 | } | |||
665 | if (derived) { | |||
666 | if (derived->HasDefaultInitialization()) { | |||
667 | messages_.Say( | |||
668 | "An INTENT(OUT) assumed-size dummy argument array may not have a derived type with any default component initialization"_err_en_US); | |||
669 | } | |||
670 | if (IsFinalizable(*derived)) { | |||
671 | messages_.Say( | |||
672 | "An INTENT(OUT) assumed-size dummy argument array may not be finalizable"_err_en_US); | |||
673 | } | |||
674 | } | |||
675 | } | |||
676 | } | |||
677 | if (InPure() && !IsStmtFunction(DEREF(innermostSymbol_)Fortran::common::Deref(innermostSymbol_, "flang/lib/Semantics/check-declarations.cpp" , 677)) && | |||
678 | !IsPointer(symbol) && !IsIntentIn(symbol) && | |||
679 | !symbol.attrs().test(Attr::VALUE)) { | |||
680 | if (InFunction()) { // C1583 | |||
681 | messages_.Say( | |||
682 | "non-POINTER dummy argument of pure function must be INTENT(IN) or VALUE"_err_en_US); | |||
683 | } else if (IsIntentOut(symbol)) { | |||
684 | if (type && type->IsPolymorphic()) { // C1588 | |||
685 | messages_.Say( | |||
686 | "An INTENT(OUT) dummy argument of a pure subroutine may not be polymorphic"_err_en_US); | |||
687 | } else if (derived) { | |||
688 | if (FindUltimateComponent(*derived, [](const Symbol &x) { | |||
689 | const DeclTypeSpec *type{x.GetType()}; | |||
690 | return type && type->IsPolymorphic(); | |||
691 | })) { // C1588 | |||
692 | messages_.Say( | |||
693 | "An INTENT(OUT) dummy argument of a pure subroutine may not have a polymorphic ultimate component"_err_en_US); | |||
694 | } | |||
695 | if (HasImpureFinal(symbol)) { // C1587 | |||
696 | messages_.Say( | |||
697 | "An INTENT(OUT) dummy argument of a pure subroutine may not have an impure FINAL subroutine"_err_en_US); | |||
698 | } | |||
699 | } | |||
700 | } else if (!IsIntentInOut(symbol)) { // C1586 | |||
701 | messages_.Say( | |||
702 | "non-POINTER dummy argument of pure subroutine must have INTENT() or VALUE attribute"_err_en_US); | |||
703 | } | |||
704 | } | |||
705 | if (auto ignoreTKR{GetIgnoreTKR(symbol)}; !ignoreTKR.empty()) { | |||
706 | const Symbol *ownerSymbol{symbol.owner().symbol()}; | |||
707 | const auto *ownerSubp{ownerSymbol->detailsIf<SubprogramDetails>()}; | |||
708 | bool inInterface{ownerSubp && ownerSubp->isInterface()}; | |||
709 | bool inExplicitInterface{ | |||
710 | inInterface && !IsSeparateModuleProcedureInterface(ownerSymbol)}; | |||
711 | bool inModuleProc{ | |||
712 | !inInterface && ownerSymbol && IsModuleProcedure(*ownerSymbol)}; | |||
713 | if (!inExplicitInterface && !inModuleProc) { | |||
714 | messages_.Say( | |||
715 | "!DIR$ IGNORE_TKR may apply only in an interface or a module procedure"_err_en_US); | |||
716 | } | |||
717 | if (ignoreTKR.test(common::IgnoreTKR::Contiguous) && | |||
718 | !IsAssumedShape(symbol)) { | |||
719 | messages_.Say( | |||
720 | "!DIR$ IGNORE_TKR(C) may apply only to an assumed-shape array"_err_en_US); | |||
721 | } | |||
722 | if (ownerSymbol && ownerSymbol->attrs().test(Attr::ELEMENTAL) && | |||
723 | details.ignoreTKR().test(common::IgnoreTKR::Rank)) { | |||
724 | messages_.Say( | |||
725 | "!DIR$ IGNORE_TKR(R) may not apply in an ELEMENTAL procedure"_err_en_US); | |||
726 | } | |||
727 | if (IsPassedViaDescriptor(symbol)) { | |||
728 | if (IsAllocatableOrPointer(symbol)) { | |||
729 | if (inExplicitInterface) { | |||
730 | messages_.Say( | |||
731 | "!DIR$ IGNORE_TKR should not apply to an allocatable or pointer"_warn_en_US); | |||
732 | } else { | |||
733 | messages_.Say( | |||
734 | "!DIR$ IGNORE_TKR may not apply to an allocatable or pointer"_err_en_US); | |||
735 | } | |||
736 | } else if (ignoreTKR.test(common::IgnoreTKR::Rank)) { | |||
737 | if (ignoreTKR.count() == 1 && evaluate::IsAssumedRank(symbol)) { | |||
738 | messages_.Say( | |||
739 | "!DIR$ IGNORE_TKR(R) is not meaningful for an assumed-rank array"_warn_en_US); | |||
740 | } else if (inExplicitInterface) { | |||
741 | messages_.Say( | |||
742 | "!DIR$ IGNORE_TKR(R) should not apply to a dummy argument passed via descriptor"_warn_en_US); | |||
743 | } else { | |||
744 | messages_.Say( | |||
745 | "!DIR$ IGNORE_TKR(R) may not apply to a dummy argument passed via descriptor"_err_en_US); | |||
746 | } | |||
747 | } | |||
748 | } | |||
749 | } | |||
750 | } else if (symbol.attrs().test(Attr::INTENT_IN) || | |||
751 | symbol.attrs().test(Attr::INTENT_OUT) || | |||
752 | symbol.attrs().test(Attr::INTENT_INOUT)) { | |||
753 | messages_.Say( | |||
754 | "INTENT attributes may apply only to a dummy argument"_err_en_US); // C843 | |||
755 | } else if (IsOptional(symbol)) { | |||
756 | messages_.Say( | |||
757 | "OPTIONAL attribute may apply only to a dummy argument"_err_en_US); // C849 | |||
758 | } else if (!details.ignoreTKR().empty()) { | |||
759 | messages_.Say( | |||
760 | "!DIR$ IGNORE_TKR directive may apply only to a dummy data argument"_err_en_US); | |||
761 | } | |||
762 | if (InElemental()) { | |||
763 | if (details.isDummy()) { // C15100 | |||
764 | if (details.shape().Rank() > 0) { | |||
765 | messages_.Say( | |||
766 | "A dummy argument of an ELEMENTAL procedure must be scalar"_err_en_US); | |||
767 | } | |||
768 | if (IsAllocatable(symbol)) { | |||
769 | messages_.Say( | |||
770 | "A dummy argument of an ELEMENTAL procedure may not be ALLOCATABLE"_err_en_US); | |||
771 | } | |||
772 | if (evaluate::IsCoarray(symbol)) { | |||
773 | messages_.Say( | |||
774 | "A dummy argument of an ELEMENTAL procedure may not be a coarray"_err_en_US); | |||
775 | } | |||
776 | if (IsPointer(symbol)) { | |||
777 | messages_.Say( | |||
778 | "A dummy argument of an ELEMENTAL procedure may not be a POINTER"_err_en_US); | |||
779 | } | |||
780 | if (!symbol.attrs().HasAny(Attrs{Attr::VALUE, Attr::INTENT_IN, | |||
781 | Attr::INTENT_INOUT, Attr::INTENT_OUT})) { // C15102 | |||
782 | messages_.Say( | |||
783 | "A dummy argument of an ELEMENTAL procedure must have an INTENT() or VALUE attribute"_err_en_US); | |||
784 | } | |||
785 | } else if (IsFunctionResult(symbol)) { // C15101 | |||
786 | if (details.shape().Rank() > 0) { | |||
787 | messages_.Say( | |||
788 | "The result of an ELEMENTAL function must be scalar"_err_en_US); | |||
789 | } | |||
790 | if (IsAllocatable(symbol)) { | |||
791 | messages_.Say( | |||
792 | "The result of an ELEMENTAL function may not be ALLOCATABLE"_err_en_US); | |||
793 | } | |||
794 | if (IsPointer(symbol)) { | |||
795 | messages_.Say( | |||
796 | "The result of an ELEMENTAL function may not be a POINTER"_err_en_US); | |||
797 | } | |||
798 | } | |||
799 | } | |||
800 | if (HasDeclarationInitializer(symbol)) { // C808; ignore DATA initialization | |||
801 | CheckPointerInitialization(symbol); | |||
802 | if (IsAutomatic(symbol)) { | |||
803 | messages_.Say( | |||
804 | "An automatic variable or component must not be initialized"_err_en_US); | |||
805 | } else if (IsDummy(symbol)) { | |||
806 | messages_.Say("A dummy argument must not be initialized"_err_en_US); | |||
807 | } else if (IsFunctionResult(symbol)) { | |||
808 | messages_.Say("A function result must not be initialized"_err_en_US); | |||
809 | } else if (IsInBlankCommon(symbol) && | |||
810 | !FindModuleFileContaining(symbol.owner())) { | |||
811 | messages_.Say( | |||
812 | "A variable in blank COMMON should not be initialized"_port_en_US); | |||
813 | } | |||
814 | } | |||
815 | if (symbol.owner().kind() == Scope::Kind::BlockData) { | |||
816 | if (IsAllocatable(symbol)) { | |||
817 | messages_.Say( | |||
818 | "An ALLOCATABLE variable may not appear in a BLOCK DATA subprogram"_err_en_US); | |||
819 | } else if (IsInitialized(symbol) && !FindCommonBlockContaining(symbol)) { | |||
820 | messages_.Say( | |||
821 | "An initialized variable in BLOCK DATA must be in a COMMON block"_err_en_US); | |||
822 | } | |||
823 | } | |||
824 | if (type && type->IsPolymorphic() && | |||
825 | !(type->IsAssumedType() || IsAllocatableOrPointer(symbol) || | |||
826 | IsDummy(symbol))) { // C708 | |||
827 | messages_.Say("CLASS entity '%s' must be a dummy argument or have " | |||
828 | "ALLOCATABLE or POINTER attribute"_err_en_US, | |||
829 | symbol.name()); | |||
830 | } | |||
831 | if (derived && InPure() && !InInterface() && | |||
832 | IsAutomaticallyDestroyed(symbol) && | |||
833 | !IsIntentOut(symbol) /*has better messages*/ && | |||
834 | !IsFunctionResult(symbol) /*ditto*/) { | |||
835 | // Check automatically deallocated local variables for possible | |||
836 | // problems with finalization in PURE. | |||
837 | if (auto whyNot{ | |||
838 | WhyNotDefinable(symbol.name(), symbol.owner(), {}, symbol)}) { | |||
839 | if (auto *msg{messages_.Say( | |||
840 | "'%s' may not be a local variable in a pure subprogram"_err_en_US, | |||
841 | symbol.name())}) { | |||
842 | msg->Attach(std::move(*whyNot)); | |||
843 | } | |||
844 | } | |||
845 | } | |||
846 | if (symbol.attrs().test(Attr::EXTERNAL)) { | |||
847 | SayWithDeclaration(symbol, | |||
848 | "'%s' is a data object and may not be EXTERNAL"_err_en_US, | |||
849 | symbol.name()); | |||
850 | } | |||
851 | } | |||
852 | ||||
853 | void CheckHelper::CheckPointerInitialization(const Symbol &symbol) { | |||
854 | if (IsPointer(symbol) && !context_.HasError(symbol) && | |||
855 | !scopeIsUninstantiatedPDT_) { | |||
856 | if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) { | |||
857 | if (object->init()) { // C764, C765; C808 | |||
858 | if (auto designator{evaluate::AsGenericExpr(symbol)}) { | |||
859 | auto restorer{messages_.SetLocation(symbol.name())}; | |||
860 | context_.set_location(symbol.name()); | |||
861 | CheckInitialTarget( | |||
862 | foldingContext_, *designator, *object->init(), DEREF(scope_)Fortran::common::Deref(scope_, "flang/lib/Semantics/check-declarations.cpp" , 862)); | |||
863 | } | |||
864 | } | |||
865 | } else if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) { | |||
866 | if (proc->init() && *proc->init()) { | |||
867 | // C1519 - must be nonelemental external or module procedure, | |||
868 | // or an unrestricted specific intrinsic function. | |||
869 | const Symbol &ultimate{(*proc->init())->GetUltimate()}; | |||
870 | if (ultimate.attrs().test(Attr::INTRINSIC)) { | |||
871 | if (const auto intrinsic{ | |||
872 | context_.intrinsics().IsSpecificIntrinsicFunction( | |||
873 | ultimate.name().ToString())}; | |||
874 | !intrinsic || intrinsic->isRestrictedSpecific) { // C1030 | |||
875 | context_.Say( | |||
876 | "Intrinsic procedure '%s' is not an unrestricted specific " | |||
877 | "intrinsic permitted for use as the initializer for procedure " | |||
878 | "pointer '%s'"_err_en_US, | |||
879 | ultimate.name(), symbol.name()); | |||
880 | } | |||
881 | } else if (!ultimate.attrs().test(Attr::EXTERNAL) && | |||
882 | ultimate.owner().kind() != Scope::Kind::Module) { | |||
883 | context_.Say("Procedure pointer '%s' initializer '%s' is neither " | |||
884 | "an external nor a module procedure"_err_en_US, | |||
885 | symbol.name(), ultimate.name()); | |||
886 | } else if (IsElementalProcedure(ultimate)) { | |||
887 | context_.Say("Procedure pointer '%s' cannot be initialized with the " | |||
888 | "elemental procedure '%s"_err_en_US, | |||
889 | symbol.name(), ultimate.name()); | |||
890 | } else { | |||
891 | // TODO: Check the "shalls" in the 15.4.3.6 paragraphs 7-10. | |||
892 | } | |||
893 | } | |||
894 | } | |||
895 | } | |||
896 | } | |||
897 | ||||
898 | // The six different kinds of array-specs: | |||
899 | // array-spec -> explicit-shape-list | deferred-shape-list | |||
900 | // | assumed-shape-list | implied-shape-list | |||
901 | // | assumed-size | assumed-rank | |||
902 | // explicit-shape -> [ lb : ] ub | |||
903 | // deferred-shape -> : | |||
904 | // assumed-shape -> [ lb ] : | |||
905 | // implied-shape -> [ lb : ] * | |||
906 | // assumed-size -> [ explicit-shape-list , ] [ lb : ] * | |||
907 | // assumed-rank -> .. | |||
908 | // Note: | |||
909 | // - deferred-shape is also an assumed-shape | |||
910 | // - A single "*" or "lb:*" might be assumed-size or implied-shape-list | |||
911 | void CheckHelper::CheckArraySpec( | |||
912 | const Symbol &symbol, const ArraySpec &arraySpec) { | |||
913 | if (arraySpec.Rank() == 0) { | |||
914 | return; | |||
915 | } | |||
916 | bool isExplicit{arraySpec.IsExplicitShape()}; | |||
917 | bool canBeDeferred{arraySpec.CanBeDeferredShape()}; | |||
918 | bool canBeImplied{arraySpec.CanBeImpliedShape()}; | |||
919 | bool canBeAssumedShape{arraySpec.CanBeAssumedShape()}; | |||
920 | bool canBeAssumedSize{arraySpec.CanBeAssumedSize()}; | |||
921 | bool isAssumedRank{arraySpec.IsAssumedRank()}; | |||
922 | std::optional<parser::MessageFixedText> msg; | |||
923 | if (symbol.test(Symbol::Flag::CrayPointee) && !isExplicit && | |||
924 | !canBeAssumedSize) { | |||
925 | msg = "Cray pointee '%s' must have explicit shape or" | |||
926 | " assumed size"_err_en_US; | |||
927 | } else if (IsAllocatableOrPointer(symbol) && !canBeDeferred && | |||
928 | !isAssumedRank) { | |||
929 | if (symbol.owner().IsDerivedType()) { // C745 | |||
930 | if (IsAllocatable(symbol)) { | |||
931 | msg = "Allocatable array component '%s' must have" | |||
932 | " deferred shape"_err_en_US; | |||
933 | } else { | |||
934 | msg = "Array pointer component '%s' must have deferred shape"_err_en_US; | |||
935 | } | |||
936 | } else { | |||
937 | if (IsAllocatable(symbol)) { // C832 | |||
938 | msg = "Allocatable array '%s' must have deferred shape or" | |||
939 | " assumed rank"_err_en_US; | |||
940 | } else { | |||
941 | msg = "Array pointer '%s' must have deferred shape or" | |||
942 | " assumed rank"_err_en_US; | |||
943 | } | |||
944 | } | |||
945 | } else if (IsDummy(symbol)) { | |||
946 | if (canBeImplied && !canBeAssumedSize) { // C836 | |||
947 | msg = "Dummy array argument '%s' may not have implied shape"_err_en_US; | |||
948 | } | |||
949 | } else if (canBeAssumedShape && !canBeDeferred) { | |||
950 | msg = "Assumed-shape array '%s' must be a dummy argument"_err_en_US; | |||
951 | } else if (canBeAssumedSize && !canBeImplied) { // C833 | |||
952 | msg = "Assumed-size array '%s' must be a dummy argument"_err_en_US; | |||
953 | } else if (isAssumedRank) { // C837 | |||
954 | msg = "Assumed-rank array '%s' must be a dummy argument"_err_en_US; | |||
955 | } else if (canBeImplied) { | |||
956 | if (!IsNamedConstant(symbol)) { // C835, C836 | |||
957 | msg = "Implied-shape array '%s' must be a named constant or a " | |||
958 | "dummy argument"_err_en_US; | |||
959 | } | |||
960 | } else if (IsNamedConstant(symbol)) { | |||
961 | if (!isExplicit && !canBeImplied) { | |||
962 | msg = "Named constant '%s' array must have constant or" | |||
963 | " implied shape"_err_en_US; | |||
964 | } | |||
965 | } else if (!IsAllocatableOrPointer(symbol) && !isExplicit) { | |||
966 | if (symbol.owner().IsDerivedType()) { // C749 | |||
967 | msg = "Component array '%s' without ALLOCATABLE or POINTER attribute must" | |||
968 | " have explicit shape"_err_en_US; | |||
969 | } else { // C816 | |||
970 | msg = "Array '%s' without ALLOCATABLE or POINTER attribute must have" | |||
971 | " explicit shape"_err_en_US; | |||
972 | } | |||
973 | } | |||
974 | if (msg) { | |||
975 | context_.Say(std::move(*msg), symbol.name()); | |||
976 | } | |||
977 | } | |||
978 | ||||
979 | void CheckHelper::CheckProcEntity( | |||
980 | const Symbol &symbol, const ProcEntityDetails &details) { | |||
981 | CheckSymbolType(symbol); | |||
982 | if (details.isDummy()) { | |||
983 | if (!symbol.attrs().test(Attr::POINTER) && // C843 | |||
984 | (symbol.attrs().test(Attr::INTENT_IN) || | |||
985 | symbol.attrs().test(Attr::INTENT_OUT) || | |||
986 | symbol.attrs().test(Attr::INTENT_INOUT))) { | |||
987 | messages_.Say("A dummy procedure without the POINTER attribute" | |||
988 | " may not have an INTENT attribute"_err_en_US); | |||
989 | } | |||
990 | if (InElemental()) { // C15100 | |||
991 | messages_.Say( | |||
992 | "An ELEMENTAL subprogram may not have a dummy procedure"_err_en_US); | |||
993 | } | |||
994 | const Symbol *interface { | |||
995 | details.procInterface() | |||
996 | }; | |||
997 | if (!symbol.attrs().test(Attr::INTRINSIC) && | |||
998 | (IsElementalProcedure(symbol) || | |||
999 | (interface && !interface->attrs().test(Attr::INTRINSIC) && | |||
1000 | IsElementalProcedure(*interface)))) { | |||
1001 | // There's no explicit constraint or "shall" that we can find in the | |||
1002 | // standard for this check, but it seems to be implied in multiple | |||
1003 | // sites, and ELEMENTAL non-intrinsic actual arguments *are* | |||
1004 | // explicitly forbidden. But we allow "PROCEDURE(SIN)::dummy" | |||
1005 | // because it is explicitly legal to *pass* the specific intrinsic | |||
1006 | // function SIN as an actual argument. | |||
1007 | messages_.Say("A dummy procedure may not be ELEMENTAL"_err_en_US); | |||
1008 | } | |||
1009 | } else if (symbol.attrs().test(Attr::INTENT_IN) || | |||
1010 | symbol.attrs().test(Attr::INTENT_OUT) || | |||
1011 | symbol.attrs().test(Attr::INTENT_INOUT)) { | |||
1012 | messages_.Say("INTENT attributes may apply only to a dummy " | |||
1013 | "argument"_err_en_US); // C843 | |||
1014 | } else if (IsOptional(symbol)) { | |||
1015 | messages_.Say("OPTIONAL attribute may apply only to a dummy " | |||
1016 | "argument"_err_en_US); // C849 | |||
1017 | } else if (symbol.owner().IsDerivedType()) { | |||
1018 | if (!symbol.attrs().test(Attr::POINTER)) { // C756 | |||
1019 | const auto &name{symbol.name()}; | |||
1020 | messages_.Say(name, | |||
1021 | "Procedure component '%s' must have POINTER attribute"_err_en_US, | |||
1022 | name); | |||
1023 | } | |||
1024 | CheckPassArg(symbol, details.procInterface(), details); | |||
1025 | } | |||
1026 | if (IsPointer(symbol)) { | |||
1027 | CheckPointerInitialization(symbol); | |||
1028 | if (const Symbol * interface{details.procInterface()}) { | |||
1029 | const Symbol &ultimate{interface->GetUltimate()}; | |||
1030 | if (ultimate.attrs().test(Attr::INTRINSIC)) { | |||
1031 | if (const auto intrinsic{ | |||
1032 | context_.intrinsics().IsSpecificIntrinsicFunction( | |||
1033 | ultimate.name().ToString())}; | |||
1034 | !intrinsic || intrinsic->isRestrictedSpecific) { // C1515 | |||
1035 | messages_.Say( | |||
1036 | "Intrinsic procedure '%s' is not an unrestricted specific " | |||
1037 | "intrinsic permitted for use as the definition of the interface " | |||
1038 | "to procedure pointer '%s'"_err_en_US, | |||
1039 | ultimate.name(), symbol.name()); | |||
1040 | } | |||
1041 | } else if (IsElementalProcedure(*interface)) { | |||
1042 | messages_.Say("Procedure pointer '%s' may not be ELEMENTAL"_err_en_US, | |||
1043 | symbol.name()); // C1517 | |||
1044 | } | |||
1045 | } | |||
1046 | } | |||
1047 | CheckExternal(symbol); | |||
1048 | } | |||
1049 | ||||
1050 | // When a module subprogram has the MODULE prefix the following must match | |||
1051 | // with the corresponding separate module procedure interface body: | |||
1052 | // - C1549: characteristics and dummy argument names | |||
1053 | // - C1550: binding label | |||
1054 | // - C1551: NON_RECURSIVE prefix | |||
1055 | class SubprogramMatchHelper { | |||
1056 | public: | |||
1057 | explicit SubprogramMatchHelper(CheckHelper &checkHelper) | |||
1058 | : checkHelper{checkHelper} {} | |||
1059 | ||||
1060 | void Check(const Symbol &, const Symbol &); | |||
1061 | ||||
1062 | private: | |||
1063 | SemanticsContext &context() { return checkHelper.context(); } | |||
1064 | void CheckDummyArg(const Symbol &, const Symbol &, const DummyArgument &, | |||
1065 | const DummyArgument &); | |||
1066 | void CheckDummyDataObject(const Symbol &, const Symbol &, | |||
1067 | const DummyDataObject &, const DummyDataObject &); | |||
1068 | void CheckDummyProcedure(const Symbol &, const Symbol &, | |||
1069 | const DummyProcedure &, const DummyProcedure &); | |||
1070 | bool CheckSameIntent( | |||
1071 | const Symbol &, const Symbol &, common::Intent, common::Intent); | |||
1072 | template <typename... A> | |||
1073 | void Say( | |||
1074 | const Symbol &, const Symbol &, parser::MessageFixedText &&, A &&...); | |||
1075 | template <typename ATTRS> | |||
1076 | bool CheckSameAttrs(const Symbol &, const Symbol &, ATTRS, ATTRS); | |||
1077 | bool ShapesAreCompatible(const DummyDataObject &, const DummyDataObject &); | |||
1078 | evaluate::Shape FoldShape(const evaluate::Shape &); | |||
1079 | std::string AsFortran(DummyDataObject::Attr attr) { | |||
1080 | return parser::ToUpperCaseLetters(DummyDataObject::EnumToString(attr)); | |||
1081 | } | |||
1082 | std::string AsFortran(DummyProcedure::Attr attr) { | |||
1083 | return parser::ToUpperCaseLetters(DummyProcedure::EnumToString(attr)); | |||
1084 | } | |||
1085 | ||||
1086 | CheckHelper &checkHelper; | |||
1087 | }; | |||
1088 | ||||
1089 | // 15.6.2.6 para 3 - can the result of an ENTRY differ from its function? | |||
1090 | bool CheckHelper::IsResultOkToDiffer(const FunctionResult &result) { | |||
1091 | if (result.attrs.test(FunctionResult::Attr::Allocatable) || | |||
1092 | result.attrs.test(FunctionResult::Attr::Pointer)) { | |||
1093 | return false; | |||
1094 | } | |||
1095 | const auto *typeAndShape{result.GetTypeAndShape()}; | |||
1096 | if (!typeAndShape || typeAndShape->Rank() != 0) { | |||
1097 | return false; | |||
1098 | } | |||
1099 | auto category{typeAndShape->type().category()}; | |||
1100 | if (category == TypeCategory::Character || | |||
1101 | category == TypeCategory::Derived) { | |||
1102 | return false; | |||
1103 | } | |||
1104 | int kind{typeAndShape->type().kind()}; | |||
1105 | return kind == context_.GetDefaultKind(category) || | |||
1106 | (category == TypeCategory::Real && | |||
1107 | kind == context_.doublePrecisionKind()); | |||
1108 | } | |||
1109 | ||||
1110 | void CheckHelper::CheckSubprogram( | |||
1111 | const Symbol &symbol, const SubprogramDetails &details) { | |||
1112 | if (const Symbol *iface{FindSeparateModuleSubprogramInterface(&symbol)}) { | |||
1113 | SubprogramMatchHelper{*this}.Check(symbol, *iface); | |||
1114 | } | |||
1115 | if (const Scope *entryScope{details.entryScope()}) { | |||
1116 | // ENTRY 15.6.2.6, esp. C1571 | |||
1117 | std::optional<parser::MessageFixedText> error; | |||
1118 | const Symbol *subprogram{entryScope->symbol()}; | |||
1119 | const SubprogramDetails *subprogramDetails{nullptr}; | |||
1120 | if (subprogram) { | |||
1121 | subprogramDetails = subprogram->detailsIf<SubprogramDetails>(); | |||
1122 | } | |||
1123 | if (!(entryScope->parent().IsGlobal() || entryScope->parent().IsModule() || | |||
1124 | entryScope->parent().IsSubmodule())) { | |||
1125 | error = "ENTRY may not appear in an internal subprogram"_err_en_US; | |||
1126 | } else if (subprogramDetails && details.isFunction() && | |||
1127 | subprogramDetails->isFunction() && | |||
1128 | !context_.HasError(details.result()) && | |||
1129 | !context_.HasError(subprogramDetails->result())) { | |||
1130 | auto result{FunctionResult::Characterize( | |||
1131 | details.result(), context_.foldingContext())}; | |||
1132 | auto subpResult{FunctionResult::Characterize( | |||
1133 | subprogramDetails->result(), context_.foldingContext())}; | |||
1134 | if (result && subpResult && *result != *subpResult && | |||
1135 | (!IsResultOkToDiffer(*result) || !IsResultOkToDiffer(*subpResult))) { | |||
1136 | error = | |||
1137 | "Result of ENTRY is not compatible with result of containing function"_err_en_US; | |||
1138 | } | |||
1139 | } | |||
1140 | if (error) { | |||
1141 | if (auto *msg{messages_.Say(symbol.name(), *error)}) { | |||
1142 | if (subprogram) { | |||
1143 | msg->Attach(subprogram->name(), "Containing subprogram"_en_US); | |||
1144 | } | |||
1145 | } | |||
1146 | } | |||
1147 | } | |||
1148 | if (const MaybeExpr & stmtFunction{details.stmtFunction()}) { | |||
1149 | if (auto msg{evaluate::CheckStatementFunction( | |||
1150 | symbol, *stmtFunction, context_.foldingContext())}) { | |||
1151 | SayWithDeclaration(symbol, std::move(*msg)); | |||
1152 | } else if (details.result().flags().test(Symbol::Flag::Implicit)) { | |||
1153 | // 15.6.4 p2 weird requirement | |||
1154 | if (const Symbol * | |||
1155 | host{symbol.owner().parent().FindSymbol(symbol.name())}) { | |||
1156 | evaluate::AttachDeclaration( | |||
1157 | messages_.Say(symbol.name(), | |||
1158 | "An implicitly typed statement function should not appear when the same symbol is available in its host scope"_port_en_US), | |||
1159 | *host); | |||
1160 | } | |||
1161 | } | |||
1162 | if (GetProgramUnitOrBlockConstructContaining(symbol).kind() == | |||
1163 | Scope::Kind::BlockConstruct) { // C1107 | |||
1164 | messages_.Say(symbol.name(), | |||
1165 | "A statement function definition may not appear in a BLOCK construct"_err_en_US); | |||
1166 | } | |||
1167 | } | |||
1168 | if (IsElementalProcedure(symbol)) { | |||
1169 | // See comment on the similar check in CheckProcEntity() | |||
1170 | if (details.isDummy()) { | |||
1171 | messages_.Say("A dummy procedure may not be ELEMENTAL"_err_en_US); | |||
1172 | } else { | |||
1173 | for (const Symbol *dummy : details.dummyArgs()) { | |||
1174 | if (!dummy) { // C15100 | |||
1175 | messages_.Say( | |||
1176 | "An ELEMENTAL subroutine may not have an alternate return dummy argument"_err_en_US); | |||
1177 | } | |||
1178 | } | |||
1179 | } | |||
1180 | } | |||
1181 | if (details.isInterface()) { | |||
1182 | if (!details.isDummy() && details.isFunction() && | |||
1183 | IsAssumedLengthCharacter(details.result())) { // C721 | |||
1184 | messages_.Say(details.result().name(), | |||
1185 | "A function interface may not declare an assumed-length CHARACTER(*) result"_err_en_US); | |||
1186 | } | |||
1187 | } | |||
1188 | CheckExternal(symbol); | |||
1189 | CheckModuleProcedureDef(symbol); | |||
1190 | } | |||
1191 | ||||
1192 | void CheckHelper::CheckExternal(const Symbol &symbol) { | |||
1193 | if (IsExternal(symbol)) { | |||
1194 | std::string interfaceName{symbol.name().ToString()}; | |||
1195 | if (const auto *bind{symbol.GetBindName()}) { | |||
1196 | interfaceName = *bind; | |||
1197 | } | |||
1198 | if (const Symbol * global{FindGlobal(symbol)}; | |||
1199 | global && global != &symbol) { | |||
1200 | std::string definitionName{global->name().ToString()}; | |||
1201 | if (const auto *bind{global->GetBindName()}) { | |||
1202 | definitionName = *bind; | |||
1203 | } | |||
1204 | if (interfaceName == definitionName) { | |||
1205 | parser::Message *msg{nullptr}; | |||
1206 | if (!IsProcedure(*global)) { | |||
1207 | if (symbol.flags().test(Symbol::Flag::Function) || | |||
1208 | symbol.flags().test(Symbol::Flag::Subroutine)) { | |||
1209 | msg = messages_.Say( | |||
1210 | "The global entity '%s' corresponding to the local procedure '%s' is not a callable subprogram"_err_en_US, | |||
1211 | global->name(), symbol.name()); | |||
1212 | } | |||
1213 | } else if (auto chars{Characterize(symbol)}) { | |||
1214 | if (auto globalChars{Characterize(*global)}) { | |||
1215 | if (chars->HasExplicitInterface()) { | |||
1216 | std::string whyNot; | |||
1217 | if (!chars->IsCompatibleWith(*globalChars, &whyNot)) { | |||
1218 | msg = messages_.Say( | |||
1219 | "The global subprogram '%s' is not compatible with its local procedure declaration (%s)"_warn_en_US, | |||
1220 | global->name(), whyNot); | |||
1221 | } | |||
1222 | } else if (!globalChars->CanBeCalledViaImplicitInterface()) { | |||
1223 | msg = messages_.Say( | |||
1224 | "The global subprogram '%s' may not be referenced via the implicit interface '%s'"_err_en_US, | |||
1225 | global->name(), symbol.name()); | |||
1226 | } | |||
1227 | } | |||
1228 | } | |||
1229 | if (msg) { | |||
1230 | if (msg->IsFatal()) { | |||
1231 | context_.SetError(symbol); | |||
1232 | } | |||
1233 | evaluate::AttachDeclaration(msg, *global); | |||
1234 | evaluate::AttachDeclaration(msg, symbol); | |||
1235 | } | |||
1236 | } | |||
1237 | } else if (auto iter{externalNames_.find(interfaceName)}; | |||
1238 | iter != externalNames_.end()) { | |||
1239 | const Symbol &previous{*iter->second}; | |||
1240 | if (auto chars{Characterize(symbol)}) { | |||
1241 | if (auto previousChars{Characterize(previous)}) { | |||
1242 | std::string whyNot; | |||
1243 | if (!chars->IsCompatibleWith(*previousChars, &whyNot)) { | |||
1244 | if (auto *msg{messages_.Say( | |||
1245 | "The external interface '%s' is not compatible with an earlier definition (%s)"_warn_en_US, | |||
1246 | symbol.name(), whyNot)}) { | |||
1247 | evaluate::AttachDeclaration(msg, previous); | |||
1248 | evaluate::AttachDeclaration(msg, symbol); | |||
1249 | } | |||
1250 | } | |||
1251 | } | |||
1252 | } | |||
1253 | } else { | |||
1254 | externalNames_.emplace(interfaceName, symbol); | |||
1255 | } | |||
1256 | } | |||
1257 | } | |||
1258 | ||||
1259 | void CheckHelper::CheckDerivedType( | |||
1260 | const Symbol &derivedType, const DerivedTypeDetails &details) { | |||
1261 | if (details.isForwardReferenced() && !context_.HasError(derivedType)) { | |||
1262 | messages_.Say("The derived type '%s' has not been defined"_err_en_US, | |||
1263 | derivedType.name()); | |||
1264 | } | |||
1265 | const Scope *scope{derivedType.scope()}; | |||
1266 | if (!scope) { | |||
1267 | CHECK(details.isForwardReferenced())((details.isForwardReferenced()) || (Fortran::common::die("CHECK(" "details.isForwardReferenced()" ") failed" " at " "flang/lib/Semantics/check-declarations.cpp" "(%d)", 1267), false)); | |||
1268 | return; | |||
1269 | } | |||
1270 | CHECK(scope->symbol() == &derivedType)((scope->symbol() == &derivedType) || (Fortran::common ::die("CHECK(" "scope->symbol() == &derivedType" ") failed" " at " "flang/lib/Semantics/check-declarations.cpp" "(%d)", 1270 ), false)); | |||
1271 | CHECK(scope->IsDerivedType())((scope->IsDerivedType()) || (Fortran::common::die("CHECK(" "scope->IsDerivedType()" ") failed" " at " "flang/lib/Semantics/check-declarations.cpp" "(%d)", 1271), false)); | |||
1272 | if (derivedType.attrs().test(Attr::ABSTRACT) && // C734 | |||
1273 | (derivedType.attrs().test(Attr::BIND_C) || details.sequence())) { | |||
1274 | messages_.Say("An ABSTRACT derived type must be extensible"_err_en_US); | |||
1275 | } | |||
1276 | if (const DeclTypeSpec *parent{FindParentTypeSpec(derivedType)}) { | |||
1277 | const DerivedTypeSpec *parentDerived{parent->AsDerived()}; | |||
1278 | if (!IsExtensibleType(parentDerived)) { // C705 | |||
1279 | messages_.Say("The parent type is not extensible"_err_en_US); | |||
1280 | } | |||
1281 | if (!derivedType.attrs().test(Attr::ABSTRACT) && parentDerived
| |||
1282 | parentDerived->typeSymbol().attrs().test(Attr::ABSTRACT)) { | |||
1283 | ScopeComponentIterator components{*parentDerived}; | |||
1284 | for (const Symbol &component : components) { | |||
1285 | if (component.attrs().test(Attr::DEFERRED)) { | |||
1286 | if (scope->FindComponent(component.name()) == &component) { | |||
1287 | SayWithDeclaration(component, | |||
1288 | "Non-ABSTRACT extension of ABSTRACT derived type '%s' lacks a binding for DEFERRED procedure '%s'"_err_en_US, | |||
1289 | parentDerived->typeSymbol().name(), component.name()); | |||
1290 | } | |||
1291 | } | |||
1292 | } | |||
1293 | } | |||
1294 | DerivedTypeSpec derived{derivedType.name(), derivedType}; | |||
1295 | derived.set_scope(*scope); | |||
1296 | if (FindCoarrayUltimateComponent(derived) && // C736 | |||
1297 | !(parentDerived && FindCoarrayUltimateComponent(*parentDerived))) { | |||
1298 | messages_.Say( | |||
1299 | "Type '%s' has a coarray ultimate component so the type at the base " | |||
1300 | "of its type extension chain ('%s') must be a type that has a " | |||
1301 | "coarray ultimate component"_err_en_US, | |||
1302 | derivedType.name(), scope->GetDerivedTypeBase().GetSymbol()->name()); | |||
1303 | } | |||
1304 | if (FindEventOrLockPotentialComponent(derived) && // C737 | |||
1305 | !(FindEventOrLockPotentialComponent(*parentDerived) || | |||
| ||||
1306 | IsEventTypeOrLockType(parentDerived))) { | |||
1307 | messages_.Say( | |||
1308 | "Type '%s' has an EVENT_TYPE or LOCK_TYPE component, so the type " | |||
1309 | "at the base of its type extension chain ('%s') must either have an " | |||
1310 | "EVENT_TYPE or LOCK_TYPE component, or be EVENT_TYPE or " | |||
1311 | "LOCK_TYPE"_err_en_US, | |||
1312 | derivedType.name(), scope->GetDerivedTypeBase().GetSymbol()->name()); | |||
1313 | } | |||
1314 | } | |||
1315 | if (HasIntrinsicTypeName(derivedType)) { // C729 | |||
1316 | messages_.Say("A derived type name cannot be the name of an intrinsic" | |||
1317 | " type"_err_en_US); | |||
1318 | } | |||
1319 | std::map<SourceName, SymbolRef> previous; | |||
1320 | for (const auto &pair : details.finals()) { | |||
1321 | SourceName source{pair.first}; | |||
1322 | const Symbol &ref{*pair.second}; | |||
1323 | if (CheckFinal(ref, source, derivedType) && | |||
1324 | std::all_of(previous.begin(), previous.end(), | |||
1325 | [&](std::pair<SourceName, SymbolRef> prev) { | |||
1326 | return CheckDistinguishableFinals( | |||
1327 | ref, source, *prev.second, prev.first, derivedType); | |||
1328 | })) { | |||
1329 | previous.emplace(source, ref); | |||
1330 | } | |||
1331 | } | |||
1332 | } | |||
1333 | ||||
1334 | // C786 | |||
1335 | bool CheckHelper::CheckFinal( | |||
1336 | const Symbol &subroutine, SourceName finalName, const Symbol &derivedType) { | |||
1337 | if (!IsModuleProcedure(subroutine)) { | |||
1338 | SayWithDeclaration(subroutine, finalName, | |||
1339 | "FINAL subroutine '%s' of derived type '%s' must be a module procedure"_err_en_US, | |||
1340 | subroutine.name(), derivedType.name()); | |||
1341 | return false; | |||
1342 | } | |||
1343 | const Procedure *proc{Characterize(subroutine)}; | |||
1344 | if (!proc) { | |||
1345 | return false; // error recovery | |||
1346 | } | |||
1347 | if (!proc->IsSubroutine()) { | |||
1348 | SayWithDeclaration(subroutine, finalName, | |||
1349 | "FINAL subroutine '%s' of derived type '%s' must be a subroutine"_err_en_US, | |||
1350 | subroutine.name(), derivedType.name()); | |||
1351 | return false; | |||
1352 | } | |||
1353 | if (proc->dummyArguments.size() != 1) { | |||
1354 | SayWithDeclaration(subroutine, finalName, | |||
1355 | "FINAL subroutine '%s' of derived type '%s' must have a single dummy argument"_err_en_US, | |||
1356 | subroutine.name(), derivedType.name()); | |||
1357 | return false; | |||
1358 | } | |||
1359 | const auto &arg{proc->dummyArguments[0]}; | |||
1360 | const Symbol *errSym{&subroutine}; | |||
1361 | if (const auto *details{subroutine.detailsIf<SubprogramDetails>()}) { | |||
1362 | if (!details->dummyArgs().empty()) { | |||
1363 | if (const Symbol *argSym{details->dummyArgs()[0]}) { | |||
1364 | errSym = argSym; | |||
1365 | } | |||
1366 | } | |||
1367 | } | |||
1368 | const auto *ddo{std::get_if<DummyDataObject>(&arg.u)}; | |||
1369 | if (!ddo) { | |||
1370 | SayWithDeclaration(subroutine, finalName, | |||
1371 | "FINAL subroutine '%s' of derived type '%s' must have a single dummy argument that is a data object"_err_en_US, | |||
1372 | subroutine.name(), derivedType.name()); | |||
1373 | return false; | |||
1374 | } | |||
1375 | bool ok{true}; | |||
1376 | if (arg.IsOptional()) { | |||
1377 | SayWithDeclaration(*errSym, finalName, | |||
1378 | "FINAL subroutine '%s' of derived type '%s' must not have an OPTIONAL dummy argument"_err_en_US, | |||
1379 | subroutine.name(), derivedType.name()); | |||
1380 | ok = false; | |||
1381 | } | |||
1382 | if (ddo->attrs.test(DummyDataObject::Attr::Allocatable)) { | |||
1383 | SayWithDeclaration(*errSym, finalName, | |||
1384 | "FINAL subroutine '%s' of derived type '%s' must not have an ALLOCATABLE dummy argument"_err_en_US, | |||
1385 | subroutine.name(), derivedType.name()); | |||
1386 | ok = false; | |||
1387 | } | |||
1388 | if (ddo->attrs.test(DummyDataObject::Attr::Pointer)) { | |||
1389 | SayWithDeclaration(*errSym, finalName, | |||
1390 | "FINAL subroutine '%s' of derived type '%s' must not have a POINTER dummy argument"_err_en_US, | |||
1391 | subroutine.name(), derivedType.name()); | |||
1392 | ok = false; | |||
1393 | } | |||
1394 | if (ddo->intent == common::Intent::Out) { | |||
1395 | SayWithDeclaration(*errSym, finalName, | |||
1396 | "FINAL subroutine '%s' of derived type '%s' must not have a dummy argument with INTENT(OUT)"_err_en_US, | |||
1397 | subroutine.name(), derivedType.name()); | |||
1398 | ok = false; | |||
1399 | } | |||
1400 | if (ddo->attrs.test(DummyDataObject::Attr::Value)) { | |||
1401 | SayWithDeclaration(*errSym, finalName, | |||
1402 | "FINAL subroutine '%s' of derived type '%s' must not have a dummy argument with the VALUE attribute"_err_en_US, | |||
1403 | subroutine.name(), derivedType.name()); | |||
1404 | ok = false; | |||
1405 | } | |||
1406 | if (ddo->type.corank() > 0) { | |||
1407 | SayWithDeclaration(*errSym, finalName, | |||
1408 | "FINAL subroutine '%s' of derived type '%s' must not have a coarray dummy argument"_err_en_US, | |||
1409 | subroutine.name(), derivedType.name()); | |||
1410 | ok = false; | |||
1411 | } | |||
1412 | if (ddo->type.type().IsPolymorphic()) { | |||
1413 | SayWithDeclaration(*errSym, finalName, | |||
1414 | "FINAL subroutine '%s' of derived type '%s' must not have a polymorphic dummy argument"_err_en_US, | |||
1415 | subroutine.name(), derivedType.name()); | |||
1416 | ok = false; | |||
1417 | } else if (ddo->type.type().category() != TypeCategory::Derived || | |||
1418 | &ddo->type.type().GetDerivedTypeSpec().typeSymbol() != &derivedType) { | |||
1419 | SayWithDeclaration(*errSym, finalName, | |||
1420 | "FINAL subroutine '%s' of derived type '%s' must have a TYPE(%s) dummy argument"_err_en_US, | |||
1421 | subroutine.name(), derivedType.name(), derivedType.name()); | |||
1422 | ok = false; | |||
1423 | } else { // check that all LEN type parameters are assumed | |||
1424 | for (auto ref : OrderParameterDeclarations(derivedType)) { | |||
1425 | if (IsLenTypeParameter(*ref)) { | |||
1426 | const auto *value{ | |||
1427 | ddo->type.type().GetDerivedTypeSpec().FindParameter(ref->name())}; | |||
1428 | if (!value || !value->isAssumed()) { | |||
1429 | SayWithDeclaration(*errSym, finalName, | |||
1430 | "FINAL subroutine '%s' of derived type '%s' must have a dummy argument with an assumed LEN type parameter '%s=*'"_err_en_US, | |||
1431 | subroutine.name(), derivedType.name(), ref->name()); | |||
1432 | ok = false; | |||
1433 | } | |||
1434 | } | |||
1435 | } | |||
1436 | } | |||
1437 | return ok; | |||
1438 | } | |||
1439 | ||||
1440 | bool CheckHelper::CheckDistinguishableFinals(const Symbol &f1, | |||
1441 | SourceName f1Name, const Symbol &f2, SourceName f2Name, | |||
1442 | const Symbol &derivedType) { | |||
1443 | const Procedure *p1{Characterize(f1)}; | |||
1444 | const Procedure *p2{Characterize(f2)}; | |||
1445 | if (p1 && p2) { | |||
1446 | if (characteristics::Distinguishable( | |||
1447 | context_.languageFeatures(), *p1, *p2)) { | |||
1448 | return true; | |||
1449 | } | |||
1450 | if (auto *msg{messages_.Say(f1Name, | |||
1451 | "FINAL subroutines '%s' and '%s' of derived type '%s' cannot be distinguished by rank or KIND type parameter value"_err_en_US, | |||
1452 | f1Name, f2Name, derivedType.name())}) { | |||
1453 | msg->Attach(f2Name, "FINAL declaration of '%s'"_en_US, f2.name()) | |||
1454 | .Attach(f1.name(), "Definition of '%s'"_en_US, f1Name) | |||
1455 | .Attach(f2.name(), "Definition of '%s'"_en_US, f2Name); | |||
1456 | } | |||
1457 | } | |||
1458 | return false; | |||
1459 | } | |||
1460 | ||||
1461 | void CheckHelper::CheckHostAssoc( | |||
1462 | const Symbol &symbol, const HostAssocDetails &details) { | |||
1463 | const Symbol &hostSymbol{details.symbol()}; | |||
1464 | if (hostSymbol.test(Symbol::Flag::ImplicitOrError)) { | |||
1465 | if (details.implicitOrSpecExprError) { | |||
1466 | messages_.Say("Implicitly typed local entity '%s' not allowed in" | |||
1467 | " specification expression"_err_en_US, | |||
1468 | symbol.name()); | |||
1469 | } else if (details.implicitOrExplicitTypeError) { | |||
1470 | messages_.Say( | |||
1471 | "No explicit type declared for '%s'"_err_en_US, symbol.name()); | |||
1472 | } | |||
1473 | } | |||
1474 | } | |||
1475 | ||||
1476 | void CheckHelper::CheckGeneric( | |||
1477 | const Symbol &symbol, const GenericDetails &details) { | |||
1478 | CheckSpecifics(symbol, details); | |||
1479 | common::visit(common::visitors{ | |||
1480 | [&](const common::DefinedIo &io) { | |||
1481 | CheckDefinedIoProc(symbol, details, io); | |||
1482 | }, | |||
1483 | [&](const GenericKind::OtherKind &other) { | |||
1484 | if (other == GenericKind::OtherKind::Name) { | |||
1485 | CheckGenericVsIntrinsic(symbol, details); | |||
1486 | } | |||
1487 | }, | |||
1488 | [](const auto &) {}, | |||
1489 | }, | |||
1490 | details.kind().u); | |||
1491 | // Ensure that shadowed symbols are checked | |||
1492 | if (details.specific()) { | |||
1493 | Check(*details.specific()); | |||
1494 | } | |||
1495 | if (details.derivedType()) { | |||
1496 | Check(*details.derivedType()); | |||
1497 | } | |||
1498 | } | |||
1499 | ||||
1500 | // Check that the specifics of this generic are distinguishable from each other | |||
1501 | void CheckHelper::CheckSpecifics( | |||
1502 | const Symbol &generic, const GenericDetails &details) { | |||
1503 | GenericKind kind{details.kind()}; | |||
1504 | DistinguishabilityHelper helper{context_}; | |||
1505 | for (const Symbol &specific : details.specificProcs()) { | |||
1506 | if (specific.attrs().test(Attr::ABSTRACT)) { | |||
1507 | if (auto *msg{messages_.Say(generic.name(), | |||
1508 | "Generic interface '%s' must not use abstract interface '%s' as a specific procedure"_err_en_US, | |||
1509 | generic.name(), specific.name())}) { | |||
1510 | msg->Attach( | |||
1511 | specific.name(), "Definition of '%s'"_en_US, specific.name()); | |||
1512 | } | |||
1513 | continue; | |||
1514 | } | |||
1515 | if (specific.attrs().test(Attr::INTRINSIC)) { | |||
1516 | if (auto *msg{messages_.Say(specific.name(), | |||
1517 | "Specific procedure '%s' of generic interface '%s' may not be INTRINSIC"_err_en_US, | |||
1518 | specific.name(), generic.name())}) { | |||
1519 | msg->Attach(generic.name(), "Definition of '%s'"_en_US, generic.name()); | |||
1520 | } | |||
1521 | continue; | |||
1522 | } | |||
1523 | if (IsStmtFunction(specific)) { | |||
1524 | if (auto *msg{messages_.Say(specific.name(), | |||
1525 | "Specific procedure '%s' of generic interface '%s' may not be a statement function"_err_en_US, | |||
1526 | specific.name(), generic.name())}) { | |||
1527 | msg->Attach(generic.name(), "Definition of '%s'"_en_US, generic.name()); | |||
1528 | } | |||
1529 | continue; | |||
1530 | } | |||
1531 | if (const Procedure *procedure{Characterize(specific)}) { | |||
1532 | if (procedure->HasExplicitInterface()) { | |||
1533 | helper.Add(generic, kind, specific, *procedure); | |||
1534 | } else { | |||
1535 | if (auto *msg{messages_.Say(specific.name(), | |||
1536 | "Specific procedure '%s' of generic interface '%s' must have an explicit interface"_err_en_US, | |||
1537 | specific.name(), generic.name())}) { | |||
1538 | msg->Attach( | |||
1539 | generic.name(), "Definition of '%s'"_en_US, generic.name()); | |||
1540 | } | |||
1541 | } | |||
1542 | } | |||
1543 | } | |||
1544 | helper.Check(generic.owner()); | |||
1545 | } | |||
1546 | ||||
1547 | static bool ConflictsWithIntrinsicAssignment(const Procedure &proc) { | |||
1548 | auto lhs{std::get<DummyDataObject>(proc.dummyArguments[0].u).type}; | |||
1549 | auto rhs{std::get<DummyDataObject>(proc.dummyArguments[1].u).type}; | |||
1550 | return Tristate::No == | |||
1551 | IsDefinedAssignment(lhs.type(), lhs.Rank(), rhs.type(), rhs.Rank()); | |||
1552 | } | |||
1553 | ||||
1554 | static bool ConflictsWithIntrinsicOperator( | |||
1555 | const GenericKind &kind, const Procedure &proc) { | |||
1556 | if (!kind.IsIntrinsicOperator()) { | |||
1557 | return false; | |||
1558 | } | |||
1559 | auto arg0{std::get<DummyDataObject>(proc.dummyArguments[0].u).type}; | |||
1560 | auto type0{arg0.type()}; | |||
1561 | if (proc.dummyArguments.size() == 1) { // unary | |||
1562 | return common::visit( | |||
1563 | common::visitors{ | |||
1564 | [&](common::NumericOperator) { return IsIntrinsicNumeric(type0); }, | |||
1565 | [&](common::LogicalOperator) { return IsIntrinsicLogical(type0); }, | |||
1566 | [](const auto &) -> bool { DIE("bad generic kind")Fortran::common::die("bad generic kind" " at " "flang/lib/Semantics/check-declarations.cpp" "(%d)", 1566); }, | |||
1567 | }, | |||
1568 | kind.u); | |||
1569 | } else { // binary | |||
1570 | int rank0{arg0.Rank()}; | |||
1571 | auto arg1{std::get<DummyDataObject>(proc.dummyArguments[1].u).type}; | |||
1572 | auto type1{arg1.type()}; | |||
1573 | int rank1{arg1.Rank()}; | |||
1574 | return common::visit( | |||
1575 | common::visitors{ | |||
1576 | [&](common::NumericOperator) { | |||
1577 | return IsIntrinsicNumeric(type0, rank0, type1, rank1); | |||
1578 | }, | |||
1579 | [&](common::LogicalOperator) { | |||
1580 | return IsIntrinsicLogical(type0, rank0, type1, rank1); | |||
1581 | }, | |||
1582 | [&](common::RelationalOperator opr) { | |||
1583 | return IsIntrinsicRelational(opr, type0, rank0, type1, rank1); | |||
1584 | }, | |||
1585 | [&](GenericKind::OtherKind x) { | |||
1586 | CHECK(x == GenericKind::OtherKind::Concat)((x == GenericKind::OtherKind::Concat) || (Fortran::common::die ("CHECK(" "x == GenericKind::OtherKind::Concat" ") failed" " at " "flang/lib/Semantics/check-declarations.cpp" "(%d)", 1586), false )); | |||
1587 | return IsIntrinsicConcat(type0, rank0, type1, rank1); | |||
1588 | }, | |||
1589 | [](const auto &) -> bool { DIE("bad generic kind")Fortran::common::die("bad generic kind" " at " "flang/lib/Semantics/check-declarations.cpp" "(%d)", 1589); }, | |||
1590 | }, | |||
1591 | kind.u); | |||
1592 | } | |||
1593 | } | |||
1594 | ||||
1595 | // Check if this procedure can be used for defined operators (see 15.4.3.4.2). | |||
1596 | bool CheckHelper::CheckDefinedOperator(SourceName opName, GenericKind kind, | |||
1597 | const Symbol &specific, const Procedure &proc) { | |||
1598 | if (context_.HasError(specific)) { | |||
1599 | return false; | |||
1600 | } | |||
1601 | std::optional<parser::MessageFixedText> msg; | |||
1602 | auto checkDefinedOperatorArgs{ | |||
1603 | [&](SourceName opName, const Symbol &specific, const Procedure &proc) { | |||
1604 | bool arg0Defined{CheckDefinedOperatorArg(opName, specific, proc, 0)}; | |||
1605 | bool arg1Defined{CheckDefinedOperatorArg(opName, specific, proc, 1)}; | |||
1606 | return arg0Defined && arg1Defined; | |||
1607 | }}; | |||
1608 | if (specific.attrs().test(Attr::NOPASS)) { // C774 | |||
1609 | msg = "%s procedure '%s' may not have NOPASS attribute"_err_en_US; | |||
1610 | } else if (!proc.functionResult.has_value()) { | |||
1611 | msg = "%s procedure '%s' must be a function"_err_en_US; | |||
1612 | } else if (proc.functionResult->IsAssumedLengthCharacter()) { | |||
1613 | const auto *subpDetails{specific.detailsIf<SubprogramDetails>()}; | |||
1614 | if (subpDetails && !subpDetails->isDummy() && subpDetails->isInterface()) { | |||
1615 | // Error is caught by more general test for interfaces with | |||
1616 | // assumed-length character function results | |||
1617 | return true; | |||
1618 | } | |||
1619 | msg = "%s function '%s' may not have assumed-length CHARACTER(*)" | |||
1620 | " result"_err_en_US; | |||
1621 | } else if (auto m{CheckNumberOfArgs(kind, proc.dummyArguments.size())}) { | |||
1622 | msg = std::move(m); | |||
1623 | } else if (!checkDefinedOperatorArgs(opName, specific, proc)) { | |||
1624 | return false; // error was reported | |||
1625 | } else if (ConflictsWithIntrinsicOperator(kind, proc)) { | |||
1626 | msg = "%s function '%s' conflicts with intrinsic operator"_err_en_US; | |||
1627 | } else { | |||
1628 | return true; // OK | |||
1629 | } | |||
1630 | bool isFatal{msg->IsFatal()}; | |||
1631 | SayWithDeclaration( | |||
1632 | specific, std::move(*msg), MakeOpName(opName), specific.name()); | |||
1633 | if (isFatal) { | |||
1634 | context_.SetError(specific); | |||
1635 | } | |||
1636 | return false; | |||
1637 | } | |||
1638 | ||||
1639 | // If the number of arguments is wrong for this intrinsic operator, return | |||
1640 | // false and return the error message in msg. | |||
1641 | std::optional<parser::MessageFixedText> CheckHelper::CheckNumberOfArgs( | |||
1642 | const GenericKind &kind, std::size_t nargs) { | |||
1643 | if (!kind.IsIntrinsicOperator()) { | |||
1644 | if (nargs < 1 || nargs > 2) { | |||
1645 | return "%s function '%s' should have 1 or 2 dummy arguments"_warn_en_US; | |||
1646 | } | |||
1647 | return std::nullopt; | |||
1648 | } | |||
1649 | std::size_t min{2}, max{2}; // allowed number of args; default is binary | |||
1650 | common::visit(common::visitors{ | |||
1651 | [&](const common::NumericOperator &x) { | |||
1652 | if (x == common::NumericOperator::Add || | |||
1653 | x == common::NumericOperator::Subtract) { | |||
1654 | min = 1; // + and - are unary or binary | |||
1655 | } | |||
1656 | }, | |||
1657 | [&](const common::LogicalOperator &x) { | |||
1658 | if (x == common::LogicalOperator::Not) { | |||
1659 | min = 1; // .NOT. is unary | |||
1660 | max = 1; | |||
1661 | } | |||
1662 | }, | |||
1663 | [](const common::RelationalOperator &) { | |||
1664 | // all are binary | |||
1665 | }, | |||
1666 | [](const GenericKind::OtherKind &x) { | |||
1667 | CHECK(x == GenericKind::OtherKind::Concat)((x == GenericKind::OtherKind::Concat) || (Fortran::common::die ("CHECK(" "x == GenericKind::OtherKind::Concat" ") failed" " at " "flang/lib/Semantics/check-declarations.cpp" "(%d)", 1667), false )); | |||
1668 | }, | |||
1669 | [](const auto &) { DIE("expected intrinsic operator")Fortran::common::die("expected intrinsic operator" " at " "flang/lib/Semantics/check-declarations.cpp" "(%d)", 1669); }, | |||
1670 | }, | |||
1671 | kind.u); | |||
1672 | if (nargs >= min && nargs <= max) { | |||
1673 | return std::nullopt; | |||
1674 | } else if (max == 1) { | |||
1675 | return "%s function '%s' must have one dummy argument"_err_en_US; | |||
1676 | } else if (min == 2) { | |||
1677 | return "%s function '%s' must have two dummy arguments"_err_en_US; | |||
1678 | } else { | |||
1679 | return "%s function '%s' must have one or two dummy arguments"_err_en_US; | |||
1680 | } | |||
1681 | } | |||
1682 | ||||
1683 | bool CheckHelper::CheckDefinedOperatorArg(const SourceName &opName, | |||
1684 | const Symbol &symbol, const Procedure &proc, std::size_t pos) { | |||
1685 | if (pos >= proc.dummyArguments.size()) { | |||
1686 | return true; | |||
1687 | } | |||
1688 | auto &arg{proc.dummyArguments.at(pos)}; | |||
1689 | std::optional<parser::MessageFixedText> msg; | |||
1690 | if (arg.IsOptional()) { | |||
1691 | msg = "In %s function '%s', dummy argument '%s' may not be" | |||
1692 | " OPTIONAL"_err_en_US; | |||
1693 | } else if (const auto *dataObject{std::get_if<DummyDataObject>(&arg.u)}; | |||
1694 | dataObject == nullptr) { | |||
1695 | msg = "In %s function '%s', dummy argument '%s' must be a" | |||
1696 | " data object"_err_en_US; | |||
1697 | } else if (dataObject->intent != common::Intent::In && | |||
1698 | !dataObject->attrs.test(DummyDataObject::Attr::Value)) { | |||
1699 | msg = "In %s function '%s', dummy argument '%s' must have INTENT(IN)" | |||
1700 | " or VALUE attribute"_err_en_US; | |||
1701 | } | |||
1702 | if (msg) { | |||
1703 | SayWithDeclaration(symbol, std::move(*msg), | |||
1704 | parser::ToUpperCaseLetters(opName.ToString()), symbol.name(), arg.name); | |||
1705 | return false; | |||
1706 | } | |||
1707 | return true; | |||
1708 | } | |||
1709 | ||||
1710 | // Check if this procedure can be used for defined assignment (see 15.4.3.4.3). | |||
1711 | bool CheckHelper::CheckDefinedAssignment( | |||
1712 | const Symbol &specific, const Procedure &proc) { | |||
1713 | if (context_.HasError(specific)) { | |||
1714 | return false; | |||
1715 | } | |||
1716 | std::optional<parser::MessageFixedText> msg; | |||
1717 | if (specific.attrs().test(Attr::NOPASS)) { // C774 | |||
1718 | msg = "Defined assignment procedure '%s' may not have" | |||
1719 | " NOPASS attribute"_err_en_US; | |||
1720 | } else if (!proc.IsSubroutine()) { | |||
1721 | msg = "Defined assignment procedure '%s' must be a subroutine"_err_en_US; | |||
1722 | } else if (proc.dummyArguments.size() != 2) { | |||
1723 | msg = "Defined assignment subroutine '%s' must have" | |||
1724 | " two dummy arguments"_err_en_US; | |||
1725 | } else { | |||
1726 | // Check both arguments even if the first has an error. | |||
1727 | bool ok0{CheckDefinedAssignmentArg(specific, proc.dummyArguments[0], 0)}; | |||
1728 | bool ok1{CheckDefinedAssignmentArg(specific, proc.dummyArguments[1], 1)}; | |||
1729 | if (!(ok0 && ok1)) { | |||
1730 | return false; // error was reported | |||
1731 | } else if (ConflictsWithIntrinsicAssignment(proc)) { | |||
1732 | msg = "Defined assignment subroutine '%s' conflicts with" | |||
1733 | " intrinsic assignment"_err_en_US; | |||
1734 | } else { | |||
1735 | return true; // OK | |||
1736 | } | |||
1737 | } | |||
1738 | SayWithDeclaration(specific, std::move(msg.value()), specific.name()); | |||
1739 | context_.SetError(specific); | |||
1740 | return false; | |||
1741 | } | |||
1742 | ||||
1743 | bool CheckHelper::CheckDefinedAssignmentArg( | |||
1744 | const Symbol &symbol, const DummyArgument &arg, int pos) { | |||
1745 | std::optional<parser::MessageFixedText> msg; | |||
1746 | if (arg.IsOptional()) { | |||
1747 | msg = "In defined assignment subroutine '%s', dummy argument '%s'" | |||
1748 | " may not be OPTIONAL"_err_en_US; | |||
1749 | } else if (const auto *dataObject{std::get_if<DummyDataObject>(&arg.u)}) { | |||
1750 | if (pos == 0) { | |||
1751 | if (dataObject->intent != common::Intent::Out && | |||
1752 | dataObject->intent != common::Intent::InOut) { | |||
1753 | msg = "In defined assignment subroutine '%s', first dummy argument '%s'" | |||
1754 | " must have INTENT(OUT) or INTENT(INOUT)"_err_en_US; | |||
1755 | } | |||
1756 | } else if (pos == 1) { | |||
1757 | if (dataObject->intent != common::Intent::In && | |||
1758 | !dataObject->attrs.test(DummyDataObject::Attr::Value)) { | |||
1759 | msg = | |||
1760 | "In defined assignment subroutine '%s', second dummy" | |||
1761 | " argument '%s' must have INTENT(IN) or VALUE attribute"_err_en_US; | |||
1762 | } else if (dataObject->attrs.test(DummyDataObject::Attr::Pointer)) { | |||
1763 | msg = | |||
1764 | "In defined assignment subroutine '%s', second dummy argument '%s' must not be a pointer"_err_en_US; | |||
1765 | } else if (dataObject->attrs.test(DummyDataObject::Attr::Allocatable)) { | |||
1766 | msg = | |||
1767 | "In defined assignment subroutine '%s', second dummy argument '%s' must not be an allocatable"_err_en_US; | |||
1768 | } | |||
1769 | } else { | |||
1770 | DIE("pos must be 0 or 1")Fortran::common::die("pos must be 0 or 1" " at " "flang/lib/Semantics/check-declarations.cpp" "(%d)", 1770); | |||
1771 | } | |||
1772 | } else { | |||
1773 | msg = "In defined assignment subroutine '%s', dummy argument '%s'" | |||
1774 | " must be a data object"_err_en_US; | |||
1775 | } | |||
1776 | if (msg) { | |||
1777 | SayWithDeclaration(symbol, std::move(*msg), symbol.name(), arg.name); | |||
1778 | context_.SetError(symbol); | |||
1779 | return false; | |||
1780 | } | |||
1781 | return true; | |||
1782 | } | |||
1783 | ||||
1784 | // Report a conflicting attribute error if symbol has both of these attributes | |||
1785 | bool CheckHelper::CheckConflicting(const Symbol &symbol, Attr a1, Attr a2) { | |||
1786 | if (symbol.attrs().test(a1) && symbol.attrs().test(a2)) { | |||
1787 | messages_.Say("'%s' may not have both the %s and %s attributes"_err_en_US, | |||
1788 | symbol.name(), AttrToString(a1), AttrToString(a2)); | |||
1789 | return true; | |||
1790 | } else { | |||
1791 | return false; | |||
1792 | } | |||
1793 | } | |||
1794 | ||||
1795 | void CheckHelper::WarnMissingFinal(const Symbol &symbol) { | |||
1796 | const auto *object{symbol.detailsIf<ObjectEntityDetails>()}; | |||
1797 | if (!object || | |||
1798 | (!IsAutomaticallyDestroyed(symbol) && | |||
1799 | symbol.owner().kind() != Scope::Kind::DerivedType)) { | |||
1800 | return; | |||
1801 | } | |||
1802 | const DeclTypeSpec *type{object->type()}; | |||
1803 | const DerivedTypeSpec *derived{type ? type->AsDerived() : nullptr}; | |||
1804 | const Symbol *derivedSym{derived ? &derived->typeSymbol() : nullptr}; | |||
1805 | int rank{object->shape().Rank()}; | |||
1806 | const Symbol *initialDerivedSym{derivedSym}; | |||
1807 | while (const auto *derivedDetails{ | |||
1808 | derivedSym ? derivedSym->detailsIf<DerivedTypeDetails>() : nullptr}) { | |||
1809 | if (!derivedDetails->finals().empty() && | |||
1810 | !derivedDetails->GetFinalForRank(rank)) { | |||
1811 | if (auto *msg{derivedSym == initialDerivedSym | |||
1812 | ? messages_.Say(symbol.name(), | |||
1813 | "'%s' of derived type '%s' does not have a FINAL subroutine for its rank (%d)"_warn_en_US, | |||
1814 | symbol.name(), derivedSym->name(), rank) | |||
1815 | : messages_.Say(symbol.name(), | |||
1816 | "'%s' of derived type '%s' extended from '%s' does not have a FINAL subroutine for its rank (%d)"_warn_en_US, | |||
1817 | symbol.name(), initialDerivedSym->name(), | |||
1818 | derivedSym->name(), rank)}) { | |||
1819 | msg->Attach(derivedSym->name(), | |||
1820 | "Declaration of derived type '%s'"_en_US, derivedSym->name()); | |||
1821 | } | |||
1822 | return; | |||
1823 | } | |||
1824 | derived = derivedSym->GetParentTypeSpec(); | |||
1825 | derivedSym = derived ? &derived->typeSymbol() : nullptr; | |||
1826 | } | |||
1827 | } | |||
1828 | ||||
1829 | const Procedure *CheckHelper::Characterize(const Symbol &symbol) { | |||
1830 | auto it{characterizeCache_.find(symbol)}; | |||
1831 | if (it == characterizeCache_.end()) { | |||
1832 | auto pair{characterizeCache_.emplace(SymbolRef{symbol}, | |||
1833 | Procedure::Characterize(symbol, context_.foldingContext()))}; | |||
1834 | it = pair.first; | |||
1835 | } | |||
1836 | return common::GetPtrFromOptional(it->second); | |||
1837 | } | |||
1838 | ||||
1839 | void CheckHelper::CheckVolatile(const Symbol &symbol, | |||
1840 | const DerivedTypeSpec *derived) { // C866 - C868 | |||
1841 | if (IsIntentIn(symbol)) { | |||
1842 | messages_.Say( | |||
1843 | "VOLATILE attribute may not apply to an INTENT(IN) argument"_err_en_US); | |||
1844 | } | |||
1845 | if (IsProcedure(symbol)) { | |||
1846 | messages_.Say("VOLATILE attribute may apply only to a variable"_err_en_US); | |||
1847 | } | |||
1848 | if (symbol.has<UseDetails>() || symbol.has<HostAssocDetails>()) { | |||
1849 | const Symbol &ultimate{symbol.GetUltimate()}; | |||
1850 | if (evaluate::IsCoarray(ultimate)) { | |||
1851 | messages_.Say( | |||
1852 | "VOLATILE attribute may not apply to a coarray accessed by USE or host association"_err_en_US); | |||
1853 | } | |||
1854 | if (derived) { | |||
1855 | if (FindCoarrayUltimateComponent(*derived)) { | |||
1856 | messages_.Say( | |||
1857 | "VOLATILE attribute may not apply to a type with a coarray ultimate component accessed by USE or host association"_err_en_US); | |||
1858 | } | |||
1859 | } | |||
1860 | } | |||
1861 | } | |||
1862 | ||||
1863 | void CheckHelper::CheckPointer(const Symbol &symbol) { // C852 | |||
1864 | CheckConflicting(symbol, Attr::POINTER, Attr::TARGET); | |||
1865 | CheckConflicting(symbol, Attr::POINTER, Attr::ALLOCATABLE); // C751 | |||
1866 | CheckConflicting(symbol, Attr::POINTER, Attr::INTRINSIC); | |||
1867 | // Prohibit constant pointers. The standard does not explicitly prohibit | |||
1868 | // them, but the PARAMETER attribute requires a entity-decl to have an | |||
1869 | // initialization that is a constant-expr, and the only form of | |||
1870 | // initialization that allows a constant-expr is the one that's not a "=>" | |||
1871 | // pointer initialization. See C811, C807, and section 8.5.13. | |||
1872 | CheckConflicting(symbol, Attr::POINTER, Attr::PARAMETER); | |||
1873 | if (symbol.Corank() > 0) { | |||
1874 | messages_.Say( | |||
1875 | "'%s' may not have the POINTER attribute because it is a coarray"_err_en_US, | |||
1876 | symbol.name()); | |||
1877 | } | |||
1878 | } | |||
1879 | ||||
1880 | // C760 constraints on the passed-object dummy argument | |||
1881 | // C757 constraints on procedure pointer components | |||
1882 | void CheckHelper::CheckPassArg( | |||
1883 | const Symbol &proc, const Symbol *interface0, const WithPassArg &details) { | |||
1884 | if (proc.attrs().test(Attr::NOPASS)) { | |||
1885 | return; | |||
1886 | } | |||
1887 | const auto &name{proc.name()}; | |||
1888 | const Symbol *interface { | |||
1889 | interface0 ? FindInterface(*interface0) : nullptr | |||
1890 | }; | |||
1891 | if (!interface) { | |||
1892 | messages_.Say(name, | |||
1893 | "Procedure component '%s' must have NOPASS attribute or explicit interface"_err_en_US, | |||
1894 | name); | |||
1895 | return; | |||
1896 | } | |||
1897 | const auto *subprogram{interface->detailsIf<SubprogramDetails>()}; | |||
1898 | if (!subprogram) { | |||
1899 | messages_.Say(name, | |||
1900 | "Procedure component '%s' has invalid interface '%s'"_err_en_US, name, | |||
1901 | interface->name()); | |||
1902 | return; | |||
1903 | } | |||
1904 | std::optional<SourceName> passName{details.passName()}; | |||
1905 | const auto &dummyArgs{subprogram->dummyArgs()}; | |||
1906 | if (!passName) { | |||
1907 | if (dummyArgs.empty()) { | |||
1908 | messages_.Say(name, | |||
1909 | proc.has<ProcEntityDetails>() | |||
1910 | ? "Procedure component '%s' with no dummy arguments" | |||
1911 | " must have NOPASS attribute"_err_en_US | |||
1912 | : "Procedure binding '%s' with no dummy arguments" | |||
1913 | " must have NOPASS attribute"_err_en_US, | |||
1914 | name); | |||
1915 | context_.SetError(*interface); | |||
1916 | return; | |||
1917 | } | |||
1918 | Symbol *argSym{dummyArgs[0]}; | |||
1919 | if (!argSym) { | |||
1920 | messages_.Say(interface->name(), | |||
1921 | "Cannot use an alternate return as the passed-object dummy " | |||
1922 | "argument"_err_en_US); | |||
1923 | return; | |||
1924 | } | |||
1925 | passName = dummyArgs[0]->name(); | |||
1926 | } | |||
1927 | std::optional<int> passArgIndex{}; | |||
1928 | for (std::size_t i{0}; i < dummyArgs.size(); ++i) { | |||
1929 | if (dummyArgs[i] && dummyArgs[i]->name() == *passName) { | |||
1930 | passArgIndex = i; | |||
1931 | break; | |||
1932 | } | |||
1933 | } | |||
1934 | if (!passArgIndex) { // C758 | |||
1935 | messages_.Say(*passName, | |||
1936 | "'%s' is not a dummy argument of procedure interface '%s'"_err_en_US, | |||
1937 | *passName, interface->name()); | |||
1938 | return; | |||
1939 | } | |||
1940 | const Symbol &passArg{*dummyArgs[*passArgIndex]}; | |||
1941 | std::optional<parser::MessageFixedText> msg; | |||
1942 | if (!passArg.has<ObjectEntityDetails>()) { | |||
1943 | msg = "Passed-object dummy argument '%s' of procedure '%s'" | |||
1944 | " must be a data object"_err_en_US; | |||
1945 | } else if (passArg.attrs().test(Attr::POINTER)) { | |||
1946 | msg = "Passed-object dummy argument '%s' of procedure '%s'" | |||
1947 | " may not have the POINTER attribute"_err_en_US; | |||
1948 | } else if (passArg.attrs().test(Attr::ALLOCATABLE)) { | |||
1949 | msg = "Passed-object dummy argument '%s' of procedure '%s'" | |||
1950 | " may not have the ALLOCATABLE attribute"_err_en_US; | |||
1951 | } else if (passArg.attrs().test(Attr::VALUE)) { | |||
1952 | msg = "Passed-object dummy argument '%s' of procedure '%s'" | |||
1953 | " may not have the VALUE attribute"_err_en_US; | |||
1954 | } else if (passArg.Rank() > 0) { | |||
1955 | msg = "Passed-object dummy argument '%s' of procedure '%s'" | |||
1956 | " must be scalar"_err_en_US; | |||
1957 | } | |||
1958 | if (msg) { | |||
1959 | messages_.Say(name, std::move(*msg), passName.value(), name); | |||
1960 | return; | |||
1961 | } | |||
1962 | const DeclTypeSpec *type{passArg.GetType()}; | |||
1963 | if (!type) { | |||
1964 | return; // an error already occurred | |||
1965 | } | |||
1966 | const Symbol &typeSymbol{*proc.owner().GetSymbol()}; | |||
1967 | const DerivedTypeSpec *derived{type->AsDerived()}; | |||
1968 | if (!derived || derived->typeSymbol() != typeSymbol) { | |||
1969 | messages_.Say(name, | |||
1970 | "Passed-object dummy argument '%s' of procedure '%s'" | |||
1971 | " must be of type '%s' but is '%s'"_err_en_US, | |||
1972 | passName.value(), name, typeSymbol.name(), type->AsFortran()); | |||
1973 | return; | |||
1974 | } | |||
1975 | if (IsExtensibleType(derived) != type->IsPolymorphic()) { | |||
1976 | messages_.Say(name, | |||
1977 | type->IsPolymorphic() | |||
1978 | ? "Passed-object dummy argument '%s' of procedure '%s'" | |||
1979 | " may not be polymorphic because '%s' is not extensible"_err_en_US | |||
1980 | : "Passed-object dummy argument '%s' of procedure '%s'" | |||
1981 | " must be polymorphic because '%s' is extensible"_err_en_US, | |||
1982 | passName.value(), name, typeSymbol.name()); | |||
1983 | return; | |||
1984 | } | |||
1985 | for (const auto &[paramName, paramValue] : derived->parameters()) { | |||
1986 | if (paramValue.isLen() && !paramValue.isAssumed()) { | |||
1987 | messages_.Say(name, | |||
1988 | "Passed-object dummy argument '%s' of procedure '%s'" | |||
1989 | " has non-assumed length parameter '%s'"_err_en_US, | |||
1990 | passName.value(), name, paramName); | |||
1991 | } | |||
1992 | } | |||
1993 | } | |||
1994 | ||||
1995 | void CheckHelper::CheckProcBinding( | |||
1996 | const Symbol &symbol, const ProcBindingDetails &binding) { | |||
1997 | const Scope &dtScope{symbol.owner()}; | |||
1998 | CHECK(dtScope.kind() == Scope::Kind::DerivedType)((dtScope.kind() == Scope::Kind::DerivedType) || (Fortran::common ::die("CHECK(" "dtScope.kind() == Scope::Kind::DerivedType" ") failed" " at " "flang/lib/Semantics/check-declarations.cpp" "(%d)", 1998 ), false)); | |||
1999 | if (symbol.attrs().test(Attr::DEFERRED)) { | |||
2000 | if (const Symbol *dtSymbol{dtScope.symbol()}) { | |||
2001 | if (!dtSymbol->attrs().test(Attr::ABSTRACT)) { // C733 | |||
2002 | SayWithDeclaration(*dtSymbol, | |||
2003 | "Procedure bound to non-ABSTRACT derived type '%s' may not be DEFERRED"_err_en_US, | |||
2004 | dtSymbol->name()); | |||
2005 | } | |||
2006 | } | |||
2007 | if (symbol.attrs().test(Attr::NON_OVERRIDABLE)) { | |||
2008 | messages_.Say( | |||
2009 | "Type-bound procedure '%s' may not be both DEFERRED and NON_OVERRIDABLE"_err_en_US, | |||
2010 | symbol.name()); | |||
2011 | } | |||
2012 | } | |||
2013 | if (binding.symbol().attrs().test(Attr::INTRINSIC) && | |||
2014 | !context_.intrinsics().IsSpecificIntrinsicFunction( | |||
2015 | binding.symbol().name().ToString())) { | |||
2016 | messages_.Say( | |||
2017 | "Intrinsic procedure '%s' is not a specific intrinsic permitted for use in the definition of binding '%s'"_err_en_US, | |||
2018 | binding.symbol().name(), symbol.name()); | |||
2019 | } | |||
2020 | if (const Symbol *overridden{FindOverriddenBinding(symbol)}) { | |||
2021 | if (overridden->attrs().test(Attr::NON_OVERRIDABLE)) { | |||
2022 | SayWithDeclaration(*overridden, | |||
2023 | "Override of NON_OVERRIDABLE '%s' is not permitted"_err_en_US, | |||
2024 | symbol.name()); | |||
2025 | } | |||
2026 | if (const auto *overriddenBinding{ | |||
2027 | overridden->detailsIf<ProcBindingDetails>()}) { | |||
2028 | if (!IsPureProcedure(symbol) && IsPureProcedure(*overridden)) { | |||
2029 | SayWithDeclaration(*overridden, | |||
2030 | "An overridden pure type-bound procedure binding must also be pure"_err_en_US); | |||
2031 | return; | |||
2032 | } | |||
2033 | if (!IsElementalProcedure(binding.symbol()) && | |||
2034 | IsElementalProcedure(*overridden)) { | |||
2035 | SayWithDeclaration(*overridden, | |||
2036 | "A type-bound procedure and its override must both, or neither, be ELEMENTAL"_err_en_US); | |||
2037 | return; | |||
2038 | } | |||
2039 | bool isNopass{symbol.attrs().test(Attr::NOPASS)}; | |||
2040 | if (isNopass != overridden->attrs().test(Attr::NOPASS)) { | |||
2041 | SayWithDeclaration(*overridden, | |||
2042 | isNopass | |||
2043 | ? "A NOPASS type-bound procedure may not override a passed-argument procedure"_err_en_US | |||
2044 | : "A passed-argument type-bound procedure may not override a NOPASS procedure"_err_en_US); | |||
2045 | } else { | |||
2046 | const auto *bindingChars{Characterize(binding.symbol())}; | |||
2047 | const auto *overriddenChars{Characterize(*overridden)}; | |||
2048 | if (bindingChars && overriddenChars) { | |||
2049 | if (isNopass) { | |||
2050 | if (!bindingChars->CanOverride(*overriddenChars, std::nullopt)) { | |||
2051 | SayWithDeclaration(*overridden, | |||
2052 | "A NOPASS type-bound procedure and its override must have identical interfaces"_err_en_US); | |||
2053 | } | |||
2054 | } else if (!context_.HasError(binding.symbol())) { | |||
2055 | int passIndex{bindingChars->FindPassIndex(binding.passName())}; | |||
2056 | int overriddenPassIndex{ | |||
2057 | overriddenChars->FindPassIndex(overriddenBinding->passName())}; | |||
2058 | if (passIndex != overriddenPassIndex) { | |||
2059 | SayWithDeclaration(*overridden, | |||
2060 | "A type-bound procedure and its override must use the same PASS argument"_err_en_US); | |||
2061 | } else if (!bindingChars->CanOverride( | |||
2062 | *overriddenChars, passIndex)) { | |||
2063 | SayWithDeclaration(*overridden, | |||
2064 | "A type-bound procedure and its override must have compatible interfaces"_err_en_US); | |||
2065 | } | |||
2066 | } | |||
2067 | } | |||
2068 | } | |||
2069 | if (symbol.attrs().test(Attr::PRIVATE)) { | |||
2070 | if (FindModuleContaining(dtScope) == | |||
2071 | FindModuleContaining(overridden->owner())) { | |||
2072 | // types declared in same madule | |||
2073 | if (!overridden->attrs().test(Attr::PRIVATE)) { | |||
2074 | SayWithDeclaration(*overridden, | |||
2075 | "A PRIVATE procedure may not override a PUBLIC procedure"_err_en_US); | |||
2076 | } | |||
2077 | } else { // types declared in distinct madules | |||
2078 | if (!CheckAccessibleSymbol(dtScope.parent(), *overridden)) { | |||
2079 | SayWithDeclaration(*overridden, | |||
2080 | "A PRIVATE procedure may not override an accessible procedure"_err_en_US); | |||
2081 | } | |||
2082 | } | |||
2083 | } | |||
2084 | } else { | |||
2085 | SayWithDeclaration(*overridden, | |||
2086 | "A type-bound procedure binding may not have the same name as a parent component"_err_en_US); | |||
2087 | } | |||
2088 | } | |||
2089 | CheckPassArg(symbol, &binding.symbol(), binding); | |||
2090 | } | |||
2091 | ||||
2092 | void CheckHelper::Check(const Scope &scope) { | |||
2093 | scope_ = &scope; | |||
2094 | common::Restorer<const Symbol *> restorer{innermostSymbol_, innermostSymbol_}; | |||
2095 | if (const Symbol *symbol{scope.symbol()}) { | |||
2096 | innermostSymbol_ = symbol; | |||
2097 | } | |||
2098 | if (scope.IsParameterizedDerivedTypeInstantiation()) { | |||
2099 | auto restorer{common::ScopedSet(scopeIsUninstantiatedPDT_, false)}; | |||
2100 | auto restorer2{context_.foldingContext().messages().SetContext( | |||
2101 | scope.instantiationContext().get())}; | |||
2102 | for (const auto &pair : scope) { | |||
2103 | CheckPointerInitialization(*pair.second); | |||
2104 | } | |||
2105 | } else { | |||
2106 | auto restorer{common::ScopedSet( | |||
2107 | scopeIsUninstantiatedPDT_, scope.IsParameterizedDerivedType())}; | |||
2108 | for (const auto &set : scope.equivalenceSets()) { | |||
2109 | CheckEquivalenceSet(set); | |||
2110 | } | |||
2111 | for (const auto &pair : scope) { | |||
2112 | Check(*pair.second); | |||
2113 | } | |||
2114 | for (const auto &pair : scope.commonBlocks()) { | |||
2115 | CheckCommonBlock(*pair.second); | |||
2116 | } | |||
2117 | int mainProgCnt{0}; | |||
2118 | for (const Scope &child : scope.children()) { | |||
2119 | Check(child); | |||
2120 | // A program shall consist of exactly one main program (5.2.2). | |||
2121 | if (child.kind() == Scope::Kind::MainProgram) { | |||
2122 | ++mainProgCnt; | |||
2123 | if (mainProgCnt > 1) { | |||
2124 | messages_.Say(child.sourceRange(), | |||
2125 | "A source file cannot contain more than one main program"_err_en_US); | |||
2126 | } | |||
2127 | } | |||
2128 | } | |||
2129 | if (scope.kind() == Scope::Kind::BlockData) { | |||
2130 | CheckBlockData(scope); | |||
2131 | } | |||
2132 | if (auto name{scope.GetName()}) { | |||
2133 | auto iter{scope.find(*name)}; | |||
2134 | if (iter != scope.end()) { | |||
2135 | const char *kind{nullptr}; | |||
2136 | switch (scope.kind()) { | |||
2137 | case Scope::Kind::Module: | |||
2138 | kind = scope.symbol()->get<ModuleDetails>().isSubmodule() | |||
2139 | ? "submodule" | |||
2140 | : "module"; | |||
2141 | break; | |||
2142 | case Scope::Kind::MainProgram: | |||
2143 | kind = "main program"; | |||
2144 | break; | |||
2145 | case Scope::Kind::BlockData: | |||
2146 | kind = "BLOCK DATA subprogram"; | |||
2147 | break; | |||
2148 | default:; | |||
2149 | } | |||
2150 | if (kind) { | |||
2151 | messages_.Say(iter->second->name(), | |||
2152 | "Name '%s' declared in a %s should not have the same name as the %s"_port_en_US, | |||
2153 | *name, kind, kind); | |||
2154 | } | |||
2155 | } | |||
2156 | } | |||
2157 | CheckGenericOps(scope); | |||
2158 | } | |||
2159 | } | |||
2160 | ||||
2161 | void CheckHelper::CheckEquivalenceSet(const EquivalenceSet &set) { | |||
2162 | auto iter{ | |||
2163 | std::find_if(set.begin(), set.end(), [](const EquivalenceObject &object) { | |||
2164 | return FindCommonBlockContaining(object.symbol) != nullptr; | |||
2165 | })}; | |||
2166 | if (iter != set.end()) { | |||
2167 | const Symbol &commonBlock{DEREF(FindCommonBlockContaining(iter->symbol))Fortran::common::Deref(FindCommonBlockContaining(iter->symbol ), "flang/lib/Semantics/check-declarations.cpp", 2167)}; | |||
2168 | for (auto &object : set) { | |||
2169 | if (&object != &*iter) { | |||
2170 | if (auto *details{object.symbol.detailsIf<ObjectEntityDetails>()}) { | |||
2171 | if (details->commonBlock()) { | |||
2172 | if (details->commonBlock() != &commonBlock) { // 8.10.3 paragraph 1 | |||
2173 | if (auto *msg{messages_.Say(object.symbol.name(), | |||
2174 | "Two objects in the same EQUIVALENCE set may not be members of distinct COMMON blocks"_err_en_US)}) { | |||
2175 | msg->Attach(iter->symbol.name(), | |||
2176 | "Other object in EQUIVALENCE set"_en_US) | |||
2177 | .Attach(details->commonBlock()->name(), | |||
2178 | "COMMON block containing '%s'"_en_US, | |||
2179 | object.symbol.name()) | |||
2180 | .Attach(commonBlock.name(), | |||
2181 | "COMMON block containing '%s'"_en_US, | |||
2182 | iter->symbol.name()); | |||
2183 | } | |||
2184 | } | |||
2185 | } else { | |||
2186 | // Mark all symbols in the equivalence set with the same COMMON | |||
2187 | // block to prevent spurious error messages about initialization | |||
2188 | // in BLOCK DATA outside COMMON | |||
2189 | details->set_commonBlock(commonBlock); | |||
2190 | } | |||
2191 | } | |||
2192 | } | |||
2193 | } | |||
2194 | } | |||
2195 | // TODO: Move C8106 (&al.) checks here from resolve-names-utils.cpp | |||
2196 | } | |||
2197 | ||||
2198 | void CheckHelper::CheckBlockData(const Scope &scope) { | |||
2199 | // BLOCK DATA subprograms should contain only named common blocks. | |||
2200 | // C1415 presents a list of statements that shouldn't appear in | |||
2201 | // BLOCK DATA, but so long as the subprogram contains no executable | |||
2202 | // code and allocates no storage outside named COMMON, we're happy | |||
2203 | // (e.g., an ENUM is strictly not allowed). | |||
2204 | for (const auto &pair : scope) { | |||
2205 | const Symbol &symbol{*pair.second}; | |||
2206 | if (!(symbol.has<CommonBlockDetails>() || symbol.has<UseDetails>() || | |||
2207 | symbol.has<UseErrorDetails>() || symbol.has<DerivedTypeDetails>() || | |||
2208 | symbol.has<SubprogramDetails>() || | |||
2209 | symbol.has<ObjectEntityDetails>() || | |||
2210 | (symbol.has<ProcEntityDetails>() && | |||
2211 | !symbol.attrs().test(Attr::POINTER)))) { | |||
2212 | messages_.Say(symbol.name(), | |||
2213 | "'%s' may not appear in a BLOCK DATA subprogram"_err_en_US, | |||
2214 | symbol.name()); | |||
2215 | } | |||
2216 | } | |||
2217 | } | |||
2218 | ||||
2219 | // Check distinguishability of generic assignment and operators. | |||
2220 | // For these, generics and generic bindings must be considered together. | |||
2221 | void CheckHelper::CheckGenericOps(const Scope &scope) { | |||
2222 | DistinguishabilityHelper helper{context_}; | |||
2223 | auto addSpecifics{[&](const Symbol &generic) { | |||
2224 | const auto *details{generic.GetUltimate().detailsIf<GenericDetails>()}; | |||
2225 | if (!details) { | |||
2226 | // Not a generic; ensure characteristics are defined if a function. | |||
2227 | auto restorer{messages_.SetLocation(generic.name())}; | |||
2228 | if (IsFunction(generic) && !context_.HasError(generic)) { | |||
2229 | if (const Symbol *result{FindFunctionResult(generic)}; | |||
2230 | result && !context_.HasError(*result)) { | |||
2231 | Characterize(generic); | |||
2232 | } | |||
2233 | } | |||
2234 | return; | |||
2235 | } | |||
2236 | GenericKind kind{details->kind()}; | |||
2237 | if (!kind.IsAssignment() && !kind.IsOperator()) { | |||
2238 | return; | |||
2239 | } | |||
2240 | const SymbolVector &specifics{details->specificProcs()}; | |||
2241 | const std::vector<SourceName> &bindingNames{details->bindingNames()}; | |||
2242 | for (std::size_t i{0}; i < specifics.size(); ++i) { | |||
2243 | const Symbol &specific{*specifics[i]}; | |||
2244 | auto restorer{messages_.SetLocation(bindingNames[i])}; | |||
2245 | if (const Procedure *proc{Characterize(specific)}) { | |||
2246 | if (kind.IsAssignment()) { | |||
2247 | if (!CheckDefinedAssignment(specific, *proc)) { | |||
2248 | continue; | |||
2249 | } | |||
2250 | } else { | |||
2251 | if (!CheckDefinedOperator(generic.name(), kind, specific, *proc)) { | |||
2252 | continue; | |||
2253 | } | |||
2254 | } | |||
2255 | helper.Add(generic, kind, specific, *proc); | |||
2256 | } | |||
2257 | } | |||
2258 | }}; | |||
2259 | for (const auto &pair : scope) { | |||
2260 | const Symbol &symbol{*pair.second}; | |||
2261 | addSpecifics(symbol); | |||
2262 | const Symbol &ultimate{symbol.GetUltimate()}; | |||
2263 | if (ultimate.has<DerivedTypeDetails>()) { | |||
2264 | if (const Scope *typeScope{ultimate.scope()}) { | |||
2265 | for (const auto &pair2 : *typeScope) { | |||
2266 | addSpecifics(*pair2.second); | |||
2267 | } | |||
2268 | } | |||
2269 | } | |||
2270 | } | |||
2271 | helper.Check(scope); | |||
2272 | } | |||
2273 | ||||
2274 | static bool IsSubprogramDefinition(const Symbol &symbol) { | |||
2275 | const auto *subp{symbol.detailsIf<SubprogramDetails>()}; | |||
2276 | return subp && !subp->isInterface() && symbol.scope() && | |||
2277 | symbol.scope()->kind() == Scope::Kind::Subprogram; | |||
2278 | } | |||
2279 | ||||
2280 | static bool IsBlockData(const Symbol &symbol) { | |||
2281 | return symbol.scope() && symbol.scope()->kind() == Scope::Kind::BlockData; | |||
2282 | } | |||
2283 | ||||
2284 | static bool IsExternalProcedureDefinition(const Symbol &symbol) { | |||
2285 | return IsBlockData(symbol) || | |||
2286 | (IsSubprogramDefinition(symbol) && | |||
2287 | (IsExternal(symbol) || symbol.GetBindName())); | |||
2288 | } | |||
2289 | ||||
2290 | static std::optional<std::string> DefinesGlobalName(const Symbol &symbol) { | |||
2291 | if (const auto *module{symbol.detailsIf<ModuleDetails>()}) { | |||
2292 | if (!module->isSubmodule() && !symbol.owner().IsIntrinsicModules()) { | |||
2293 | return symbol.name().ToString(); | |||
2294 | } | |||
2295 | } else if (IsBlockData(symbol)) { | |||
2296 | return symbol.name().ToString(); | |||
2297 | } else { | |||
2298 | const std::string *bindC{symbol.GetBindName()}; | |||
2299 | if (symbol.has<CommonBlockDetails>() || | |||
2300 | IsExternalProcedureDefinition(symbol)) { | |||
2301 | return bindC ? *bindC : symbol.name().ToString(); | |||
2302 | } else if (bindC && | |||
2303 | (symbol.has<ObjectEntityDetails>() || IsModuleProcedure(symbol))) { | |||
2304 | return *bindC; | |||
2305 | } | |||
2306 | } | |||
2307 | return std::nullopt; | |||
2308 | } | |||
2309 | ||||
2310 | // 19.2 p2 | |||
2311 | void CheckHelper::CheckGlobalName(const Symbol &symbol) { | |||
2312 | if (auto global{DefinesGlobalName(symbol)}) { | |||
2313 | auto pair{globalNames_.emplace(std::move(*global), symbol)}; | |||
2314 | if (!pair.second) { | |||
2315 | const Symbol &other{*pair.first->second}; | |||
2316 | if (context_.HasError(symbol) || context_.HasError(other)) { | |||
2317 | // don't pile on | |||
2318 | } else if (symbol.has<CommonBlockDetails>() && | |||
2319 | other.has<CommonBlockDetails>() && symbol.name() == other.name()) { | |||
2320 | // Two common blocks can have the same global name so long as | |||
2321 | // they're not in the same scope. | |||
2322 | } else if ((IsProcedure(symbol) || IsBlockData(symbol)) && | |||
2323 | (IsProcedure(other) || IsBlockData(other)) && | |||
2324 | (!IsExternalProcedureDefinition(symbol) || | |||
2325 | !IsExternalProcedureDefinition(other))) { | |||
2326 | // both are procedures/BLOCK DATA, not both definitions | |||
2327 | } else if (symbol.has<ModuleDetails>()) { | |||
2328 | messages_.Say(symbol.name(), | |||
2329 | "Module '%s' conflicts with a global name"_port_en_US, | |||
2330 | pair.first->first); | |||
2331 | } else if (other.has<ModuleDetails>()) { | |||
2332 | messages_.Say(symbol.name(), | |||
2333 | "Global name '%s' conflicts with a module"_port_en_US, | |||
2334 | pair.first->first); | |||
2335 | } else if (auto *msg{messages_.Say(symbol.name(), | |||
2336 | "Two entities have the same global name '%s'"_err_en_US, | |||
2337 | pair.first->first)}) { | |||
2338 | msg->Attach(other.name(), "Conflicting declaration"_en_US); | |||
2339 | context_.SetError(symbol); | |||
2340 | context_.SetError(other); | |||
2341 | } | |||
2342 | } | |||
2343 | } | |||
2344 | } | |||
2345 | ||||
2346 | void CheckHelper::CheckBindC(const Symbol &symbol) { | |||
2347 | bool isExplicitBindC{symbol.attrs().test(Attr::BIND_C)}; | |||
2348 | if (isExplicitBindC) { | |||
2349 | CheckConflicting(symbol, Attr::BIND_C, Attr::PARAMETER); | |||
2350 | CheckConflicting(symbol, Attr::BIND_C, Attr::ELEMENTAL); | |||
2351 | } else { | |||
2352 | // symbol must be interoperable (e.g., dummy argument of interoperable | |||
2353 | // procedure interface) but is not itself BIND(C). | |||
2354 | } | |||
2355 | if (const std::string * bindName{symbol.GetBindName()}; | |||
2356 | bindName) { // has a binding name | |||
2357 | if (!bindName->empty()) { | |||
2358 | bool ok{bindName->front() == '_' || parser::IsLetter(bindName->front())}; | |||
2359 | for (char ch : *bindName) { | |||
2360 | ok &= ch == '_' || parser::IsLetter(ch) || parser::IsDecimalDigit(ch); | |||
2361 | } | |||
2362 | if (!ok) { | |||
2363 | messages_.Say(symbol.name(), | |||
2364 | "Symbol has a BIND(C) name that is not a valid C language identifier"_err_en_US); | |||
2365 | context_.SetError(symbol); | |||
2366 | } | |||
2367 | } | |||
2368 | } | |||
2369 | if (symbol.GetIsExplicitBindName()) { // BIND(C,NAME=...); C1552, C1529 | |||
2370 | auto defClass{ClassifyProcedure(symbol)}; | |||
2371 | if (IsProcedurePointer(symbol)) { | |||
2372 | messages_.Say(symbol.name(), | |||
2373 | "A procedure pointer may not have a BIND attribute with a name"_err_en_US); | |||
2374 | context_.SetError(symbol); | |||
2375 | } else if (defClass == ProcedureDefinitionClass::None || | |||
2376 | IsExternal(symbol)) { | |||
2377 | } else if (symbol.attrs().test(Attr::ABSTRACT)) { | |||
2378 | messages_.Say(symbol.name(), | |||
2379 | "An ABSTRACT interface may not have a BIND attribute with a name"_err_en_US); | |||
2380 | context_.SetError(symbol); | |||
2381 | } else if (defClass == ProcedureDefinitionClass::Internal || | |||
2382 | defClass == ProcedureDefinitionClass::Dummy) { | |||
2383 | messages_.Say(symbol.name(), | |||
2384 | "An internal or dummy procedure may not have a BIND(C,NAME=) binding label"_err_en_US); | |||
2385 | context_.SetError(symbol); | |||
2386 | } | |||
2387 | } | |||
2388 | if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) { | |||
2389 | if (isExplicitBindC && !symbol.owner().IsModule()) { | |||
2390 | messages_.Say(symbol.name(), | |||
2391 | "A variable with BIND(C) attribute may only appear in the specification part of a module"_err_en_US); | |||
2392 | context_.SetError(symbol); | |||
2393 | } | |||
2394 | auto shape{evaluate::GetShape(foldingContext_, symbol)}; | |||
2395 | if (shape) { | |||
2396 | if (evaluate::GetRank(*shape) == 0) { // 18.3.4 | |||
2397 | if (isExplicitBindC && IsAllocatableOrPointer(symbol)) { | |||
2398 | messages_.Say(symbol.name(), | |||
2399 | "A scalar interoperable variable may not be ALLOCATABLE or POINTER"_err_en_US); | |||
2400 | context_.SetError(symbol); | |||
2401 | } | |||
2402 | } else { // 18.3.5 | |||
2403 | if (auto extents{ | |||
2404 | evaluate::AsConstantExtents(foldingContext_, *shape)}) { | |||
2405 | if (evaluate::GetSize(*extents) == 0) { | |||
2406 | SayWithDeclaration(symbol, symbol.name(), | |||
2407 | "Interoperable array must have at least one element"_err_en_US); | |||
2408 | context_.SetError(symbol); | |||
2409 | } | |||
2410 | } else if ((isExplicitBindC || symbol.attrs().test(Attr::VALUE)) && | |||
2411 | !evaluate::IsExplicitShape(symbol) && !object->IsAssumedSize()) { | |||
2412 | SayWithDeclaration(symbol, symbol.name(), | |||
2413 | "BIND(C) array must have explicit shape or be assumed-size unless a dummy argument without the VALUE attribute"_err_en_US); | |||
2414 | context_.SetError(symbol); | |||
2415 | } | |||
2416 | } | |||
2417 | } | |||
2418 | if (const auto *type{symbol.GetType()}) { | |||
2419 | const auto *derived{type->AsDerived()}; | |||
2420 | if (derived && !derived->typeSymbol().attrs().test(Attr::BIND_C)) { | |||
2421 | if (auto *msg{messages_.Say(symbol.name(), | |||
2422 | "The derived type of a BIND(C) object must also be BIND(C)"_err_en_US)}) { | |||
2423 | msg->Attach( | |||
2424 | derived->typeSymbol().name(), "Non-interoperable type"_en_US); | |||
2425 | } | |||
2426 | context_.SetError(symbol); | |||
2427 | } | |||
2428 | if (type->IsAssumedType() || IsAssumedLengthCharacter(symbol)) { | |||
2429 | // ok | |||
2430 | } else if (IsAllocatableOrPointer(symbol) && | |||
2431 | type->category() == DeclTypeSpec::Character && | |||
2432 | type->characterTypeSpec().length().isDeferred()) { | |||
2433 | // ok; F'2018 18.3.6 p2(6) | |||
2434 | } else if (derived || IsInteroperableIntrinsicType(*type)) { | |||
2435 | // F'2018 18.3.6 p2(4,5) | |||
2436 | } else if (type->category() == DeclTypeSpec::Logical && IsDummy(symbol) && | |||
2437 | evaluate::GetRank(*shape) == 0) { | |||
2438 | // Special exception: LOGICAL scalar dummy arguments can be converted | |||
2439 | // before a call -- & after if not INTENT(IN) -- without loss of | |||
2440 | // information, and are accepted by some older compilers. | |||
2441 | messages_.Say(symbol.name(), | |||
2442 | "A BIND(C) LOGICAL dummy argument should have the interoperable KIND=C_BOOL"_port_en_US); | |||
2443 | } else if (symbol.attrs().test(Attr::VALUE)) { | |||
2444 | messages_.Say(symbol.name(), | |||
2445 | "A BIND(C) VALUE dummy argument must have an interoperable type"_err_en_US); | |||
2446 | context_.SetError(symbol); | |||
2447 | } else { | |||
2448 | messages_.Say(symbol.name(), | |||
2449 | "A BIND(C) object must have an interoperable type"_err_en_US); | |||
2450 | context_.SetError(symbol); | |||
2451 | } | |||
2452 | } | |||
2453 | if (IsOptional(symbol) && !symbol.attrs().test(Attr::VALUE)) { | |||
2454 | messages_.Say(symbol.name(), | |||
2455 | "An interoperable procedure with an OPTIONAL dummy argument might not be portable"_port_en_US); | |||
2456 | } | |||
2457 | } else if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) { | |||
2458 | if (!proc->procInterface() || | |||
2459 | !proc->procInterface()->attrs().test(Attr::BIND_C)) { | |||
2460 | messages_.Say(symbol.name(), | |||
2461 | "An interface name with BIND attribute must be specified if the BIND attribute is specified in a procedure declaration statement"_err_en_US); | |||
2462 | context_.SetError(symbol); | |||
2463 | } | |||
2464 | } else if (const auto *subp{symbol.detailsIf<SubprogramDetails>()}) { | |||
2465 | for (const Symbol *dummy : subp->dummyArgs()) { | |||
2466 | if (dummy) { | |||
2467 | CheckBindC(*dummy); | |||
2468 | } else { | |||
2469 | messages_.Say(symbol.name(), | |||
2470 | "A subprogram interface with the BIND attribute may not have an alternate return argument"_err_en_US); | |||
2471 | context_.SetError(symbol); | |||
2472 | } | |||
2473 | } | |||
2474 | } else if (const auto *derived{symbol.detailsIf<DerivedTypeDetails>()}) { | |||
2475 | if (derived->sequence()) { // C1801 | |||
2476 | messages_.Say(symbol.name(), | |||
2477 | "A derived type with the BIND attribute cannot have the SEQUENCE attribute"_err_en_US); | |||
2478 | context_.SetError(symbol); | |||
2479 | } else if (!derived->paramDecls().empty()) { // C1802 | |||
2480 | messages_.Say(symbol.name(), | |||
2481 | "A derived type with the BIND attribute has type parameter(s)"_err_en_US); | |||
2482 | context_.SetError(symbol); | |||
2483 | } else if (symbol.scope()->GetDerivedTypeParent()) { // C1803 | |||
2484 | messages_.Say(symbol.name(), | |||
2485 | "A derived type with the BIND attribute cannot extend from another derived type"_err_en_US); | |||
2486 | context_.SetError(symbol); | |||
2487 | } else { | |||
2488 | for (const auto &pair : *symbol.scope()) { | |||
2489 | const Symbol *component{&*pair.second}; | |||
2490 | if (IsProcedure(*component)) { // C1804 | |||
2491 | messages_.Say(component->name(), | |||
2492 | "A derived type with the BIND attribute cannot have a type bound procedure"_err_en_US); | |||
2493 | context_.SetError(symbol); | |||
2494 | } | |||
2495 | if (IsAllocatableOrPointer(*component)) { // C1806 | |||
2496 | messages_.Say(component->name(), | |||
2497 | "A derived type with the BIND attribute cannot have a pointer or allocatable component"_err_en_US); | |||
2498 | context_.SetError(symbol); | |||
2499 | } | |||
2500 | if (const auto *type{component->GetType()}) { | |||
2501 | if (const auto *derived{type->AsDerived()}) { | |||
2502 | if (!derived->typeSymbol().attrs().test(Attr::BIND_C)) { | |||
2503 | if (auto *msg{messages_.Say(component->name(), | |||
2504 | "Component '%s' of an interoperable derived type must have the BIND attribute"_err_en_US, | |||
2505 | component->name())}) { | |||
2506 | msg->Attach(derived->typeSymbol().name(), | |||
2507 | "Non-interoperable component type"_en_US); | |||
2508 | } | |||
2509 | context_.SetError(symbol); | |||
2510 | } | |||
2511 | } else if (!IsInteroperableIntrinsicType(*type)) { | |||
2512 | messages_.Say(component->name(), | |||
2513 | "Each component of an interoperable derived type must have an interoperable type"_err_en_US); | |||
2514 | context_.SetError(symbol); | |||
2515 | } | |||
2516 | } | |||
2517 | if (auto extents{ | |||
2518 | evaluate::GetConstantExtents(foldingContext_, component)}; | |||
2519 | extents && evaluate::GetSize(*extents) == 0) { | |||
2520 | messages_.Say(component->name(), | |||
2521 | "An array component of an interoperable type must have at least one element"_err_en_US); | |||
2522 | context_.SetError(symbol); | |||
2523 | } | |||
2524 | } | |||
2525 | } | |||
2526 | if (derived->componentNames().empty() && | |||
2527 | !FindModuleFileContaining(symbol.owner())) { // C1805 | |||
2528 | messages_.Say(symbol.name(), | |||
2529 | "A derived type with the BIND attribute is empty"_port_en_US); | |||
2530 | } | |||
2531 | } | |||
2532 | } | |||
2533 | ||||
2534 | bool CheckHelper::CheckDioDummyIsData( | |||
2535 | const Symbol &subp, const Symbol *arg, std::size_t position) { | |||
2536 | if (arg && arg->detailsIf<ObjectEntityDetails>()) { | |||
2537 | return true; | |||
2538 | } else { | |||
2539 | if (arg) { | |||
2540 | messages_.Say(arg->name(), | |||
2541 | "Dummy argument '%s' must be a data object"_err_en_US, arg->name()); | |||
2542 | } else { | |||
2543 | messages_.Say(subp.name(), | |||
2544 | "Dummy argument %d of '%s' must be a data object"_err_en_US, position, | |||
2545 | subp.name()); | |||
2546 | } | |||
2547 | return false; | |||
2548 | } | |||
2549 | } | |||
2550 | ||||
2551 | void CheckHelper::CheckAlreadySeenDefinedIo(const DerivedTypeSpec &derivedType, | |||
2552 | common::DefinedIo ioKind, const Symbol &proc, const Symbol &generic) { | |||
2553 | // Check for conflict between non-type-bound defined I/O and type-bound | |||
2554 | // generics. It's okay to have two or more distinct defined I/O procedures for | |||
2555 | // the same type if they're coming from distinct non-type-bound interfaces. | |||
2556 | // (The non-type-bound interfaces would have been merged into a single generic | |||
2557 | // -- with errors where indistinguishable -- when both were visible from the | |||
2558 | // same scope.) | |||
2559 | if (generic.owner().IsDerivedType()) { | |||
2560 | return; | |||
2561 | } | |||
2562 | if (const Scope * dtScope{derivedType.scope()}) { | |||
2563 | if (auto iter{dtScope->find(generic.name())}; iter != dtScope->end()) { | |||
2564 | for (auto specRef : iter->second->get<GenericDetails>().specificProcs()) { | |||
2565 | const Symbol &specific{specRef->get<ProcBindingDetails>().symbol()}; | |||
2566 | if (specific == proc) { // unambiguous, accept | |||
2567 | continue; | |||
2568 | } | |||
2569 | if (const auto *specDT{GetDtvArgDerivedType(specific)}; | |||
2570 | specDT && evaluate::AreSameDerivedType(derivedType, *specDT)) { | |||
2571 | SayWithDeclaration(*specRef, proc.name(), | |||
2572 | "Derived type '%s' has conflicting type-bound input/output procedure '%s'"_err_en_US, | |||
2573 | derivedType.name(), GenericKind::AsFortran(ioKind)); | |||
2574 | return; | |||
2575 | } | |||
2576 | } | |||
2577 | } | |||
2578 | } | |||
2579 | } | |||
2580 | ||||
2581 | void CheckHelper::CheckDioDummyIsDerived(const Symbol &subp, const Symbol &arg, | |||
2582 | common::DefinedIo ioKind, const Symbol &generic) { | |||
2583 | if (const DeclTypeSpec *type{arg.GetType()}) { | |||
2584 | if (const DerivedTypeSpec *derivedType{type->AsDerived()}) { | |||
2585 | CheckAlreadySeenDefinedIo(*derivedType, ioKind, subp, generic); | |||
2586 | bool isPolymorphic{type->IsPolymorphic()}; | |||
2587 | if (isPolymorphic != IsExtensibleType(derivedType)) { | |||
2588 | messages_.Say(arg.name(), | |||
2589 | "Dummy argument '%s' of a defined input/output procedure must be %s when the derived type is %s"_err_en_US, | |||
2590 | arg.name(), isPolymorphic ? "TYPE()" : "CLASS()", | |||
2591 | isPolymorphic ? "not extensible" : "extensible"); | |||
2592 | } | |||
2593 | } else { | |||
2594 | messages_.Say(arg.name(), | |||
2595 | "Dummy argument '%s' of a defined input/output procedure must have a" | |||
2596 | " derived type"_err_en_US, | |||
2597 | arg.name()); | |||
2598 | } | |||
2599 | } | |||
2600 | } | |||
2601 | ||||
2602 | void CheckHelper::CheckDioDummyIsDefaultInteger( | |||
2603 | const Symbol &subp, const Symbol &arg) { | |||
2604 | if (const DeclTypeSpec *type{arg.GetType()}; | |||
2605 | type && type->IsNumeric(TypeCategory::Integer)) { | |||
2606 | if (const auto kind{evaluate::ToInt64(type->numericTypeSpec().kind())}; | |||
2607 | kind && *kind == context_.GetDefaultKind(TypeCategory::Integer)) { | |||
2608 | return; | |||
2609 | } | |||
2610 | } | |||
2611 | messages_.Say(arg.name(), | |||
2612 | "Dummy argument '%s' of a defined input/output procedure" | |||
2613 | " must be an INTEGER of default KIND"_err_en_US, | |||
2614 | arg.name()); | |||
2615 | } | |||
2616 | ||||
2617 | void CheckHelper::CheckDioDummyIsScalar(const Symbol &subp, const Symbol &arg) { | |||
2618 | if (arg.Rank() > 0 || arg.Corank() > 0) { | |||
2619 | messages_.Say(arg.name(), | |||
2620 | "Dummy argument '%s' of a defined input/output procedure" | |||
2621 | " must be a scalar"_err_en_US, | |||
2622 | arg.name()); | |||
2623 | } | |||
2624 | } | |||
2625 | ||||
2626 | void CheckHelper::CheckDioDtvArg(const Symbol &subp, const Symbol *arg, | |||
2627 | common::DefinedIo ioKind, const Symbol &generic) { | |||
2628 | // Dtv argument looks like: dtv-type-spec, INTENT(INOUT) :: dtv | |||
2629 | if (CheckDioDummyIsData(subp, arg, 0)) { | |||
2630 | CheckDioDummyIsDerived(subp, *arg, ioKind, generic); | |||
2631 | CheckDioDummyAttrs(subp, *arg, | |||
2632 | ioKind == common::DefinedIo::ReadFormatted || | |||
2633 | ioKind == common::DefinedIo::ReadUnformatted | |||
2634 | ? Attr::INTENT_INOUT | |||
2635 | : Attr::INTENT_IN); | |||
2636 | } | |||
2637 | } | |||
2638 | ||||
2639 | // If an explicit INTRINSIC name is a function, so must all the specifics be, | |||
2640 | // and similarly for subroutines | |||
2641 | void CheckHelper::CheckGenericVsIntrinsic( | |||
2642 | const Symbol &symbol, const GenericDetails &generic) { | |||
2643 | if (symbol.attrs().test(Attr::INTRINSIC)) { | |||
2644 | const evaluate::IntrinsicProcTable &table{ | |||
2645 | context_.foldingContext().intrinsics()}; | |||
2646 | bool isSubroutine{table.IsIntrinsicSubroutine(symbol.name().ToString())}; | |||
2647 | if (isSubroutine || table.IsIntrinsicFunction(symbol.name().ToString())) { | |||
2648 | for (const SymbolRef &ref : generic.specificProcs()) { | |||
2649 | const Symbol &ultimate{ref->GetUltimate()}; | |||
2650 | bool specificFunc{ultimate.test(Symbol::Flag::Function)}; | |||
2651 | bool specificSubr{ultimate.test(Symbol::Flag::Subroutine)}; | |||
2652 | if (!specificFunc && !specificSubr) { | |||
2653 | if (const auto *proc{ultimate.detailsIf<SubprogramDetails>()}) { | |||
2654 | if (proc->isFunction()) { | |||
2655 | specificFunc = true; | |||
2656 | } else { | |||
2657 | specificSubr = true; | |||
2658 | } | |||
2659 | } | |||
2660 | } | |||
2661 | if ((specificFunc || specificSubr) && | |||
2662 | isSubroutine != specificSubr) { // C848 | |||
2663 | messages_.Say(symbol.name(), | |||
2664 | "Generic interface '%s' with explicit intrinsic %s of the same name may not have specific procedure '%s' that is a %s"_err_en_US, | |||
2665 | symbol.name(), isSubroutine ? "subroutine" : "function", | |||
2666 | ref->name(), isSubroutine ? "function" : "subroutine"); | |||
2667 | } | |||
2668 | } | |||
2669 | } | |||
2670 | } | |||
2671 | } | |||
2672 | ||||
2673 | void CheckHelper::CheckDefaultIntegerArg( | |||
2674 | const Symbol &subp, const Symbol *arg, Attr intent) { | |||
2675 | // Argument looks like: INTEGER, INTENT(intent) :: arg | |||
2676 | if (CheckDioDummyIsData(subp, arg, 1)) { | |||
2677 | CheckDioDummyIsDefaultInteger(subp, *arg); | |||
2678 | CheckDioDummyIsScalar(subp, *arg); | |||
2679 | CheckDioDummyAttrs(subp, *arg, intent); | |||
2680 | } | |||
2681 | } | |||
2682 | ||||
2683 | void CheckHelper::CheckDioAssumedLenCharacterArg(const Symbol &subp, | |||
2684 | const Symbol *arg, std::size_t argPosition, Attr intent) { | |||
2685 | // Argument looks like: CHARACTER (LEN=*), INTENT(intent) :: (iotype OR iomsg) | |||
2686 | if (CheckDioDummyIsData(subp, arg, argPosition)) { | |||
2687 | CheckDioDummyAttrs(subp, *arg, intent); | |||
2688 | const DeclTypeSpec *type{arg ? arg->GetType() : nullptr}; | |||
2689 | const IntrinsicTypeSpec *intrinsic{type ? type->AsIntrinsic() : nullptr}; | |||
2690 | const auto kind{ | |||
2691 | intrinsic ? evaluate::ToInt64(intrinsic->kind()) : std::nullopt}; | |||
2692 | if (!IsAssumedLengthCharacter(*arg) || | |||
2693 | (!kind || | |||
2694 | *kind != | |||
2695 | context_.defaultKinds().GetDefaultKind( | |||
2696 | TypeCategory::Character))) { | |||
2697 | messages_.Say(arg->name(), | |||
2698 | "Dummy argument '%s' of a defined input/output procedure" | |||
2699 | " must be assumed-length CHARACTER of default kind"_err_en_US, | |||
2700 | arg->name()); | |||
2701 | } | |||
2702 | } | |||
2703 | } | |||
2704 | ||||
2705 | void CheckHelper::CheckDioVlistArg( | |||
2706 | const Symbol &subp, const Symbol *arg, std::size_t argPosition) { | |||
2707 | // Vlist argument looks like: INTEGER, INTENT(IN) :: v_list(:) | |||
2708 | if (CheckDioDummyIsData(subp, arg, argPosition)) { | |||
2709 | CheckDioDummyIsDefaultInteger(subp, *arg); | |||
2710 | CheckDioDummyAttrs(subp, *arg, Attr::INTENT_IN); | |||
2711 | const auto *objectDetails{arg->detailsIf<ObjectEntityDetails>()}; | |||
2712 | if (!objectDetails || !objectDetails->shape().CanBeDeferredShape()) { | |||
2713 | messages_.Say(arg->name(), | |||
2714 | "Dummy argument '%s' of a defined input/output procedure must be" | |||
2715 | " deferred shape"_err_en_US, | |||
2716 | arg->name()); | |||
2717 | } | |||
2718 | } | |||
2719 | } | |||
2720 | ||||
2721 | void CheckHelper::CheckDioArgCount( | |||
2722 | const Symbol &subp, common::DefinedIo ioKind, std::size_t argCount) { | |||
2723 | const std::size_t requiredArgCount{ | |||
2724 | (std::size_t)(ioKind == common::DefinedIo::ReadFormatted || | |||
2725 | ioKind == common::DefinedIo::WriteFormatted | |||
2726 | ? 6 | |||
2727 | : 4)}; | |||
2728 | if (argCount != requiredArgCount) { | |||
2729 | SayWithDeclaration(subp, | |||
2730 | "Defined input/output procedure '%s' must have" | |||
2731 | " %d dummy arguments rather than %d"_err_en_US, | |||
2732 | subp.name(), requiredArgCount, argCount); | |||
2733 | context_.SetError(subp); | |||
2734 | } | |||
2735 | } | |||
2736 | ||||
2737 | void CheckHelper::CheckDioDummyAttrs( | |||
2738 | const Symbol &subp, const Symbol &arg, Attr goodIntent) { | |||
2739 | // Defined I/O procedures can't have attributes other than INTENT | |||
2740 | Attrs attrs{arg.attrs()}; | |||
2741 | if (!attrs.test(goodIntent)) { | |||
2742 | messages_.Say(arg.name(), | |||
2743 | "Dummy argument '%s' of a defined input/output procedure" | |||
2744 | " must have intent '%s'"_err_en_US, | |||
2745 | arg.name(), AttrToString(goodIntent)); | |||
2746 | } | |||
2747 | attrs = attrs - Attr::INTENT_IN - Attr::INTENT_OUT - Attr::INTENT_INOUT; | |||
2748 | if (!attrs.empty()) { | |||
2749 | messages_.Say(arg.name(), | |||
2750 | "Dummy argument '%s' of a defined input/output procedure may not have" | |||
2751 | " any attributes"_err_en_US, | |||
2752 | arg.name()); | |||
2753 | } | |||
2754 | } | |||
2755 | ||||
2756 | // Enforce semantics for defined input/output procedures (12.6.4.8.2) and C777 | |||
2757 | void CheckHelper::CheckDefinedIoProc(const Symbol &symbol, | |||
2758 | const GenericDetails &details, common::DefinedIo ioKind) { | |||
2759 | for (auto ref : details.specificProcs()) { | |||
2760 | const Symbol &ultimate{ref->GetUltimate()}; | |||
2761 | const auto *binding{ultimate.detailsIf<ProcBindingDetails>()}; | |||
2762 | const Symbol &specific{*(binding ? &binding->symbol() : &ultimate)}; | |||
2763 | if (ultimate.attrs().test(Attr::NOPASS)) { // C774 | |||
2764 | messages_.Say("Defined input/output procedure '%s' may not have NOPASS " | |||
2765 | "attribute"_err_en_US, | |||
2766 | ultimate.name()); | |||
2767 | context_.SetError(ultimate); | |||
2768 | } | |||
2769 | if (const auto *subpDetails{specific.detailsIf<SubprogramDetails>()}) { | |||
2770 | const std::vector<Symbol *> &dummyArgs{subpDetails->dummyArgs()}; | |||
2771 | CheckDioArgCount(specific, ioKind, dummyArgs.size()); | |||
2772 | int argCount{0}; | |||
2773 | for (auto *arg : dummyArgs) { | |||
2774 | switch (argCount++) { | |||
2775 | case 0: | |||
2776 | // dtv-type-spec, INTENT(INOUT) :: dtv | |||
2777 | CheckDioDtvArg(specific, arg, ioKind, symbol); | |||
2778 | break; | |||
2779 | case 1: | |||
2780 | // INTEGER, INTENT(IN) :: unit | |||
2781 | CheckDefaultIntegerArg(specific, arg, Attr::INTENT_IN); | |||
2782 | break; | |||
2783 | case 2: | |||
2784 | if (ioKind == common::DefinedIo::ReadFormatted || | |||
2785 | ioKind == common::DefinedIo::WriteFormatted) { | |||
2786 | // CHARACTER (LEN=*), INTENT(IN) :: iotype | |||
2787 | CheckDioAssumedLenCharacterArg( | |||
2788 | specific, arg, argCount, Attr::INTENT_IN); | |||
2789 | } else { | |||
2790 | // INTEGER, INTENT(OUT) :: iostat | |||
2791 | CheckDefaultIntegerArg(specific, arg, Attr::INTENT_OUT); | |||
2792 | } | |||
2793 | break; | |||
2794 | case 3: | |||
2795 | if (ioKind == common::DefinedIo::ReadFormatted || | |||
2796 | ioKind == common::DefinedIo::WriteFormatted) { | |||
2797 | // INTEGER, INTENT(IN) :: v_list(:) | |||
2798 | CheckDioVlistArg(specific, arg, argCount); | |||
2799 | } else { | |||
2800 | // CHARACTER (LEN=*), INTENT(INOUT) :: iomsg | |||
2801 | CheckDioAssumedLenCharacterArg( | |||
2802 | specific, arg, argCount, Attr::INTENT_INOUT); | |||
2803 | } | |||
2804 | break; | |||
2805 | case 4: | |||
2806 | // INTEGER, INTENT(OUT) :: iostat | |||
2807 | CheckDefaultIntegerArg(specific, arg, Attr::INTENT_OUT); | |||
2808 | break; | |||
2809 | case 5: | |||
2810 | // CHARACTER (LEN=*), INTENT(INOUT) :: iomsg | |||
2811 | CheckDioAssumedLenCharacterArg( | |||
2812 | specific, arg, argCount, Attr::INTENT_INOUT); | |||
2813 | break; | |||
2814 | default:; | |||
2815 | } | |||
2816 | } | |||
2817 | } | |||
2818 | } | |||
2819 | } | |||
2820 | ||||
2821 | void CheckHelper::CheckSymbolType(const Symbol &symbol) { | |||
2822 | if (!IsAllocatable(symbol) && | |||
2823 | (!IsPointer(symbol) || | |||
2824 | (IsProcedure(symbol) && !symbol.HasExplicitInterface()))) { // C702 | |||
2825 | if (auto dyType{evaluate::DynamicType::From(symbol)}) { | |||
2826 | if (dyType->HasDeferredTypeParameter()) { | |||
2827 | messages_.Say( | |||
2828 | "'%s' has a type %s with a deferred type parameter but is neither an allocatable nor an object pointer"_err_en_US, | |||
2829 | symbol.name(), dyType->AsFortran()); | |||
2830 | } | |||
2831 | } | |||
2832 | } | |||
2833 | } | |||
2834 | ||||
2835 | void CheckHelper::CheckModuleProcedureDef(const Symbol &symbol) { | |||
2836 | auto procClass{ClassifyProcedure(symbol)}; | |||
2837 | if (const auto *subprogram{symbol.detailsIf<SubprogramDetails>()}; | |||
2838 | subprogram && | |||
2839 | (procClass == ProcedureDefinitionClass::Module && | |||
2840 | symbol.attrs().test(Attr::MODULE)) && | |||
2841 | !subprogram->bindName() && !subprogram->isInterface()) { | |||
2842 | const Symbol *module{nullptr}; | |||
2843 | if (const Scope * moduleScope{FindModuleContaining(symbol.owner())}; | |||
2844 | moduleScope && moduleScope->symbol()) { | |||
2845 | if (const auto *details{ | |||
2846 | moduleScope->symbol()->detailsIf<ModuleDetails>()}) { | |||
2847 | if (details->parent()) { | |||
2848 | moduleScope = details->parent(); | |||
2849 | } | |||
2850 | module = moduleScope->symbol(); | |||
2851 | } | |||
2852 | } | |||
2853 | if (module) { | |||
2854 | std::pair<SourceName, const Symbol *> key{symbol.name(), module}; | |||
2855 | auto iter{moduleProcs_.find(key)}; | |||
2856 | if (iter == moduleProcs_.end()) { | |||
2857 | moduleProcs_.emplace(std::move(key), symbol); | |||
2858 | } else if ( | |||
2859 | auto *msg{messages_.Say(symbol.name(), | |||
2860 | "Module procedure '%s' in module '%s' has multiple definitions"_err_en_US, | |||
2861 | symbol.name(), module->name())}) { | |||
2862 | msg->Attach(iter->second->name(), "Previous definition of '%s'"_en_US, | |||
2863 | symbol.name()); | |||
2864 | } | |||
2865 | } | |||
2866 | } | |||
2867 | } | |||
2868 | ||||
2869 | void SubprogramMatchHelper::Check( | |||
2870 | const Symbol &symbol1, const Symbol &symbol2) { | |||
2871 | const auto details1{symbol1.get<SubprogramDetails>()}; | |||
2872 | const auto details2{symbol2.get<SubprogramDetails>()}; | |||
2873 | if (details1.isFunction() != details2.isFunction()) { | |||
2874 | Say(symbol1, symbol2, | |||
2875 | details1.isFunction() | |||
2876 | ? "Module function '%s' was declared as a subroutine in the" | |||
2877 | " corresponding interface body"_err_en_US | |||
2878 | : "Module subroutine '%s' was declared as a function in the" | |||
2879 | " corresponding interface body"_err_en_US); | |||
2880 | return; | |||
2881 | } | |||
2882 | const auto &args1{details1.dummyArgs()}; | |||
2883 | const auto &args2{details2.dummyArgs()}; | |||
2884 | int nargs1{static_cast<int>(args1.size())}; | |||
2885 | int nargs2{static_cast<int>(args2.size())}; | |||
2886 | if (nargs1 != nargs2) { | |||
2887 | Say(symbol1, symbol2, | |||
2888 | "Module subprogram '%s' has %d args but the corresponding interface" | |||
2889 | " body has %d"_err_en_US, | |||
2890 | nargs1, nargs2); | |||
2891 | return; | |||
2892 | } | |||
2893 | bool nonRecursive1{symbol1.attrs().test(Attr::NON_RECURSIVE)}; | |||
2894 | if (nonRecursive1 != symbol2.attrs().test(Attr::NON_RECURSIVE)) { // C1551 | |||
2895 | Say(symbol1, symbol2, | |||
2896 | nonRecursive1 | |||
2897 | ? "Module subprogram '%s' has NON_RECURSIVE prefix but" | |||
2898 | " the corresponding interface body does not"_err_en_US | |||
2899 | : "Module subprogram '%s' does not have NON_RECURSIVE prefix but " | |||
2900 | "the corresponding interface body does"_err_en_US); | |||
2901 | } | |||
2902 | const std::string *bindName1{details1.bindName()}; | |||
2903 | const std::string *bindName2{details2.bindName()}; | |||
2904 | if (!bindName1 && !bindName2) { | |||
2905 | // OK - neither has a binding label | |||
2906 | } else if (!bindName1) { | |||
2907 | Say(symbol1, symbol2, | |||
2908 | "Module subprogram '%s' does not have a binding label but the" | |||
2909 | " corresponding interface body does"_err_en_US); | |||
2910 | } else if (!bindName2) { | |||
2911 | Say(symbol1, symbol2, | |||
2912 | "Module subprogram '%s' has a binding label but the" | |||
2913 | " corresponding interface body does not"_err_en_US); | |||
2914 | } else if (*bindName1 != *bindName2) { | |||
2915 | Say(symbol1, symbol2, | |||
2916 | "Module subprogram '%s' has binding label '%s' but the corresponding" | |||
2917 | " interface body has '%s'"_err_en_US, | |||
2918 | *details1.bindName(), *details2.bindName()); | |||
2919 | } | |||
2920 | const Procedure *proc1{checkHelper.Characterize(symbol1)}; | |||
2921 | const Procedure *proc2{checkHelper.Characterize(symbol2)}; | |||
2922 | if (!proc1 || !proc2) { | |||
2923 | return; | |||
2924 | } | |||
2925 | if (proc1->attrs.test(Procedure::Attr::Pure) != | |||
2926 | proc2->attrs.test(Procedure::Attr::Pure)) { | |||
2927 | Say(symbol1, symbol2, | |||
2928 | "Module subprogram '%s' and its corresponding interface body are not both PURE"_err_en_US); | |||
2929 | } | |||
2930 | if (proc1->attrs.test(Procedure::Attr::Elemental) != | |||
2931 | proc2->attrs.test(Procedure::Attr::Elemental)) { | |||
2932 | Say(symbol1, symbol2, | |||
2933 | "Module subprogram '%s' and its corresponding interface body are not both ELEMENTAL"_err_en_US); | |||
2934 | } | |||
2935 | if (proc1->attrs.test(Procedure::Attr::BindC) != | |||
2936 | proc2->attrs.test(Procedure::Attr::BindC)) { | |||
2937 | Say(symbol1, symbol2, | |||
2938 | "Module subprogram '%s' and its corresponding interface body are not both BIND(C)"_err_en_US); | |||
2939 | } | |||
2940 | if (proc1->functionResult && proc2->functionResult && | |||
2941 | *proc1->functionResult != *proc2->functionResult) { | |||
2942 | Say(symbol1, symbol2, | |||
2943 | "Return type of function '%s' does not match return type of" | |||
2944 | " the corresponding interface body"_err_en_US); | |||
2945 | } | |||
2946 | for (int i{0}; i < nargs1; ++i) { | |||
2947 | const Symbol *arg1{args1[i]}; | |||
2948 | const Symbol *arg2{args2[i]}; | |||
2949 | if (arg1 && !arg2) { | |||
2950 | Say(symbol1, symbol2, | |||
2951 | "Dummy argument %2$d of '%1$s' is not an alternate return indicator" | |||
2952 | " but the corresponding argument in the interface body is"_err_en_US, | |||
2953 | i + 1); | |||
2954 | } else if (!arg1 && arg2) { | |||
2955 | Say(symbol1, symbol2, | |||
2956 | "Dummy argument %2$d of '%1$s' is an alternate return indicator but" | |||
2957 | " the corresponding argument in the interface body is not"_err_en_US, | |||
2958 | i + 1); | |||
2959 | } else if (arg1 && arg2) { | |||
2960 | SourceName name1{arg1->name()}; | |||
2961 | SourceName name2{arg2->name()}; | |||
2962 | if (name1 != name2) { | |||
2963 | Say(*arg1, *arg2, | |||
2964 | "Dummy argument name '%s' does not match corresponding name '%s'" | |||
2965 | " in interface body"_err_en_US, | |||
2966 | name2); | |||
2967 | } else { | |||
2968 | CheckDummyArg( | |||
2969 | *arg1, *arg2, proc1->dummyArguments[i], proc2->dummyArguments[i]); | |||
2970 | } | |||
2971 | } | |||
2972 | } | |||
2973 | } | |||
2974 | ||||
2975 | void SubprogramMatchHelper::CheckDummyArg(const Symbol &symbol1, | |||
2976 | const Symbol &symbol2, const DummyArgument &arg1, | |||
2977 | const DummyArgument &arg2) { | |||
2978 | common::visit( | |||
2979 | common::visitors{ | |||
2980 | [&](const DummyDataObject &obj1, const DummyDataObject &obj2) { | |||
2981 | CheckDummyDataObject(symbol1, symbol2, obj1, obj2); | |||
2982 | }, | |||
2983 | [&](const DummyProcedure &proc1, const DummyProcedure &proc2) { | |||
2984 | CheckDummyProcedure(symbol1, symbol2, proc1, proc2); | |||
2985 | }, | |||
2986 | [&](const DummyDataObject &, const auto &) { | |||
2987 | Say(symbol1, symbol2, | |||
2988 | "Dummy argument '%s' is a data object; the corresponding" | |||
2989 | " argument in the interface body is not"_err_en_US); | |||
2990 | }, | |||
2991 | [&](const DummyProcedure &, const auto &) { | |||
2992 | Say(symbol1, symbol2, | |||
2993 | "Dummy argument '%s' is a procedure; the corresponding" | |||
2994 | " argument in the interface body is not"_err_en_US); | |||
2995 | }, | |||
2996 | [&](const auto &, const auto &) { | |||
2997 | llvm_unreachable("Dummy arguments are not data objects or"::llvm::llvm_unreachable_internal("Dummy arguments are not data objects or" "procedures", "flang/lib/Semantics/check-declarations.cpp", 2998 ) | |||
2998 | "procedures")::llvm::llvm_unreachable_internal("Dummy arguments are not data objects or" "procedures", "flang/lib/Semantics/check-declarations.cpp", 2998 ); | |||
2999 | }, | |||
3000 | }, | |||
3001 | arg1.u, arg2.u); | |||
3002 | } | |||
3003 | ||||
3004 | void SubprogramMatchHelper::CheckDummyDataObject(const Symbol &symbol1, | |||
3005 | const Symbol &symbol2, const DummyDataObject &obj1, | |||
3006 | const DummyDataObject &obj2) { | |||
3007 | if (!CheckSameIntent(symbol1, symbol2, obj1.intent, obj2.intent)) { | |||
3008 | } else if (!CheckSameAttrs(symbol1, symbol2, obj1.attrs, obj2.attrs)) { | |||
3009 | } else if (obj1.type.type() != obj2.type.type()) { | |||
3010 | Say(symbol1, symbol2, | |||
3011 | "Dummy argument '%s' has type %s; the corresponding argument in the" | |||
3012 | " interface body has type %s"_err_en_US, | |||
3013 | obj1.type.type().AsFortran(), obj2.type.type().AsFortran()); | |||
3014 | } else if (!ShapesAreCompatible(obj1, obj2)) { | |||
3015 | Say(symbol1, symbol2, | |||
3016 | "The shape of dummy argument '%s' does not match the shape of the" | |||
3017 | " corresponding argument in the interface body"_err_en_US); | |||
3018 | } | |||
3019 | // TODO: coshape | |||
3020 | } | |||
3021 | ||||
3022 | void SubprogramMatchHelper::CheckDummyProcedure(const Symbol &symbol1, | |||
3023 | const Symbol &symbol2, const DummyProcedure &proc1, | |||
3024 | const DummyProcedure &proc2) { | |||
3025 | if (!CheckSameIntent(symbol1, symbol2, proc1.intent, proc2.intent)) { | |||
3026 | } else if (!CheckSameAttrs(symbol1, symbol2, proc1.attrs, proc2.attrs)) { | |||
3027 | } else if (proc1 != proc2) { | |||
3028 | Say(symbol1, symbol2, | |||
3029 | "Dummy procedure '%s' does not match the corresponding argument in" | |||
3030 | " the interface body"_err_en_US); | |||
3031 | } | |||
3032 | } | |||
3033 | ||||
3034 | bool SubprogramMatchHelper::CheckSameIntent(const Symbol &symbol1, | |||
3035 | const Symbol &symbol2, common::Intent intent1, common::Intent intent2) { | |||
3036 | if (intent1 == intent2) { | |||
3037 | return true; | |||
3038 | } else { | |||
3039 | Say(symbol1, symbol2, | |||
3040 | "The intent of dummy argument '%s' does not match the intent" | |||
3041 | " of the corresponding argument in the interface body"_err_en_US); | |||
3042 | return false; | |||
3043 | } | |||
3044 | } | |||
3045 | ||||
3046 | // Report an error referring to first symbol with declaration of second symbol | |||
3047 | template <typename... A> | |||
3048 | void SubprogramMatchHelper::Say(const Symbol &symbol1, const Symbol &symbol2, | |||
3049 | parser::MessageFixedText &&text, A &&...args) { | |||
3050 | auto &message{context().Say(symbol1.name(), std::move(text), symbol1.name(), | |||
3051 | std::forward<A>(args)...)}; | |||
3052 | evaluate::AttachDeclaration(message, symbol2); | |||
3053 | } | |||
3054 | ||||
3055 | template <typename ATTRS> | |||
3056 | bool SubprogramMatchHelper::CheckSameAttrs( | |||
3057 | const Symbol &symbol1, const Symbol &symbol2, ATTRS attrs1, ATTRS attrs2) { | |||
3058 | if (attrs1 == attrs2) { | |||
3059 | return true; | |||
3060 | } | |||
3061 | attrs1.IterateOverMembers([&](auto attr) { | |||
3062 | if (!attrs2.test(attr)) { | |||
3063 | Say(symbol1, symbol2, | |||
3064 | "Dummy argument '%s' has the %s attribute; the corresponding" | |||
3065 | " argument in the interface body does not"_err_en_US, | |||
3066 | AsFortran(attr)); | |||
3067 | } | |||
3068 | }); | |||
3069 | attrs2.IterateOverMembers([&](auto attr) { | |||
3070 | if (!attrs1.test(attr)) { | |||
3071 | Say(symbol1, symbol2, | |||
3072 | "Dummy argument '%s' does not have the %s attribute; the" | |||
3073 | " corresponding argument in the interface body does"_err_en_US, | |||
3074 | AsFortran(attr)); | |||
3075 | } | |||
3076 | }); | |||
3077 | return false; | |||
3078 | } | |||
3079 | ||||
3080 | bool SubprogramMatchHelper::ShapesAreCompatible( | |||
3081 | const DummyDataObject &obj1, const DummyDataObject &obj2) { | |||
3082 | return characteristics::ShapesAreCompatible( | |||
3083 | FoldShape(obj1.type.shape()), FoldShape(obj2.type.shape())); | |||
3084 | } | |||
3085 | ||||
3086 | evaluate::Shape SubprogramMatchHelper::FoldShape(const evaluate::Shape &shape) { | |||
3087 | evaluate::Shape result; | |||
3088 | for (const auto &extent : shape) { | |||
3089 | result.emplace_back( | |||
3090 | evaluate::Fold(context().foldingContext(), common::Clone(extent))); | |||
3091 | } | |||
3092 | return result; | |||
3093 | } | |||
3094 | ||||
3095 | void DistinguishabilityHelper::Add(const Symbol &generic, GenericKind kind, | |||
3096 | const Symbol &specific, const Procedure &procedure) { | |||
3097 | if (!context_.HasError(specific)) { | |||
3098 | nameToInfo_[generic.name()].emplace_back( | |||
3099 | ProcedureInfo{kind, specific, procedure}); | |||
3100 | } | |||
3101 | } | |||
3102 | ||||
3103 | void DistinguishabilityHelper::Check(const Scope &scope) { | |||
3104 | for (const auto &[name, info] : nameToInfo_) { | |||
3105 | auto count{info.size()}; | |||
3106 | for (std::size_t i1{0}; i1 < count - 1; ++i1) { | |||
3107 | const auto &[kind, symbol, proc]{info[i1]}; | |||
3108 | for (std::size_t i2{i1 + 1}; i2 < count; ++i2) { | |||
3109 | auto distinguishable{kind.IsName() | |||
3110 | ? evaluate::characteristics::Distinguishable | |||
3111 | : evaluate::characteristics::DistinguishableOpOrAssign}; | |||
3112 | if (!distinguishable( | |||
3113 | context_.languageFeatures(), proc, info[i2].procedure)) { | |||
3114 | SayNotDistinguishable(GetTopLevelUnitContaining(scope), name, kind, | |||
3115 | symbol, info[i2].symbol); | |||
3116 | } | |||
3117 | } | |||
3118 | } | |||
3119 | } | |||
3120 | } | |||
3121 | ||||
3122 | void DistinguishabilityHelper::SayNotDistinguishable(const Scope &scope, | |||
3123 | const SourceName &name, GenericKind kind, const Symbol &proc1, | |||
3124 | const Symbol &proc2) { | |||
3125 | std::string name1{proc1.name().ToString()}; | |||
3126 | std::string name2{proc2.name().ToString()}; | |||
3127 | if (kind.IsOperator() || kind.IsAssignment()) { | |||
3128 | // proc1 and proc2 may come from different scopes so qualify their names | |||
3129 | if (proc1.owner().IsDerivedType()) { | |||
3130 | name1 = proc1.owner().GetName()->ToString() + '%' + name1; | |||
3131 | } | |||
3132 | if (proc2.owner().IsDerivedType()) { | |||
3133 | name2 = proc2.owner().GetName()->ToString() + '%' + name2; | |||
3134 | } | |||
3135 | } | |||
3136 | parser::Message *msg; | |||
3137 | if (scope.sourceRange().Contains(name)) { | |||
3138 | msg = &context_.Say(name, | |||
3139 | "Generic '%s' may not have specific procedures '%s' and '%s' as their interfaces are not distinguishable"_err_en_US, | |||
3140 | MakeOpName(name), name1, name2); | |||
3141 | } else { | |||
3142 | msg = &context_.Say(*GetTopLevelUnitContaining(proc1).GetName(), | |||
3143 | "USE-associated generic '%s' may not have specific procedures '%s' and '%s' as their interfaces are not distinguishable"_err_en_US, | |||
3144 | MakeOpName(name), name1, name2); | |||
3145 | } | |||
3146 | AttachDeclaration(*msg, scope, proc1); | |||
3147 | AttachDeclaration(*msg, scope, proc2); | |||
3148 | } | |||
3149 | ||||
3150 | // `evaluate::AttachDeclaration` doesn't handle the generic case where `proc` | |||
3151 | // comes from a different module but is not necessarily use-associated. | |||
3152 | void DistinguishabilityHelper::AttachDeclaration( | |||
3153 | parser::Message &msg, const Scope &scope, const Symbol &proc) { | |||
3154 | const Scope &unit{GetTopLevelUnitContaining(proc)}; | |||
3155 | if (unit == scope) { | |||
3156 | evaluate::AttachDeclaration(msg, proc); | |||
3157 | } else { | |||
3158 | msg.Attach(unit.GetName().value(), | |||
3159 | "'%s' is USE-associated from module '%s'"_en_US, proc.name(), | |||
3160 | unit.GetName().value()); | |||
3161 | } | |||
3162 | } | |||
3163 | ||||
3164 | void CheckDeclarations(SemanticsContext &context) { | |||
3165 | CheckHelper{context}.Check(); | |||
3166 | } | |||
3167 | } // namespace Fortran::semantics |