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