//===-- lib/Semantics/type.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "flang/Semantics/type.h" #include "check-declarations.h" #include "compute-offsets.h" #include "flang/Evaluate/fold.h" #include "flang/Evaluate/tools.h" #include "flang/Evaluate/type.h" #include "flang/Parser/characters.h" #include "flang/Parser/parse-tree-visitor.h" #include "flang/Semantics/scope.h" #include "flang/Semantics/symbol.h" #include "flang/Semantics/tools.h" #include "llvm/Support/raw_ostream.h" namespace Fortran::semantics { DerivedTypeSpec::DerivedTypeSpec(SourceName name, const Symbol &typeSymbol) : name_{name}, originalTypeSymbol_{typeSymbol}, typeSymbol_{typeSymbol.GetUltimate()} { CHECK(typeSymbol_.has()); } DerivedTypeSpec::DerivedTypeSpec(const DerivedTypeSpec &that) = default; DerivedTypeSpec::DerivedTypeSpec(DerivedTypeSpec &&that) = default; void DerivedTypeSpec::set_scope(const Scope &scope) { CHECK(!scope_); ReplaceScope(scope); } void DerivedTypeSpec::ReplaceScope(const Scope &scope) { CHECK(scope.IsDerivedType()); scope_ = &scope; } const Scope *DerivedTypeSpec::GetScope() const { return scope_ ? scope_ : typeSymbol_.scope(); } void DerivedTypeSpec::AddRawParamValue( const parser::Keyword *keyword, ParamValue &&value) { CHECK(parameters_.empty()); rawParameters_.emplace_back(keyword, std::move(value)); } void DerivedTypeSpec::CookParameters(evaluate::FoldingContext &foldingContext) { if (cooked_) { return; } cooked_ = true; auto &messages{foldingContext.messages()}; if (IsForwardReferenced()) { messages.Say(typeSymbol_.name(), "Derived type '%s' was used but never defined"_err_en_US, typeSymbol_.name()); return; } // Parameters of the most deeply nested "base class" come first when the // derived type is an extension. auto parameterNames{OrderParameterNames(typeSymbol_)}; auto nextNameIter{parameterNames.begin()}; RawParameters raw{std::move(rawParameters_)}; for (auto &[maybeKeyword, value] : raw) { SourceName name; common::TypeParamAttr attr{common::TypeParamAttr::Kind}; if (maybeKeyword) { name = maybeKeyword->v.source; auto it{std::find_if(parameterNames.begin(), parameterNames.end(), [&](const Symbol &symbol) { return symbol.name() == name; })}; if (it == parameterNames.end()) { messages.Say(name, "'%s' is not the name of a parameter for derived type '%s'"_err_en_US, name, typeSymbol_.name()); } else { // Resolve the keyword's symbol maybeKeyword->v.symbol = const_cast(&it->get()); if (const auto *tpd{it->get().detailsIf()}) { attr = tpd->attr().value_or(attr); } } } else if (nextNameIter != parameterNames.end()) { name = nextNameIter->get().name(); if (const auto *tpd{nextNameIter->get().detailsIf()}) { attr = tpd->attr().value_or(attr); } ++nextNameIter; } else { messages.Say(name_, "Too many type parameters given for derived type '%s'"_err_en_US, typeSymbol_.name()); break; } if (FindParameter(name)) { messages.Say(name_, "Multiple values given for type parameter '%s'"_err_en_US, name); } else { value.set_attr(attr); AddParamValue(name, std::move(value)); } } } void DerivedTypeSpec::EvaluateParameters(SemanticsContext &context) { evaluate::FoldingContext &foldingContext{context.foldingContext()}; CookParameters(foldingContext); if (evaluated_) { return; } evaluated_ = true; auto &messages{foldingContext.messages()}; for (const Symbol &symbol : OrderParameterDeclarations(typeSymbol_)) { SourceName name{symbol.name()}; int parameterKind{evaluate::TypeParamInquiry::Result::kind}; // Compute the integer kind value of the type parameter, // which may depend on the values of earlier ones. if (const auto *typeSpec{symbol.GetType()}) { if (const IntrinsicTypeSpec * intrinType{typeSpec->AsIntrinsic()}; intrinType && intrinType->category() == TypeCategory::Integer) { auto restorer{foldingContext.WithPDTInstance(*this)}; auto folded{Fold(foldingContext, KindExpr{intrinType->kind()})}; if (auto k{evaluate::ToInt64(folded)}; k && evaluate::IsValidKindOfIntrinsicType(TypeCategory::Integer, *k)) { parameterKind = static_cast(*k); } else { messages.Say( "Type of type parameter '%s' (%s) is not a valid kind of INTEGER"_err_en_US, name, intrinType->kind().AsFortran()); } } } bool ok{ symbol.get().attr() == common::TypeParamAttr::Len}; if (ParamValue * paramValue{FindParameter(name)}) { // Explicit type parameter value expressions are not folded within // the scope of the derived type being instantiated, as the expressions // themselves are not in that scope and cannot reference its type // parameters. if (const MaybeIntExpr & expr{paramValue->GetExplicit()}) { evaluate::DynamicType dyType{TypeCategory::Integer, parameterKind}; if (auto converted{evaluate::ConvertToType(dyType, SomeExpr{*expr})}) { SomeExpr folded{ evaluate::Fold(foldingContext, std::move(*converted))}; if (auto *intExpr{std::get_if(&folded.u)}) { ok = ok || evaluate::IsActuallyConstant(*intExpr); paramValue->SetExplicit(std::move(*intExpr)); } } else if (!context.HasError(symbol)) { evaluate::SayWithDeclaration(messages, symbol, "Value of type parameter '%s' (%s) is not convertible to its type (%s)"_err_en_US, name, expr->AsFortran(), dyType.AsFortran()); } } } else { // Default type parameter value expressions are folded within // the scope of the derived type being instantiated. const TypeParamDetails &details{symbol.get()}; if (details.init() && details.attr()) { evaluate::DynamicType dyType{TypeCategory::Integer, parameterKind}; if (auto converted{ evaluate::ConvertToType(dyType, SomeExpr{*details.init()})}) { auto restorer{foldingContext.WithPDTInstance(*this)}; SomeExpr folded{ evaluate::Fold(foldingContext, std::move(*converted))}; ok = ok || evaluate::IsActuallyConstant(folded); AddParamValue(name, ParamValue{std::move(std::get(folded.u)), details.attr().value()}); } else { if (!context.HasError(symbol)) { evaluate::SayWithDeclaration(messages, symbol, "Default value of type parameter '%s' (%s) is not convertible to its type (%s)"_err_en_US, name, details.init()->AsFortran(), dyType.AsFortran()); } } } else if (!context.HasError(symbol)) { messages.Say(name_, "Type parameter '%s' lacks a value and has no default"_err_en_US, name); } } if (!ok && !context.HasError(symbol)) { messages.Say( "Value of KIND type parameter '%s' must be constant"_err_en_US, name); } } } void DerivedTypeSpec::AddParamValue(SourceName name, ParamValue &&value) { CHECK(cooked_); auto pair{parameters_.insert(std::make_pair(name, std::move(value)))}; CHECK(pair.second); // name was not already present } bool DerivedTypeSpec::MightBeParameterized() const { return !cooked_ || !parameters_.empty(); } bool DerivedTypeSpec::IsForwardReferenced() const { return typeSymbol_.get().isForwardReferenced(); } bool DerivedTypeSpec::HasDefaultInitialization( bool ignoreAllocatable, bool ignorePointer) const { DirectComponentIterator components{*this}; return bool{std::find_if( components.begin(), components.end(), [&](const Symbol &component) { return IsInitialized(component, /*ignoreDataStatements=*/true, ignoreAllocatable, ignorePointer); })}; } bool DerivedTypeSpec::HasDestruction() const { if (!FinalsForDerivedTypeInstantiation(*this).empty()) { return true; } DirectComponentIterator components{*this}; return bool{std::find_if( components.begin(), components.end(), [&](const Symbol &component) { return IsDestructible(component, &typeSymbol()); })}; } ParamValue *DerivedTypeSpec::FindParameter(SourceName target) { return const_cast( const_cast(this)->FindParameter(target)); } static bool MatchKindParams(const Symbol &typeSymbol, const DerivedTypeSpec &thisSpec, const DerivedTypeSpec &thatSpec) { for (auto ref : typeSymbol.get().paramNameOrder()) { if (ref->get().attr() == common::TypeParamAttr::Kind) { const auto *thisValue{thisSpec.FindParameter(ref->name())}; const auto *thatValue{thatSpec.FindParameter(ref->name())}; if (!thisValue || !thatValue || *thisValue != *thatValue) { return false; } } } if (const DerivedTypeSpec * parent{typeSymbol.GetParentTypeSpec(typeSymbol.scope())}) { return MatchKindParams(parent->typeSymbol(), thisSpec, thatSpec); } else { return true; } } bool DerivedTypeSpec::MatchesOrExtends(const DerivedTypeSpec &that) const { const Symbol *typeSymbol{&typeSymbol_}; while (typeSymbol != &that.typeSymbol_) { if (const DerivedTypeSpec * parent{typeSymbol->GetParentTypeSpec(typeSymbol->scope())}) { typeSymbol = &parent->typeSymbol_; } else { return false; } } return MatchKindParams(*typeSymbol, *this, that); } class InstantiateHelper { public: InstantiateHelper(Scope &scope) : scope_{scope} {} // Instantiate components from fromScope into scope_ void InstantiateComponents(const Scope &); private: SemanticsContext &context() const { return scope_.context(); } evaluate::FoldingContext &foldingContext() { return context().foldingContext(); } template A Fold(A &&expr) { return evaluate::Fold(foldingContext(), std::move(expr)); } void InstantiateComponent(const Symbol &); const DeclTypeSpec *InstantiateType(const Symbol &); const DeclTypeSpec &InstantiateIntrinsicType( SourceName, const DeclTypeSpec &); DerivedTypeSpec CreateDerivedTypeSpec(const DerivedTypeSpec &, bool); Scope &scope_; }; static int PlumbPDTInstantiationDepth(const Scope *scope) { int depth{0}; while (scope->IsParameterizedDerivedTypeInstantiation()) { ++depth; scope = &scope->parent(); } return depth; } // Completes component derived type instantiation and initializer folding // for a non-parameterized derived type Scope. static void InstantiateNonPDTScope(Scope &typeScope, Scope &containingScope) { auto &context{containingScope.context()}; auto &foldingContext{context.foldingContext()}; for (auto &pair : typeScope) { Symbol &symbol{*pair.second}; if (DeclTypeSpec * type{symbol.GetType()}) { if (DerivedTypeSpec * derived{type->AsDerived()}) { if (!(derived->IsForwardReferenced() && IsAllocatableOrPointer(symbol))) { derived->Instantiate(containingScope); } } } if (!IsPointer(symbol)) { if (auto *object{symbol.detailsIf()}) { if (MaybeExpr & init{object->init()}) { auto restorer{foldingContext.messages().SetLocation(symbol.name())}; init = evaluate::NonPointerInitializationExpr( symbol, std::move(*init), foldingContext); } } } } ComputeOffsets(context, typeScope); } void DerivedTypeSpec::Instantiate(Scope &containingScope) { if (instantiated_) { return; } instantiated_ = true; auto &context{containingScope.context()}; auto &foldingContext{context.foldingContext()}; if (IsForwardReferenced()) { foldingContext.messages().Say(typeSymbol_.name(), "The derived type '%s' was forward-referenced but not defined"_err_en_US, typeSymbol_.name()); context.SetError(typeSymbol_); return; } EvaluateParameters(context); const Scope &typeScope{DEREF(typeSymbol_.scope())}; if (!MightBeParameterized()) { scope_ = &typeScope; if (!typeScope.derivedTypeSpec() || *this != *typeScope.derivedTypeSpec()) { Scope &mutableTypeScope{const_cast(typeScope)}; mutableTypeScope.set_derivedTypeSpec(*this); InstantiateNonPDTScope(mutableTypeScope, containingScope); } return; } // New PDT instantiation. Create a new scope and populate it // with components that have been specialized for this set of // parameters. Scope &newScope{containingScope.MakeScope(Scope::Kind::DerivedType)}; newScope.set_derivedTypeSpec(*this); ReplaceScope(newScope); auto restorer{foldingContext.WithPDTInstance(*this)}; std::string desc{typeSymbol_.name().ToString()}; char sep{'('}; for (const Symbol &symbol : OrderParameterDeclarations(typeSymbol_)) { const SourceName &name{symbol.name()}; if (typeScope.find(symbol.name()) != typeScope.end()) { // This type parameter belongs to the derived type itself, not to // one of its ancestors. Put the type parameter expression value, // when there is one, into the new scope as the initialization value // for the parameter. And when there is no explicit value, add an // uninitialized type parameter to forestall use of any default. if (ParamValue * paramValue{FindParameter(name)}) { const TypeParamDetails &details{symbol.get()}; TypeParamDetails instanceDetails{}; if (details.attr()) { paramValue->set_attr(*details.attr()); instanceDetails.set_attr(*details.attr()); } desc += sep; desc += name.ToString(); desc += '='; sep = ','; if (MaybeIntExpr expr{paramValue->GetExplicit()}) { desc += expr->AsFortran(); instanceDetails.set_init( std::move(DEREF(evaluate::UnwrapExpr(*expr)))); if (auto dyType{expr->GetType()}) { instanceDetails.set_type(newScope.MakeNumericType( TypeCategory::Integer, KindExpr{dyType->kind()})); } } if (!instanceDetails.type()) { if (const DeclTypeSpec * type{details.type()}) { instanceDetails.set_type(*type); } } if (!instanceDetails.init()) { desc += '*'; } newScope.try_emplace(name, std::move(instanceDetails)); } } } parser::Message *contextMessage{nullptr}; if (sep != '(') { desc += ')'; contextMessage = new parser::Message{foldingContext.messages().at(), "instantiation of parameterized derived type '%s'"_en_US, desc}; if (auto outer{containingScope.instantiationContext()}) { contextMessage->SetContext(outer.get()); } newScope.set_instantiationContext(contextMessage); } // Instantiate nearly every non-parameter symbol from the original derived // type's scope into the new instance. auto restorer2{foldingContext.messages().SetContext(contextMessage)}; if (PlumbPDTInstantiationDepth(&containingScope) > 100) { foldingContext.messages().Say( "Too many recursive parameterized derived type instantiations"_err_en_US); } else { InstantiateHelper{newScope}.InstantiateComponents(typeScope); } } void InstantiateHelper::InstantiateComponents(const Scope &fromScope) { // Instantiate symbols in declaration order; this ensures that // parent components and type parameters of ancestor types exist // by the time that they're needed. for (SymbolRef ref : fromScope.GetSymbols()) { InstantiateComponent(*ref); } ComputeOffsets(context(), scope_); } // Walks a parsed expression to prepare it for (re)analysis; // clears out the typedExpr analysis results and re-resolves // symbol table pointers of type parameters. class ComponentInitResetHelper { public: explicit ComponentInitResetHelper(Scope &scope) : scope_{scope} {} template bool Pre(const A &) { return true; } template void Post(const A &x) { if constexpr (parser::HasTypedExpr()) { x.typedExpr.Reset(); } } void Post(const parser::Name &name) { if (name.symbol && name.symbol->has()) { name.symbol = scope_.FindComponent(name.source); } } private: Scope &scope_; }; void InstantiateHelper::InstantiateComponent(const Symbol &oldSymbol) { auto pair{scope_.try_emplace( oldSymbol.name(), oldSymbol.attrs(), common::Clone(oldSymbol.details()))}; Symbol &newSymbol{*pair.first->second}; if (!pair.second) { // Symbol was already present in the scope, which can only happen // in the case of type parameters. CHECK(oldSymbol.has()); return; } newSymbol.flags() = oldSymbol.flags(); if (auto *details{newSymbol.detailsIf()}) { if (const DeclTypeSpec * newType{InstantiateType(newSymbol)}) { details->ReplaceType(*newType); } for (ShapeSpec &dim : details->shape()) { if (dim.lbound().isExplicit()) { dim.lbound().SetExplicit(Fold(std::move(dim.lbound().GetExplicit()))); } if (dim.ubound().isExplicit()) { dim.ubound().SetExplicit(Fold(std::move(dim.ubound().GetExplicit()))); } } for (ShapeSpec &dim : details->coshape()) { if (dim.lbound().isExplicit()) { dim.lbound().SetExplicit(Fold(std::move(dim.lbound().GetExplicit()))); } if (dim.ubound().isExplicit()) { dim.ubound().SetExplicit(Fold(std::move(dim.ubound().GetExplicit()))); } } if (const auto *parsedExpr{details->unanalyzedPDTComponentInit()}) { // Analyze the parsed expression in this PDT instantiation context. ComponentInitResetHelper resetter{scope_}; parser::Walk(*parsedExpr, resetter); auto restorer{foldingContext().messages().SetLocation(newSymbol.name())}; details->set_init(evaluate::Fold( foldingContext(), AnalyzeExpr(context(), *parsedExpr))); details->set_unanalyzedPDTComponentInit(nullptr); // Remove analysis results to prevent unparsing or other use of // instantiation-specific expressions. parser::Walk(*parsedExpr, resetter); } if (MaybeExpr & init{details->init()}) { // Non-pointer components with default initializers are // processed now so that those default initializers can be used // in PARAMETER structure constructors. auto restorer{foldingContext().messages().SetLocation(newSymbol.name())}; init = IsPointer(newSymbol) ? Fold(std::move(*init)) : evaluate::NonPointerInitializationExpr( newSymbol, std::move(*init), foldingContext()); } } else if (auto *procDetails{newSymbol.detailsIf()}) { // We have a procedure pointer. Instantiate its return type if (const DeclTypeSpec * returnType{InstantiateType(newSymbol)}) { if (!procDetails->procInterface()) { procDetails->ReplaceType(*returnType); } } } } const DeclTypeSpec *InstantiateHelper::InstantiateType(const Symbol &symbol) { const DeclTypeSpec *type{symbol.GetType()}; if (!type) { return nullptr; // error has occurred } else if (const DerivedTypeSpec * spec{type->AsDerived()}) { return &FindOrInstantiateDerivedType(scope_, CreateDerivedTypeSpec(*spec, symbol.test(Symbol::Flag::ParentComp)), type->category()); } else if (type->AsIntrinsic()) { return &InstantiateIntrinsicType(symbol.name(), *type); } else if (type->category() == DeclTypeSpec::ClassStar) { return type; } else { common::die("InstantiateType: %s", type->AsFortran().c_str()); } } /// Fold explicit length parameters of character components when the explicit /// expression is a constant expression (if it only depends on KIND parameters). /// Do not fold `character(len=pdt_length)`, even if the length parameter is /// constant in the pdt instantiation, in order to avoid losing the information /// that the character component is automatic (and must be a descriptor). static ParamValue FoldCharacterLength(evaluate::FoldingContext &foldingContext, const CharacterTypeSpec &characterSpec) { if (const auto &len{characterSpec.length().GetExplicit()}) { if (evaluate::IsConstantExpr(*len)) { return ParamValue{evaluate::Fold(foldingContext, common::Clone(*len)), common::TypeParamAttr::Len}; } } return characterSpec.length(); } // Apply type parameter values to an intrinsic type spec. const DeclTypeSpec &InstantiateHelper::InstantiateIntrinsicType( SourceName symbolName, const DeclTypeSpec &spec) { const IntrinsicTypeSpec &intrinsic{DEREF(spec.AsIntrinsic())}; if (spec.category() != DeclTypeSpec::Character && evaluate::IsActuallyConstant(intrinsic.kind())) { return spec; // KIND is already a known constant } // The expression was not originally constant, but now it must be so // in the context of a parameterized derived type instantiation. KindExpr copy{Fold(common::Clone(intrinsic.kind()))}; int kind{context().GetDefaultKind(intrinsic.category())}; if (auto value{evaluate::ToInt64(copy)}) { if (foldingContext().targetCharacteristics().IsTypeEnabled( intrinsic.category(), *value)) { kind = *value; } else { foldingContext().messages().Say(symbolName, "KIND parameter value (%jd) of intrinsic type %s " "did not resolve to a supported value"_err_en_US, *value, parser::ToUpperCaseLetters(EnumToString(intrinsic.category()))); } } switch (spec.category()) { case DeclTypeSpec::Numeric: return scope_.MakeNumericType(intrinsic.category(), KindExpr{kind}); case DeclTypeSpec::Logical: return scope_.MakeLogicalType(KindExpr{kind}); case DeclTypeSpec::Character: return scope_.MakeCharacterType( FoldCharacterLength(foldingContext(), spec.characterTypeSpec()), KindExpr{kind}); default: CRASH_NO_CASE; } } DerivedTypeSpec InstantiateHelper::CreateDerivedTypeSpec( const DerivedTypeSpec &spec, bool isParentComp) { DerivedTypeSpec result{spec}; result.CookParameters(foldingContext()); // enables AddParamValue() if (isParentComp) { // Forward any explicit type parameter values from the // derived type spec under instantiation that define type parameters // of the parent component to the derived type spec of the // parent component. const DerivedTypeSpec &instanceSpec{DEREF(foldingContext().pdtInstance())}; for (const auto &[name, value] : instanceSpec.parameters()) { if (scope_.find(name) == scope_.end()) { result.AddParamValue(name, ParamValue{value}); } } } return result; } std::string DerivedTypeSpec::VectorTypeAsFortran() const { std::string buf; llvm::raw_string_ostream ss{buf}; switch (category()) { SWITCH_COVERS_ALL_CASES case (Fortran::semantics::DerivedTypeSpec::Category::IntrinsicVector): { int64_t vecElemKind; int64_t vecElemCategory; for (const auto &pair : parameters()) { if (pair.first == "element_category") { vecElemCategory = Fortran::evaluate::ToInt64(pair.second.GetExplicit()).value_or(-1); } else if (pair.first == "element_kind") { vecElemKind = Fortran::evaluate::ToInt64(pair.second.GetExplicit()).value_or(0); } } assert((vecElemCategory >= 0 && static_cast(vecElemCategory) < Fortran::common::VectorElementCategory_enumSize) && "Vector element type is not specified"); assert(vecElemKind && "Vector element kind is not specified"); ss << "vector("; switch (static_cast(vecElemCategory)) { SWITCH_COVERS_ALL_CASES case common::VectorElementCategory::Integer: ss << "integer(" << vecElemKind << ")"; break; case common::VectorElementCategory::Unsigned: ss << "unsigned(" << vecElemKind << ")"; break; case common::VectorElementCategory::Real: ss << "real(" << vecElemKind << ")"; break; } ss << ")"; break; } case (Fortran::semantics::DerivedTypeSpec::Category::PairVector): ss << "__vector_pair"; break; case (Fortran::semantics::DerivedTypeSpec::Category::QuadVector): ss << "__vector_quad"; break; case (Fortran::semantics::DerivedTypeSpec::Category::DerivedType): Fortran::common::die("Vector element type not implemented"); } return buf; } std::string DerivedTypeSpec::AsFortran() const { std::string buf; llvm::raw_string_ostream ss{buf}; ss << originalTypeSymbol_.name(); if (!rawParameters_.empty()) { CHECK(parameters_.empty()); ss << '('; bool first = true; for (const auto &[maybeKeyword, value] : rawParameters_) { if (first) { first = false; } else { ss << ','; } if (maybeKeyword) { ss << maybeKeyword->v.source.ToString() << '='; } ss << value.AsFortran(); } ss << ')'; } else if (!parameters_.empty()) { ss << '('; bool first = true; for (const auto &[name, value] : parameters_) { if (first) { first = false; } else { ss << ','; } ss << name.ToString() << '=' << value.AsFortran(); } ss << ')'; } return buf; } llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const DerivedTypeSpec &x) { return o << x.AsFortran(); } Bound::Bound(common::ConstantSubscript bound) : expr_{bound} {} llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const Bound &x) { if (x.isStar()) { o << '*'; } else if (x.isColon()) { o << ':'; } else if (x.expr_) { x.expr_->AsFortran(o); } else { o << ""; } return o; } llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const ShapeSpec &x) { if (x.lb_.isStar()) { CHECK(x.ub_.isStar()); o << ".."; } else { if (!x.lb_.isColon()) { o << x.lb_; } o << ':'; if (!x.ub_.isColon()) { o << x.ub_; } } return o; } llvm::raw_ostream &operator<<( llvm::raw_ostream &os, const ArraySpec &arraySpec) { char sep{'('}; for (auto &shape : arraySpec) { os << sep << shape; sep = ','; } if (sep == ',') { os << ')'; } return os; } ParamValue::ParamValue(MaybeIntExpr &&expr, common::TypeParamAttr attr) : attr_{attr}, expr_{std::move(expr)} {} ParamValue::ParamValue(SomeIntExpr &&expr, common::TypeParamAttr attr) : attr_{attr}, expr_{std::move(expr)} {} ParamValue::ParamValue( common::ConstantSubscript value, common::TypeParamAttr attr) : ParamValue(SomeIntExpr{evaluate::Expr{value}}, attr) {} void ParamValue::SetExplicit(SomeIntExpr &&x) { category_ = Category::Explicit; expr_ = std::move(x); } std::string ParamValue::AsFortran() const { switch (category_) { SWITCH_COVERS_ALL_CASES case Category::Assumed: return "*"; case Category::Deferred: return ":"; case Category::Explicit: if (expr_) { std::string buf; llvm::raw_string_ostream ss{buf}; expr_->AsFortran(ss); return buf; } else { return ""; } } } llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const ParamValue &x) { return o << x.AsFortran(); } IntrinsicTypeSpec::IntrinsicTypeSpec(TypeCategory category, KindExpr &&kind) : category_{category}, kind_{std::move(kind)} { CHECK(category != TypeCategory::Derived); } static std::string KindAsFortran(const KindExpr &kind) { std::string buf; llvm::raw_string_ostream ss{buf}; if (auto k{evaluate::ToInt64(kind)}) { ss << *k; // emit unsuffixed kind code } else { kind.AsFortran(ss); } return buf; } std::string IntrinsicTypeSpec::AsFortran() const { return parser::ToUpperCaseLetters(common::EnumToString(category_)) + '(' + KindAsFortran(kind_) + ')'; } llvm::raw_ostream &operator<<( llvm::raw_ostream &os, const IntrinsicTypeSpec &x) { return os << x.AsFortran(); } std::string CharacterTypeSpec::AsFortran() const { return "CHARACTER(" + length_.AsFortran() + ',' + KindAsFortran(kind()) + ')'; } llvm::raw_ostream &operator<<( llvm::raw_ostream &os, const CharacterTypeSpec &x) { return os << x.AsFortran(); } DeclTypeSpec::DeclTypeSpec(NumericTypeSpec &&typeSpec) : category_{Numeric}, typeSpec_{std::move(typeSpec)} {} DeclTypeSpec::DeclTypeSpec(LogicalTypeSpec &&typeSpec) : category_{Logical}, typeSpec_{std::move(typeSpec)} {} DeclTypeSpec::DeclTypeSpec(const CharacterTypeSpec &typeSpec) : category_{Character}, typeSpec_{typeSpec} {} DeclTypeSpec::DeclTypeSpec(CharacterTypeSpec &&typeSpec) : category_{Character}, typeSpec_{std::move(typeSpec)} {} DeclTypeSpec::DeclTypeSpec(Category category, const DerivedTypeSpec &typeSpec) : category_{category}, typeSpec_{typeSpec} { CHECK(category == TypeDerived || category == ClassDerived); } DeclTypeSpec::DeclTypeSpec(Category category, DerivedTypeSpec &&typeSpec) : category_{category}, typeSpec_{std::move(typeSpec)} { CHECK(category == TypeDerived || category == ClassDerived); } DeclTypeSpec::DeclTypeSpec(Category category) : category_{category} { CHECK(category == TypeStar || category == ClassStar); } bool DeclTypeSpec::IsNumeric(TypeCategory tc) const { return category_ == Numeric && numericTypeSpec().category() == tc; } bool DeclTypeSpec::IsSequenceType() const { if (const DerivedTypeSpec * derivedType{AsDerived()}) { const auto *typeDetails{ derivedType->typeSymbol().detailsIf()}; return typeDetails && typeDetails->sequence(); } return false; } const NumericTypeSpec &DeclTypeSpec::numericTypeSpec() const { CHECK(category_ == Numeric); return std::get(typeSpec_); } const LogicalTypeSpec &DeclTypeSpec::logicalTypeSpec() const { CHECK(category_ == Logical); return std::get(typeSpec_); } bool DeclTypeSpec::operator==(const DeclTypeSpec &that) const { return category_ == that.category_ && typeSpec_ == that.typeSpec_; } std::string DeclTypeSpec::AsFortran() const { switch (category_) { SWITCH_COVERS_ALL_CASES case Numeric: return numericTypeSpec().AsFortran(); case Logical: return logicalTypeSpec().AsFortran(); case Character: return characterTypeSpec().AsFortran(); case TypeDerived: if (derivedTypeSpec() .typeSymbol() .get() .isDECStructure()) { return "RECORD" + derivedTypeSpec().typeSymbol().name().ToString(); } else if (derivedTypeSpec().IsVectorType()) { return derivedTypeSpec().VectorTypeAsFortran(); } else { return "TYPE(" + derivedTypeSpec().AsFortran() + ')'; } case ClassDerived: return "CLASS(" + derivedTypeSpec().AsFortran() + ')'; case TypeStar: return "TYPE(*)"; case ClassStar: return "CLASS(*)"; } } llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const DeclTypeSpec &x) { return o << x.AsFortran(); } } // namespace Fortran::semantics