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           if (lastSymbol->has<ProcEntityDetails>()) {
379             GetImage().AddPointer(offsetSymbol.offset(), *expr);
380             return true;
381           } else {
382             evaluate::AttachDeclaration(
383                 exprAnalyzer_.context().Say(
384                     "DATA statement initialization of procedure pointer '%s' declared using a POINTER statement and an INTERFACE instead of a PROCEDURE statement"_todo_en_US,
385                     DescribeElement()),
386                 *lastSymbol);
387           }
388         }
389       } else {
390         exprAnalyzer_.Say(
391             "Data object '%s' may not be used to initialize '%s', which is a procedure pointer"_err_en_US,
392             expr->AsFortran(), DescribeElement());
393       }
394     } else if (evaluate::IsProcedure(*expr)) {
395       exprAnalyzer_.Say(
396           "Procedure '%s' may not be used to initialize '%s', which is not a procedure pointer"_err_en_US,
397           expr->AsFortran(), DescribeElement());
398     } else if (CheckInitialTarget(context, designator, *expr)) {
399       GetImage().AddPointer(offsetSymbol.offset(), *expr);
400       return true;
401     }
402   } else if (evaluate::IsNullPointer(*expr)) {
403     exprAnalyzer_.Say("Initializer for '%s' must not be a pointer"_err_en_US,
404         DescribeElement());
405   } else if (evaluate::IsProcedure(*expr)) {
406     exprAnalyzer_.Say("Initializer for '%s' must not be a procedure"_err_en_US,
407         DescribeElement());
408   } else if (auto designatorType{designator.GetType()}) {
409     if (expr->Rank() > 0) {
410       // Because initial-data-target is ambiguous with scalar-constant and
411       // scalar-constant-subobject at parse time, enforcement of scalar-*
412       // must be deferred to here.
413       exprAnalyzer_.Say(
414           "DATA statement value initializes '%s' with an array"_err_en_US,
415           DescribeElement());
416     } else if (auto converted{ConvertElement(*expr, *designatorType)}) {
417       // value non-pointer initialization
418       if (IsBOZLiteral(*expr) &&
419           designatorType->category() != TypeCategory::Integer) { // 8.6.7(11)
420         exprAnalyzer_.Say(
421             "BOZ literal should appear in a DATA statement only as a value for an integer object, but '%s' is '%s'"_port_en_US,
422             DescribeElement(), designatorType->AsFortran());
423       } else if (converted->second) {
424         exprAnalyzer_.context().Say(
425             "DATA statement value initializes '%s' of type '%s' with CHARACTER"_port_en_US,
426             DescribeElement(), designatorType->AsFortran());
427       }
428       auto folded{evaluate::Fold(context, std::move(converted->first))};
429       switch (GetImage().Add(
430           offsetSymbol.offset(), offsetSymbol.size(), folded, context)) {
431       case evaluate::InitialImage::Ok:
432         return true;
433       case evaluate::InitialImage::NotAConstant:
434         exprAnalyzer_.Say(
435             "DATA statement value '%s' for '%s' is not a constant"_err_en_US,
436             folded.AsFortran(), DescribeElement());
437         break;
438       case evaluate::InitialImage::OutOfRange:
439         OutOfRangeError();
440         break;
441       default:
442         CHECK(exprAnalyzer_.context().AnyFatalError());
443         break;
444       }
445     } else {
446       exprAnalyzer_.context().Say(
447           "DATA statement value could not be converted to the type '%s' of the object '%s'"_err_en_US,
448           designatorType->AsFortran(), DescribeElement());
449     }
450   } else {
451     CHECK(exprAnalyzer_.context().AnyFatalError());
452   }
453   return false;
454 }
455 
456 void AccumulateDataInitializations(DataInitializations &inits,
457     evaluate::ExpressionAnalyzer &exprAnalyzer,
458     const parser::DataStmtSet &set) {
459   DataInitializationCompiler scanner{
460       inits, exprAnalyzer, std::get<std::list<parser::DataStmtValue>>(set.t)};
461   for (const auto &object :
462       std::get<std::list<parser::DataStmtObject>>(set.t)) {
463     if (!scanner.Scan(object)) {
464       return;
465     }
466   }
467   if (scanner.HasSurplusValues()) {
468     exprAnalyzer.context().Say(
469         "DATA statement set has more values than objects"_err_en_US);
470   }
471 }
472 
473 void AccumulateDataInitializations(DataInitializations &inits,
474     evaluate::ExpressionAnalyzer &exprAnalyzer, const Symbol &symbol,
475     const std::list<common::Indirection<parser::DataStmtValue>> &list) {
476   DataInitializationCompiler<common::Indirection<parser::DataStmtValue>>
477       scanner{inits, exprAnalyzer, list};
478   if (scanner.Scan(symbol) && scanner.HasSurplusValues()) {
479     exprAnalyzer.context().Say(
480         "DATA statement set has more values than objects"_err_en_US);
481   }
482 }
483 
484 // Looks for default derived type component initialization -- but
485 // *not* allocatables.
486 static const DerivedTypeSpec *HasDefaultInitialization(const Symbol &symbol) {
487   if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
488     if (object->init().has_value()) {
489       return nullptr; // init is explicit, not default
490     } else if (!object->isDummy() && object->type()) {
491       if (const DerivedTypeSpec * derived{object->type()->AsDerived()}) {
492         DirectComponentIterator directs{*derived};
493         if (std::find_if(
494                 directs.begin(), directs.end(), [](const Symbol &component) {
495                   return !IsAllocatable(component) &&
496                       HasDeclarationInitializer(component);
497                 })) {
498           return derived;
499         }
500       }
501     }
502   }
503   return nullptr;
504 }
505 
506 // PopulateWithComponentDefaults() adds initializations to an instance
507 // of SymbolDataInitialization containing all of the default component
508 // initializers
509 
510 static void PopulateWithComponentDefaults(SymbolDataInitialization &init,
511     std::size_t offset, const DerivedTypeSpec &derived,
512     evaluate::FoldingContext &foldingContext);
513 
514 static void PopulateWithComponentDefaults(SymbolDataInitialization &init,
515     std::size_t offset, const DerivedTypeSpec &derived,
516     evaluate::FoldingContext &foldingContext, const Symbol &symbol) {
517   if (auto extents{evaluate::GetConstantExtents(foldingContext, symbol)}) {
518     const Scope &scope{derived.scope() ? *derived.scope()
519                                        : DEREF(derived.typeSymbol().scope())};
520     std::size_t stride{scope.size()};
521     if (std::size_t alignment{scope.alignment().value_or(0)}) {
522       stride = ((stride + alignment - 1) / alignment) * alignment;
523     }
524     for (auto elements{evaluate::GetSize(*extents)}; elements-- > 0;
525          offset += stride) {
526       PopulateWithComponentDefaults(init, offset, derived, foldingContext);
527     }
528   }
529 }
530 
531 // F'2018 19.5.3(10) allows storage-associated default component initialization
532 // when the values are identical.
533 static void PopulateWithComponentDefaults(SymbolDataInitialization &init,
534     std::size_t offset, const DerivedTypeSpec &derived,
535     evaluate::FoldingContext &foldingContext) {
536   const Scope &scope{
537       derived.scope() ? *derived.scope() : DEREF(derived.typeSymbol().scope())};
538   for (const auto &pair : scope) {
539     const Symbol &component{*pair.second};
540     std::size_t componentOffset{offset + component.offset()};
541     if (const auto *object{component.detailsIf<ObjectEntityDetails>()}) {
542       if (!IsAllocatable(component) && !IsAutomatic(component)) {
543         bool initialized{false};
544         if (object->init()) {
545           initialized = true;
546           if (IsPointer(component)) {
547             if (auto extant{init.image.AsConstantPointer(componentOffset)}) {
548               initialized = !(*extant == *object->init());
549             }
550             if (initialized) {
551               init.image.AddPointer(componentOffset, *object->init());
552             }
553           } else { // data, not pointer
554             if (auto dyType{evaluate::DynamicType::From(component)}) {
555               if (auto extents{evaluate::GetConstantExtents(
556                       foldingContext, component)}) {
557                 if (auto extant{init.image.AsConstant(
558                         foldingContext, *dyType, *extents, componentOffset)}) {
559                   initialized = !(*extant == *object->init());
560                 }
561               }
562             }
563             if (initialized) {
564               init.image.Add(componentOffset, component.size(), *object->init(),
565                   foldingContext);
566             }
567           }
568         } else if (const DeclTypeSpec * type{component.GetType()}) {
569           if (const DerivedTypeSpec * componentDerived{type->AsDerived()}) {
570             PopulateWithComponentDefaults(init, componentOffset,
571                 *componentDerived, foldingContext, component);
572           }
573         }
574         if (initialized) {
575           init.initializedRanges.emplace_back(
576               componentOffset, component.size());
577         }
578       }
579     } else if (const auto *proc{component.detailsIf<ProcEntityDetails>()}) {
580       if (proc->init() && *proc->init()) {
581         SomeExpr procPtrInit{evaluate::ProcedureDesignator{**proc->init()}};
582         auto extant{init.image.AsConstantPointer(componentOffset)};
583         if (!extant || !(*extant == procPtrInit)) {
584           init.initializedRanges.emplace_back(
585               componentOffset, component.size());
586           init.image.AddPointer(componentOffset, std::move(procPtrInit));
587         }
588       }
589     }
590   }
591 }
592 
593 static bool CheckForOverlappingInitialization(
594     const std::list<SymbolRef> &symbols,
595     SymbolDataInitialization &initialization,
596     evaluate::ExpressionAnalyzer &exprAnalyzer, const std::string &what) {
597   bool result{true};
598   auto &context{exprAnalyzer.GetFoldingContext()};
599   initialization.initializedRanges.sort();
600   ConstantSubscript next{0};
601   for (const auto &range : initialization.initializedRanges) {
602     if (range.start() < next) {
603       result = false; // error: overlap
604       bool hit{false};
605       for (const Symbol &symbol : symbols) {
606         auto offset{range.start() -
607             static_cast<ConstantSubscript>(
608                 symbol.offset() - symbols.front()->offset())};
609         if (offset >= 0) {
610           if (auto badDesignator{evaluate::OffsetToDesignator(
611                   context, symbol, offset, range.size())}) {
612             hit = true;
613             exprAnalyzer.Say(symbol.name(),
614                 "%s affect '%s' more than once"_err_en_US, what,
615                 badDesignator->AsFortran());
616           }
617         }
618       }
619       CHECK(hit);
620     }
621     next = range.start() + range.size();
622     CHECK(next <= static_cast<ConstantSubscript>(initialization.image.size()));
623   }
624   return result;
625 }
626 
627 static void IncorporateExplicitInitialization(
628     SymbolDataInitialization &combined, DataInitializations &inits,
629     const Symbol &symbol, ConstantSubscript firstOffset,
630     evaluate::FoldingContext &foldingContext) {
631   auto iter{inits.find(&symbol)};
632   const auto offset{symbol.offset() - firstOffset};
633   if (iter != inits.end()) { // DATA statement initialization
634     for (const auto &range : iter->second.initializedRanges) {
635       auto at{offset + range.start()};
636       combined.initializedRanges.emplace_back(at, range.size());
637       combined.image.Incorporate(
638           at, iter->second.image, range.start(), range.size());
639     }
640     if (removeOriginalInits) {
641       inits.erase(iter);
642     }
643   } else { // Declaration initialization
644     Symbol &mutableSymbol{const_cast<Symbol &>(symbol)};
645     if (IsPointer(mutableSymbol)) {
646       if (auto *object{mutableSymbol.detailsIf<ObjectEntityDetails>()}) {
647         if (object->init()) {
648           combined.initializedRanges.emplace_back(offset, mutableSymbol.size());
649           combined.image.AddPointer(offset, *object->init());
650           if (removeOriginalInits) {
651             object->init().reset();
652           }
653         }
654       } else if (auto *proc{mutableSymbol.detailsIf<ProcEntityDetails>()}) {
655         if (proc->init() && *proc->init()) {
656           combined.initializedRanges.emplace_back(offset, mutableSymbol.size());
657           combined.image.AddPointer(
658               offset, SomeExpr{evaluate::ProcedureDesignator{**proc->init()}});
659           if (removeOriginalInits) {
660             proc->init().reset();
661           }
662         }
663       }
664     } else if (auto *object{mutableSymbol.detailsIf<ObjectEntityDetails>()}) {
665       if (!IsNamedConstant(mutableSymbol) && object->init()) {
666         combined.initializedRanges.emplace_back(offset, mutableSymbol.size());
667         combined.image.Add(
668             offset, mutableSymbol.size(), *object->init(), foldingContext);
669         if (removeOriginalInits) {
670           object->init().reset();
671         }
672       }
673     }
674   }
675 }
676 
677 // Finds the size of the smallest element type in a list of
678 // storage-associated objects.
679 static std::size_t ComputeMinElementBytes(
680     const std::list<SymbolRef> &associated,
681     evaluate::FoldingContext &foldingContext) {
682   std::size_t minElementBytes{1};
683   const Symbol &first{*associated.front()};
684   for (const Symbol &s : associated) {
685     if (auto dyType{evaluate::DynamicType::From(s)}) {
686       auto size{static_cast<std::size_t>(
687           evaluate::ToInt64(dyType->MeasureSizeInBytes(foldingContext, true))
688               .value_or(1))};
689       if (std::size_t alignment{dyType->GetAlignment(foldingContext)}) {
690         size = ((size + alignment - 1) / alignment) * alignment;
691       }
692       if (&s == &first) {
693         minElementBytes = size;
694       } else {
695         minElementBytes = std::min(minElementBytes, size);
696       }
697     } else {
698       minElementBytes = 1;
699     }
700   }
701   return minElementBytes;
702 }
703 
704 // Checks for overlapping initialization errors in a list of
705 // storage-associated objects.  Default component initializations
706 // are allowed to be overridden by explicit initializations.
707 // If the objects are static, save the combined initializer as
708 // a compiler-created object that covers all of them.
709 static bool CombineEquivalencedInitialization(
710     const std::list<SymbolRef> &associated,
711     evaluate::ExpressionAnalyzer &exprAnalyzer, DataInitializations &inits) {
712   // Compute the minimum common granularity and total size
713   const Symbol &first{*associated.front()};
714   std::size_t maxLimit{0};
715   for (const Symbol &s : associated) {
716     CHECK(s.offset() >= first.offset());
717     auto limit{s.offset() + s.size()};
718     if (limit > maxLimit) {
719       maxLimit = limit;
720     }
721   }
722   auto bytes{static_cast<common::ConstantSubscript>(maxLimit - first.offset())};
723   Scope &scope{const_cast<Scope &>(first.owner())};
724   // Combine the initializations of the associated objects.
725   // Apply all default initializations first.
726   SymbolDataInitialization combined{static_cast<std::size_t>(bytes)};
727   auto &foldingContext{exprAnalyzer.GetFoldingContext()};
728   for (const Symbol &s : associated) {
729     if (!IsNamedConstant(s)) {
730       if (const auto *derived{HasDefaultInitialization(s)}) {
731         PopulateWithComponentDefaults(
732             combined, s.offset() - first.offset(), *derived, foldingContext, s);
733       }
734     }
735   }
736   if (!CheckForOverlappingInitialization(associated, combined, exprAnalyzer,
737           "Distinct default component initializations of equivalenced objects"s)) {
738     return false;
739   }
740   // Don't complain about overlap between explicit initializations and
741   // default initializations.
742   combined.initializedRanges.clear();
743   // Now overlay all explicit initializations from DATA statements and
744   // from initializers in declarations.
745   for (const Symbol &symbol : associated) {
746     IncorporateExplicitInitialization(
747         combined, inits, symbol, first.offset(), foldingContext);
748   }
749   if (!CheckForOverlappingInitialization(associated, combined, exprAnalyzer,
750           "Explicit initializations of equivalenced objects"s)) {
751     return false;
752   }
753   // If the items are in static storage, save the final initialization.
754   if (std::find_if(associated.begin(), associated.end(),
755           [](SymbolRef ref) { return IsSaved(*ref); }) != associated.end()) {
756     // Create a compiler array temp that overlaps all the items.
757     SourceName name{exprAnalyzer.context().GetTempName(scope)};
758     auto emplaced{
759         scope.try_emplace(name, Attrs{Attr::SAVE}, ObjectEntityDetails{})};
760     CHECK(emplaced.second);
761     Symbol &combinedSymbol{*emplaced.first->second};
762     combinedSymbol.set(Symbol::Flag::CompilerCreated);
763     inits.emplace(&combinedSymbol, std::move(combined));
764     auto &details{combinedSymbol.get<ObjectEntityDetails>()};
765     combinedSymbol.set_offset(first.offset());
766     combinedSymbol.set_size(bytes);
767     std::size_t minElementBytes{
768         ComputeMinElementBytes(associated, foldingContext)};
769     if (!evaluate::IsValidKindOfIntrinsicType(
770             TypeCategory::Integer, minElementBytes) ||
771         (bytes % minElementBytes) != 0) {
772       minElementBytes = 1;
773     }
774     const DeclTypeSpec &typeSpec{scope.MakeNumericType(
775         TypeCategory::Integer, KindExpr{minElementBytes})};
776     details.set_type(typeSpec);
777     ArraySpec arraySpec;
778     arraySpec.emplace_back(ShapeSpec::MakeExplicit(Bound{
779         bytes / static_cast<common::ConstantSubscript>(minElementBytes)}));
780     details.set_shape(arraySpec);
781     if (const auto *commonBlock{FindCommonBlockContaining(first)}) {
782       details.set_commonBlock(*commonBlock);
783     }
784     // Add an EQUIVALENCE set to the scope so that the new object appears in
785     // the results of GetStorageAssociations().
786     auto &newSet{scope.equivalenceSets().emplace_back()};
787     newSet.emplace_back(combinedSymbol);
788     newSet.emplace_back(const_cast<Symbol &>(first));
789   }
790   return true;
791 }
792 
793 // When a statically-allocated derived type variable has no explicit
794 // initialization, but its type has at least one nonallocatable ultimate
795 // component with default initialization, make its initialization explicit.
796 [[maybe_unused]] static void MakeDefaultInitializationExplicit(
797     const Scope &scope, const std::list<std::list<SymbolRef>> &associations,
798     evaluate::FoldingContext &foldingContext, DataInitializations &inits) {
799   UnorderedSymbolSet equivalenced;
800   for (const std::list<SymbolRef> &association : associations) {
801     for (const Symbol &symbol : association) {
802       equivalenced.emplace(symbol);
803     }
804   }
805   for (const auto &pair : scope) {
806     const Symbol &symbol{*pair.second};
807     if (!symbol.test(Symbol::Flag::InDataStmt) &&
808         !HasDeclarationInitializer(symbol) && IsSaved(symbol) &&
809         equivalenced.find(symbol) == equivalenced.end()) {
810       // Static object, no local storage association, no explicit initialization
811       if (const DerivedTypeSpec * derived{HasDefaultInitialization(symbol)}) {
812         auto newInitIter{inits.emplace(&symbol, symbol.size())};
813         CHECK(newInitIter.second);
814         auto &newInit{newInitIter.first->second};
815         PopulateWithComponentDefaults(
816             newInit, 0, *derived, foldingContext, symbol);
817       }
818     }
819   }
820 }
821 
822 // Traverses the Scopes to:
823 // 1) combine initialization of equivalenced objects, &
824 // 2) optionally make initialization explicit for otherwise uninitialized static
825 //    objects of derived types with default component initialization
826 // Returns false on error.
827 static bool ProcessScopes(const Scope &scope,
828     evaluate::ExpressionAnalyzer &exprAnalyzer, DataInitializations &inits) {
829   bool result{true}; // no error
830   switch (scope.kind()) {
831   case Scope::Kind::Global:
832   case Scope::Kind::Module:
833   case Scope::Kind::MainProgram:
834   case Scope::Kind::Subprogram:
835   case Scope::Kind::BlockData:
836   case Scope::Kind::Block: {
837     std::list<std::list<SymbolRef>> associations{GetStorageAssociations(scope)};
838     for (const std::list<SymbolRef> &associated : associations) {
839       if (std::find_if(associated.begin(), associated.end(), [](SymbolRef ref) {
840             return IsInitialized(*ref);
841           }) != associated.end()) {
842         result &=
843             CombineEquivalencedInitialization(associated, exprAnalyzer, inits);
844       }
845     }
846     if constexpr (makeDefaultInitializationExplicit) {
847       MakeDefaultInitializationExplicit(
848           scope, associations, exprAnalyzer.GetFoldingContext(), inits);
849     }
850     for (const Scope &child : scope.children()) {
851       result &= ProcessScopes(child, exprAnalyzer, inits);
852     }
853   } break;
854   default:;
855   }
856   return result;
857 }
858 
859 // Converts the static initialization image for a single symbol with
860 // one or more DATA statement appearances.
861 void ConstructInitializer(const Symbol &symbol,
862     SymbolDataInitialization &initialization,
863     evaluate::ExpressionAnalyzer &exprAnalyzer) {
864   std::list<SymbolRef> symbols{symbol};
865   CheckForOverlappingInitialization(
866       symbols, initialization, exprAnalyzer, "DATA statement initializations"s);
867   auto &context{exprAnalyzer.GetFoldingContext()};
868   if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) {
869     CHECK(IsProcedurePointer(symbol));
870     auto &mutableProc{const_cast<ProcEntityDetails &>(*proc)};
871     if (MaybeExpr expr{initialization.image.AsConstantPointer()}) {
872       if (const auto *procDesignator{
873               std::get_if<evaluate::ProcedureDesignator>(&expr->u)}) {
874         CHECK(!procDesignator->GetComponent());
875         mutableProc.set_init(DEREF(procDesignator->GetSymbol()));
876       } else {
877         CHECK(evaluate::IsNullPointer(*expr));
878         mutableProc.set_init(nullptr);
879       }
880     } else {
881       mutableProc.set_init(nullptr);
882     }
883   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
884     auto &mutableObject{const_cast<ObjectEntityDetails &>(*object)};
885     if (IsPointer(symbol)) {
886       if (auto ptr{initialization.image.AsConstantPointer()}) {
887         mutableObject.set_init(*ptr);
888       } else {
889         mutableObject.set_init(SomeExpr{evaluate::NullPointer{}});
890       }
891     } else if (auto symbolType{evaluate::DynamicType::From(symbol)}) {
892       if (auto extents{evaluate::GetConstantExtents(context, symbol)}) {
893         mutableObject.set_init(
894             initialization.image.AsConstant(context, *symbolType, *extents));
895       } else {
896         exprAnalyzer.Say(symbol.name(),
897             "internal: unknown shape for '%s' while constructing initializer from DATA"_err_en_US,
898             symbol.name());
899         return;
900       }
901     } else {
902       exprAnalyzer.Say(symbol.name(),
903           "internal: no type for '%s' while constructing initializer from DATA"_err_en_US,
904           symbol.name());
905       return;
906     }
907     if (!object->init()) {
908       exprAnalyzer.Say(symbol.name(),
909           "internal: could not construct an initializer from DATA statements for '%s'"_err_en_US,
910           symbol.name());
911     }
912   } else {
913     CHECK(exprAnalyzer.context().AnyFatalError());
914   }
915 }
916 
917 void ConvertToInitializers(
918     DataInitializations &inits, evaluate::ExpressionAnalyzer &exprAnalyzer) {
919   if (ProcessScopes(
920           exprAnalyzer.context().globalScope(), exprAnalyzer, inits)) {
921     for (auto &[symbolPtr, initialization] : inits) {
922       ConstructInitializer(*symbolPtr, initialization, exprAnalyzer);
923     }
924   }
925 }
926 } // namespace Fortran::semantics
927