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