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