Bug Summary

File:build/source/flang/runtime/derived.cpp
Warning:line 27, column 5
Dereference of 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 derived.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 -D FLANG_INCLUDE_TESTS=1 -D FLANG_LITTLE_ENDIAN=1 -D FLANG_VENDOR="Debian " -D _DEBUG -D _GLIBCXX_ASSERTIONS -D _GNU_SOURCE -D _LIBCPP_ENABLE_ASSERTIONS -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -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 _FORTIFY_SOURCE=2 -D NDEBUG -U _GLIBCXX_ASSERTIONS -U _LIBCPP_ENABLE_ASSERTIONS -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 1683717183 -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-05-10-133810-16478-1 -x c++ /build/source/flang/runtime/derived.cpp
1//===-- runtime/derived.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 "derived.h"
10#include "stat.h"
11#include "terminator.h"
12#include "type-info.h"
13#include "flang/Runtime/descriptor.h"
14
15namespace Fortran::runtime {
16
17int Initialize(const Descriptor &instance, const typeInfo::DerivedType &derived,
18 Terminator &terminator, bool hasStat, const Descriptor *errMsg) {
19 const Descriptor &componentDesc{derived.component()};
20 std::size_t elements{instance.Elements()};
21 std::size_t byteStride{instance.ElementBytes()};
22 int stat{StatOk};
23 // Initialize data components in each element; the per-element iterations
24 // constitute the inner loops, not the outer ones
25 std::size_t myComponents{componentDesc.Elements()};
26 for (std::size_t k{0}; k < myComponents; ++k) {
1
Assuming 'k' is < 'myComponents'
2
Loop condition is true. Entering loop body
27 const auto &comp{
3
Dereference of null pointer
28 *componentDesc.ZeroBasedIndexedElement<typeInfo::Component>(k)};
29 if (comp.genre() == typeInfo::Component::Genre::Allocatable ||
30 comp.genre() == typeInfo::Component::Genre::Automatic) {
31 for (std::size_t j{0}; j < elements; ++j) {
32 Descriptor &allocDesc{*instance.OffsetElement<Descriptor>(
33 j * byteStride + comp.offset())};
34 comp.EstablishDescriptor(allocDesc, instance, terminator);
35 allocDesc.raw().attribute = CFI_attribute_allocatable2;
36 if (comp.genre() == typeInfo::Component::Genre::Automatic) {
37 stat = ReturnError(terminator, allocDesc.Allocate(), errMsg, hasStat);
38 if (stat == StatOk) {
39 if (const DescriptorAddendum * addendum{allocDesc.Addendum()}) {
40 if (const auto *derived{addendum->derivedType()}) {
41 if (!derived->noInitializationNeeded()) {
42 stat = Initialize(
43 allocDesc, *derived, terminator, hasStat, errMsg);
44 }
45 }
46 }
47 }
48 if (stat != StatOk) {
49 break;
50 }
51 }
52 }
53 } else if (const void *init{comp.initialization()}) {
54 // Explicit initialization of data pointers and
55 // non-allocatable non-automatic components
56 std::size_t bytes{comp.SizeInBytes(instance)};
57 for (std::size_t j{0}; j < elements; ++j) {
58 char *ptr{instance.ZeroBasedIndexedElement<char>(j) + comp.offset()};
59 std::memcpy(ptr, init, bytes);
60 }
61 } else if (comp.genre() == typeInfo::Component::Genre::Pointer) {
62 // Data pointers without explicit initialization are established
63 // so that they are valid right-hand side targets of pointer
64 // assignment statements.
65 for (std::size_t j{0}; j < elements; ++j) {
66 Descriptor &ptrDesc{*instance.OffsetElement<Descriptor>(
67 j * byteStride + comp.offset())};
68 comp.EstablishDescriptor(ptrDesc, instance, terminator);
69 ptrDesc.raw().attribute = CFI_attribute_pointer1;
70 }
71 } else if (comp.genre() == typeInfo::Component::Genre::Data &&
72 comp.derivedType() && !comp.derivedType()->noInitializationNeeded()) {
73 // Default initialization of non-pointer non-allocatable/automatic
74 // data component. Handles parent component's elements. Recursive.
75 SubscriptValue extent[maxRank];
76 const typeInfo::Value *bounds{comp.bounds()};
77 for (int dim{0}; dim < comp.rank(); ++dim) {
78 typeInfo::TypeParameterValue lb{
79 bounds[2 * dim].GetValue(&instance).value_or(0)};
80 typeInfo::TypeParameterValue ub{
81 bounds[2 * dim + 1].GetValue(&instance).value_or(0)};
82 extent[dim] = ub >= lb ? ub - lb + 1 : 0;
83 }
84 StaticDescriptor<maxRank, true, 0> staticDescriptor;
85 Descriptor &compDesc{staticDescriptor.descriptor()};
86 const typeInfo::DerivedType &compType{*comp.derivedType()};
87 for (std::size_t j{0}; j < elements; ++j) {
88 compDesc.Establish(compType,
89 instance.OffsetElement<char>(j * byteStride + comp.offset()),
90 comp.rank(), extent);
91 stat = Initialize(compDesc, compType, terminator, hasStat, errMsg);
92 if (stat != StatOk) {
93 break;
94 }
95 }
96 }
97 }
98 // Initialize procedure pointer components in each element
99 const Descriptor &procPtrDesc{derived.procPtr()};
100 std::size_t myProcPtrs{procPtrDesc.Elements()};
101 for (std::size_t k{0}; k < myProcPtrs; ++k) {
102 const auto &comp{
103 *procPtrDesc.ZeroBasedIndexedElement<typeInfo::ProcPtrComponent>(k)};
104 for (std::size_t j{0}; j < elements; ++j) {
105 auto &pptr{*instance.OffsetElement<typeInfo::ProcedurePointer>(
106 j * byteStride + comp.offset)};
107 pptr = comp.procInitialization;
108 }
109 }
110 return stat;
111}
112
113static const typeInfo::SpecialBinding *FindFinal(
114 const typeInfo::DerivedType &derived, int rank) {
115 if (const auto *ranked{derived.FindSpecialBinding(
116 typeInfo::SpecialBinding::RankFinal(rank))}) {
117 return ranked;
118 } else if (const auto *assumed{derived.FindSpecialBinding(
119 typeInfo::SpecialBinding::Which::AssumedRankFinal)}) {
120 return assumed;
121 } else {
122 return derived.FindSpecialBinding(
123 typeInfo::SpecialBinding::Which::ElementalFinal);
124 }
125}
126
127static void CallFinalSubroutine(
128 const Descriptor &descriptor, const typeInfo::DerivedType &derived) {
129 if (const auto *special{FindFinal(derived, descriptor.rank())}) {
130 // The following code relies on the fact that finalizable objects
131 // must be contiguous.
132 if (special->which() == typeInfo::SpecialBinding::Which::ElementalFinal) {
133 std::size_t byteStride{descriptor.ElementBytes()};
134 std::size_t elements{descriptor.Elements()};
135 if (special->IsArgDescriptor(0)) {
136 StaticDescriptor<maxRank, true, 8 /*?*/> statDesc;
137 Descriptor &elemDesc{statDesc.descriptor()};
138 elemDesc = descriptor;
139 elemDesc.raw().attribute = CFI_attribute_pointer1;
140 elemDesc.raw().rank = 0;
141 auto *p{special->GetProc<void (*)(const Descriptor &)>()};
142 for (std::size_t j{0}; j < elements; ++j) {
143 elemDesc.set_base_addr(
144 descriptor.OffsetElement<char>(j * byteStride));
145 p(elemDesc);
146 }
147 } else {
148 auto *p{special->GetProc<void (*)(char *)>()};
149 for (std::size_t j{0}; j < elements; ++j) {
150 p(descriptor.OffsetElement<char>(j * byteStride));
151 }
152 }
153 } else if (special->IsArgDescriptor(0)) {
154 StaticDescriptor<maxRank, true, 8 /*?*/> statDesc;
155 Descriptor &tmpDesc{statDesc.descriptor()};
156 tmpDesc = descriptor;
157 tmpDesc.raw().attribute = CFI_attribute_pointer1;
158 tmpDesc.Addendum()->set_derivedType(&derived);
159 auto *p{special->GetProc<void (*)(const Descriptor &)>()};
160 p(tmpDesc);
161 } else {
162 auto *p{special->GetProc<void (*)(char *)>()};
163 p(descriptor.OffsetElement<char>());
164 }
165 }
166}
167
168// Fortran 2018 subclause 7.5.6.2
169void Finalize(
170 const Descriptor &descriptor, const typeInfo::DerivedType &derived) {
171 if (derived.noFinalizationNeeded() || !descriptor.IsAllocated()) {
172 return;
173 }
174 CallFinalSubroutine(descriptor, derived);
175 const auto *parentType{derived.GetParentType()};
176 bool recurse{parentType && !parentType->noFinalizationNeeded()};
177 // If there's a finalizable parent component, handle it last, as required
178 // by the Fortran standard (7.5.6.2), and do so recursively with the same
179 // descriptor so that the rank is preserved.
180 const Descriptor &componentDesc{derived.component()};
181 std::size_t myComponents{componentDesc.Elements()};
182 std::size_t elements{descriptor.Elements()};
183 std::size_t byteStride{descriptor.ElementBytes()};
184 for (auto k{recurse
185 ? std::size_t{1} /* skip first component, it's the parent */
186 : 0};
187 k < myComponents; ++k) {
188 const auto &comp{
189 *componentDesc.ZeroBasedIndexedElement<typeInfo::Component>(k)};
190 if (comp.genre() == typeInfo::Component::Genre::Allocatable ||
191 comp.genre() == typeInfo::Component::Genre::Automatic) {
192 if (const typeInfo::DerivedType * compType{comp.derivedType()}) {
193 if (!compType->noFinalizationNeeded()) {
194 for (std::size_t j{0}; j < elements; ++j) {
195 const Descriptor &compDesc{*descriptor.OffsetElement<Descriptor>(
196 j * byteStride + comp.offset())};
197 if (compDesc.IsAllocated()) {
198 Finalize(compDesc, *compType);
199 }
200 }
201 }
202 }
203 } else if (comp.genre() == typeInfo::Component::Genre::Data &&
204 comp.derivedType() && !comp.derivedType()->noFinalizationNeeded()) {
205 SubscriptValue extent[maxRank];
206 const typeInfo::Value *bounds{comp.bounds()};
207 for (int dim{0}; dim < comp.rank(); ++dim) {
208 SubscriptValue lb{bounds[2 * dim].GetValue(&descriptor).value_or(0)};
209 SubscriptValue ub{
210 bounds[2 * dim + 1].GetValue(&descriptor).value_or(0)};
211 extent[dim] = ub >= lb ? ub - lb + 1 : 0;
212 }
213 StaticDescriptor<maxRank, true, 0> staticDescriptor;
214 Descriptor &compDesc{staticDescriptor.descriptor()};
215 const typeInfo::DerivedType &compType{*comp.derivedType()};
216 for (std::size_t j{0}; j < elements; ++j) {
217 compDesc.Establish(compType,
218 descriptor.OffsetElement<char>(j * byteStride + comp.offset()),
219 comp.rank(), extent);
220 Finalize(compDesc, compType);
221 }
222 }
223 }
224 if (recurse) {
225 Finalize(descriptor, *parentType);
226 }
227}
228
229// The order of finalization follows Fortran 2018 7.5.6.2, with
230// elementwise finalization of non-parent components taking place
231// before parent component finalization, and with all finalization
232// preceding any deallocation.
233void Destroy(const Descriptor &descriptor, bool finalize,
234 const typeInfo::DerivedType &derived) {
235 if (derived.noDestructionNeeded() || !descriptor.IsAllocated()) {
236 return;
237 }
238 if (finalize && !derived.noFinalizationNeeded()) {
239 Finalize(descriptor, derived);
240 }
241 const Descriptor &componentDesc{derived.component()};
242 std::size_t myComponents{componentDesc.Elements()};
243 std::size_t elements{descriptor.Elements()};
244 SubscriptValue at[maxRank];
245 descriptor.GetLowerBounds(at);
246 for (std::size_t k{0}; k < myComponents; ++k) {
247 const auto &comp{
248 *componentDesc.ZeroBasedIndexedElement<typeInfo::Component>(k)};
249 if (comp.genre() == typeInfo::Component::Genre::Allocatable ||
250 comp.genre() == typeInfo::Component::Genre::Automatic) {
251 for (std::size_t j{0}; j < elements; ++j) {
252 Descriptor *d{reinterpret_cast<Descriptor *>(
253 descriptor.Element<char>(at) + comp.offset())};
254 d->Deallocate();
255 descriptor.IncrementSubscripts(at);
256 }
257 }
258 }
259}
260
261} // namespace Fortran::runtime