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         if (!symbol.test(Symbol::Flag::Error)) {
127           evaluate::SayWithDeclaration(messages, symbol,
128               "Value of type parameter '%s' (%s) is not convertible to its"
129               " type"_err_en_US,
130               name, expr->AsFortran());
131         }
132       }
133     }
134   }
135 
136   // Default initialization expressions for the derived type's parameters
137   // may reference other parameters so long as the declaration precedes the
138   // use in the expression (10.1.12).  This is not necessarily the same
139   // order as "type parameter order" (7.5.3.2).
140   // Type parameter default value expressions are folded in declaration order
141   // within the scope of the derived type so that the values of earlier type
142   // parameters are available for use in the default initialization
143   // expressions of later parameters.
144   auto restorer{foldingContext.WithPDTInstance(*this)};
145   for (const Symbol &symbol : parameterDecls) {
146     const SourceName &name{symbol.name()};
147     if (!FindParameter(name)) {
148       const TypeParamDetails &details{symbol.get<TypeParamDetails>()};
149       if (details.init()) {
150         auto expr{
151             evaluate::Fold(foldingContext, common::Clone(details.init()))};
152         AddParamValue(name, ParamValue{std::move(*expr), details.attr()});
153       } else if (!symbol.test(Symbol::Flag::Error)) {
154         messages.Say(name_,
155             "Type parameter '%s' lacks a value and has no default"_err_en_US,
156             name);
157       }
158     }
159   }
160 }
161 
162 void DerivedTypeSpec::AddParamValue(SourceName name, ParamValue &&value) {
163   CHECK(cooked_);
164   auto pair{parameters_.insert(std::make_pair(name, std::move(value)))};
165   CHECK(pair.second); // name was not already present
166 }
167 
168 bool DerivedTypeSpec::MightBeParameterized() const {
169   return !cooked_ || !parameters_.empty();
170 }
171 
172 bool DerivedTypeSpec::IsForwardReferenced() const {
173   return typeSymbol_.get<DerivedTypeDetails>().isForwardReferenced();
174 }
175 
176 bool DerivedTypeSpec::HasDefaultInitialization() const {
177   for (const Scope *scope{scope_}; scope;
178        scope = scope->GetDerivedTypeParent()) {
179     for (const auto &pair : *scope) {
180       const Symbol &symbol{*pair.second};
181       if (IsAllocatable(symbol) || IsInitialized(symbol)) {
182         return true;
183       }
184     }
185   }
186   return false;
187 }
188 
189 ParamValue *DerivedTypeSpec::FindParameter(SourceName target) {
190   return const_cast<ParamValue *>(
191       const_cast<const DerivedTypeSpec *>(this)->FindParameter(target));
192 }
193 
194 class InstantiateHelper {
195 public:
196   InstantiateHelper(SemanticsContext &context, Scope &scope)
197       : context_{context}, scope_{scope} {}
198   // Instantiate components from fromScope into scope_
199   void InstantiateComponents(const Scope &);
200 
201 private:
202   evaluate::FoldingContext &foldingContext() {
203     return context_.foldingContext();
204   }
205   template <typename T> T Fold(T &&expr) {
206     return evaluate::Fold(foldingContext(), std::move(expr));
207   }
208   void InstantiateComponent(const Symbol &);
209   const DeclTypeSpec *InstantiateType(const Symbol &);
210   const DeclTypeSpec &InstantiateIntrinsicType(const DeclTypeSpec &);
211   DerivedTypeSpec CreateDerivedTypeSpec(const DerivedTypeSpec &, bool);
212 
213   SemanticsContext &context_;
214   Scope &scope_;
215 };
216 
217 void DerivedTypeSpec::Instantiate(
218     Scope &containingScope, SemanticsContext &context) {
219   if (instantiated_) {
220     return;
221   }
222   instantiated_ = true;
223   auto &foldingContext{context.foldingContext()};
224   if (IsForwardReferenced()) {
225     foldingContext.messages().Say(typeSymbol_.name(),
226         "The derived type '%s' was forward-referenced but not defined"_err_en_US,
227         typeSymbol_.name());
228     return;
229   }
230   CookParameters(foldingContext);
231   EvaluateParameters(foldingContext);
232   const Scope &typeScope{DEREF(typeSymbol_.scope())};
233   if (!MightBeParameterized()) {
234     scope_ = &typeScope;
235     for (auto &pair : typeScope) {
236       Symbol &symbol{*pair.second};
237       if (DeclTypeSpec * type{symbol.GetType()}) {
238         if (DerivedTypeSpec * derived{type->AsDerived()}) {
239           if (!(derived->IsForwardReferenced() &&
240                   IsAllocatableOrPointer(symbol))) {
241             derived->Instantiate(containingScope, context);
242           }
243         }
244       }
245     }
246     return;
247   }
248   Scope &newScope{containingScope.MakeScope(Scope::Kind::DerivedType)};
249   newScope.set_derivedTypeSpec(*this);
250   ReplaceScope(newScope);
251   for (const Symbol &symbol : OrderParameterDeclarations(typeSymbol_)) {
252     const SourceName &name{symbol.name()};
253     if (typeScope.find(symbol.name()) != typeScope.end()) {
254       // This type parameter belongs to the derived type itself, not to
255       // one of its ancestors.  Put the type parameter expression value
256       // into the new scope as the initialization value for the parameter.
257       if (ParamValue * paramValue{FindParameter(name)}) {
258         const TypeParamDetails &details{symbol.get<TypeParamDetails>()};
259         paramValue->set_attr(details.attr());
260         if (MaybeIntExpr expr{paramValue->GetExplicit()}) {
261           // Ensure that any kind type parameters with values are
262           // constant by now.
263           if (details.attr() == common::TypeParamAttr::Kind) {
264             // Any errors in rank and type will have already elicited
265             // messages, so don't pile on by complaining further here.
266             if (auto maybeDynamicType{expr->GetType()}) {
267               if (expr->Rank() == 0 &&
268                   maybeDynamicType->category() == TypeCategory::Integer) {
269                 if (!evaluate::ToInt64(*expr)) {
270                   if (auto *msg{foldingContext.messages().Say(
271                           "Value of kind type parameter '%s' (%s) is not "
272                           "a scalar INTEGER constant"_err_en_US,
273                           name, expr->AsFortran())}) {
274                     msg->Attach(name, "declared here"_en_US);
275                   }
276                 }
277               }
278             }
279           }
280           TypeParamDetails instanceDetails{details.attr()};
281           if (const DeclTypeSpec * type{details.type()}) {
282             instanceDetails.set_type(*type);
283           }
284           instanceDetails.set_init(std::move(*expr));
285           newScope.try_emplace(name, std::move(instanceDetails));
286         }
287       }
288     }
289   }
290   // Instantiate every non-parameter symbol from the original derived
291   // type's scope into the new instance.
292   auto restorer{foldingContext.WithPDTInstance(*this)};
293   newScope.AddSourceRange(typeScope.sourceRange());
294   InstantiateHelper{context, newScope}.InstantiateComponents(typeScope);
295 }
296 
297 void InstantiateHelper::InstantiateComponents(const Scope &fromScope) {
298   for (const auto &pair : fromScope) {
299     InstantiateComponent(*pair.second);
300   }
301 }
302 
303 void InstantiateHelper::InstantiateComponent(const Symbol &oldSymbol) {
304   auto pair{scope_.try_emplace(
305       oldSymbol.name(), oldSymbol.attrs(), common::Clone(oldSymbol.details()))};
306   Symbol &newSymbol{*pair.first->second};
307   if (!pair.second) {
308     // Symbol was already present in the scope, which can only happen
309     // in the case of type parameters.
310     CHECK(oldSymbol.has<TypeParamDetails>());
311     return;
312   }
313   newSymbol.flags() = oldSymbol.flags();
314   if (auto *details{newSymbol.detailsIf<ObjectEntityDetails>()}) {
315     if (const DeclTypeSpec * newType{InstantiateType(newSymbol)}) {
316       details->ReplaceType(*newType);
317     }
318     details->set_init(Fold(std::move(details->init())));
319     for (ShapeSpec &dim : details->shape()) {
320       if (dim.lbound().isExplicit()) {
321         dim.lbound().SetExplicit(Fold(std::move(dim.lbound().GetExplicit())));
322       }
323       if (dim.ubound().isExplicit()) {
324         dim.ubound().SetExplicit(Fold(std::move(dim.ubound().GetExplicit())));
325       }
326     }
327     for (ShapeSpec &dim : details->coshape()) {
328       if (dim.lbound().isExplicit()) {
329         dim.lbound().SetExplicit(Fold(std::move(dim.lbound().GetExplicit())));
330       }
331       if (dim.ubound().isExplicit()) {
332         dim.ubound().SetExplicit(Fold(std::move(dim.ubound().GetExplicit())));
333       }
334     }
335   }
336 }
337 
338 const DeclTypeSpec *InstantiateHelper::InstantiateType(const Symbol &symbol) {
339   const DeclTypeSpec *type{symbol.GetType()};
340   if (!type) {
341     return nullptr; // error has occurred
342   } else if (const DerivedTypeSpec * spec{type->AsDerived()}) {
343     return &FindOrInstantiateDerivedType(scope_,
344         CreateDerivedTypeSpec(*spec, symbol.test(Symbol::Flag::ParentComp)),
345         context_, type->category());
346   } else if (type->AsIntrinsic()) {
347     return &InstantiateIntrinsicType(*type);
348   } else if (type->category() == DeclTypeSpec::ClassStar) {
349     return type;
350   } else {
351     common::die("InstantiateType: %s", type->AsFortran().c_str());
352   }
353 }
354 
355 // Apply type parameter values to an intrinsic type spec.
356 const DeclTypeSpec &InstantiateHelper::InstantiateIntrinsicType(
357     const DeclTypeSpec &spec) {
358   const IntrinsicTypeSpec &intrinsic{DEREF(spec.AsIntrinsic())};
359   if (evaluate::ToInt64(intrinsic.kind())) {
360     return spec; // KIND is already a known constant
361   }
362   // The expression was not originally constant, but now it must be so
363   // in the context of a parameterized derived type instantiation.
364   KindExpr copy{Fold(common::Clone(intrinsic.kind()))};
365   int kind{context_.GetDefaultKind(intrinsic.category())};
366   if (auto value{evaluate::ToInt64(copy)}) {
367     if (evaluate::IsValidKindOfIntrinsicType(intrinsic.category(), *value)) {
368       kind = *value;
369     } else {
370       foldingContext().messages().Say(
371           "KIND parameter value (%jd) of intrinsic type %s "
372           "did not resolve to a supported value"_err_en_US,
373           *value,
374           parser::ToUpperCaseLetters(EnumToString(intrinsic.category())));
375     }
376   }
377   switch (spec.category()) {
378   case DeclTypeSpec::Numeric:
379     return scope_.MakeNumericType(intrinsic.category(), KindExpr{kind});
380   case DeclTypeSpec::Logical:
381     return scope_.MakeLogicalType(KindExpr{kind});
382   case DeclTypeSpec::Character:
383     return scope_.MakeCharacterType(
384         ParamValue{spec.characterTypeSpec().length()}, KindExpr{kind});
385   default:
386     CRASH_NO_CASE;
387   }
388 }
389 
390 DerivedTypeSpec InstantiateHelper::CreateDerivedTypeSpec(
391     const DerivedTypeSpec &spec, bool isParentComp) {
392   DerivedTypeSpec result{spec};
393   result.CookParameters(foldingContext()); // enables AddParamValue()
394   if (isParentComp) {
395     // Forward any explicit type parameter values from the
396     // derived type spec under instantiation that define type parameters
397     // of the parent component to the derived type spec of the
398     // parent component.
399     const DerivedTypeSpec &instanceSpec{DEREF(foldingContext().pdtInstance())};
400     for (const auto &[name, value] : instanceSpec.parameters()) {
401       if (scope_.find(name) == scope_.end()) {
402         result.AddParamValue(name, ParamValue{value});
403       }
404     }
405   }
406   return result;
407 }
408 
409 std::string DerivedTypeSpec::AsFortran() const {
410   std::string buf;
411   llvm::raw_string_ostream ss{buf};
412   ss << name_;
413   if (!rawParameters_.empty()) {
414     CHECK(parameters_.empty());
415     ss << '(';
416     bool first = true;
417     for (const auto &[maybeKeyword, value] : rawParameters_) {
418       if (first) {
419         first = false;
420       } else {
421         ss << ',';
422       }
423       if (maybeKeyword) {
424         ss << maybeKeyword->v.source.ToString() << '=';
425       }
426       ss << value.AsFortran();
427     }
428     ss << ')';
429   } else if (!parameters_.empty()) {
430     ss << '(';
431     bool first = true;
432     for (const auto &[name, value] : parameters_) {
433       if (first) {
434         first = false;
435       } else {
436         ss << ',';
437       }
438       ss << name.ToString() << '=' << value.AsFortran();
439     }
440     ss << ')';
441   }
442   return ss.str();
443 }
444 
445 llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const DerivedTypeSpec &x) {
446   return o << x.AsFortran();
447 }
448 
449 Bound::Bound(int bound) : expr_{bound} {}
450 
451 llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const Bound &x) {
452   if (x.isAssumed()) {
453     o << '*';
454   } else if (x.isDeferred()) {
455     o << ':';
456   } else if (x.expr_) {
457     x.expr_->AsFortran(o);
458   } else {
459     o << "<no-expr>";
460   }
461   return o;
462 }
463 
464 llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const ShapeSpec &x) {
465   if (x.lb_.isAssumed()) {
466     CHECK(x.ub_.isAssumed());
467     o << "..";
468   } else {
469     if (!x.lb_.isDeferred()) {
470       o << x.lb_;
471     }
472     o << ':';
473     if (!x.ub_.isDeferred()) {
474       o << x.ub_;
475     }
476   }
477   return o;
478 }
479 
480 llvm::raw_ostream &operator<<(
481     llvm::raw_ostream &os, const ArraySpec &arraySpec) {
482   char sep{'('};
483   for (auto &shape : arraySpec) {
484     os << sep << shape;
485     sep = ',';
486   }
487   if (sep == ',') {
488     os << ')';
489   }
490   return os;
491 }
492 
493 ParamValue::ParamValue(MaybeIntExpr &&expr, common::TypeParamAttr attr)
494     : attr_{attr}, expr_{std::move(expr)} {}
495 ParamValue::ParamValue(SomeIntExpr &&expr, common::TypeParamAttr attr)
496     : attr_{attr}, expr_{std::move(expr)} {}
497 ParamValue::ParamValue(
498     common::ConstantSubscript value, common::TypeParamAttr attr)
499     : ParamValue(SomeIntExpr{evaluate::Expr<evaluate::SubscriptInteger>{value}},
500           attr) {}
501 
502 void ParamValue::SetExplicit(SomeIntExpr &&x) {
503   category_ = Category::Explicit;
504   expr_ = std::move(x);
505 }
506 
507 std::string ParamValue::AsFortran() const {
508   switch (category_) {
509     SWITCH_COVERS_ALL_CASES
510   case Category::Assumed:
511     return "*";
512   case Category::Deferred:
513     return ":";
514   case Category::Explicit:
515     if (expr_) {
516       std::string buf;
517       llvm::raw_string_ostream ss{buf};
518       expr_->AsFortran(ss);
519       return ss.str();
520     } else {
521       return "";
522     }
523   }
524 }
525 
526 llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const ParamValue &x) {
527   return o << x.AsFortran();
528 }
529 
530 IntrinsicTypeSpec::IntrinsicTypeSpec(TypeCategory category, KindExpr &&kind)
531     : category_{category}, kind_{std::move(kind)} {
532   CHECK(category != TypeCategory::Derived);
533 }
534 
535 static std::string KindAsFortran(const KindExpr &kind) {
536   std::string buf;
537   llvm::raw_string_ostream ss{buf};
538   if (auto k{evaluate::ToInt64(kind)}) {
539     ss << *k; // emit unsuffixed kind code
540   } else {
541     kind.AsFortran(ss);
542   }
543   return ss.str();
544 }
545 
546 std::string IntrinsicTypeSpec::AsFortran() const {
547   return parser::ToUpperCaseLetters(common::EnumToString(category_)) + '(' +
548       KindAsFortran(kind_) + ')';
549 }
550 
551 llvm::raw_ostream &operator<<(
552     llvm::raw_ostream &os, const IntrinsicTypeSpec &x) {
553   return os << x.AsFortran();
554 }
555 
556 std::string CharacterTypeSpec::AsFortran() const {
557   return "CHARACTER(" + length_.AsFortran() + ',' + KindAsFortran(kind()) + ')';
558 }
559 
560 llvm::raw_ostream &operator<<(
561     llvm::raw_ostream &os, const CharacterTypeSpec &x) {
562   return os << x.AsFortran();
563 }
564 
565 DeclTypeSpec::DeclTypeSpec(NumericTypeSpec &&typeSpec)
566     : category_{Numeric}, typeSpec_{std::move(typeSpec)} {}
567 DeclTypeSpec::DeclTypeSpec(LogicalTypeSpec &&typeSpec)
568     : category_{Logical}, typeSpec_{std::move(typeSpec)} {}
569 DeclTypeSpec::DeclTypeSpec(const CharacterTypeSpec &typeSpec)
570     : category_{Character}, typeSpec_{typeSpec} {}
571 DeclTypeSpec::DeclTypeSpec(CharacterTypeSpec &&typeSpec)
572     : category_{Character}, typeSpec_{std::move(typeSpec)} {}
573 DeclTypeSpec::DeclTypeSpec(Category category, const DerivedTypeSpec &typeSpec)
574     : category_{category}, typeSpec_{typeSpec} {
575   CHECK(category == TypeDerived || category == ClassDerived);
576 }
577 DeclTypeSpec::DeclTypeSpec(Category category, DerivedTypeSpec &&typeSpec)
578     : category_{category}, typeSpec_{std::move(typeSpec)} {
579   CHECK(category == TypeDerived || category == ClassDerived);
580 }
581 DeclTypeSpec::DeclTypeSpec(Category category) : category_{category} {
582   CHECK(category == TypeStar || category == ClassStar);
583 }
584 bool DeclTypeSpec::IsNumeric(TypeCategory tc) const {
585   return category_ == Numeric && numericTypeSpec().category() == tc;
586 }
587 bool DeclTypeSpec::IsSequenceType() const {
588   if (const DerivedTypeSpec * derivedType{AsDerived()}) {
589     const auto *typeDetails{
590         derivedType->typeSymbol().detailsIf<DerivedTypeDetails>()};
591     return typeDetails && typeDetails->sequence();
592   }
593   return false;
594 }
595 
596 const NumericTypeSpec &DeclTypeSpec::numericTypeSpec() const {
597   CHECK(category_ == Numeric);
598   return std::get<NumericTypeSpec>(typeSpec_);
599 }
600 const LogicalTypeSpec &DeclTypeSpec::logicalTypeSpec() const {
601   CHECK(category_ == Logical);
602   return std::get<LogicalTypeSpec>(typeSpec_);
603 }
604 bool DeclTypeSpec::operator==(const DeclTypeSpec &that) const {
605   return category_ == that.category_ && typeSpec_ == that.typeSpec_;
606 }
607 
608 std::string DeclTypeSpec::AsFortran() const {
609   switch (category_) {
610     SWITCH_COVERS_ALL_CASES
611   case Numeric:
612     return numericTypeSpec().AsFortran();
613   case Logical:
614     return logicalTypeSpec().AsFortran();
615   case Character:
616     return characterTypeSpec().AsFortran();
617   case TypeDerived:
618     return "TYPE(" + derivedTypeSpec().AsFortran() + ')';
619   case ClassDerived:
620     return "CLASS(" + derivedTypeSpec().AsFortran() + ')';
621   case TypeStar:
622     return "TYPE(*)";
623   case ClassStar:
624     return "CLASS(*)";
625   }
626 }
627 
628 llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const DeclTypeSpec &x) {
629   return o << x.AsFortran();
630 }
631 
632 void ProcInterface::set_symbol(const Symbol &symbol) {
633   CHECK(!type_);
634   symbol_ = &symbol;
635 }
636 void ProcInterface::set_type(const DeclTypeSpec &type) {
637   CHECK(!symbol_);
638   type_ = &type;
639 }
640 } // namespace Fortran::semantics
641