1 //===-- lib/Semantics/data-to-inits.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 // DATA statement object/value checking and conversion to static
10 // initializers
11 // - Applies specific checks to each scalar element initialization with a
12 //   constant value or pointer target with class DataInitializationCompiler;
13 // - Collects the elemental initializations for each symbol and converts them
14 //   into a single init() expression with member function
15 //   DataChecker::ConstructInitializer().
16 
17 #include "data-to-inits.h"
18 #include "pointer-assignment.h"
19 #include "flang/Evaluate/fold-designator.h"
20 #include "flang/Evaluate/tools.h"
21 #include "flang/Semantics/tools.h"
22 
23 // The job of generating explicit static initializers for objects that don't
24 // have them in order to implement default component initialization is now being
25 // done in lowering, so don't do it here in semantics; but the code remains here
26 // in case we change our minds.
27 static constexpr bool makeDefaultInitializationExplicit{false};
28 
29 // Whether to delete the original "init()" initializers from storage-associated
30 // objects and pointers.
31 static constexpr bool removeOriginalInits{false};
32 
33 namespace Fortran::semantics {
34 
35 // Steps through a list of values in a DATA statement set; implements
36 // repetition.
37 class ValueListIterator {
38 public:
39   explicit ValueListIterator(const parser::DataStmtSet &set)
40       : end_{std::get<std::list<parser::DataStmtValue>>(set.t).end()},
41         at_{std::get<std::list<parser::DataStmtValue>>(set.t).begin()} {
42     SetRepetitionCount();
43   }
44   bool hasFatalError() const { return hasFatalError_; }
45   bool IsAtEnd() const { return at_ == end_; }
46   const SomeExpr *operator*() const { return GetExpr(GetConstant()); }
47   parser::CharBlock LocateSource() const { return GetConstant().source; }
48   ValueListIterator &operator++() {
49     if (repetitionsRemaining_ > 0) {
50       --repetitionsRemaining_;
51     } else if (at_ != end_) {
52       ++at_;
53       SetRepetitionCount();
54     }
55     return *this;
56   }
57 
58 private:
59   using listIterator = std::list<parser::DataStmtValue>::const_iterator;
60   void SetRepetitionCount();
61   const parser::DataStmtConstant &GetConstant() const {
62     return std::get<parser::DataStmtConstant>(at_->t);
63   }
64 
65   listIterator end_;
66   listIterator at_;
67   ConstantSubscript repetitionsRemaining_{0};
68   bool hasFatalError_{false};
69 };
70 
71 void ValueListIterator::SetRepetitionCount() {
72   for (repetitionsRemaining_ = 1; at_ != end_; ++at_) {
73     if (at_->repetitions < 0) {
74       hasFatalError_ = true;
75     }
76     if (at_->repetitions > 0) {
77       repetitionsRemaining_ = at_->repetitions - 1;
78       return;
79     }
80   }
81   repetitionsRemaining_ = 0;
82 }
83 
84 // Collects all of the elemental initializations from DATA statements
85 // into a single image for each symbol that appears in any DATA.
86 // Expands the implied DO loops and array references.
87 // Applies checks that validate each distinct elemental initialization
88 // of the variables in a data-stmt-set, as well as those that apply
89 // to the corresponding values being use to initialize each element.
90 class DataInitializationCompiler {
91 public:
92   DataInitializationCompiler(DataInitializations &inits,
93       evaluate::ExpressionAnalyzer &a, const parser::DataStmtSet &set)
94       : inits_{inits}, exprAnalyzer_{a}, values_{set} {}
95   const DataInitializations &inits() const { return inits_; }
96   bool HasSurplusValues() const { return !values_.IsAtEnd(); }
97   bool Scan(const parser::DataStmtObject &);
98 
99 private:
100   bool Scan(const parser::Variable &);
101   bool Scan(const parser::Designator &);
102   bool Scan(const parser::DataImpliedDo &);
103   bool Scan(const parser::DataIDoObject &);
104 
105   // Initializes all elements of a designator, which can be an array or section.
106   bool InitDesignator(const SomeExpr &);
107   // Initializes a single object.
108   bool InitElement(const evaluate::OffsetSymbol &, const SomeExpr &designator);
109   // If the returned flag is true, emit a warning about CHARACTER misusage.
110   std::optional<std::pair<SomeExpr, bool>> ConvertElement(
111       const SomeExpr &, const evaluate::DynamicType &);
112 
113   DataInitializations &inits_;
114   evaluate::ExpressionAnalyzer &exprAnalyzer_;
115   ValueListIterator values_;
116 };
117 
118 bool DataInitializationCompiler::Scan(const parser::DataStmtObject &object) {
119   return std::visit(
120       common::visitors{
121           [&](const common::Indirection<parser::Variable> &var) {
122             return Scan(var.value());
123           },
124           [&](const parser::DataImpliedDo &ido) { return Scan(ido); },
125       },
126       object.u);
127 }
128 
129 bool DataInitializationCompiler::Scan(const parser::Variable &var) {
130   if (const auto *expr{GetExpr(var)}) {
131     exprAnalyzer_.GetFoldingContext().messages().SetLocation(var.GetSource());
132     if (InitDesignator(*expr)) {
133       return true;
134     }
135   }
136   return false;
137 }
138 
139 bool DataInitializationCompiler::Scan(const parser::Designator &designator) {
140   if (auto expr{exprAnalyzer_.Analyze(designator)}) {
141     exprAnalyzer_.GetFoldingContext().messages().SetLocation(
142         parser::FindSourceLocation(designator));
143     if (InitDesignator(*expr)) {
144       return true;
145     }
146   }
147   return false;
148 }
149 
150 bool DataInitializationCompiler::Scan(const parser::DataImpliedDo &ido) {
151   const auto &bounds{std::get<parser::DataImpliedDo::Bounds>(ido.t)};
152   auto name{bounds.name.thing.thing};
153   const auto *lowerExpr{GetExpr(bounds.lower.thing.thing)};
154   const auto *upperExpr{GetExpr(bounds.upper.thing.thing)};
155   const auto *stepExpr{
156       bounds.step ? GetExpr(bounds.step->thing.thing) : nullptr};
157   if (lowerExpr && upperExpr) {
158     // Fold the bounds expressions (again) in case any of them depend
159     // on outer implied DO loops.
160     evaluate::FoldingContext &context{exprAnalyzer_.GetFoldingContext()};
161     std::int64_t stepVal{1};
162     if (stepExpr) {
163       auto foldedStep{evaluate::Fold(context, SomeExpr{*stepExpr})};
164       stepVal = ToInt64(foldedStep).value_or(1);
165       if (stepVal == 0) {
166         exprAnalyzer_.Say(name.source,
167             "DATA statement implied DO loop has a step value of zero"_err_en_US);
168         return false;
169       }
170     }
171     auto foldedLower{evaluate::Fold(context, SomeExpr{*lowerExpr})};
172     auto lower{ToInt64(foldedLower)};
173     auto foldedUpper{evaluate::Fold(context, SomeExpr{*upperExpr})};
174     auto upper{ToInt64(foldedUpper)};
175     if (lower && upper) {
176       int kind{evaluate::ResultType<evaluate::ImpliedDoIndex>::kind};
177       if (const auto dynamicType{evaluate::DynamicType::From(*name.symbol)}) {
178         if (dynamicType->category() == TypeCategory::Integer) {
179           kind = dynamicType->kind();
180         }
181       }
182       if (exprAnalyzer_.AddImpliedDo(name.source, kind)) {
183         auto &value{context.StartImpliedDo(name.source, *lower)};
184         bool result{true};
185         for (auto n{(*upper - value + stepVal) / stepVal}; n > 0;
186              --n, value += stepVal) {
187           for (const auto &object :
188               std::get<std::list<parser::DataIDoObject>>(ido.t)) {
189             if (!Scan(object)) {
190               result = false;
191               break;
192             }
193           }
194         }
195         context.EndImpliedDo(name.source);
196         exprAnalyzer_.RemoveImpliedDo(name.source);
197         return result;
198       }
199     }
200   }
201   return false;
202 }
203 
204 bool DataInitializationCompiler::Scan(const parser::DataIDoObject &object) {
205   return std::visit(
206       common::visitors{
207           [&](const parser::Scalar<common::Indirection<parser::Designator>>
208                   &var) { return Scan(var.thing.value()); },
209           [&](const common::Indirection<parser::DataImpliedDo> &ido) {
210             return Scan(ido.value());
211           },
212       },
213       object.u);
214 }
215 
216 bool DataInitializationCompiler::InitDesignator(const SomeExpr &designator) {
217   evaluate::FoldingContext &context{exprAnalyzer_.GetFoldingContext()};
218   evaluate::DesignatorFolder folder{context};
219   while (auto offsetSymbol{folder.FoldDesignator(designator)}) {
220     if (folder.isOutOfRange()) {
221       if (auto bad{evaluate::OffsetToDesignator(context, *offsetSymbol)}) {
222         exprAnalyzer_.context().Say(
223             "DATA statement designator '%s' is out of range"_err_en_US,
224             bad->AsFortran());
225       } else {
226         exprAnalyzer_.context().Say(
227             "DATA statement designator '%s' is out of range"_err_en_US,
228             designator.AsFortran());
229       }
230       return false;
231     } else if (!InitElement(*offsetSymbol, designator)) {
232       return false;
233     } else {
234       ++values_;
235     }
236   }
237   return folder.isEmpty();
238 }
239 
240 std::optional<std::pair<SomeExpr, bool>>
241 DataInitializationCompiler::ConvertElement(
242     const SomeExpr &expr, const evaluate::DynamicType &type) {
243   if (auto converted{evaluate::ConvertToType(type, SomeExpr{expr})}) {
244     return {std::make_pair(std::move(*converted), false)};
245   }
246   if (std::optional<std::string> chValue{
247           evaluate::GetScalarConstantValue<evaluate::Ascii>(expr)}) {
248     // Allow DATA initialization with Hollerith and kind=1 CHARACTER like
249     // (most) other Fortran compilers do.  Pad on the right with spaces
250     // when short, truncate the right if long.
251     // TODO: big-endian targets
252     auto bytes{static_cast<std::size_t>(evaluate::ToInt64(
253         type.MeasureSizeInBytes(exprAnalyzer_.GetFoldingContext(), false))
254                                             .value())};
255     evaluate::BOZLiteralConstant bits{0};
256     for (std::size_t j{0}; j < bytes; ++j) {
257       char ch{j >= chValue->size() ? ' ' : chValue->at(j)};
258       evaluate::BOZLiteralConstant chBOZ{static_cast<unsigned char>(ch)};
259       bits = bits.IOR(chBOZ.SHIFTL(8 * j));
260     }
261     if (auto converted{evaluate::ConvertToType(type, SomeExpr{bits})}) {
262       return {std::make_pair(std::move(*converted), true)};
263     }
264   }
265   return std::nullopt;
266 }
267 
268 bool DataInitializationCompiler::InitElement(
269     const evaluate::OffsetSymbol &offsetSymbol, const SomeExpr &designator) {
270   const Symbol &symbol{offsetSymbol.symbol()};
271   const Symbol *lastSymbol{GetLastSymbol(designator)};
272   bool isPointer{lastSymbol && IsPointer(*lastSymbol)};
273   bool isProcPointer{lastSymbol && IsProcedurePointer(*lastSymbol)};
274   evaluate::FoldingContext &context{exprAnalyzer_.GetFoldingContext()};
275   auto restorer{context.messages().SetLocation(values_.LocateSource())};
276 
277   const auto DescribeElement{[&]() {
278     if (auto badDesignator{
279             evaluate::OffsetToDesignator(context, offsetSymbol)}) {
280       return badDesignator->AsFortran();
281     } else {
282       // Error recovery
283       std::string buf;
284       llvm::raw_string_ostream ss{buf};
285       ss << offsetSymbol.symbol().name() << " offset " << offsetSymbol.offset()
286          << " bytes for " << offsetSymbol.size() << " bytes";
287       return ss.str();
288     }
289   }};
290   const auto GetImage{[&]() -> evaluate::InitialImage & {
291     auto iter{inits_.emplace(&symbol, symbol.size())};
292     auto &symbolInit{iter.first->second};
293     symbolInit.initializedRanges.emplace_back(
294         offsetSymbol.offset(), offsetSymbol.size());
295     return symbolInit.image;
296   }};
297   const auto OutOfRangeError{[&]() {
298     evaluate::AttachDeclaration(
299         exprAnalyzer_.context().Say(
300             "DATA statement designator '%s' is out of range for its variable '%s'"_err_en_US,
301             DescribeElement(), symbol.name()),
302         symbol);
303   }};
304 
305   if (values_.hasFatalError()) {
306     return false;
307   } else if (values_.IsAtEnd()) {
308     exprAnalyzer_.context().Say(
309         "DATA statement set has no value for '%s'"_err_en_US,
310         DescribeElement());
311     return false;
312   } else if (static_cast<std::size_t>(
313                  offsetSymbol.offset() + offsetSymbol.size()) > symbol.size()) {
314     OutOfRangeError();
315     return false;
316   }
317 
318   const SomeExpr *expr{*values_};
319   if (!expr) {
320     CHECK(exprAnalyzer_.context().AnyFatalError());
321   } else if (isPointer) {
322     if (static_cast<std::size_t>(offsetSymbol.offset() + offsetSymbol.size()) >
323         symbol.size()) {
324       OutOfRangeError();
325     } else if (evaluate::IsNullPointer(*expr)) {
326       // nothing to do; rely on zero initialization
327       return true;
328     } else if (isProcPointer) {
329       if (evaluate::IsProcedure(*expr)) {
330         if (CheckPointerAssignment(context, designator, *expr)) {
331           GetImage().AddPointer(offsetSymbol.offset(), *expr);
332           return true;
333         }
334       } else {
335         exprAnalyzer_.Say(
336             "Data object '%s' may not be used to initialize '%s', which is a procedure pointer"_err_en_US,
337             expr->AsFortran(), DescribeElement());
338       }
339     } else if (evaluate::IsProcedure(*expr)) {
340       exprAnalyzer_.Say(
341           "Procedure '%s' may not be used to initialize '%s', which is not a procedure pointer"_err_en_US,
342           expr->AsFortran(), DescribeElement());
343     } else if (CheckInitialTarget(context, designator, *expr)) {
344       GetImage().AddPointer(offsetSymbol.offset(), *expr);
345       return true;
346     }
347   } else if (evaluate::IsNullPointer(*expr)) {
348     exprAnalyzer_.Say("Initializer for '%s' must not be a pointer"_err_en_US,
349         DescribeElement());
350   } else if (evaluate::IsProcedure(*expr)) {
351     exprAnalyzer_.Say("Initializer for '%s' must not be a procedure"_err_en_US,
352         DescribeElement());
353   } else if (auto designatorType{designator.GetType()}) {
354     if (expr->Rank() > 0) {
355       // Because initial-data-target is ambiguous with scalar-constant and
356       // scalar-constant-subobject at parse time, enforcement of scalar-*
357       // must be deferred to here.
358       exprAnalyzer_.Say(
359           "DATA statement value initializes '%s' with an array"_err_en_US,
360           DescribeElement());
361     } else if (auto converted{ConvertElement(*expr, *designatorType)}) {
362       // value non-pointer initialization
363       if (IsBOZLiteral(*expr) &&
364           designatorType->category() != TypeCategory::Integer) { // 8.6.7(11)
365         exprAnalyzer_.Say(
366             "BOZ literal should appear in a DATA statement only as a value for an integer object, but '%s' is '%s'"_en_US,
367             DescribeElement(), designatorType->AsFortran());
368       } else if (converted->second) {
369         exprAnalyzer_.context().Say(
370             "DATA statement value initializes '%s' of type '%s' with CHARACTER"_en_US,
371             DescribeElement(), designatorType->AsFortran());
372       }
373       auto folded{evaluate::Fold(context, std::move(converted->first))};
374       switch (GetImage().Add(
375           offsetSymbol.offset(), offsetSymbol.size(), folded, context)) {
376       case evaluate::InitialImage::Ok:
377         return true;
378       case evaluate::InitialImage::NotAConstant:
379         exprAnalyzer_.Say(
380             "DATA statement value '%s' for '%s' is not a constant"_err_en_US,
381             folded.AsFortran(), DescribeElement());
382         break;
383       case evaluate::InitialImage::OutOfRange:
384         OutOfRangeError();
385         break;
386       default:
387         CHECK(exprAnalyzer_.context().AnyFatalError());
388         break;
389       }
390     } else {
391       exprAnalyzer_.context().Say(
392           "DATA statement value could not be converted to the type '%s' of the object '%s'"_err_en_US,
393           designatorType->AsFortran(), DescribeElement());
394     }
395   } else {
396     CHECK(exprAnalyzer_.context().AnyFatalError());
397   }
398   return false;
399 }
400 
401 void AccumulateDataInitializations(DataInitializations &inits,
402     evaluate::ExpressionAnalyzer &exprAnalyzer,
403     const parser::DataStmtSet &set) {
404   DataInitializationCompiler scanner{inits, exprAnalyzer, set};
405   for (const auto &object :
406       std::get<std::list<parser::DataStmtObject>>(set.t)) {
407     if (!scanner.Scan(object)) {
408       return;
409     }
410   }
411   if (scanner.HasSurplusValues()) {
412     exprAnalyzer.context().Say(
413         "DATA statement set has more values than objects"_err_en_US);
414   }
415 }
416 
417 // Looks for default derived type component initialization -- but
418 // *not* allocatables.
419 static const DerivedTypeSpec *HasDefaultInitialization(const Symbol &symbol) {
420   if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
421     if (object->init().has_value()) {
422       return nullptr; // init is explicit, not default
423     } else if (!object->isDummy() && object->type()) {
424       if (const DerivedTypeSpec * derived{object->type()->AsDerived()}) {
425         DirectComponentIterator directs{*derived};
426         if (std::find_if(
427                 directs.begin(), directs.end(), [](const Symbol &component) {
428                   return !IsAllocatable(component) &&
429                       HasDeclarationInitializer(component);
430                 })) {
431           return derived;
432         }
433       }
434     }
435   }
436   return nullptr;
437 }
438 
439 // PopulateWithComponentDefaults() adds initializations to an instance
440 // of SymbolDataInitialization containing all of the default component
441 // initializers
442 
443 static void PopulateWithComponentDefaults(SymbolDataInitialization &init,
444     std::size_t offset, const DerivedTypeSpec &derived,
445     evaluate::FoldingContext &foldingContext);
446 
447 static void PopulateWithComponentDefaults(SymbolDataInitialization &init,
448     std::size_t offset, const DerivedTypeSpec &derived,
449     evaluate::FoldingContext &foldingContext, const Symbol &symbol) {
450   if (auto extents{evaluate::GetConstantExtents(foldingContext, symbol)}) {
451     const Scope &scope{derived.scope() ? *derived.scope()
452                                        : DEREF(derived.typeSymbol().scope())};
453     std::size_t stride{scope.size()};
454     if (std::size_t alignment{scope.alignment().value_or(0)}) {
455       stride = ((stride + alignment - 1) / alignment) * alignment;
456     }
457     for (auto elements{evaluate::GetSize(*extents)}; elements-- > 0;
458          offset += stride) {
459       PopulateWithComponentDefaults(init, offset, derived, foldingContext);
460     }
461   }
462 }
463 
464 // F'2018 19.5.3(10) allows storage-associated default component initialization
465 // when the values are identical.
466 static void PopulateWithComponentDefaults(SymbolDataInitialization &init,
467     std::size_t offset, const DerivedTypeSpec &derived,
468     evaluate::FoldingContext &foldingContext) {
469   const Scope &scope{
470       derived.scope() ? *derived.scope() : DEREF(derived.typeSymbol().scope())};
471   for (const auto &pair : scope) {
472     const Symbol &component{*pair.second};
473     std::size_t componentOffset{offset + component.offset()};
474     if (const auto *object{component.detailsIf<ObjectEntityDetails>()}) {
475       if (!IsAllocatable(component) && !IsAutomatic(component)) {
476         bool initialized{false};
477         if (object->init()) {
478           initialized = true;
479           if (IsPointer(component)) {
480             if (auto extant{init.image.AsConstantPointer(componentOffset)}) {
481               initialized = !(*extant == *object->init());
482             }
483             if (initialized) {
484               init.image.AddPointer(componentOffset, *object->init());
485             }
486           } else { // data, not pointer
487             if (auto dyType{evaluate::DynamicType::From(component)}) {
488               if (auto extents{evaluate::GetConstantExtents(
489                       foldingContext, component)}) {
490                 if (auto extant{init.image.AsConstant(
491                         foldingContext, *dyType, *extents, componentOffset)}) {
492                   initialized = !(*extant == *object->init());
493                 }
494               }
495             }
496             if (initialized) {
497               init.image.Add(componentOffset, component.size(), *object->init(),
498                   foldingContext);
499             }
500           }
501         } else if (const DeclTypeSpec * type{component.GetType()}) {
502           if (const DerivedTypeSpec * componentDerived{type->AsDerived()}) {
503             PopulateWithComponentDefaults(init, componentOffset,
504                 *componentDerived, foldingContext, component);
505           }
506         }
507         if (initialized) {
508           init.initializedRanges.emplace_back(
509               componentOffset, component.size());
510         }
511       }
512     } else if (const auto *proc{component.detailsIf<ProcEntityDetails>()}) {
513       if (proc->init() && *proc->init()) {
514         SomeExpr procPtrInit{evaluate::ProcedureDesignator{**proc->init()}};
515         auto extant{init.image.AsConstantPointer(componentOffset)};
516         if (!extant || !(*extant == procPtrInit)) {
517           init.initializedRanges.emplace_back(
518               componentOffset, component.size());
519           init.image.AddPointer(componentOffset, std::move(procPtrInit));
520         }
521       }
522     }
523   }
524 }
525 
526 static bool CheckForOverlappingInitialization(
527     const std::list<SymbolRef> &symbols,
528     SymbolDataInitialization &initialization,
529     evaluate::ExpressionAnalyzer &exprAnalyzer, const std::string &what) {
530   bool result{true};
531   auto &context{exprAnalyzer.GetFoldingContext()};
532   initialization.initializedRanges.sort();
533   ConstantSubscript next{0};
534   for (const auto &range : initialization.initializedRanges) {
535     if (range.start() < next) {
536       result = false; // error: overlap
537       bool hit{false};
538       for (const Symbol &symbol : symbols) {
539         auto offset{range.start() -
540             static_cast<ConstantSubscript>(
541                 symbol.offset() - symbols.front()->offset())};
542         if (offset >= 0) {
543           if (auto badDesignator{evaluate::OffsetToDesignator(
544                   context, symbol, offset, range.size())}) {
545             hit = true;
546             exprAnalyzer.Say(symbol.name(),
547                 "%s affect '%s' more than once"_err_en_US, what,
548                 badDesignator->AsFortran());
549           }
550         }
551       }
552       CHECK(hit);
553     }
554     next = range.start() + range.size();
555     CHECK(next <= static_cast<ConstantSubscript>(initialization.image.size()));
556   }
557   return result;
558 }
559 
560 static void IncorporateExplicitInitialization(
561     SymbolDataInitialization &combined, DataInitializations &inits,
562     const Symbol &symbol, ConstantSubscript firstOffset,
563     evaluate::FoldingContext &foldingContext) {
564   auto iter{inits.find(&symbol)};
565   const auto offset{symbol.offset() - firstOffset};
566   if (iter != inits.end()) { // DATA statement initialization
567     for (const auto &range : iter->second.initializedRanges) {
568       auto at{offset + range.start()};
569       combined.initializedRanges.emplace_back(at, range.size());
570       combined.image.Incorporate(
571           at, iter->second.image, range.start(), range.size());
572     }
573     if (removeOriginalInits) {
574       inits.erase(iter);
575     }
576   } else { // Declaration initialization
577     Symbol &mutableSymbol{const_cast<Symbol &>(symbol)};
578     if (IsPointer(mutableSymbol)) {
579       if (auto *object{mutableSymbol.detailsIf<ObjectEntityDetails>()}) {
580         if (object->init()) {
581           combined.initializedRanges.emplace_back(offset, mutableSymbol.size());
582           combined.image.AddPointer(offset, *object->init());
583           if (removeOriginalInits) {
584             object->init().reset();
585           }
586         }
587       } else if (auto *proc{mutableSymbol.detailsIf<ProcEntityDetails>()}) {
588         if (proc->init() && *proc->init()) {
589           combined.initializedRanges.emplace_back(offset, mutableSymbol.size());
590           combined.image.AddPointer(
591               offset, SomeExpr{evaluate::ProcedureDesignator{**proc->init()}});
592           if (removeOriginalInits) {
593             proc->init().reset();
594           }
595         }
596       }
597     } else if (auto *object{mutableSymbol.detailsIf<ObjectEntityDetails>()}) {
598       if (!IsNamedConstant(mutableSymbol) && object->init()) {
599         combined.initializedRanges.emplace_back(offset, mutableSymbol.size());
600         combined.image.Add(
601             offset, mutableSymbol.size(), *object->init(), foldingContext);
602         if (removeOriginalInits) {
603           object->init().reset();
604         }
605       }
606     }
607   }
608 }
609 
610 // Finds the size of the smallest element type in a list of
611 // storage-associated objects.
612 static std::size_t ComputeMinElementBytes(
613     const std::list<SymbolRef> &associated,
614     evaluate::FoldingContext &foldingContext) {
615   std::size_t minElementBytes{1};
616   const Symbol &first{*associated.front()};
617   for (const Symbol &s : associated) {
618     if (auto dyType{evaluate::DynamicType::From(s)}) {
619       auto size{static_cast<std::size_t>(
620           evaluate::ToInt64(dyType->MeasureSizeInBytes(foldingContext, true))
621               .value_or(1))};
622       if (std::size_t alignment{dyType->GetAlignment(foldingContext)}) {
623         size = ((size + alignment - 1) / alignment) * alignment;
624       }
625       if (&s == &first) {
626         minElementBytes = size;
627       } else {
628         minElementBytes = std::min(minElementBytes, size);
629       }
630     } else {
631       minElementBytes = 1;
632     }
633   }
634   return minElementBytes;
635 }
636 
637 // Checks for overlapping initialization errors in a list of
638 // storage-associated objects.  Default component initializations
639 // are allowed to be overridden by explicit initializations.
640 // If the objects are static, save the combined initializer as
641 // a compiler-created object that covers all of them.
642 static bool CombineEquivalencedInitialization(
643     const std::list<SymbolRef> &associated,
644     evaluate::ExpressionAnalyzer &exprAnalyzer, DataInitializations &inits) {
645   // Compute the minimum common granularity and total size
646   const Symbol &first{*associated.front()};
647   std::size_t maxLimit{0};
648   for (const Symbol &s : associated) {
649     CHECK(s.offset() >= first.offset());
650     auto limit{s.offset() + s.size()};
651     if (limit > maxLimit) {
652       maxLimit = limit;
653     }
654   }
655   auto bytes{static_cast<common::ConstantSubscript>(maxLimit - first.offset())};
656   Scope &scope{const_cast<Scope &>(first.owner())};
657   // Combine the initializations of the associated objects.
658   // Apply all default initializations first.
659   SymbolDataInitialization combined{static_cast<std::size_t>(bytes)};
660   auto &foldingContext{exprAnalyzer.GetFoldingContext()};
661   for (const Symbol &s : associated) {
662     if (!IsNamedConstant(s)) {
663       if (const auto *derived{HasDefaultInitialization(s)}) {
664         PopulateWithComponentDefaults(
665             combined, s.offset() - first.offset(), *derived, foldingContext, s);
666       }
667     }
668   }
669   if (!CheckForOverlappingInitialization(associated, combined, exprAnalyzer,
670           "Distinct default component initializations of equivalenced objects"s)) {
671     return false;
672   }
673   // Don't complain about overlap between explicit initializations and
674   // default initializations.
675   combined.initializedRanges.clear();
676   // Now overlay all explicit initializations from DATA statements and
677   // from initializers in declarations.
678   for (const Symbol &symbol : associated) {
679     IncorporateExplicitInitialization(
680         combined, inits, symbol, first.offset(), foldingContext);
681   }
682   if (!CheckForOverlappingInitialization(associated, combined, exprAnalyzer,
683           "Explicit initializations of equivalenced objects"s)) {
684     return false;
685   }
686   // If the items are in static storage, save the final initialization.
687   if (std::find_if(associated.begin(), associated.end(),
688           [](SymbolRef ref) { return IsSaved(*ref); }) != associated.end()) {
689     // Create a compiler array temp that overlaps all the items.
690     SourceName name{exprAnalyzer.context().GetTempName(scope)};
691     auto emplaced{
692         scope.try_emplace(name, Attrs{Attr::SAVE}, ObjectEntityDetails{})};
693     CHECK(emplaced.second);
694     Symbol &combinedSymbol{*emplaced.first->second};
695     combinedSymbol.set(Symbol::Flag::CompilerCreated);
696     inits.emplace(&combinedSymbol, std::move(combined));
697     auto &details{combinedSymbol.get<ObjectEntityDetails>()};
698     combinedSymbol.set_offset(first.offset());
699     combinedSymbol.set_size(bytes);
700     std::size_t minElementBytes{
701         ComputeMinElementBytes(associated, foldingContext)};
702     if (!evaluate::IsValidKindOfIntrinsicType(
703             TypeCategory::Integer, minElementBytes) ||
704         (bytes % minElementBytes) != 0) {
705       minElementBytes = 1;
706     }
707     const DeclTypeSpec &typeSpec{scope.MakeNumericType(
708         TypeCategory::Integer, KindExpr{minElementBytes})};
709     details.set_type(typeSpec);
710     ArraySpec arraySpec;
711     arraySpec.emplace_back(ShapeSpec::MakeExplicit(Bound{
712         bytes / static_cast<common::ConstantSubscript>(minElementBytes)}));
713     details.set_shape(arraySpec);
714     if (const auto *commonBlock{FindCommonBlockContaining(first)}) {
715       details.set_commonBlock(*commonBlock);
716     }
717     // Add an EQUIVALENCE set to the scope so that the new object appears in
718     // the results of GetStorageAssociations().
719     auto &newSet{scope.equivalenceSets().emplace_back()};
720     newSet.emplace_back(combinedSymbol);
721     newSet.emplace_back(const_cast<Symbol &>(first));
722   }
723   return true;
724 }
725 
726 // When a statically-allocated derived type variable has no explicit
727 // initialization, but its type has at least one nonallocatable ultimate
728 // component with default initialization, make its initialization explicit.
729 [[maybe_unused]] static void MakeDefaultInitializationExplicit(
730     const Scope &scope, const std::list<std::list<SymbolRef>> &associations,
731     evaluate::FoldingContext &foldingContext, DataInitializations &inits) {
732   UnorderedSymbolSet equivalenced;
733   for (const std::list<SymbolRef> &association : associations) {
734     for (const Symbol &symbol : association) {
735       equivalenced.emplace(symbol);
736     }
737   }
738   for (const auto &pair : scope) {
739     const Symbol &symbol{*pair.second};
740     if (!symbol.test(Symbol::Flag::InDataStmt) &&
741         !HasDeclarationInitializer(symbol) && IsSaved(symbol) &&
742         equivalenced.find(symbol) == equivalenced.end()) {
743       // Static object, no local storage association, no explicit initialization
744       if (const DerivedTypeSpec * derived{HasDefaultInitialization(symbol)}) {
745         auto newInitIter{inits.emplace(&symbol, symbol.size())};
746         CHECK(newInitIter.second);
747         auto &newInit{newInitIter.first->second};
748         PopulateWithComponentDefaults(
749             newInit, 0, *derived, foldingContext, symbol);
750       }
751     }
752   }
753 }
754 
755 // Traverses the Scopes to:
756 // 1) combine initialization of equivalenced objects, &
757 // 2) optionally make initialization explicit for otherwise uninitialized static
758 //    objects of derived types with default component initialization
759 // Returns false on error.
760 static bool ProcessScopes(const Scope &scope,
761     evaluate::ExpressionAnalyzer &exprAnalyzer, DataInitializations &inits) {
762   bool result{true}; // no error
763   switch (scope.kind()) {
764   case Scope::Kind::Global:
765   case Scope::Kind::Module:
766   case Scope::Kind::MainProgram:
767   case Scope::Kind::Subprogram:
768   case Scope::Kind::BlockData:
769   case Scope::Kind::Block: {
770     std::list<std::list<SymbolRef>> associations{GetStorageAssociations(scope)};
771     for (const std::list<SymbolRef> &associated : associations) {
772       if (std::find_if(associated.begin(), associated.end(), [](SymbolRef ref) {
773             return IsInitialized(*ref);
774           }) != associated.end()) {
775         result &=
776             CombineEquivalencedInitialization(associated, exprAnalyzer, inits);
777       }
778     }
779     if constexpr (makeDefaultInitializationExplicit) {
780       MakeDefaultInitializationExplicit(
781           scope, associations, exprAnalyzer.GetFoldingContext(), inits);
782     }
783     for (const Scope &child : scope.children()) {
784       result &= ProcessScopes(child, exprAnalyzer, inits);
785     }
786   } break;
787   default:;
788   }
789   return result;
790 }
791 
792 // Converts the static initialization image for a single symbol with
793 // one or more DATA statement appearances.
794 void ConstructInitializer(const Symbol &symbol,
795     SymbolDataInitialization &initialization,
796     evaluate::ExpressionAnalyzer &exprAnalyzer) {
797   std::list<SymbolRef> symbols{symbol};
798   CheckForOverlappingInitialization(
799       symbols, initialization, exprAnalyzer, "DATA statement initializations"s);
800   auto &context{exprAnalyzer.GetFoldingContext()};
801   if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) {
802     CHECK(IsProcedurePointer(symbol));
803     auto &mutableProc{const_cast<ProcEntityDetails &>(*proc)};
804     if (MaybeExpr expr{initialization.image.AsConstantPointer()}) {
805       if (const auto *procDesignator{
806               std::get_if<evaluate::ProcedureDesignator>(&expr->u)}) {
807         CHECK(!procDesignator->GetComponent());
808         mutableProc.set_init(DEREF(procDesignator->GetSymbol()));
809       } else {
810         CHECK(evaluate::IsNullPointer(*expr));
811         mutableProc.set_init(nullptr);
812       }
813     } else {
814       mutableProc.set_init(nullptr);
815     }
816   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
817     auto &mutableObject{const_cast<ObjectEntityDetails &>(*object)};
818     if (IsPointer(symbol)) {
819       if (auto ptr{initialization.image.AsConstantPointer()}) {
820         mutableObject.set_init(*ptr);
821       } else {
822         mutableObject.set_init(SomeExpr{evaluate::NullPointer{}});
823       }
824     } else if (auto symbolType{evaluate::DynamicType::From(symbol)}) {
825       if (auto extents{evaluate::GetConstantExtents(context, symbol)}) {
826         mutableObject.set_init(
827             initialization.image.AsConstant(context, *symbolType, *extents));
828       } else {
829         exprAnalyzer.Say(symbol.name(),
830             "internal: unknown shape for '%s' while constructing initializer from DATA"_err_en_US,
831             symbol.name());
832         return;
833       }
834     } else {
835       exprAnalyzer.Say(symbol.name(),
836           "internal: no type for '%s' while constructing initializer from DATA"_err_en_US,
837           symbol.name());
838       return;
839     }
840     if (!object->init()) {
841       exprAnalyzer.Say(symbol.name(),
842           "internal: could not construct an initializer from DATA statements for '%s'"_err_en_US,
843           symbol.name());
844     }
845   } else {
846     CHECK(exprAnalyzer.context().AnyFatalError());
847   }
848 }
849 
850 void ConvertToInitializers(
851     DataInitializations &inits, evaluate::ExpressionAnalyzer &exprAnalyzer) {
852   if (ProcessScopes(
853           exprAnalyzer.context().globalScope(), exprAnalyzer, inits)) {
854     for (auto &[symbolPtr, initialization] : inits) {
855       ConstructInitializer(*symbolPtr, initialization, exprAnalyzer);
856     }
857   }
858 }
859 } // namespace Fortran::semantics
860