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 namespace Fortran::semantics {
24 
25 // Steps through a list of values in a DATA statement set; implements
26 // repetition.
27 class ValueListIterator {
28 public:
29   explicit ValueListIterator(const parser::DataStmtSet &set)
30       : end_{std::get<std::list<parser::DataStmtValue>>(set.t).end()},
31         at_{std::get<std::list<parser::DataStmtValue>>(set.t).begin()} {
32     SetRepetitionCount();
33   }
34   bool hasFatalError() const { return hasFatalError_; }
35   bool IsAtEnd() const { return at_ == end_; }
36   const SomeExpr *operator*() const { return GetExpr(GetConstant()); }
37   parser::CharBlock LocateSource() const { return GetConstant().source; }
38   ValueListIterator &operator++() {
39     if (repetitionsRemaining_ > 0) {
40       --repetitionsRemaining_;
41     } else if (at_ != end_) {
42       ++at_;
43       SetRepetitionCount();
44     }
45     return *this;
46   }
47 
48 private:
49   using listIterator = std::list<parser::DataStmtValue>::const_iterator;
50   void SetRepetitionCount();
51   const parser::DataStmtConstant &GetConstant() const {
52     return std::get<parser::DataStmtConstant>(at_->t);
53   }
54 
55   listIterator end_;
56   listIterator at_;
57   ConstantSubscript repetitionsRemaining_{0};
58   bool hasFatalError_{false};
59 };
60 
61 void ValueListIterator::SetRepetitionCount() {
62   for (repetitionsRemaining_ = 1; at_ != end_; ++at_) {
63     if (at_->repetitions < 0) {
64       hasFatalError_ = true;
65     }
66     if (at_->repetitions > 0) {
67       repetitionsRemaining_ = at_->repetitions - 1;
68       return;
69     }
70   }
71   repetitionsRemaining_ = 0;
72 }
73 
74 // Collects all of the elemental initializations from DATA statements
75 // into a single image for each symbol that appears in any DATA.
76 // Expands the implied DO loops and array references.
77 // Applies checks that validate each distinct elemental initialization
78 // of the variables in a data-stmt-set, as well as those that apply
79 // to the corresponding values being use to initialize each element.
80 class DataInitializationCompiler {
81 public:
82   DataInitializationCompiler(DataInitializations &inits,
83       evaluate::ExpressionAnalyzer &a, const parser::DataStmtSet &set)
84       : inits_{inits}, exprAnalyzer_{a}, values_{set} {}
85   const DataInitializations &inits() const { return inits_; }
86   bool HasSurplusValues() const { return !values_.IsAtEnd(); }
87   bool Scan(const parser::DataStmtObject &);
88 
89 private:
90   bool Scan(const parser::Variable &);
91   bool Scan(const parser::Designator &);
92   bool Scan(const parser::DataImpliedDo &);
93   bool Scan(const parser::DataIDoObject &);
94 
95   // Initializes all elements of a designator, which can be an array or section.
96   bool InitDesignator(const SomeExpr &);
97   // Initializes a single object.
98   bool InitElement(const evaluate::OffsetSymbol &, const SomeExpr &designator);
99   // If the returned flag is true, emit a warning about CHARACTER misusage.
100   std::optional<std::pair<SomeExpr, bool>> ConvertElement(
101       const SomeExpr &, const evaluate::DynamicType &);
102 
103   DataInitializations &inits_;
104   evaluate::ExpressionAnalyzer &exprAnalyzer_;
105   ValueListIterator values_;
106 };
107 
108 bool DataInitializationCompiler::Scan(const parser::DataStmtObject &object) {
109   return std::visit(
110       common::visitors{
111           [&](const common::Indirection<parser::Variable> &var) {
112             return Scan(var.value());
113           },
114           [&](const parser::DataImpliedDo &ido) { return Scan(ido); },
115       },
116       object.u);
117 }
118 
119 bool DataInitializationCompiler::Scan(const parser::Variable &var) {
120   if (const auto *expr{GetExpr(var)}) {
121     exprAnalyzer_.GetFoldingContext().messages().SetLocation(var.GetSource());
122     if (InitDesignator(*expr)) {
123       return true;
124     }
125   }
126   return false;
127 }
128 
129 bool DataInitializationCompiler::Scan(const parser::Designator &designator) {
130   if (auto expr{exprAnalyzer_.Analyze(designator)}) {
131     exprAnalyzer_.GetFoldingContext().messages().SetLocation(
132         parser::FindSourceLocation(designator));
133     if (InitDesignator(*expr)) {
134       return true;
135     }
136   }
137   return false;
138 }
139 
140 bool DataInitializationCompiler::Scan(const parser::DataImpliedDo &ido) {
141   const auto &bounds{std::get<parser::DataImpliedDo::Bounds>(ido.t)};
142   auto name{bounds.name.thing.thing};
143   const auto *lowerExpr{GetExpr(bounds.lower.thing.thing)};
144   const auto *upperExpr{GetExpr(bounds.upper.thing.thing)};
145   const auto *stepExpr{
146       bounds.step ? GetExpr(bounds.step->thing.thing) : nullptr};
147   if (lowerExpr && upperExpr) {
148     auto lower{ToInt64(*lowerExpr)};
149     auto upper{ToInt64(*upperExpr)};
150     auto step{stepExpr ? ToInt64(*stepExpr) : std::nullopt};
151     auto stepVal{step.value_or(1)};
152     if (stepVal == 0) {
153       exprAnalyzer_.Say(name.source,
154           "DATA statement implied DO loop has a step value of zero"_err_en_US);
155     } else if (lower && upper) {
156       int kind{evaluate::ResultType<evaluate::ImpliedDoIndex>::kind};
157       if (const auto dynamicType{evaluate::DynamicType::From(*name.symbol)}) {
158         if (dynamicType->category() == TypeCategory::Integer) {
159           kind = dynamicType->kind();
160         }
161       }
162       if (exprAnalyzer_.AddImpliedDo(name.source, kind)) {
163         auto &value{exprAnalyzer_.GetFoldingContext().StartImpliedDo(
164             name.source, *lower)};
165         bool result{true};
166         for (auto n{(*upper - value + stepVal) / stepVal}; n > 0;
167              --n, value += stepVal) {
168           for (const auto &object :
169               std::get<std::list<parser::DataIDoObject>>(ido.t)) {
170             if (!Scan(object)) {
171               result = false;
172               break;
173             }
174           }
175         }
176         exprAnalyzer_.GetFoldingContext().EndImpliedDo(name.source);
177         exprAnalyzer_.RemoveImpliedDo(name.source);
178         return result;
179       }
180     }
181   }
182   return false;
183 }
184 
185 bool DataInitializationCompiler::Scan(const parser::DataIDoObject &object) {
186   return std::visit(
187       common::visitors{
188           [&](const parser::Scalar<common::Indirection<parser::Designator>>
189                   &var) { return Scan(var.thing.value()); },
190           [&](const common::Indirection<parser::DataImpliedDo> &ido) {
191             return Scan(ido.value());
192           },
193       },
194       object.u);
195 }
196 
197 bool DataInitializationCompiler::InitDesignator(const SomeExpr &designator) {
198   evaluate::FoldingContext &context{exprAnalyzer_.GetFoldingContext()};
199   evaluate::DesignatorFolder folder{context};
200   while (auto offsetSymbol{folder.FoldDesignator(designator)}) {
201     if (folder.isOutOfRange()) {
202       if (auto bad{evaluate::OffsetToDesignator(context, *offsetSymbol)}) {
203         exprAnalyzer_.context().Say(
204             "DATA statement designator '%s' is out of range"_err_en_US,
205             bad->AsFortran());
206       } else {
207         exprAnalyzer_.context().Say(
208             "DATA statement designator '%s' is out of range"_err_en_US,
209             designator.AsFortran());
210       }
211       return false;
212     } else if (!InitElement(*offsetSymbol, designator)) {
213       return false;
214     } else {
215       ++values_;
216     }
217   }
218   return folder.isEmpty();
219 }
220 
221 std::optional<std::pair<SomeExpr, bool>>
222 DataInitializationCompiler::ConvertElement(
223     const SomeExpr &expr, const evaluate::DynamicType &type) {
224   if (auto converted{evaluate::ConvertToType(type, SomeExpr{expr})}) {
225     return {std::make_pair(std::move(*converted), false)};
226   }
227   if (std::optional<std::string> chValue{evaluate::GetScalarConstantValue<
228           evaluate::Type<TypeCategory::Character, 1>>(expr)}) {
229     // Allow DATA initialization with Hollerith and kind=1 CHARACTER like
230     // (most) other Fortran compilers do.  Pad on the right with spaces
231     // when short, truncate the right if long.
232     // TODO: big-endian targets
233     auto bytes{static_cast<std::size_t>(evaluate::ToInt64(
234         type.MeasureSizeInBytes(exprAnalyzer_.GetFoldingContext(), false))
235                                             .value())};
236     evaluate::BOZLiteralConstant bits{0};
237     for (std::size_t j{0}; j < bytes; ++j) {
238       char ch{j >= chValue->size() ? ' ' : chValue->at(j)};
239       evaluate::BOZLiteralConstant chBOZ{static_cast<unsigned char>(ch)};
240       bits = bits.IOR(chBOZ.SHIFTL(8 * j));
241     }
242     if (auto converted{evaluate::ConvertToType(type, SomeExpr{bits})}) {
243       return {std::make_pair(std::move(*converted), true)};
244     }
245   }
246   return std::nullopt;
247 }
248 
249 bool DataInitializationCompiler::InitElement(
250     const evaluate::OffsetSymbol &offsetSymbol, const SomeExpr &designator) {
251   const Symbol &symbol{offsetSymbol.symbol()};
252   const Symbol *lastSymbol{GetLastSymbol(designator)};
253   bool isPointer{lastSymbol && IsPointer(*lastSymbol)};
254   bool isProcPointer{lastSymbol && IsProcedurePointer(*lastSymbol)};
255   evaluate::FoldingContext &context{exprAnalyzer_.GetFoldingContext()};
256   auto restorer{context.messages().SetLocation(values_.LocateSource())};
257 
258   const auto DescribeElement{[&]() {
259     if (auto badDesignator{
260             evaluate::OffsetToDesignator(context, offsetSymbol)}) {
261       return badDesignator->AsFortran();
262     } else {
263       // Error recovery
264       std::string buf;
265       llvm::raw_string_ostream ss{buf};
266       ss << offsetSymbol.symbol().name() << " offset " << offsetSymbol.offset()
267          << " bytes for " << offsetSymbol.size() << " bytes";
268       return ss.str();
269     }
270   }};
271   const auto GetImage{[&]() -> evaluate::InitialImage & {
272     auto &symbolInit{inits_.emplace(&symbol, symbol.size()).first->second};
273     symbolInit.inits.emplace_back(offsetSymbol.offset(), offsetSymbol.size());
274     return symbolInit.image;
275   }};
276   const auto OutOfRangeError{[&]() {
277     evaluate::AttachDeclaration(
278         exprAnalyzer_.context().Say(
279             "DATA statement designator '%s' is out of range for its variable '%s'"_err_en_US,
280             DescribeElement(), symbol.name()),
281         symbol);
282   }};
283 
284   if (values_.hasFatalError()) {
285     return false;
286   } else if (values_.IsAtEnd()) {
287     exprAnalyzer_.context().Say(
288         "DATA statement set has no value for '%s'"_err_en_US,
289         DescribeElement());
290     return false;
291   } else if (static_cast<std::size_t>(
292                  offsetSymbol.offset() + offsetSymbol.size()) > symbol.size()) {
293     OutOfRangeError();
294     return false;
295   }
296 
297   const SomeExpr *expr{*values_};
298   if (!expr) {
299     CHECK(exprAnalyzer_.context().AnyFatalError());
300   } else if (isPointer) {
301     if (static_cast<std::size_t>(offsetSymbol.offset() + offsetSymbol.size()) >
302         symbol.size()) {
303       OutOfRangeError();
304     } else if (evaluate::IsNullPointer(*expr)) {
305       // nothing to do; rely on zero initialization
306       return true;
307     } else if (isProcPointer) {
308       if (evaluate::IsProcedure(*expr)) {
309         if (CheckPointerAssignment(context, designator, *expr)) {
310           GetImage().AddPointer(offsetSymbol.offset(), *expr);
311           return true;
312         }
313       } else {
314         exprAnalyzer_.Say(
315             "Data object '%s' may not be used to initialize '%s', which is a procedure pointer"_err_en_US,
316             expr->AsFortran(), DescribeElement());
317       }
318     } else if (evaluate::IsProcedure(*expr)) {
319       exprAnalyzer_.Say(
320           "Procedure '%s' may not be used to initialize '%s', which is not a procedure pointer"_err_en_US,
321           expr->AsFortran(), DescribeElement());
322     } else if (CheckInitialTarget(context, designator, *expr)) {
323       GetImage().AddPointer(offsetSymbol.offset(), *expr);
324       return true;
325     }
326   } else if (evaluate::IsNullPointer(*expr)) {
327     exprAnalyzer_.Say("Initializer for '%s' must not be a pointer"_err_en_US,
328         DescribeElement());
329   } else if (evaluate::IsProcedure(*expr)) {
330     exprAnalyzer_.Say("Initializer for '%s' must not be a procedure"_err_en_US,
331         DescribeElement());
332   } else if (auto designatorType{designator.GetType()}) {
333     if (expr->Rank() > 0) {
334       // Because initial-data-target is ambiguous with scalar-constant and
335       // scalar-constant-subobject at parse time, enforcement of scalar-*
336       // must be deferred to here.
337       exprAnalyzer_.Say(
338           "DATA statement value initializes '%s' with an array"_err_en_US,
339           DescribeElement());
340     } else if (auto converted{ConvertElement(*expr, *designatorType)}) {
341       // value non-pointer initialization
342       if (IsBOZLiteral(*expr) &&
343           designatorType->category() != TypeCategory::Integer) { // 8.6.7(11)
344         exprAnalyzer_.Say(
345             "BOZ literal should appear in a DATA statement only as a value for an integer object, but '%s' is '%s'"_en_US,
346             DescribeElement(), designatorType->AsFortran());
347       } else if (converted->second) {
348         exprAnalyzer_.context().Say(
349             "DATA statement value initializes '%s' of type '%s' with CHARACTER"_en_US,
350             DescribeElement(), designatorType->AsFortran());
351       }
352       auto folded{evaluate::Fold(context, std::move(converted->first))};
353       switch (GetImage().Add(
354           offsetSymbol.offset(), offsetSymbol.size(), folded, context)) {
355       case evaluate::InitialImage::Ok:
356         return true;
357       case evaluate::InitialImage::NotAConstant:
358         exprAnalyzer_.Say(
359             "DATA statement value '%s' for '%s' is not a constant"_err_en_US,
360             folded.AsFortran(), DescribeElement());
361         break;
362       case evaluate::InitialImage::OutOfRange:
363         OutOfRangeError();
364         break;
365       default:
366         CHECK(exprAnalyzer_.context().AnyFatalError());
367         break;
368       }
369     } else {
370       exprAnalyzer_.context().Say(
371           "DATA statement value could not be converted to the type '%s' of the object '%s'"_err_en_US,
372           designatorType->AsFortran(), DescribeElement());
373     }
374   } else {
375     CHECK(exprAnalyzer_.context().AnyFatalError());
376   }
377   return false;
378 }
379 
380 void AccumulateDataInitializations(DataInitializations &inits,
381     evaluate::ExpressionAnalyzer &exprAnalyzer,
382     const parser::DataStmtSet &set) {
383   DataInitializationCompiler scanner{inits, exprAnalyzer, set};
384   for (const auto &object :
385       std::get<std::list<parser::DataStmtObject>>(set.t)) {
386     if (!scanner.Scan(object)) {
387       return;
388     }
389   }
390   if (scanner.HasSurplusValues()) {
391     exprAnalyzer.context().Say(
392         "DATA statement set has more values than objects"_err_en_US);
393   }
394 }
395 
396 static bool CombineSomeEquivalencedInits(
397     DataInitializations &inits, evaluate::ExpressionAnalyzer &exprAnalyzer) {
398   auto end{inits.end()};
399   for (auto iter{inits.begin()}; iter != end; ++iter) {
400     const Symbol &symbol{*iter->first};
401     Scope &scope{const_cast<Scope &>(symbol.owner())};
402     if (scope.equivalenceSets().empty()) {
403       continue; // no problem to solve here
404     }
405     const auto *commonBlock{FindCommonBlockContaining(symbol)};
406     // Sweep following DATA initializations in search of overlapping
407     // objects, accumulating into a vector; iterate to a fixed point.
408     std::vector<const Symbol *> conflicts;
409     auto minStart{symbol.offset()};
410     auto maxEnd{symbol.offset() + symbol.size()};
411     std::size_t minElementBytes{1};
412     while (true) {
413       auto prevCount{conflicts.size()};
414       conflicts.clear();
415       for (auto scan{iter}; ++scan != end;) {
416         const Symbol &other{*scan->first};
417         const Scope &otherScope{other.owner()};
418         if (&otherScope == &scope &&
419             FindCommonBlockContaining(other) == commonBlock &&
420             maxEnd > other.offset() &&
421             other.offset() + other.size() > minStart) {
422           // "other" conflicts with "symbol" or another conflict
423           conflicts.push_back(&other);
424           minStart = std::min(minStart, other.offset());
425           maxEnd = std::max(maxEnd, other.offset() + other.size());
426         }
427       }
428       if (conflicts.size() == prevCount) {
429         break;
430       }
431     }
432     if (conflicts.empty()) {
433       continue;
434     }
435     // Compute the minimum common granularity
436     if (auto dyType{evaluate::DynamicType::From(symbol)}) {
437       minElementBytes = evaluate::ToInt64(
438           dyType->MeasureSizeInBytes(exprAnalyzer.GetFoldingContext(), true))
439                             .value_or(1);
440     }
441     for (const Symbol *s : conflicts) {
442       if (auto dyType{evaluate::DynamicType::From(*s)}) {
443         minElementBytes = std::min<std::size_t>(minElementBytes,
444             evaluate::ToInt64(dyType->MeasureSizeInBytes(
445                                   exprAnalyzer.GetFoldingContext(), true))
446                 .value_or(1));
447       } else {
448         minElementBytes = 1;
449       }
450     }
451     CHECK(minElementBytes > 0);
452     CHECK((minElementBytes & (minElementBytes - 1)) == 0);
453     auto bytes{static_cast<common::ConstantSubscript>(maxEnd - minStart)};
454     CHECK(bytes % minElementBytes == 0);
455     const DeclTypeSpec &typeSpec{scope.MakeNumericType(
456         TypeCategory::Integer, KindExpr{minElementBytes})};
457     // Combine "symbol" and "conflicts[]" into a compiler array temp
458     // that overlaps all of them, and merge their initial values into
459     // the temp's initializer.
460     SourceName name{exprAnalyzer.context().GetTempName(scope)};
461     auto emplaced{
462         scope.try_emplace(name, Attrs{Attr::SAVE}, ObjectEntityDetails{})};
463     CHECK(emplaced.second);
464     Symbol &combinedSymbol{*emplaced.first->second};
465     auto &details{combinedSymbol.get<ObjectEntityDetails>()};
466     combinedSymbol.set_offset(minStart);
467     combinedSymbol.set_size(bytes);
468     details.set_type(typeSpec);
469     ArraySpec arraySpec;
470     arraySpec.emplace_back(ShapeSpec::MakeExplicit(Bound{
471         bytes / static_cast<common::ConstantSubscript>(minElementBytes)}));
472     details.set_shape(arraySpec);
473     if (commonBlock) {
474       details.set_commonBlock(*commonBlock);
475     }
476     // Merge these EQUIVALENCE'd DATA initializations, and remove the
477     // original initializations from the map.
478     auto combinedInit{
479         inits.emplace(&combinedSymbol, static_cast<std::size_t>(bytes))};
480     evaluate::InitialImage &combined{combinedInit.first->second.image};
481     combined.Incorporate(symbol.offset() - minStart, iter->second.image);
482     inits.erase(iter);
483     for (const Symbol *s : conflicts) {
484       auto sIter{inits.find(s)};
485       CHECK(sIter != inits.end());
486       combined.Incorporate(s->offset() - minStart, sIter->second.image);
487       inits.erase(sIter);
488     }
489     return true; // got one
490   }
491   return false; // no remaining EQUIVALENCE'd DATA initializations
492 }
493 
494 // Converts the initialization image for all the DATA statement appearances of
495 // a single symbol into an init() expression in the symbol table entry.
496 void ConstructInitializer(const Symbol &symbol,
497     SymbolDataInitialization &initialization,
498     evaluate::ExpressionAnalyzer &exprAnalyzer) {
499   auto &context{exprAnalyzer.GetFoldingContext()};
500   initialization.inits.sort();
501   ConstantSubscript next{0};
502   for (const auto &init : initialization.inits) {
503     if (init.start() < next) {
504       auto badDesignator{evaluate::OffsetToDesignator(
505           context, symbol, init.start(), init.size())};
506       CHECK(badDesignator);
507       exprAnalyzer.Say(symbol.name(),
508           "DATA statement initializations affect '%s' more than once"_err_en_US,
509           badDesignator->AsFortran());
510     }
511     next = init.start() + init.size();
512     CHECK(next <= static_cast<ConstantSubscript>(initialization.image.size()));
513   }
514   if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) {
515     CHECK(IsProcedurePointer(symbol));
516     const auto &procDesignator{initialization.image.AsConstantProcPointer()};
517     CHECK(!procDesignator.GetComponent());
518     auto &mutableProc{const_cast<ProcEntityDetails &>(*proc)};
519     mutableProc.set_init(DEREF(procDesignator.GetSymbol()));
520   } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
521     if (auto symbolType{evaluate::DynamicType::From(symbol)}) {
522       auto &mutableObject{const_cast<ObjectEntityDetails &>(*object)};
523       if (IsPointer(symbol)) {
524         mutableObject.set_init(
525             initialization.image.AsConstantDataPointer(*symbolType));
526       } else {
527         if (auto extents{evaluate::GetConstantExtents(context, symbol)}) {
528           mutableObject.set_init(
529               initialization.image.AsConstant(context, *symbolType, *extents));
530         } else {
531           exprAnalyzer.Say(symbol.name(),
532               "internal: unknown shape for '%s' while constructing initializer from DATA"_err_en_US,
533               symbol.name());
534           return;
535         }
536       }
537     } else {
538       exprAnalyzer.Say(symbol.name(),
539           "internal: no type for '%s' while constructing initializer from DATA"_err_en_US,
540           symbol.name());
541       return;
542     }
543     if (!object->init()) {
544       exprAnalyzer.Say(symbol.name(),
545           "internal: could not construct an initializer from DATA statements for '%s'"_err_en_US,
546           symbol.name());
547     }
548   } else {
549     CHECK(exprAnalyzer.context().AnyFatalError());
550   }
551 }
552 
553 void ConvertToInitializers(
554     DataInitializations &inits, evaluate::ExpressionAnalyzer &exprAnalyzer) {
555   while (CombineSomeEquivalencedInits(inits, exprAnalyzer)) {
556   }
557   for (auto &[symbolPtr, initialization] : inits) {
558     ConstructInitializer(*symbolPtr, initialization, exprAnalyzer);
559   }
560 }
561 } // namespace Fortran::semantics
562