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