blob: 69145b90c4e3197fb79a9b9a6c29a2d6b7ddf48f [file] [log] [blame]
//===-- 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/Parser/characters.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}, typeSymbol_{typeSymbol} {
CHECK(typeSymbol.has<DerivedTypeDetails>());
}
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;
}
void DerivedTypeSpec::AddRawParamValue(
const std::optional<parser::Keyword> &keyword, ParamValue &&value) {
CHECK(parameters_.empty());
rawParameters_.emplace_back(keyword ? &*keyword : nullptr, 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 parameterDecls{OrderParameterDeclarations(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(parameterDecls.begin(), parameterDecls.end(),
[&](const Symbol &symbol) { return symbol.name() == name; })};
if (it == parameterDecls.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<Symbol *>(&it->get());
attr = it->get().get<TypeParamDetails>().attr();
}
} else if (nextNameIter != parameterNames.end()) {
name = *nextNameIter++;
auto it{std::find_if(parameterDecls.begin(), parameterDecls.end(),
[&](const Symbol &symbol) { return symbol.name() == name; })};
if (it == parameterDecls.end()) {
break;
}
attr = it->get().get<TypeParamDetails>().attr();
} 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()};
// Fold the explicit type parameter value expressions first. Do not
// fold them within the scope of the derived type being instantiated;
// these expressions cannot use its type parameters. Convert the values
// of the expressions to the declared types of the type parameters.
auto parameterDecls{OrderParameterDeclarations(typeSymbol_)};
for (const Symbol &symbol : parameterDecls) {
const SourceName &name{symbol.name()};
if (ParamValue * paramValue{FindParameter(name)}) {
if (const MaybeIntExpr & expr{paramValue->GetExplicit()}) {
if (auto converted{evaluate::ConvertToType(symbol, SomeExpr{*expr})}) {
SomeExpr folded{
evaluate::Fold(foldingContext, std::move(*converted))};
if (auto *intExpr{std::get_if<SomeIntExpr>(&folded.u)}) {
paramValue->SetExplicit(std::move(*intExpr));
continue;
}
}
if (!context.HasError(symbol)) {
evaluate::SayWithDeclaration(messages, symbol,
"Value of type parameter '%s' (%s) is not convertible to its"
" type"_err_en_US,
name, expr->AsFortran());
}
}
}
}
// Default initialization expressions for the derived type's parameters
// may reference other parameters so long as the declaration precedes the
// use in the expression (10.1.12). This is not necessarily the same
// order as "type parameter order" (7.5.3.2).
// Type parameter default value expressions are folded in declaration order
// within the scope of the derived type so that the values of earlier type
// parameters are available for use in the default initialization
// expressions of later parameters.
auto restorer{foldingContext.WithPDTInstance(*this)};
for (const Symbol &symbol : parameterDecls) {
const SourceName &name{symbol.name()};
if (!FindParameter(name)) {
const TypeParamDetails &details{symbol.get<TypeParamDetails>()};
if (details.init()) {
auto expr{evaluate::Fold(foldingContext, SomeExpr{*details.init()})};
AddParamValue(name,
ParamValue{
std::move(std::get<SomeIntExpr>(expr.u)), details.attr()});
} else if (!context.HasError(symbol)) {
messages.Say(name_,
"Type parameter '%s' lacks a value and has no default"_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<DerivedTypeDetails>().isForwardReferenced();
}
bool DerivedTypeSpec::HasDefaultInitialization() const {
DirectComponentIterator components{*this};
return bool{std::find_if(components.begin(), components.end(),
[&](const Symbol &component) { return IsInitialized(component); })};
}
bool DerivedTypeSpec::HasDestruction() const {
if (!typeSymbol().get<DerivedTypeDetails>().finals().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<ParamValue *>(
const_cast<const DerivedTypeSpec *>(this)->FindParameter(target));
}
// Objects of derived types might be assignment compatible if they are equal
// with respect to everything other than their instantiated type parameters
// and their constant instantiated type parameters have the same values.
bool DerivedTypeSpec::MightBeAssignmentCompatibleWith(
const DerivedTypeSpec &that) const {
if (!RawEquals(that)) {
return false;
}
return AreTypeParamCompatible(*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 <typename A> 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<ObjectEntityDetails>()}) {
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()) {
CHECK(*this == *typeScope.derivedTypeSpec());
} else {
Scope &mutableTypeScope{const_cast<Scope &>(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
// into the new scope as the initialization value for the parameter.
if (ParamValue * paramValue{FindParameter(name)}) {
const TypeParamDetails &details{symbol.get<TypeParamDetails>()};
paramValue->set_attr(details.attr());
if (MaybeIntExpr expr{paramValue->GetExplicit()}) {
if (auto folded{evaluate::NonPointerInitializationExpr(symbol,
SomeExpr{std::move(*expr)}, foldingContext, &newScope)}) {
desc += sep;
desc += name.ToString();
desc += '=';
desc += folded->AsFortran();
sep = ',';
TypeParamDetails instanceDetails{details.attr()};
if (const DeclTypeSpec * type{details.type()}) {
instanceDetails.set_type(*type);
}
instanceDetails.set_init(
std::move(DEREF(evaluate::UnwrapExpr<SomeIntExpr>(*folded))));
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 every non-parameter symbol from the original derived
// type's scope into the new instance.
newScope.AddSourceRange(typeScope.sourceRange());
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) {
for (const auto &pair : fromScope) {
InstantiateComponent(*pair.second);
}
ComputeOffsets(context(), 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<TypeParamDetails>());
return;
}
newSymbol.flags() = oldSymbol.flags();
if (auto *details{newSymbol.detailsIf<ObjectEntityDetails>()}) {
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 (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<ProcEntityDetails>()}) {
// We have a procedure pointer. Instantiate its return type
if (const DeclTypeSpec * returnType{InstantiateType(newSymbol)}) {
ProcInterface &interface{procDetails->interface()};
if (!interface.symbol()) {
// Don't change the type for interfaces based on symbols
interface.set_type(*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());
}
}
// 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 (evaluate::ToInt64(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 (evaluate::IsValidKindOfIntrinsicType(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(
ParamValue{spec.characterTypeSpec().length()}, 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::AsFortran() const {
std::string buf;
llvm::raw_string_ostream ss{buf};
ss << 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 ss.str();
}
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.isAssumed()) {
o << '*';
} else if (x.isDeferred()) {
o << ':';
} else if (x.expr_) {
x.expr_->AsFortran(o);
} else {
o << "<no-expr>";
}
return o;
}
llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const ShapeSpec &x) {
if (x.lb_.isAssumed()) {
CHECK(x.ub_.isAssumed());
o << "..";
} else {
if (!x.lb_.isDeferred()) {
o << x.lb_;
}
o << ':';
if (!x.ub_.isDeferred()) {
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<evaluate::SubscriptInteger>{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 ss.str();
} 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 ss.str();
}
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<DerivedTypeDetails>()};
return typeDetails && typeDetails->sequence();
}
return false;
}
const NumericTypeSpec &DeclTypeSpec::numericTypeSpec() const {
CHECK(category_ == Numeric);
return std::get<NumericTypeSpec>(typeSpec_);
}
const LogicalTypeSpec &DeclTypeSpec::logicalTypeSpec() const {
CHECK(category_ == Logical);
return std::get<LogicalTypeSpec>(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:
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();
}
void ProcInterface::set_symbol(const Symbol &symbol) {
CHECK(!type_);
symbol_ = &symbol;
}
void ProcInterface::set_type(const DeclTypeSpec &type) {
CHECK(!symbol_);
type_ = &type;
}
} // namespace Fortran::semantics