1 //===-- lib/Semantics/type.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 #include "flang/Semantics/type.h"
10 #include "flang/Evaluate/fold.h"
11 #include "flang/Parser/characters.h"
12 #include "flang/Semantics/scope.h"
13 #include "flang/Semantics/symbol.h"
14 #include "flang/Semantics/tools.h"
15 #include "llvm/Support/raw_ostream.h"
16 
17 namespace Fortran::semantics {
18 
19 DerivedTypeSpec::DerivedTypeSpec(SourceName name, const Symbol &typeSymbol)
20     : name_{name}, typeSymbol_{typeSymbol} {
21   CHECK(typeSymbol.has<DerivedTypeDetails>());
22 }
23 DerivedTypeSpec::DerivedTypeSpec(const DerivedTypeSpec &that) = default;
24 DerivedTypeSpec::DerivedTypeSpec(DerivedTypeSpec &&that) = default;
25 
26 void DerivedTypeSpec::set_scope(const Scope &scope) {
27   CHECK(!scope_);
28   ReplaceScope(scope);
29 }
30 void DerivedTypeSpec::ReplaceScope(const Scope &scope) {
31   CHECK(scope.IsDerivedType());
32   scope_ = &scope;
33 }
34 
35 void DerivedTypeSpec::AddRawParamValue(
36     const std::optional<parser::Keyword> &keyword, ParamValue &&value) {
37   CHECK(parameters_.empty());
38   rawParameters_.emplace_back(keyword ? &*keyword : nullptr, std::move(value));
39 }
40 
41 void DerivedTypeSpec::CookParameters(evaluate::FoldingContext &foldingContext) {
42   if (cooked_) {
43     return;
44   }
45   cooked_ = true;
46   auto &messages{foldingContext.messages()};
47   if (IsForwardReferenced()) {
48     messages.Say(typeSymbol_.name(),
49         "Derived type '%s' was used but never defined"_err_en_US,
50         typeSymbol_.name());
51     return;
52   }
53 
54   // Parameters of the most deeply nested "base class" come first when the
55   // derived type is an extension.
56   auto parameterNames{OrderParameterNames(typeSymbol_)};
57   auto parameterDecls{OrderParameterDeclarations(typeSymbol_)};
58   auto nextNameIter{parameterNames.begin()};
59   RawParameters raw{std::move(rawParameters_)};
60   for (auto &[maybeKeyword, value] : raw) {
61     SourceName name;
62     common::TypeParamAttr attr{common::TypeParamAttr::Kind};
63     if (maybeKeyword) {
64       name = maybeKeyword->v.source;
65       auto it{std::find_if(parameterDecls.begin(), parameterDecls.end(),
66           [&](const Symbol &symbol) { return symbol.name() == name; })};
67       if (it == parameterDecls.end()) {
68         messages.Say(name,
69             "'%s' is not the name of a parameter for derived type '%s'"_err_en_US,
70             name, typeSymbol_.name());
71       } else {
72         // Resolve the keyword's symbol
73         maybeKeyword->v.symbol = const_cast<Symbol *>(&it->get());
74         attr = it->get().get<TypeParamDetails>().attr();
75       }
76     } else if (nextNameIter != parameterNames.end()) {
77       name = *nextNameIter++;
78       auto it{std::find_if(parameterDecls.begin(), parameterDecls.end(),
79           [&](const Symbol &symbol) { return symbol.name() == name; })};
80       if (it == parameterDecls.end()) {
81         break;
82       }
83       attr = it->get().get<TypeParamDetails>().attr();
84     } else {
85       messages.Say(name_,
86           "Too many type parameters given for derived type '%s'"_err_en_US,
87           typeSymbol_.name());
88       break;
89     }
90     if (FindParameter(name)) {
91       messages.Say(name_,
92           "Multiple values given for type parameter '%s'"_err_en_US, name);
93     } else {
94       value.set_attr(attr);
95       AddParamValue(name, std::move(value));
96     }
97   }
98 }
99 
100 void DerivedTypeSpec::EvaluateParameters(
101     evaluate::FoldingContext &foldingContext) {
102   CookParameters(foldingContext);
103   if (evaluated_) {
104     return;
105   }
106   evaluated_ = true;
107   auto &messages{foldingContext.messages()};
108 
109   // Fold the explicit type parameter value expressions first.  Do not
110   // fold them within the scope of the derived type being instantiated;
111   // these expressions cannot use its type parameters.  Convert the values
112   // of the expressions to the declared types of the type parameters.
113   auto parameterDecls{OrderParameterDeclarations(typeSymbol_)};
114   for (const Symbol &symbol : parameterDecls) {
115     const SourceName &name{symbol.name()};
116     if (ParamValue * paramValue{FindParameter(name)}) {
117       if (const MaybeIntExpr & expr{paramValue->GetExplicit()}) {
118         if (auto converted{evaluate::ConvertToType(symbol, SomeExpr{*expr})}) {
119           SomeExpr folded{
120               evaluate::Fold(foldingContext, std::move(*converted))};
121           if (auto *intExpr{std::get_if<SomeIntExpr>(&folded.u)}) {
122             paramValue->SetExplicit(std::move(*intExpr));
123             continue;
124           }
125         }
126         evaluate::SayWithDeclaration(messages, symbol,
127             "Value of type parameter '%s' (%s) is not convertible to its type"_err_en_US,
128             name, expr->AsFortran());
129       }
130     }
131   }
132 
133   // Default initialization expressions for the derived type's parameters
134   // may reference other parameters so long as the declaration precedes the
135   // use in the expression (10.1.12).  This is not necessarily the same
136   // order as "type parameter order" (7.5.3.2).
137   // Type parameter default value expressions are folded in declaration order
138   // within the scope of the derived type so that the values of earlier type
139   // parameters are available for use in the default initialization
140   // expressions of later parameters.
141   auto restorer{foldingContext.WithPDTInstance(*this)};
142   for (const Symbol &symbol : parameterDecls) {
143     const SourceName &name{symbol.name()};
144     if (!FindParameter(name)) {
145       const TypeParamDetails &details{symbol.get<TypeParamDetails>()};
146       if (details.init()) {
147         auto expr{
148             evaluate::Fold(foldingContext, common::Clone(details.init()))};
149         AddParamValue(name, ParamValue{std::move(*expr), details.attr()});
150       } else {
151         messages.Say(name_,
152             "Type parameter '%s' lacks a value and has no default"_err_en_US,
153             name);
154       }
155     }
156   }
157 }
158 
159 void DerivedTypeSpec::AddParamValue(SourceName name, ParamValue &&value) {
160   CHECK(cooked_);
161   auto pair{parameters_.insert(std::make_pair(name, std::move(value)))};
162   CHECK(pair.second); // name was not already present
163 }
164 
165 int DerivedTypeSpec::NumLengthParameters() const {
166   int result{0};
167   for (const auto &pair : parameters_) {
168     if (pair.second.isLen()) {
169       ++result;
170     }
171   }
172   return result;
173 }
174 
175 bool DerivedTypeSpec::MightBeParameterized() const {
176   return !cooked_ || !parameters_.empty();
177 }
178 
179 bool DerivedTypeSpec::IsForwardReferenced() const {
180   return typeSymbol_.get<DerivedTypeDetails>().isForwardReferenced();
181 }
182 
183 bool DerivedTypeSpec::HasDefaultInitialization() const {
184   for (const Scope *scope{scope_}; scope;
185        scope = scope->GetDerivedTypeParent()) {
186     for (const auto &pair : *scope) {
187       const Symbol &symbol{*pair.second};
188       if (IsAllocatable(symbol) || IsInitialized(symbol)) {
189         return true;
190       }
191     }
192   }
193   return false;
194 }
195 
196 ParamValue *DerivedTypeSpec::FindParameter(SourceName target) {
197   return const_cast<ParamValue *>(
198       const_cast<const DerivedTypeSpec *>(this)->FindParameter(target));
199 }
200 
201 class InstantiateHelper {
202 public:
203   InstantiateHelper(SemanticsContext &context, Scope &scope)
204       : context_{context}, scope_{scope} {}
205   // Instantiate components from fromScope into scope_
206   void InstantiateComponents(const Scope &);
207 
208 private:
209   evaluate::FoldingContext &foldingContext() {
210     return context_.foldingContext();
211   }
212   template <typename T> T Fold(T &&expr) {
213     return evaluate::Fold(foldingContext(), std::move(expr));
214   }
215   void InstantiateComponent(const Symbol &);
216   const DeclTypeSpec *InstantiateType(const Symbol &);
217   const DeclTypeSpec &InstantiateIntrinsicType(const DeclTypeSpec &);
218   DerivedTypeSpec CreateDerivedTypeSpec(const DerivedTypeSpec &, bool);
219 
220   SemanticsContext &context_;
221   Scope &scope_;
222 };
223 
224 void DerivedTypeSpec::Instantiate(
225     Scope &containingScope, SemanticsContext &context) {
226   if (instantiated_) {
227     return;
228   }
229   instantiated_ = true;
230   auto &foldingContext{context.foldingContext()};
231   if (IsForwardReferenced()) {
232     foldingContext.messages().Say(typeSymbol_.name(),
233         "The derived type '%s' was forward-referenced but not defined"_err_en_US,
234         typeSymbol_.name());
235     return;
236   }
237   CookParameters(foldingContext);
238   EvaluateParameters(foldingContext);
239   const Scope &typeScope{DEREF(typeSymbol_.scope())};
240   if (!MightBeParameterized()) {
241     scope_ = &typeScope;
242     for (auto &pair : typeScope) {
243       Symbol &symbol{*pair.second};
244       if (DeclTypeSpec * type{symbol.GetType()}) {
245         if (DerivedTypeSpec * derived{type->AsDerived()}) {
246           if (!(derived->IsForwardReferenced() &&
247                   IsAllocatableOrPointer(symbol))) {
248             derived->Instantiate(containingScope, context);
249           }
250         }
251       }
252     }
253     return;
254   }
255   Scope &newScope{containingScope.MakeScope(Scope::Kind::DerivedType)};
256   newScope.set_derivedTypeSpec(*this);
257   ReplaceScope(newScope);
258   for (const Symbol &symbol : OrderParameterDeclarations(typeSymbol_)) {
259     const SourceName &name{symbol.name()};
260     if (typeScope.find(symbol.name()) != typeScope.end()) {
261       // This type parameter belongs to the derived type itself, not to
262       // one of its ancestors.  Put the type parameter expression value
263       // into the new scope as the initialization value for the parameter.
264       if (ParamValue * paramValue{FindParameter(name)}) {
265         const TypeParamDetails &details{symbol.get<TypeParamDetails>()};
266         paramValue->set_attr(details.attr());
267         if (MaybeIntExpr expr{paramValue->GetExplicit()}) {
268           // Ensure that any kind type parameters with values are
269           // constant by now.
270           if (details.attr() == common::TypeParamAttr::Kind) {
271             // Any errors in rank and type will have already elicited
272             // messages, so don't pile on by complaining further here.
273             if (auto maybeDynamicType{expr->GetType()}) {
274               if (expr->Rank() == 0 &&
275                   maybeDynamicType->category() == TypeCategory::Integer) {
276                 if (!evaluate::ToInt64(*expr)) {
277                   if (auto *msg{foldingContext.messages().Say(
278                           "Value of kind type parameter '%s' (%s) is not "
279                           "a scalar INTEGER constant"_err_en_US,
280                           name, expr->AsFortran())}) {
281                     msg->Attach(name, "declared here"_en_US);
282                   }
283                 }
284               }
285             }
286           }
287           TypeParamDetails instanceDetails{details.attr()};
288           if (const DeclTypeSpec * type{details.type()}) {
289             instanceDetails.set_type(*type);
290           }
291           instanceDetails.set_init(std::move(*expr));
292           newScope.try_emplace(name, std::move(instanceDetails));
293         }
294       }
295     }
296   }
297   // Instantiate every non-parameter symbol from the original derived
298   // type's scope into the new instance.
299   auto restorer{foldingContext.WithPDTInstance(*this)};
300   newScope.AddSourceRange(typeScope.sourceRange());
301   InstantiateHelper{context, newScope}.InstantiateComponents(typeScope);
302 }
303 
304 void InstantiateHelper::InstantiateComponents(const Scope &fromScope) {
305   for (const auto &pair : fromScope) {
306     InstantiateComponent(*pair.second);
307   }
308 }
309 
310 void InstantiateHelper::InstantiateComponent(const Symbol &oldSymbol) {
311   auto pair{scope_.try_emplace(
312       oldSymbol.name(), oldSymbol.attrs(), common::Clone(oldSymbol.details()))};
313   Symbol &newSymbol{*pair.first->second};
314   if (!pair.second) {
315     // Symbol was already present in the scope, which can only happen
316     // in the case of type parameters.
317     CHECK(oldSymbol.has<TypeParamDetails>());
318     return;
319   }
320   newSymbol.flags() = oldSymbol.flags();
321   if (auto *details{newSymbol.detailsIf<ObjectEntityDetails>()}) {
322     if (const DeclTypeSpec * newType{InstantiateType(newSymbol)}) {
323       details->ReplaceType(*newType);
324     }
325     details->set_init(Fold(std::move(details->init())));
326     for (ShapeSpec &dim : details->shape()) {
327       if (dim.lbound().isExplicit()) {
328         dim.lbound().SetExplicit(Fold(std::move(dim.lbound().GetExplicit())));
329       }
330       if (dim.ubound().isExplicit()) {
331         dim.ubound().SetExplicit(Fold(std::move(dim.ubound().GetExplicit())));
332       }
333     }
334     for (ShapeSpec &dim : details->coshape()) {
335       if (dim.lbound().isExplicit()) {
336         dim.lbound().SetExplicit(Fold(std::move(dim.lbound().GetExplicit())));
337       }
338       if (dim.ubound().isExplicit()) {
339         dim.ubound().SetExplicit(Fold(std::move(dim.ubound().GetExplicit())));
340       }
341     }
342   }
343 }
344 
345 const DeclTypeSpec *InstantiateHelper::InstantiateType(const Symbol &symbol) {
346   const DeclTypeSpec *type{symbol.GetType()};
347   if (!type) {
348     return nullptr; // error has occurred
349   } else if (const DerivedTypeSpec * spec{type->AsDerived()}) {
350     return &FindOrInstantiateDerivedType(scope_,
351         CreateDerivedTypeSpec(*spec, symbol.test(Symbol::Flag::ParentComp)),
352         context_, type->category());
353   } else if (type->AsIntrinsic()) {
354     return &InstantiateIntrinsicType(*type);
355   } else if (type->category() == DeclTypeSpec::ClassStar) {
356     return type;
357   } else {
358     common::die("InstantiateType: %s", type->AsFortran().c_str());
359   }
360 }
361 
362 // Apply type parameter values to an intrinsic type spec.
363 const DeclTypeSpec &InstantiateHelper::InstantiateIntrinsicType(
364     const DeclTypeSpec &spec) {
365   const IntrinsicTypeSpec &intrinsic{DEREF(spec.AsIntrinsic())};
366   if (evaluate::ToInt64(intrinsic.kind())) {
367     return spec; // KIND is already a known constant
368   }
369   // The expression was not originally constant, but now it must be so
370   // in the context of a parameterized derived type instantiation.
371   KindExpr copy{Fold(common::Clone(intrinsic.kind()))};
372   int kind{context_.GetDefaultKind(intrinsic.category())};
373   if (auto value{evaluate::ToInt64(copy)}) {
374     if (evaluate::IsValidKindOfIntrinsicType(intrinsic.category(), *value)) {
375       kind = *value;
376     } else {
377       foldingContext().messages().Say(
378           "KIND parameter value (%jd) of intrinsic type %s "
379           "did not resolve to a supported value"_err_en_US,
380           *value,
381           parser::ToUpperCaseLetters(EnumToString(intrinsic.category())));
382     }
383   }
384   switch (spec.category()) {
385   case DeclTypeSpec::Numeric:
386     return scope_.MakeNumericType(intrinsic.category(), KindExpr{kind});
387   case DeclTypeSpec::Logical:
388     return scope_.MakeLogicalType(KindExpr{kind});
389   case DeclTypeSpec::Character:
390     return scope_.MakeCharacterType(
391         ParamValue{spec.characterTypeSpec().length()}, KindExpr{kind});
392   default:
393     CRASH_NO_CASE;
394   }
395 }
396 
397 DerivedTypeSpec InstantiateHelper::CreateDerivedTypeSpec(
398     const DerivedTypeSpec &spec, bool isParentComp) {
399   DerivedTypeSpec result{spec};
400   result.CookParameters(foldingContext()); // enables AddParamValue()
401   if (isParentComp) {
402     // Forward any explicit type parameter values from the
403     // derived type spec under instantiation that define type parameters
404     // of the parent component to the derived type spec of the
405     // parent component.
406     const DerivedTypeSpec &instanceSpec{DEREF(foldingContext().pdtInstance())};
407     for (const auto &[name, value] : instanceSpec.parameters()) {
408       if (scope_.find(name) == scope_.end()) {
409         result.AddParamValue(name, ParamValue{value});
410       }
411     }
412   }
413   return result;
414 }
415 
416 std::string DerivedTypeSpec::AsFortran() const {
417   std::string buf;
418   llvm::raw_string_ostream ss{buf};
419   ss << name_;
420   if (!rawParameters_.empty()) {
421     CHECK(parameters_.empty());
422     ss << '(';
423     bool first = true;
424     for (const auto &[maybeKeyword, value] : rawParameters_) {
425       if (first) {
426         first = false;
427       } else {
428         ss << ',';
429       }
430       if (maybeKeyword) {
431         ss << maybeKeyword->v.source.ToString() << '=';
432       }
433       ss << value.AsFortran();
434     }
435     ss << ')';
436   } else if (!parameters_.empty()) {
437     ss << '(';
438     bool first = true;
439     for (const auto &[name, value] : parameters_) {
440       if (first) {
441         first = false;
442       } else {
443         ss << ',';
444       }
445       ss << name.ToString() << '=' << value.AsFortran();
446     }
447     ss << ')';
448   }
449   return ss.str();
450 }
451 
452 llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const DerivedTypeSpec &x) {
453   return o << x.AsFortran();
454 }
455 
456 Bound::Bound(int bound) : expr_{bound} {}
457 
458 llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const Bound &x) {
459   if (x.isAssumed()) {
460     o << '*';
461   } else if (x.isDeferred()) {
462     o << ':';
463   } else if (x.expr_) {
464     x.expr_->AsFortran(o);
465   } else {
466     o << "<no-expr>";
467   }
468   return o;
469 }
470 
471 llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const ShapeSpec &x) {
472   if (x.lb_.isAssumed()) {
473     CHECK(x.ub_.isAssumed());
474     o << "..";
475   } else {
476     if (!x.lb_.isDeferred()) {
477       o << x.lb_;
478     }
479     o << ':';
480     if (!x.ub_.isDeferred()) {
481       o << x.ub_;
482     }
483   }
484   return o;
485 }
486 
487 bool ArraySpec::IsExplicitShape() const {
488   return CheckAll([](const ShapeSpec &x) { return x.ubound().isExplicit(); });
489 }
490 bool ArraySpec::IsAssumedShape() const {
491   return CheckAll([](const ShapeSpec &x) { return x.ubound().isDeferred(); });
492 }
493 bool ArraySpec::IsDeferredShape() const {
494   return CheckAll([](const ShapeSpec &x) {
495     return x.lbound().isDeferred() && x.ubound().isDeferred();
496   });
497 }
498 bool ArraySpec::IsImpliedShape() const {
499   return !IsAssumedRank() &&
500       CheckAll([](const ShapeSpec &x) { return x.ubound().isAssumed(); });
501 }
502 bool ArraySpec::IsAssumedSize() const {
503   return !empty() && !IsAssumedRank() && back().ubound().isAssumed() &&
504       std::all_of(begin(), end() - 1,
505           [](const ShapeSpec &x) { return x.ubound().isExplicit(); });
506 }
507 bool ArraySpec::IsAssumedRank() const {
508   return Rank() == 1 && front().lbound().isAssumed();
509 }
510 bool ArraySpec::IsConstantShape() const {
511   return CheckAll([](const ShapeSpec &x) {
512     const auto &lb{x.lbound().GetExplicit()};
513     const auto &ub{x.ubound().GetExplicit()};
514     return lb && ub && IsConstantExpr(*lb) && IsConstantExpr(*ub);
515   });
516 }
517 
518 llvm::raw_ostream &operator<<(
519     llvm::raw_ostream &os, const ArraySpec &arraySpec) {
520   char sep{'('};
521   for (auto &shape : arraySpec) {
522     os << sep << shape;
523     sep = ',';
524   }
525   if (sep == ',') {
526     os << ')';
527   }
528   return os;
529 }
530 
531 ParamValue::ParamValue(MaybeIntExpr &&expr, common::TypeParamAttr attr)
532     : attr_{attr}, expr_{std::move(expr)} {}
533 ParamValue::ParamValue(SomeIntExpr &&expr, common::TypeParamAttr attr)
534     : attr_{attr}, expr_{std::move(expr)} {}
535 ParamValue::ParamValue(
536     common::ConstantSubscript value, common::TypeParamAttr attr)
537     : ParamValue(SomeIntExpr{evaluate::Expr<evaluate::SubscriptInteger>{value}},
538           attr) {}
539 
540 void ParamValue::SetExplicit(SomeIntExpr &&x) {
541   category_ = Category::Explicit;
542   expr_ = std::move(x);
543 }
544 
545 std::string ParamValue::AsFortran() const {
546   switch (category_) {
547     SWITCH_COVERS_ALL_CASES
548   case Category::Assumed:
549     return "*";
550   case Category::Deferred:
551     return ":";
552   case Category::Explicit:
553     if (expr_) {
554       std::string buf;
555       llvm::raw_string_ostream ss{buf};
556       expr_->AsFortran(ss);
557       return ss.str();
558     } else {
559       return "";
560     }
561   }
562 }
563 
564 llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const ParamValue &x) {
565   return o << x.AsFortran();
566 }
567 
568 IntrinsicTypeSpec::IntrinsicTypeSpec(TypeCategory category, KindExpr &&kind)
569     : category_{category}, kind_{std::move(kind)} {
570   CHECK(category != TypeCategory::Derived);
571 }
572 
573 static std::string KindAsFortran(const KindExpr &kind) {
574   std::string buf;
575   llvm::raw_string_ostream ss{buf};
576   if (auto k{evaluate::ToInt64(kind)}) {
577     ss << *k; // emit unsuffixed kind code
578   } else {
579     kind.AsFortran(ss);
580   }
581   return ss.str();
582 }
583 
584 std::string IntrinsicTypeSpec::AsFortran() const {
585   return parser::ToUpperCaseLetters(common::EnumToString(category_)) + '(' +
586       KindAsFortran(kind_) + ')';
587 }
588 
589 llvm::raw_ostream &operator<<(
590     llvm::raw_ostream &os, const IntrinsicTypeSpec &x) {
591   return os << x.AsFortran();
592 }
593 
594 std::string CharacterTypeSpec::AsFortran() const {
595   return "CHARACTER(" + length_.AsFortran() + ',' + KindAsFortran(kind()) + ')';
596 }
597 
598 llvm::raw_ostream &operator<<(
599     llvm::raw_ostream &os, const CharacterTypeSpec &x) {
600   return os << x.AsFortran();
601 }
602 
603 DeclTypeSpec::DeclTypeSpec(NumericTypeSpec &&typeSpec)
604     : category_{Numeric}, typeSpec_{std::move(typeSpec)} {}
605 DeclTypeSpec::DeclTypeSpec(LogicalTypeSpec &&typeSpec)
606     : category_{Logical}, typeSpec_{std::move(typeSpec)} {}
607 DeclTypeSpec::DeclTypeSpec(const CharacterTypeSpec &typeSpec)
608     : category_{Character}, typeSpec_{typeSpec} {}
609 DeclTypeSpec::DeclTypeSpec(CharacterTypeSpec &&typeSpec)
610     : category_{Character}, typeSpec_{std::move(typeSpec)} {}
611 DeclTypeSpec::DeclTypeSpec(Category category, const DerivedTypeSpec &typeSpec)
612     : category_{category}, typeSpec_{typeSpec} {
613   CHECK(category == TypeDerived || category == ClassDerived);
614 }
615 DeclTypeSpec::DeclTypeSpec(Category category, DerivedTypeSpec &&typeSpec)
616     : category_{category}, typeSpec_{std::move(typeSpec)} {
617   CHECK(category == TypeDerived || category == ClassDerived);
618 }
619 DeclTypeSpec::DeclTypeSpec(Category category) : category_{category} {
620   CHECK(category == TypeStar || category == ClassStar);
621 }
622 bool DeclTypeSpec::IsNumeric(TypeCategory tc) const {
623   return category_ == Numeric && numericTypeSpec().category() == tc;
624 }
625 bool DeclTypeSpec::IsSequenceType() const {
626   if (const DerivedTypeSpec * derivedType{AsDerived()}) {
627     const auto *typeDetails{
628         derivedType->typeSymbol().detailsIf<DerivedTypeDetails>()};
629     return typeDetails && typeDetails->sequence();
630   }
631   return false;
632 }
633 
634 IntrinsicTypeSpec *DeclTypeSpec::AsIntrinsic() {
635   switch (category_) {
636   case Numeric:
637     return &std::get<NumericTypeSpec>(typeSpec_);
638   case Logical:
639     return &std::get<LogicalTypeSpec>(typeSpec_);
640   case Character:
641     return &std::get<CharacterTypeSpec>(typeSpec_);
642   default:
643     return nullptr;
644   }
645 }
646 const IntrinsicTypeSpec *DeclTypeSpec::AsIntrinsic() const {
647   return const_cast<DeclTypeSpec *>(this)->AsIntrinsic();
648 }
649 
650 DerivedTypeSpec *DeclTypeSpec::AsDerived() {
651   switch (category_) {
652   case TypeDerived:
653   case ClassDerived:
654     return &std::get<DerivedTypeSpec>(typeSpec_);
655   default:
656     return nullptr;
657   }
658 }
659 const DerivedTypeSpec *DeclTypeSpec::AsDerived() const {
660   return const_cast<DeclTypeSpec *>(this)->AsDerived();
661 }
662 
663 const NumericTypeSpec &DeclTypeSpec::numericTypeSpec() const {
664   CHECK(category_ == Numeric);
665   return std::get<NumericTypeSpec>(typeSpec_);
666 }
667 const LogicalTypeSpec &DeclTypeSpec::logicalTypeSpec() const {
668   CHECK(category_ == Logical);
669   return std::get<LogicalTypeSpec>(typeSpec_);
670 }
671 bool DeclTypeSpec::operator==(const DeclTypeSpec &that) const {
672   return category_ == that.category_ && typeSpec_ == that.typeSpec_;
673 }
674 
675 std::string DeclTypeSpec::AsFortran() const {
676   switch (category_) {
677     SWITCH_COVERS_ALL_CASES
678   case Numeric:
679     return numericTypeSpec().AsFortran();
680   case Logical:
681     return logicalTypeSpec().AsFortran();
682   case Character:
683     return characterTypeSpec().AsFortran();
684   case TypeDerived:
685     return "TYPE(" + derivedTypeSpec().AsFortran() + ')';
686   case ClassDerived:
687     return "CLASS(" + derivedTypeSpec().AsFortran() + ')';
688   case TypeStar:
689     return "TYPE(*)";
690   case ClassStar:
691     return "CLASS(*)";
692   }
693 }
694 
695 llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const DeclTypeSpec &x) {
696   return o << x.AsFortran();
697 }
698 
699 void ProcInterface::set_symbol(const Symbol &symbol) {
700   CHECK(!type_);
701   symbol_ = &symbol;
702 }
703 void ProcInterface::set_type(const DeclTypeSpec &type) {
704   CHECK(!symbol_);
705   type_ = &type;
706 }
707 } // namespace Fortran::semantics
708