Bug Summary

File:build/source/flang/runtime/assign.cpp
Warning:line 143, column 33
Forming reference to null pointer

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name assign.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=none -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/build/source/build-llvm/tools/clang/stage2-bins -resource-dir /usr/lib/llvm-17/lib/clang/17 -isystem /build/source/llvm/../mlir/include -isystem tools/mlir/include -isystem tools/clang/include -isystem /build/source/llvm/../clang/include -I tools/flang/runtime -I /build/source/flang/runtime -I /build/source/flang/include -I tools/flang/include -I include -I /build/source/llvm/include -D FLANG_INCLUDE_TESTS=1 -D FLANG_LITTLE_ENDIAN=1 -D FLANG_VENDOR="Debian " -D _DEBUG -D _GNU_SOURCE -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -D _FORTIFY_SOURCE=2 -D NDEBUG -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward -internal-isystem /usr/lib/llvm-17/lib/clang/17/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fmacro-prefix-map=/build/source/= -fcoverage-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fcoverage-prefix-map=/build/source/= -source-date-epoch 1675289259 -O2 -Wno-unused-command-line-argument -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wno-comment -Wno-misleading-indentation -Wno-deprecated-copy -Wno-ctad-maybe-unsupported -std=c++17 -fdeprecated-macro -fdebug-compilation-dir=/build/source/build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/source/= -ferror-limit 19 -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2023-02-02-055145-558594-1 -x c++ /build/source/flang/runtime/assign.cpp
1//===-- runtime/assign.cpp ------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "flang/Runtime/assign.h"
10#include "assign.h"
11#include "derived.h"
12#include "stat.h"
13#include "terminator.h"
14#include "type-info.h"
15#include "flang/Runtime/descriptor.h"
16
17namespace Fortran::runtime {
18
19static void DoScalarDefinedAssignment(const Descriptor &to,
20 const Descriptor &from, const typeInfo::SpecialBinding &special) {
21 bool toIsDesc{special.IsArgDescriptor(0)};
22 bool fromIsDesc{special.IsArgDescriptor(1)};
23 if (toIsDesc) {
24 if (fromIsDesc) {
25 auto *p{
26 special.GetProc<void (*)(const Descriptor &, const Descriptor &)>()};
27 p(to, from);
28 } else {
29 auto *p{special.GetProc<void (*)(const Descriptor &, void *)>()};
30 p(to, from.raw().base_addr);
31 }
32 } else {
33 if (fromIsDesc) {
34 auto *p{special.GetProc<void (*)(void *, const Descriptor &)>()};
35 p(to.raw().base_addr, from);
36 } else {
37 auto *p{special.GetProc<void (*)(void *, void *)>()};
38 p(to.raw().base_addr, from.raw().base_addr);
39 }
40 }
41}
42
43static void DoElementalDefinedAssignment(const Descriptor &to,
44 const Descriptor &from, const typeInfo::SpecialBinding &special,
45 std::size_t toElements, SubscriptValue toAt[], SubscriptValue fromAt[]) {
46 StaticDescriptor<maxRank, true, 8 /*?*/> statDesc[2];
47 Descriptor &toElementDesc{statDesc[0].descriptor()};
48 Descriptor &fromElementDesc{statDesc[1].descriptor()};
49 toElementDesc = to;
50 toElementDesc.raw().attribute = CFI_attribute_pointer1;
51 toElementDesc.raw().rank = 0;
52 fromElementDesc = from;
53 fromElementDesc.raw().attribute = CFI_attribute_pointer1;
54 fromElementDesc.raw().rank = 0;
55 for (std::size_t j{0}; j < toElements;
56 ++j, to.IncrementSubscripts(toAt), from.IncrementSubscripts(fromAt)) {
57 toElementDesc.set_base_addr(to.Element<char>(toAt));
58 fromElementDesc.set_base_addr(from.Element<char>(fromAt));
59 DoScalarDefinedAssignment(toElementDesc, fromElementDesc, special);
60 }
61}
62
63// Assigns one object to another via intrinsic assignment (F'2018 10.2.1.3) or
64// type-bound (only!) defined assignment (10.2.1.4), as appropriate. Performs
65// finalization, scalar expansion, & allocatable (re)allocation as needed.
66// Does not perform intrinsic assignment implicit type conversion. Both
67// descriptors must be initialized. Recurses as needed to handle components.
68// Do not perform allocatable reallocation if \p skipRealloc is true, which is
69// used for allocate statement with source specifier.
70static void Assign(Descriptor &to, const Descriptor &from,
71 Terminator &terminator, bool skipRealloc = false) {
72 DescriptorAddendum *toAddendum{to.Addendum()};
73 const typeInfo::DerivedType *toDerived{
2
'toDerived' initialized to a null pointer value
74 toAddendum
0.1
'toAddendum' is null
? toAddendum->derivedType() : nullptr};
1
'?' condition is false
75 const DescriptorAddendum *fromAddendum{from.Addendum()};
76 const typeInfo::DerivedType *fromDerived{
77 fromAddendum
2.1
'fromAddendum' is non-null
? fromAddendum->derivedType() : nullptr};
3
'?' condition is true
78 bool wasJustAllocated{false};
79 if (to.IsAllocatable()) {
80 std::size_t lenParms{fromDerived ? fromDerived->LenParameters() : 0};
4
Taking true branch
5
Assuming 'fromDerived' is non-null
6
'?' condition is true
81 if (to.IsAllocated() && !skipRealloc) {
82 // Top-level assignments to allocatable variables (*not* components)
83 // may first deallocate existing content if there's about to be a
84 // change in type or shape; see F'2018 10.2.1.3(3).
85 bool deallocate{false};
86 if (to.type() != from.type()) {
87 deallocate = true;
88 } else if (toDerived != fromDerived) {
89 deallocate = true;
90 } else {
91 if (toAddendum) {
92 // Distinct LEN parameters? Deallocate
93 for (std::size_t j{0}; j < lenParms; ++j) {
94 if (toAddendum->LenParameterValue(j) !=
95 fromAddendum->LenParameterValue(j)) {
96 deallocate = true;
97 break;
98 }
99 }
100 }
101 if (from.rank() > 0) {
102 // Distinct shape? Deallocate
103 int rank{to.rank()};
104 for (int j{0}; j < rank; ++j) {
105 if (to.GetDimension(j).Extent() != from.GetDimension(j).Extent()) {
106 deallocate = true;
107 break;
108 }
109 }
110 }
111 }
112 if (deallocate) {
113 to.Destroy(true /*finalize*/);
114 }
115 } else if (to.rank() != from.rank()) {
7
Assuming the condition is false
8
Taking false branch
116 terminator.Crash("Assign: mismatched ranks (%d != %d) in assignment to "
117 "unallocated allocatable",
118 to.rank(), from.rank());
119 }
120 if (!to.IsAllocated()) {
9
Taking true branch
121 to.raw().type = from.raw().type;
122 to.raw().elem_len = from.ElementBytes();
123 if (toAddendum
9.1
'toAddendum' is null
) {
10
Taking false branch
124 toDerived = fromDerived;
125 toAddendum->set_derivedType(toDerived);
126 for (std::size_t j{0}; j < lenParms; ++j) {
127 toAddendum->SetLenParameterValue(
128 j, fromAddendum->LenParameterValue(j));
129 }
130 }
131 // subtle: leave bounds in place when "from" is scalar (10.2.1.3(3))
132 int rank{from.rank()};
133 auto stride{static_cast<SubscriptValue>(to.ElementBytes())};
134 for (int j{0}; j < rank; ++j) {
11
Assuming 'j' is >= 'rank'
12
Loop condition is false. Execution continues on line 141
135 auto &toDim{to.GetDimension(j)};
136 const auto &fromDim{from.GetDimension(j)};
137 toDim.SetBounds(fromDim.LowerBound(), fromDim.UpperBound());
138 toDim.SetByteStride(stride);
139 stride *= toDim.Extent();
140 }
141 ReturnError(terminator, to.Allocate());
142 if (fromDerived
12.1
'fromDerived' is non-null
&& !fromDerived->noInitializationNeeded()) {
13
Assuming the condition is true
14
Taking true branch
143 ReturnError(terminator, Initialize(to, *toDerived, terminator));
15
Forming reference to null pointer
144 }
145 wasJustAllocated = true;
146 }
147 }
148 SubscriptValue toAt[maxRank];
149 to.GetLowerBounds(toAt);
150 // Scalar expansion of the RHS is implied by using the same empty
151 // subscript values on each (seemingly) elemental reference into
152 // "from".
153 SubscriptValue fromAt[maxRank];
154 from.GetLowerBounds(fromAt);
155 std::size_t toElements{to.Elements()};
156 if (from.rank() > 0 && toElements != from.Elements()) {
157 terminator.Crash("Assign: mismatching element counts in array assignment "
158 "(to %zd, from %zd)",
159 toElements, from.Elements());
160 }
161 if (to.type() != from.type()) {
162 terminator.Crash("Assign: mismatching types (to code %d != from code %d)",
163 to.type().raw(), from.type().raw());
164 }
165 std::size_t elementBytes{to.ElementBytes()};
166 if (elementBytes != from.ElementBytes()) {
167 terminator.Crash(
168 "Assign: mismatching element sizes (to %zd bytes != from %zd bytes)",
169 elementBytes, from.ElementBytes());
170 }
171 if (toDerived) { // Derived type assignment
172 // Check for defined assignment type-bound procedures (10.2.1.4-5)
173 if (to.rank() == 0) {
174 if (const auto *special{toDerived->FindSpecialBinding(
175 typeInfo::SpecialBinding::Which::ScalarAssignment)}) {
176 return DoScalarDefinedAssignment(to, from, *special);
177 }
178 }
179 if (const auto *special{toDerived->FindSpecialBinding(
180 typeInfo::SpecialBinding::Which::ElementalAssignment)}) {
181 return DoElementalDefinedAssignment(
182 to, from, *special, toElements, toAt, fromAt);
183 }
184 // Derived type intrinsic assignment, which is componentwise and elementwise
185 // for all components, including parent components (10.2.1.2-3).
186 // The target is first finalized if still necessary (7.5.6.3(1))
187 if (!wasJustAllocated && !toDerived->noFinalizationNeeded()) {
188 Finalize(to, *toDerived);
189 }
190 // Copy the data components (incl. the parent) first.
191 const Descriptor &componentDesc{toDerived->component()};
192 std::size_t numComponents{componentDesc.Elements()};
193 for (std::size_t k{0}; k < numComponents; ++k) {
194 const auto &comp{
195 *componentDesc.ZeroBasedIndexedElement<typeInfo::Component>(
196 k)}; // TODO: exploit contiguity here
197 switch (comp.genre()) {
198 case typeInfo::Component::Genre::Data:
199 if (comp.category() == TypeCategory::Derived) {
200 StaticDescriptor<maxRank, true, 10 /*?*/> statDesc[2];
201 Descriptor &toCompDesc{statDesc[0].descriptor()};
202 Descriptor &fromCompDesc{statDesc[1].descriptor()};
203 for (std::size_t j{0}; j < toElements; ++j,
204 to.IncrementSubscripts(toAt), from.IncrementSubscripts(fromAt)) {
205 comp.CreatePointerDescriptor(toCompDesc, to, terminator, toAt);
206 comp.CreatePointerDescriptor(
207 fromCompDesc, from, terminator, fromAt);
208 Assign(toCompDesc, fromCompDesc, terminator, /*skipRealloc=*/false);
209 }
210 } else { // Component has intrinsic type; simply copy raw bytes
211 std::size_t componentByteSize{comp.SizeInBytes(to)};
212 for (std::size_t j{0}; j < toElements; ++j,
213 to.IncrementSubscripts(toAt), from.IncrementSubscripts(fromAt)) {
214 std::memmove(to.Element<char>(toAt) + comp.offset(),
215 from.Element<const char>(fromAt) + comp.offset(),
216 componentByteSize);
217 }
218 }
219 break;
220 case typeInfo::Component::Genre::Pointer: {
221 std::size_t componentByteSize{comp.SizeInBytes(to)};
222 for (std::size_t j{0}; j < toElements; ++j,
223 to.IncrementSubscripts(toAt), from.IncrementSubscripts(fromAt)) {
224 std::memmove(to.Element<char>(toAt) + comp.offset(),
225 from.Element<const char>(fromAt) + comp.offset(),
226 componentByteSize);
227 }
228 } break;
229 case typeInfo::Component::Genre::Allocatable:
230 case typeInfo::Component::Genre::Automatic:
231 for (std::size_t j{0}; j < toElements; ++j,
232 to.IncrementSubscripts(toAt), from.IncrementSubscripts(fromAt)) {
233 auto *toDesc{reinterpret_cast<Descriptor *>(
234 to.Element<char>(toAt) + comp.offset())};
235 const auto *fromDesc{reinterpret_cast<const Descriptor *>(
236 from.Element<char>(fromAt) + comp.offset())};
237 if (toDesc->IsAllocatable()) {
238 if (toDesc->IsAllocated()) {
239 // Allocatable components of the LHS are unconditionally
240 // deallocated before assignment (F'2018 10.2.1.3(13)(1)),
241 // unlike a "top-level" assignment to a variable, where
242 // deallocation is optional.
243 // TODO: Consider skipping this step and deferring the
244 // deallocation to the recursive activation of Assign(),
245 // which might be able to avoid deallocation/reallocation
246 // when the existing allocation can be reoccupied.
247 toDesc->Destroy(false /*already finalized*/);
248 }
249 if (!fromDesc->IsAllocated()) {
250 continue; // F'2018 10.2.1.3(13)(2)
251 }
252 }
253 Assign(*toDesc, *fromDesc, terminator, /*skipRealloc=*/false);
254 }
255 break;
256 }
257 }
258 // Copy procedure pointer components
259 const Descriptor &procPtrDesc{toDerived->procPtr()};
260 std::size_t numProcPtrs{procPtrDesc.Elements()};
261 for (std::size_t k{0}; k < numProcPtrs; ++k) {
262 const auto &procPtr{
263 *procPtrDesc.ZeroBasedIndexedElement<typeInfo::ProcPtrComponent>(k)};
264 for (std::size_t j{0}; j < toElements; ++j, to.IncrementSubscripts(toAt),
265 from.IncrementSubscripts(fromAt)) {
266 std::memmove(to.Element<char>(toAt) + procPtr.offset,
267 from.Element<const char>(fromAt) + procPtr.offset,
268 sizeof(typeInfo::ProcedurePointer));
269 }
270 }
271 } else { // intrinsic type, intrinsic assignment
272 if (to.rank() == from.rank() && to.IsContiguous() && from.IsContiguous()) {
273 // Everything is contiguous; do a single big copy
274 std::memmove(
275 to.raw().base_addr, from.raw().base_addr, toElements * elementBytes);
276 } else { // elemental copies
277 for (std::size_t n{toElements}; n-- > 0;
278 to.IncrementSubscripts(toAt), from.IncrementSubscripts(fromAt)) {
279 std::memmove(to.Element<char>(toAt), from.Element<const char>(fromAt),
280 elementBytes);
281 }
282 }
283 }
284}
285
286void DoFromSourceAssign(
287 Descriptor &alloc, const Descriptor &source, Terminator &terminator) {
288 if (alloc.rank() > 0 && source.rank() == 0) {
289 // The value of each element of allocate object becomes the value of source.
290 DescriptorAddendum *allocAddendum{alloc.Addendum()};
291 const typeInfo::DerivedType *allocDerived{
292 allocAddendum ? allocAddendum->derivedType() : nullptr};
293 SubscriptValue allocAt[maxRank];
294 alloc.GetLowerBounds(allocAt);
295 if (allocDerived) {
296 for (std::size_t n{alloc.Elements()}; n-- > 0;
297 alloc.IncrementSubscripts(allocAt)) {
298 Descriptor allocElement{*Descriptor::Create(*allocDerived,
299 reinterpret_cast<void *>(alloc.Element<char>(allocAt)), 0)};
300 Assign(allocElement, source, terminator, /*skipRealloc=*/true);
301 }
302 } else { // intrinsic type
303 for (std::size_t n{alloc.Elements()}; n-- > 0;
304 alloc.IncrementSubscripts(allocAt)) {
305 std::memmove(alloc.Element<char>(allocAt), source.raw().base_addr,
306 alloc.ElementBytes());
307 }
308 }
309 } else {
310 Assign(alloc, source, terminator, /*skipRealloc=*/true);
311 }
312}
313
314extern "C" {
315void RTNAME(Assign)_FortranAAssign(Descriptor &to, const Descriptor &from,
316 const char *sourceFile, int sourceLine) {
317 Terminator terminator{sourceFile, sourceLine};
318 Assign(to, from, terminator);
319}
320
321} // extern "C"
322} // namespace Fortran::runtime