1 //===-- lib/Semantics/pointer-assignment.cpp ------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "pointer-assignment.h"
10 #include "definable.h"
11 #include "flang/Common/idioms.h"
12 #include "flang/Common/restorer.h"
13 #include "flang/Common/template.h"
14 #include "flang/Evaluate/characteristics.h"
15 #include "flang/Evaluate/expression.h"
16 #include "flang/Evaluate/fold.h"
17 #include "flang/Evaluate/tools.h"
18 #include "flang/Parser/message.h"
19 #include "flang/Parser/parse-tree-visitor.h"
20 #include "flang/Parser/parse-tree.h"
21 #include "flang/Semantics/expression.h"
22 #include "flang/Semantics/symbol.h"
23 #include "flang/Semantics/tools.h"
24 #include "llvm/Support/raw_ostream.h"
28 #include <type_traits>
30 // Semantic checks for pointer assignment.
32 namespace Fortran::semantics
{
34 using namespace parser::literals
;
35 using evaluate::characteristics::DummyDataObject
;
36 using evaluate::characteristics::FunctionResult
;
37 using evaluate::characteristics::Procedure
;
38 using evaluate::characteristics::TypeAndShape
;
39 using parser::MessageFixedText
;
40 using parser::MessageFormattedText
;
42 class PointerAssignmentChecker
{
44 PointerAssignmentChecker(SemanticsContext
&context
, const Scope
&scope
,
45 parser::CharBlock source
, const std::string
&description
)
46 : context_
{context
}, scope_
{scope
}, source_
{source
}, description_
{
48 PointerAssignmentChecker(
49 SemanticsContext
&context
, const Scope
&scope
, const Symbol
&lhs
)
50 : context_
{context
}, scope_
{scope
}, source_
{lhs
.name()},
51 description_
{"pointer '"s
+ lhs
.name().ToString() + '\''}, lhs_
{&lhs
} {
52 set_lhsType(TypeAndShape::Characterize(lhs
, foldingContext_
));
53 set_isContiguous(lhs
.attrs().test(Attr::CONTIGUOUS
));
54 set_isVolatile(lhs
.attrs().test(Attr::VOLATILE
));
56 PointerAssignmentChecker
&set_lhsType(std::optional
<TypeAndShape
> &&);
57 PointerAssignmentChecker
&set_isContiguous(bool);
58 PointerAssignmentChecker
&set_isVolatile(bool);
59 PointerAssignmentChecker
&set_isBoundsRemapping(bool);
60 PointerAssignmentChecker
&set_isAssumedRank(bool);
61 PointerAssignmentChecker
&set_pointerComponentLHS(const Symbol
*);
62 bool CheckLeftHandSide(const SomeExpr
&);
63 bool Check(const SomeExpr
&);
66 bool CharacterizeProcedure();
67 template <typename T
> bool Check(const T
&);
68 template <typename T
> bool Check(const evaluate::Expr
<T
> &);
69 template <typename T
> bool Check(const evaluate::FunctionRef
<T
> &);
70 template <typename T
> bool Check(const evaluate::Designator
<T
> &);
71 bool Check(const evaluate::NullPointer
&);
72 bool Check(const evaluate::ProcedureDesignator
&);
73 bool Check(const evaluate::ProcedureRef
&);
74 // Target is a procedure
75 bool Check(parser::CharBlock rhsName
, bool isCall
,
76 const Procedure
* = nullptr,
77 const evaluate::SpecificIntrinsic
*specific
= nullptr);
78 bool LhsOkForUnlimitedPoly() const;
79 template <typename
... A
> parser::Message
*Say(A
&&...);
80 template <typename FeatureOrUsageWarning
, typename
... A
>
81 parser::Message
*Warn(FeatureOrUsageWarning
, A
&&...);
83 SemanticsContext
&context_
;
84 evaluate::FoldingContext
&foldingContext_
{context_
.foldingContext()};
86 const parser::CharBlock source_
;
87 const std::string description_
;
88 const Symbol
*lhs_
{nullptr};
89 std::optional
<TypeAndShape
> lhsType_
;
90 std::optional
<Procedure
> procedure_
;
91 bool characterizedProcedure_
{false};
92 bool isContiguous_
{false};
93 bool isVolatile_
{false};
94 bool isBoundsRemapping_
{false};
95 bool isAssumedRank_
{false};
96 const Symbol
*pointerComponentLHS_
{nullptr};
99 PointerAssignmentChecker
&PointerAssignmentChecker::set_lhsType(
100 std::optional
<TypeAndShape
> &&lhsType
) {
101 lhsType_
= std::move(lhsType
);
105 PointerAssignmentChecker
&PointerAssignmentChecker::set_isContiguous(
107 isContiguous_
= isContiguous
;
111 PointerAssignmentChecker
&PointerAssignmentChecker::set_isVolatile(
113 isVolatile_
= isVolatile
;
117 PointerAssignmentChecker
&PointerAssignmentChecker::set_isBoundsRemapping(
118 bool isBoundsRemapping
) {
119 isBoundsRemapping_
= isBoundsRemapping
;
123 PointerAssignmentChecker
&PointerAssignmentChecker::set_isAssumedRank(
124 bool isAssumedRank
) {
125 isAssumedRank_
= isAssumedRank
;
129 PointerAssignmentChecker
&PointerAssignmentChecker::set_pointerComponentLHS(
130 const Symbol
*symbol
) {
131 pointerComponentLHS_
= symbol
;
135 bool PointerAssignmentChecker::CharacterizeProcedure() {
136 if (!characterizedProcedure_
) {
137 characterizedProcedure_
= true;
138 if (lhs_
&& IsProcedure(*lhs_
)) {
139 procedure_
= Procedure::Characterize(*lhs_
, foldingContext_
);
142 return procedure_
.has_value();
145 bool PointerAssignmentChecker::CheckLeftHandSide(const SomeExpr
&lhs
) {
146 if (auto whyNot
{WhyNotDefinable(foldingContext_
.messages().at(), scope_
,
147 DefinabilityFlags
{DefinabilityFlag::PointerDefinition
}, lhs
)}) {
149 "The left-hand side of a pointer assignment is not definable"_err_en_US
)}) {
150 msg
->Attach(std::move(whyNot
->set_severity(parser::Severity::Because
)));
153 } else if (evaluate::IsAssumedRank(lhs
)) {
154 Say("The left-hand side of a pointer assignment must not be an assumed-rank dummy argument"_err_en_US
);
161 template <typename T
> bool PointerAssignmentChecker::Check(const T
&) {
162 // Catch-all case for really bad target expression
163 Say("Target associated with %s must be a designator or a call to a"
164 " pointer-valued function"_err_en_US
,
169 template <typename T
>
170 bool PointerAssignmentChecker::Check(const evaluate::Expr
<T
> &x
) {
171 return common::visit([&](const auto &x
) { return Check(x
); }, x
.u
);
174 bool PointerAssignmentChecker::Check(const SomeExpr
&rhs
) {
175 if (HasVectorSubscript(rhs
)) { // C1025
176 Say("An array section with a vector subscript may not be a pointer target"_err_en_US
);
179 if (ExtractCoarrayRef(rhs
)) { // C1026
180 Say("A coindexed object may not be a pointer target"_err_en_US
);
183 if (!common::visit([&](const auto &x
) { return Check(x
); }, rhs
.u
)) {
186 if (IsNullPointer(rhs
)) {
189 if (lhs_
&& IsProcedure(*lhs_
)) {
192 if (const auto *pureProc
{FindPureProcedureContaining(scope_
)}) {
193 if (pointerComponentLHS_
) { // C1594(4) is a hard error
194 if (const Symbol
* object
{FindExternallyVisibleObject(rhs
, *pureProc
)}) {
196 "Externally visible object '%s' may not be associated with pointer component '%s' in a pure procedure"_err_en_US
,
197 object
->name(), pointerComponentLHS_
->name())}) {
198 msg
->Attach(object
->name(), "Object declaration"_en_US
)
200 pointerComponentLHS_
->name(), "Pointer declaration"_en_US
);
204 } else if (const Symbol
* base
{GetFirstSymbol(rhs
)}) {
205 if (const char *why
{WhyBaseObjectIsSuspicious(
206 base
->GetUltimate(), scope_
)}) { // C1594(3)
207 evaluate::SayWithDeclaration(foldingContext_
.messages(), *base
,
208 "A pure subprogram may not use '%s' as the target of pointer assignment because it is %s"_err_en_US
,
215 if (auto contiguous
{evaluate::IsContiguous(rhs
, foldingContext_
)}) {
217 Say("CONTIGUOUS pointer may not be associated with a discontiguous target"_err_en_US
);
221 Warn(common::UsageWarning::PointerToPossibleNoncontiguous
,
222 "Target of CONTIGUOUS pointer association is not known to be contiguous"_warn_en_US
);
225 // Warn about undefinable data targets
227 WhyNotDefinable(foldingContext_
.messages().at(), scope_
, {}, rhs
)}) {
228 if (auto *msg
{Warn(common::UsageWarning::PointerToUndefinable
,
229 "Pointer target is not a definable variable"_warn_en_US
)}) {
230 msg
->Attach(std::move(because
->set_severity(parser::Severity::Because
)));
237 bool PointerAssignmentChecker::Check(const evaluate::NullPointer
&) {
238 return true; // P => NULL() without MOLD=; always OK
241 template <typename T
>
242 bool PointerAssignmentChecker::Check(const evaluate::FunctionRef
<T
> &f
) {
243 std::string funcName
;
244 const auto *symbol
{f
.proc().GetSymbol()};
246 funcName
= symbol
->name().ToString();
247 } else if (const auto *intrinsic
{f
.proc().GetSpecificIntrinsic()}) {
248 funcName
= intrinsic
->name
;
251 Procedure::Characterize(f
.proc(), foldingContext_
, /*emitError=*/true)};
255 std::optional
<MessageFixedText
> msg
;
256 const auto &funcResult
{proc
->functionResult
}; // C1025
258 msg
= "%s is associated with the non-existent result of reference to"
259 " procedure"_err_en_US
;
260 } else if (CharacterizeProcedure()) {
261 // Shouldn't be here in this function unless lhs is an object pointer.
262 msg
= "Procedure %s is associated with the result of a reference to"
263 " function '%s' that does not return a procedure pointer"_err_en_US
;
264 } else if (funcResult
->IsProcedurePointer()) {
265 msg
= "Object %s is associated with the result of a reference to"
266 " function '%s' that is a procedure pointer"_err_en_US
;
267 } else if (!funcResult
->attrs
.test(FunctionResult::Attr::Pointer
)) {
268 msg
= "%s is associated with the result of a reference to function '%s'"
269 " that is a not a pointer"_err_en_US
;
270 } else if (isContiguous_
&&
271 !funcResult
->attrs
.test(FunctionResult::Attr::Contiguous
)) {
272 auto restorer
{common::ScopedSet(lhs_
, symbol
)};
273 if (Warn(common::UsageWarning::PointerToPossibleNoncontiguous
,
274 "CONTIGUOUS %s is associated with the result of reference to function '%s' that is not known to be contiguous"_warn_en_US
,
275 description_
, funcName
)) {
278 } else if (lhsType_
) {
279 const auto *frTypeAndShape
{funcResult
->GetTypeAndShape()};
280 CHECK(frTypeAndShape
);
281 if (!lhsType_
->IsCompatibleWith(foldingContext_
.messages(), *frTypeAndShape
,
282 "pointer", "function result",
283 /*omitShapeConformanceCheck=*/isBoundsRemapping_
|| isAssumedRank_
,
284 evaluate::CheckConformanceFlags::BothDeferredShape
)) {
285 return false; // IsCompatibleWith() emitted message
289 auto restorer
{common::ScopedSet(lhs_
, symbol
)};
290 Say(*msg
, description_
, funcName
);
296 template <typename T
>
297 bool PointerAssignmentChecker::Check(const evaluate::Designator
<T
> &d
) {
298 const Symbol
*last
{d
.GetLastSymbol()};
299 const Symbol
*base
{d
.GetBaseObject().symbol()};
300 if (!last
|| !base
) {
301 // P => "character literal"(1:3)
302 Say("Pointer target is not a named entity"_err_en_US
);
305 std::optional
<std::variant
<MessageFixedText
, MessageFormattedText
>> msg
;
306 if (CharacterizeProcedure()) {
307 // Shouldn't be here in this function unless lhs is an object pointer.
308 msg
= "In assignment to procedure %s, the target is not a procedure or"
309 " procedure pointer"_err_en_US
;
310 } else if (!evaluate::GetLastTarget(GetSymbolVector(d
))) { // C1025
311 msg
= "In assignment to object %s, the target '%s' is not an object with"
312 " POINTER or TARGET attributes"_err_en_US
;
313 } else if (auto rhsType
{TypeAndShape::Characterize(d
, foldingContext_
)}) {
315 msg
= "%s associated with object '%s' with incompatible type or"
317 } else if (rhsType
->corank() > 0 &&
318 (isVolatile_
!= last
->attrs().test(Attr::VOLATILE
))) { // C1020
319 // TODO: what if A is VOLATILE in A%B%C? need a better test here
321 msg
= "Pointer may not be VOLATILE when target is a"
322 " non-VOLATILE coarray"_err_en_US
;
324 msg
= "Pointer must be VOLATILE when target is a"
325 " VOLATILE coarray"_err_en_US
;
327 } else if (rhsType
->type().IsUnlimitedPolymorphic()) {
328 if (!LhsOkForUnlimitedPoly()) {
329 msg
= "Pointer type must be unlimited polymorphic or non-extensible"
330 " derived type when target is unlimited polymorphic"_err_en_US
;
333 if (!lhsType_
->type().IsTkLenCompatibleWith(rhsType
->type())) {
334 msg
= MessageFormattedText
{
335 "Target type %s is not compatible with pointer type %s"_err_en_US
,
336 rhsType
->type().AsFortran(), lhsType_
->type().AsFortran()};
338 } else if (!isBoundsRemapping_
&&
339 !lhsType_
->attrs().test(TypeAndShape::Attr::AssumedRank
)) {
340 int lhsRank
{lhsType_
->Rank()};
341 int rhsRank
{rhsType
->Rank()};
342 if (lhsRank
!= rhsRank
) {
343 msg
= MessageFormattedText
{
344 "Pointer has rank %d but target has rank %d"_err_en_US
, lhsRank
,
351 auto restorer
{common::ScopedSet(lhs_
, last
)};
352 if (auto *m
{std::get_if
<MessageFixedText
>(&*msg
)}) {
354 llvm::raw_string_ostream ss
{buf
};
356 Say(*m
, description_
, buf
);
358 Say(std::get
<MessageFormattedText
>(*msg
));
362 context_
.NoteDefinedSymbol(*base
);
367 // Common handling for procedure pointer right-hand sides
368 bool PointerAssignmentChecker::Check(parser::CharBlock rhsName
, bool isCall
,
369 const Procedure
*rhsProcedure
,
370 const evaluate::SpecificIntrinsic
*specific
) {
372 std::optional
<std::string
> warning
;
373 CharacterizeProcedure();
374 if (std::optional
<MessageFixedText
> msg
{evaluate::CheckProcCompatibility(
375 isCall
, procedure_
, rhsProcedure
, specific
, whyNot
, warning
,
376 /*ignoreImplicitVsExplicit=*/isCall
)}) {
377 Say(std::move(*msg
), description_
, rhsName
, whyNot
);
381 Warn(common::UsageWarning::ProcDummyArgShapes
,
382 "%s and %s may not be completely compatible procedures: %s"_warn_en_US
,
383 description_
, rhsName
, std::move(*warning
));
388 bool PointerAssignmentChecker::Check(const evaluate::ProcedureDesignator
&d
) {
389 const Symbol
*symbol
{d
.GetSymbol()};
391 if (const auto *subp
{
392 symbol
->GetUltimate().detailsIf
<SubprogramDetails
>()}) {
393 if (subp
->stmtFunction()) {
394 evaluate::SayWithDeclaration(foldingContext_
.messages(), *symbol
,
395 "Statement function '%s' may not be the target of a pointer assignment"_err_en_US
,
399 } else if (symbol
->has
<ProcBindingDetails
>()) {
400 evaluate::AttachDeclaration(
401 Warn(common::LanguageFeature::BindingAsProcedure
,
402 "Procedure binding '%s' used as target of a pointer assignment"_port_en_US
,
408 Procedure::Characterize(d
, foldingContext_
, /*emitError=*/true)}) {
409 // Disregard the elemental attribute of RHS intrinsics.
410 if (symbol
&& symbol
->GetUltimate().attrs().test(Attr::INTRINSIC
)) {
411 chars
->attrs
.reset(Procedure::Attr::Elemental
);
413 return Check(d
.GetName(), false, &*chars
, d
.GetSpecificIntrinsic());
415 return Check(d
.GetName(), false);
419 bool PointerAssignmentChecker::Check(const evaluate::ProcedureRef
&ref
) {
420 auto chars
{Procedure::Characterize(ref
, foldingContext_
)};
421 return Check(ref
.proc().GetName(), true, common::GetPtrFromOptional(chars
));
424 // The target can be unlimited polymorphic if the pointer is, or if it is
425 // a non-extensible derived type.
426 bool PointerAssignmentChecker::LhsOkForUnlimitedPoly() const {
427 const auto &type
{lhsType_
->type()};
428 if (type
.category() != TypeCategory::Derived
|| type
.IsAssumedType()) {
430 } else if (type
.IsUnlimitedPolymorphic()) {
433 return !IsExtensibleType(&type
.GetDerivedTypeSpec());
437 template <typename
... A
>
438 parser::Message
*PointerAssignmentChecker::Say(A
&&...x
) {
439 auto *msg
{foldingContext_
.messages().Say(std::forward
<A
>(x
)...)};
442 return evaluate::AttachDeclaration(msg
, *lhs_
);
444 if (!source_
.empty()) {
445 msg
->Attach(source_
, "Declaration of %s"_en_US
, description_
);
451 template <typename FeatureOrUsageWarning
, typename
... A
>
452 parser::Message
*PointerAssignmentChecker::Warn(
453 FeatureOrUsageWarning warning
, A
&&...x
) {
454 auto *msg
{context_
.Warn(
455 warning
, foldingContext_
.messages().at(), std::forward
<A
>(x
)...)};
458 return evaluate::AttachDeclaration(msg
, *lhs_
);
460 if (!source_
.empty()) {
461 msg
->Attach(source_
, "Declaration of %s"_en_US
, description_
);
467 // Verify that any bounds on the LHS of a pointer assignment are valid.
468 // Return true if it is a bound-remapping so we can perform further checks.
469 static bool CheckPointerBounds(
470 evaluate::FoldingContext
&context
, const evaluate::Assignment
&assignment
) {
471 auto &messages
{context
.messages()};
472 const SomeExpr
&lhs
{assignment
.lhs
};
473 const SomeExpr
&rhs
{assignment
.rhs
};
474 bool isBoundsRemapping
{false};
475 std::size_t numBounds
{common::visit(
477 [&](const evaluate::Assignment::BoundsSpec
&bounds
) {
478 return bounds
.size();
480 [&](const evaluate::Assignment::BoundsRemapping
&bounds
) {
481 isBoundsRemapping
= true;
482 evaluate::ExtentExpr lhsSizeExpr
{1};
483 for (const auto &bound
: bounds
) {
484 lhsSizeExpr
= std::move(lhsSizeExpr
) *
485 (common::Clone(bound
.second
) - common::Clone(bound
.first
) +
486 evaluate::ExtentExpr
{1});
488 if (std::optional
<std::int64_t> lhsSize
{evaluate::ToInt64(
489 evaluate::Fold(context
, std::move(lhsSizeExpr
)))}) {
490 if (auto shape
{evaluate::GetShape(context
, rhs
)}) {
491 if (std::optional
<std::int64_t> rhsSize
{
492 evaluate::ToInt64(evaluate::Fold(
493 context
, evaluate::GetSize(std::move(*shape
))))}) {
494 if (*lhsSize
> *rhsSize
) {
496 "Pointer bounds require %d elements but target has"
497 " only %d"_err_en_US
,
498 *lhsSize
, *rhsSize
); // 10.2.2.3(9)
503 return bounds
.size();
505 [](const auto &) -> std::size_t {
506 DIE("not valid for pointer assignment");
511 if (lhs
.Rank() != static_cast<int>(numBounds
)) {
512 messages
.Say("Pointer '%s' has rank %d but the number of bounds specified"
514 lhs
.AsFortran(), lhs
.Rank(), numBounds
); // C1018
517 if (isBoundsRemapping
&& rhs
.Rank() != 1 &&
518 !evaluate::IsSimplyContiguous(rhs
, context
)) {
519 messages
.Say("Pointer bounds remapping target must have rank 1 or be"
520 " simply contiguous"_err_en_US
); // 10.2.2.3(9)
522 return isBoundsRemapping
;
525 bool CheckPointerAssignment(SemanticsContext
&context
,
526 const evaluate::Assignment
&assignment
, const Scope
&scope
) {
527 return CheckPointerAssignment(context
, assignment
.lhs
, assignment
.rhs
, scope
,
528 CheckPointerBounds(context
.foldingContext(), assignment
),
529 /*isAssumedRank=*/false);
532 bool CheckPointerAssignment(SemanticsContext
&context
, const SomeExpr
&lhs
,
533 const SomeExpr
&rhs
, const Scope
&scope
, bool isBoundsRemapping
,
534 bool isAssumedRank
) {
535 const Symbol
*pointer
{GetLastSymbol(lhs
)};
537 return false; // error was reported
539 PointerAssignmentChecker checker
{context
, scope
, *pointer
};
540 checker
.set_isBoundsRemapping(isBoundsRemapping
);
541 checker
.set_isAssumedRank(isAssumedRank
);
542 bool lhsOk
{checker
.CheckLeftHandSide(lhs
)};
543 bool rhsOk
{checker
.Check(rhs
)};
544 return lhsOk
&& rhsOk
; // don't short-circuit
547 bool CheckStructConstructorPointerComponent(SemanticsContext
&context
,
548 const Symbol
&lhs
, const SomeExpr
&rhs
, const Scope
&scope
) {
549 return PointerAssignmentChecker
{context
, scope
, lhs
}
550 .set_pointerComponentLHS(&lhs
)
554 bool CheckPointerAssignment(SemanticsContext
&context
, parser::CharBlock source
,
555 const std::string
&description
, const DummyDataObject
&lhs
,
556 const SomeExpr
&rhs
, const Scope
&scope
, bool isAssumedRank
) {
557 return PointerAssignmentChecker
{context
, scope
, source
, description
}
558 .set_lhsType(common::Clone(lhs
.type
))
559 .set_isContiguous(lhs
.attrs
.test(DummyDataObject::Attr::Contiguous
))
560 .set_isVolatile(lhs
.attrs
.test(DummyDataObject::Attr::Volatile
))
561 .set_isAssumedRank(isAssumedRank
)
565 bool CheckInitialDataPointerTarget(SemanticsContext
&context
,
566 const SomeExpr
&pointer
, const SomeExpr
&init
, const Scope
&scope
) {
567 return evaluate::IsInitialDataTarget(
568 init
, &context
.foldingContext().messages()) &&
569 CheckPointerAssignment(context
, pointer
, init
, scope
,
570 /*isBoundsRemapping=*/false,
571 /*isAssumedRank=*/false);
574 } // namespace Fortran::semantics