[flang] checkpoint

Original-commit: flang-compiler/f18@7fae33797a
Reviewed-on: https://github.com/flang-compiler/f18/pull/144
Tree-same-pre-rewrite: false
This commit is contained in:
peter klausler 2018-07-19 09:53:42 -07:00
parent aa6c6f97a9
commit 0aae9893a5
8 changed files with 263 additions and 46 deletions

View File

@ -381,6 +381,87 @@ std::optional<typename IntegerExpr<KIND>::Constant> IntegerExpr<KIND>::Fold(
u_); u_);
} }
template<int KIND>
std::optional<typename RealExpr<KIND>::Constant>
RealExpr<KIND>::ConstantValue() const {
if (auto c{std::get_if<Constant>(&u_)}) {
return {*c};
}
return {};
}
template<int KIND> void RealExpr<KIND>::Fold(FoldingContext &context) {
// TODO
}
template<int KIND>
std::optional<typename ComplexExpr<KIND>::Constant>
ComplexExpr<KIND>::ConstantValue() const {
if (auto c{std::get_if<Constant>(&u_)}) {
return {*c};
}
return {};
}
template<int KIND> void ComplexExpr<KIND>::Fold(FoldingContext &context) {
// TODO
}
template<int KIND>
std::optional<typename CharacterExpr<KIND>::Constant>
CharacterExpr<KIND>::ConstantValue() const {
if (auto c{std::get_if<Constant>(&u_)}) {
return {*c};
}
return {};
}
template<int KIND> void CharacterExpr<KIND>::Fold(FoldingContext &context) {
// TODO
}
std::optional<bool> LogicalExpr::ConstantValue() const {
if (auto c{std::get_if<bool>(&u_)}) {
return {*c};
}
return {};
}
void LogicalExpr::Fold(FoldingContext &context) {
// TODO and comparisons too
}
std::optional<GenericConstant> GenericExpr::ConstantValue() const {
return std::visit([](const auto &x) -> std::optional<GenericConstant> {
if (auto c{x.ConstantValue()}) {
return {GenericConstant{std::move(*c)}};
}
return {};
}, u);
}
template<Category CAT> std::optional<CategoryConstant<CAT>> CategoryExpr<CAT>::ConstantValue() const {
return std::visit([](const auto &x) -> std::optional<CategoryConstant<CAT>> {
if (auto c{x.ConstantValue()}) {
return {CategoryConstant<CAT>{std::move(*c)}};
}
return {};
}, u);
}
template<Category CAT> void CategoryExpr<CAT>::Fold(FoldingContext &context) {
std::visit([&](auto &x){ x.Fold(context); }, u);
}
void GenericExpr::Fold(FoldingContext &context) {
std::visit([&](auto &x){ x.Fold(context); }, u);
}
template struct CategoryExpr<Category::Integer>;
template struct CategoryExpr<Category::Real>;
template struct CategoryExpr<Category::Complex>;
template struct CategoryExpr<Category::Character>;
template class Expr<Category::Integer, 1>; template class Expr<Category::Integer, 1>;
template class Expr<Category::Integer, 2>; template class Expr<Category::Integer, 2>;
template class Expr<Category::Integer, 4>; template class Expr<Category::Integer, 4>;

View File

@ -236,6 +236,9 @@ public:
Expr(std::enable_if_t<!std::is_reference_v<A>, A> &&x) : u_{std::move(x)} {} Expr(std::enable_if_t<!std::is_reference_v<A>, A> &&x) : u_{std::move(x)} {}
template<typename A> Expr(CopyableIndirection<A> &&x) : u_{std::move(x)} {} template<typename A> Expr(CopyableIndirection<A> &&x) : u_{std::move(x)} {}
std::optional<Constant> ConstantValue() const;
void Fold(FoldingContext &c);
private: private:
std::variant<Constant, CopyableIndirection<DataRef>, std::variant<Constant, CopyableIndirection<DataRef>,
CopyableIndirection<ComplexPart>, CopyableIndirection<FunctionRef>, CopyableIndirection<ComplexPart>, CopyableIndirection<FunctionRef>,
@ -285,6 +288,9 @@ public:
Expr(std::enable_if_t<!std::is_reference_v<A>, A> &&x) : u_{std::move(x)} {} Expr(std::enable_if_t<!std::is_reference_v<A>, A> &&x) : u_{std::move(x)} {}
template<typename A> Expr(CopyableIndirection<A> &&x) : u_{std::move(x)} {} template<typename A> Expr(CopyableIndirection<A> &&x) : u_{std::move(x)} {}
std::optional<Constant> ConstantValue() const;
void Fold(FoldingContext &c);
private: private:
std::variant<Constant, CopyableIndirection<DataRef>, std::variant<Constant, CopyableIndirection<DataRef>,
CopyableIndirection<FunctionRef>, Parentheses, Negate, Add, Subtract, CopyableIndirection<FunctionRef>, Parentheses, Negate, Add, Subtract,
@ -315,6 +321,8 @@ public:
Expr(std::enable_if_t<!std::is_reference_v<A>, A> &&x) : u_{std::move(x)} {} Expr(std::enable_if_t<!std::is_reference_v<A>, A> &&x) : u_{std::move(x)} {}
template<typename A> Expr(CopyableIndirection<A> &&x) : u_{std::move(x)} {} template<typename A> Expr(CopyableIndirection<A> &&x) : u_{std::move(x)} {}
std::optional<Constant> ConstantValue() const;
void Fold(FoldingContext &c);
SubscriptIntegerExpr LEN() const; SubscriptIntegerExpr LEN() const;
private: private:
@ -335,6 +343,7 @@ template<typename EXPR> struct Comparison : Binary<EXPR, EXPR, bool> {
: Binary<EXPR, EXPR, bool>{a, b}, opr{r} {} : Binary<EXPR, EXPR, bool>{a, b}, opr{r} {}
Comparison(RelationalOperator r, EXPR &&a, EXPR &&b) Comparison(RelationalOperator r, EXPR &&a, EXPR &&b)
: Binary<EXPR, EXPR, bool>{std::move(a), std::move(b)}, opr{r} {} : Binary<EXPR, EXPR, bool>{std::move(a), std::move(b)}, opr{r} {}
std::optional<bool> Fold(FoldingContext &c);
RelationalOperator opr; RelationalOperator opr;
}; };
@ -359,11 +368,12 @@ extern template struct Comparison<CharacterExpr<1>>;
// of a specific category. // of a specific category.
template<Category CAT> struct CategoryComparison { template<Category CAT> struct CategoryComparison {
CLASS_BOILERPLATE(CategoryComparison) CLASS_BOILERPLATE(CategoryComparison)
template<int KIND> using KindComparison = Comparison<Expr<CAT, KIND>>;
template<int KIND> template<int KIND>
CategoryComparison(const Comparison<Expr<CAT, KIND>> &x) : u{x} {} CategoryComparison(const KindComparison<KIND> &x) : u{x} {}
template<int KIND> template<int KIND>
CategoryComparison(Comparison<Expr<CAT, KIND>> &&x) : u{std::move(x)} {} CategoryComparison(KindComparison<KIND> &&x) : u{std::move(x)} {}
template<int K> using KindComparison = Comparison<Expr<CAT, K>>; std::optional<bool> Fold(FoldingContext &c);
typename KindsVariant<CAT, KindComparison>::type u; typename KindsVariant<CAT, KindComparison>::type u;
}; };
@ -372,7 +382,7 @@ template<> class Expr<Category::Logical, 1> {
public: public:
using Constant = bool; using Constant = bool;
struct Not : Unary<Expr, bool> { struct Not : Unary<Expr, bool> {
using Unary<Expr, Constant>::Unary; using Unary<Expr, bool>::Unary;
}; };
using Bin = Binary<Expr, Expr, bool>; using Bin = Binary<Expr, Expr, bool>;
struct And : public Bin { struct And : public Bin {
@ -389,7 +399,7 @@ public:
}; };
CLASS_BOILERPLATE(Expr) CLASS_BOILERPLATE(Expr)
Expr(Constant x) : u_{x} {} Expr(bool x) : u_{x} {}
template<Category CAT, int KIND> template<Category CAT, int KIND>
Expr(const Comparison<Expr<CAT, KIND>> &x) : u_{CategoryComparison<CAT>{x}} {} Expr(const Comparison<Expr<CAT, KIND>> &x) : u_{CategoryComparison<CAT>{x}} {}
template<Category CAT, int KIND> template<Category CAT, int KIND>
@ -400,8 +410,11 @@ public:
Expr(std::enable_if_t<!std::is_reference_v<A>, A> &&x) : u_{std::move(x)} {} Expr(std::enable_if_t<!std::is_reference_v<A>, A> &&x) : u_{std::move(x)} {}
template<typename A> Expr(CopyableIndirection<A> &&x) : u_{std::move(x)} {} template<typename A> Expr(CopyableIndirection<A> &&x) : u_{std::move(x)} {}
std::optional<bool> ConstantValue() const;
void Fold(FoldingContext &c);
private: private:
std::variant<Constant, CopyableIndirection<DataRef>, std::variant<bool, CopyableIndirection<DataRef>,
CopyableIndirection<FunctionRef>, Not, And, Or, Eqv, Neqv, CopyableIndirection<FunctionRef>, Not, And, Or, Eqv, Neqv,
CategoryComparison<Category::Integer>, CategoryComparison<Category::Real>, CategoryComparison<Category::Integer>, CategoryComparison<Category::Real>,
CategoryComparison<Category::Complex>, CategoryComparison<Category::Complex>,
@ -427,17 +440,43 @@ extern template class Expr<Category::Complex, 16>;
extern template class Expr<Category::Character, 1>; extern template class Expr<Category::Character, 1>;
extern template class Expr<Category::Logical, 1>; extern template class Expr<Category::Logical, 1>;
// Holds a constant of any kind in an intrinsic type category.
template<Category CAT> struct CategoryConstant {
CLASS_BOILERPLATE(CategoryConstant)
template<int KIND> using KindConstant = typename Expr<CAT, KIND>::Constant;
template<typename A> CategoryConstant(const A &x) : u{x} {}
template<typename A> CategoryConstant(std::enable_if_t<!std::is_reference_v<A>, A> &&x) : u{std::move(x)} {}
typename KindsVariant<CAT, KindConstant>::type u;
};
// Holds a constant of any intrinsic category and size.
struct GenericConstant {
CLASS_BOILERPLATE(GenericConstant)
template<Category CAT, int KIND>
GenericConstant(const typename Expr<CAT, KIND>::Constant &x) : u{CategoryConstant<CAT>{x}} {}
template<Category CAT, int KIND>
GenericConstant(typename Expr<CAT, KIND>::Constant &&x) : u{CategoryConstant<CAT>{std::move(x)}} {}
template<typename A> GenericConstant(const A &x) : u{x} {}
template<typename A>
GenericConstant(std::enable_if_t<!std::is_reference_v<A>, A> &&x)
: u{std::move(x)} {}
std::variant<CategoryConstant<Category::Integer>, CategoryConstant<Category::Real>, CategoryConstant<Category::Complex>, CategoryConstant<Category::Character>, bool> u;
};
// Dynamically polymorphic expressions that can hold any supported kind // Dynamically polymorphic expressions that can hold any supported kind
// of a specific category. // of a specific intrinsic type category.
template<Category CAT> struct CategoryExpr { template<Category CAT> struct CategoryExpr {
CLASS_BOILERPLATE(CategoryExpr) CLASS_BOILERPLATE(CategoryExpr)
template<int KIND> CategoryExpr(const Expr<CAT, KIND> &x) : u{x} {} template<int KIND> using KindExpr = Expr<CAT, KIND>;
template<int KIND> CategoryExpr(Expr<CAT, KIND> &&x) : u{std::move(x)} {} template<int KIND> CategoryExpr(const KindExpr<KIND> &x) : u{x} {}
template<int K> using KindExpr = Expr<CAT, K>; template<int KIND> CategoryExpr(KindExpr<KIND> &&x) : u{std::move(x)} {}
std::optional<CategoryConstant<CAT>> ConstantValue() const;
void Fold(FoldingContext &);
typename KindsVariant<CAT, KindExpr>::type u; typename KindsVariant<CAT, KindExpr>::type u;
}; };
// A completely generic expression, polymorphic across the type categories. // A completely generic expression, polymorphic across the intrinsic type
// categories and each of their kinds.
struct GenericExpr { struct GenericExpr {
CLASS_BOILERPLATE(GenericExpr) CLASS_BOILERPLATE(GenericExpr)
template<Category CAT, int KIND> template<Category CAT, int KIND>
@ -448,6 +487,9 @@ struct GenericExpr {
template<typename A> template<typename A>
GenericExpr(std::enable_if_t<!std::is_reference_v<A>, A> &&x) GenericExpr(std::enable_if_t<!std::is_reference_v<A>, A> &&x)
: u{std::move(x)} {} : u{std::move(x)} {}
std::optional<GenericConstant> ConstantValue() const;
void Fold(FoldingContext &);
int Rank() const { return 1; } // TODO
std::variant<GenericIntegerExpr, GenericRealExpr, GenericComplexExpr, std::variant<GenericIntegerExpr, GenericRealExpr, GenericComplexExpr,
GenericCharacterExpr, LogicalExpr> GenericCharacterExpr, LogicalExpr>
u; u;

View File

@ -117,13 +117,24 @@ using DefaultCharacter = Type<Category::Character, 1>;
using SubscriptInteger = Type<Category::Integer, 8>; using SubscriptInteger = Type<Category::Integer, 8>;
// These macros invoke other macros on each of the supported kinds of
// a given category.
#define COMMA ,
#define FOR_EACH_INTEGER_KIND(M,SEP) M(1) SEP M(2) SEP M(4) SEP M(8) SEP M(16)
#define FOR_EACH_REAL_KIND(M,SEP) M(2) SEP M(4) SEP M(8) SEP M(10) SEP M(16)
#define FOR_EACH_COMPLEX_KIND(M,SEP) FOR_EACH_REAL_KIND(M,SEP)
#define FOR_EACH_CHARACTER_KIND(M,SEP) M(1)
#define FOR_EACH_LOGICAL_KIND(M,SEP) M(1) SEP M(2) SEP M(4) SEP M(8)
// These templates create instances of std::variant<> that can contain // These templates create instances of std::variant<> that can contain
// applications of some class template to all of the supported kinds of // applications of some class template to all of the supported kinds of
// a category of intrinsic type. // a category of intrinsic type.
#define TKIND(K) T<K>
template<Category CAT, template<int> class T> struct KindsVariant; template<Category CAT, template<int> class T> struct KindsVariant;
template<template<int> class T> struct KindsVariant<Category::Integer, T> { template<template<int> class T> struct KindsVariant<Category::Integer, T> {
using type = std::variant<T<1>, T<2>, T<4>, T<8>, T<16>>; using type = std::variant<FOR_EACH_INTEGER_KIND(TKIND,COMMA)>;
}; };
// TODO use FOR_EACH...
template<template<int> class T> struct KindsVariant<Category::Real, T> { template<template<int> class T> struct KindsVariant<Category::Real, T> {
using type = std::variant<T<2>, T<4>, T<8>, T<10>, T<16>>; using type = std::variant<T<2>, T<4>, T<8>, T<10>, T<16>>;
}; };
@ -136,5 +147,6 @@ template<template<int> class T> struct KindsVariant<Category::Character, T> {
template<template<int> class T> struct KindsVariant<Category::Logical, T> { template<template<int> class T> struct KindsVariant<Category::Logical, T> {
using type = std::variant<T<1>, T<2>, T<4>, T<8>>; using type = std::variant<T<1>, T<2>, T<4>, T<8>>;
}; };
#undef TKIND
} // namespace Fortran::evaluate } // namespace Fortran::evaluate
#endif // FORTRAN_EVALUATE_TYPE_H_ #endif // FORTRAN_EVALUATE_TYPE_H_

View File

@ -113,6 +113,8 @@ public:
Message(ProvenanceRange pr, const MessageFixedText &t) Message(ProvenanceRange pr, const MessageFixedText &t)
: location_{pr}, text_{t} {} : location_{pr}, text_{t} {}
Message(ProvenanceRange pr, const MessageFormattedText &s)
: location_{pr}, text_{std::move(s)} {}
Message(ProvenanceRange pr, MessageFormattedText &&s) Message(ProvenanceRange pr, MessageFormattedText &&s)
: location_{pr}, text_{std::move(s)} {} : location_{pr}, text_{std::move(s)} {}
Message(ProvenanceRange pr, const MessageExpectedText &t) Message(ProvenanceRange pr, const MessageExpectedText &t)
@ -120,6 +122,8 @@ public:
Message(CharBlock csr, const MessageFixedText &t) Message(CharBlock csr, const MessageFixedText &t)
: location_{csr}, text_{t} {} : location_{csr}, text_{t} {}
Message(CharBlock csr, const MessageFormattedText &s)
: location_{csr}, text_{std::move(s)} {}
Message(CharBlock csr, MessageFormattedText &&s) Message(CharBlock csr, MessageFormattedText &&s)
: location_{csr}, text_{std::move(s)} {} : location_{csr}, text_{std::move(s)} {}
Message(CharBlock csr, const MessageExpectedText &t) Message(CharBlock csr, const MessageExpectedText &t)

View File

@ -62,7 +62,7 @@ class Symbol;
} // namespace Fortran::semantics } // namespace Fortran::semantics
namespace Fortran::evaluate { namespace Fortran::evaluate {
class GenericExpr; struct GenericExpr;
} // namespace Fortran::evaluate } // namespace Fortran::evaluate
// Most non-template classes in this file use these default definitions // Most non-template classes in this file use these default definitions

View File

@ -19,45 +19,112 @@ using namespace Fortran::parser::literals;
namespace Fortran::semantics { namespace Fortran::semantics {
std::optional<evaluate::GenericExpr> ExpressionAnalyzer::Analyze( template<typename A>
const parser::Expr &x) { std::optional<evaluate::GenericExpr> AnalyzeHelper(ExpressionAnalyzer &ea, const A &tree) {
return std::visit( return ea.Analyze(tree);
common::visitors{
[&](const parser::LiteralConstant &c) { return Analyze(c); },
[&](const auto &) { return std::optional<evaluate::GenericExpr>{}; }},
x.u);
} }
std::optional<evaluate::GenericExpr> ExpressionAnalyzer::Analyze( template<typename A>
const parser::IntLiteralConstant &x) { std::optional<evaluate::GenericExpr> AnalyzeHelper(ExpressionAnalyzer &ea,
std::uint64_t kind = defaultIntegerKind_; const parser::Scalar<A> &tree) {
const auto &kindParam{std::get<std::optional<parser::KindParam>>(x.t)}; std::optional<evaluate::GenericExpr> result{AnalyzeHelper(ea, tree.thing)};
if (kindParam.has_value()) { if (result.has_value()) {
std::visit(common::visitors{[&](std::uint64_t k) { kind = k; }, if (result->Rank() > 1) {
[&](const auto &) { ea.Say("must be scalar"_err_en_US);
messages_.Say(at_, "unimp kind param"_err_en_US); return {};
}},
kindParam->u);
} }
}
return result;
}
template<typename A>
std::optional<evaluate::GenericExpr> AnalyzeHelper(ExpressionAnalyzer &ea,
const parser::Constant<A> &tree) {
std::optional<evaluate::GenericExpr> result{AnalyzeHelper(ea, tree.thing)};
if (result.has_value()) {
result->Fold(ea.context());
if (!result->ConstantValue().has_value()) {
ea.Say("must be constant"_err_en_US);
return {};
}
}
return result;
}
template<typename A>
std::optional<evaluate::GenericExpr> AnalyzeHelper(ExpressionAnalyzer &ea,
const parser::Integer<A> &tree) {
std::optional<evaluate::GenericExpr> result{AnalyzeHelper(ea, tree.thing)};
if (result.has_value() && !std::holds_alternative<evaluate::GenericIntegerExpr>(result->u)) {
ea.Say("must be integer"_err_en_US);
return {};
}
return result;
}
template<> std::optional<evaluate::GenericExpr> AnalyzeHelper(ExpressionAnalyzer &ea,
const parser::Name &n) {
// TODO
return {};
}
ExpressionAnalyzer::KindParam
ExpressionAnalyzer::Analyze(const std::optional<parser::KindParam> &kindParam,
KindParam defaultKind, KindParam kanjiKind) {
if (!kindParam.has_value()) {
return defaultKind;
}
return std::visit(common::visitors{[](std::uint64_t k) { return static_cast<KindParam>(k); },
[&](const parser::Scalar<parser::Integer<parser::Constant<parser::Name>>> &n) {
if (std::optional<evaluate::GenericExpr> oge{AnalyzeHelper(*this, n)}) {
if (std::optional<evaluate::GenericConstant> ogc{oge->ConstantValue()}) {
// TODO pmk more here next
}
}
return defaultKind;
},
[&](parser::KindParam::Kanji) {
if (kanjiKind >= 0) {
return kanjiKind;
}
Say("Kanji not allowed here"_err_en_US);
return defaultKind; }}, kindParam->u);
}
template<> std::optional<evaluate::GenericExpr> AnalyzeHelper(ExpressionAnalyzer &ea,
const parser::IntLiteralConstant &x) {
auto kind{ea.Analyze(std::get<std::optional<parser::KindParam>>(x.t),
ea.defaultIntegerKind())};
std::uint64_t value{std::get<std::uint64_t>(x.t)}; std::uint64_t value{std::get<std::uint64_t>(x.t)};
switch (kind) { switch (kind) {
case 4: #define CASE(k) case k: return {evaluate::GenericExpr{evaluate::GenericIntegerExpr{evaluate::IntegerExpr<k>{value}}}};
return {evaluate::GenericExpr{ FOR_EACH_INTEGER_KIND(CASE,)
evaluate::GenericIntegerExpr{evaluate::IntegerExpr<4>{value}}}}; #undef CASE
default: default:
messages_.Say(at_, ea.Say(parser::MessageFormattedText{
parser::MessageFormattedText{
"unimplemented INTEGER kind (%ju)"_err_en_US, "unimplemented INTEGER kind (%ju)"_err_en_US,
static_cast<std::uintmax_t>(kind)}); static_cast<std::uintmax_t>(kind)});
return {}; return {};
} }
} }
std::optional<evaluate::GenericExpr> ExpressionAnalyzer::Analyze( template<> std::optional<evaluate::GenericExpr> AnalyzeHelper(ExpressionAnalyzer &ea,
const parser::LiteralConstant &x) { const parser::LiteralConstant &x) {
return std::visit( return std::visit(
common::visitors{ common::visitors{
[&](const parser::IntLiteralConstant &c) { return Analyze(c); }, [&](const parser::IntLiteralConstant &c) { return AnalyzeHelper(ea, c); },
// TODO next [&](const parser::RealLiteralConstant &c) { return AnalyzeHelper(ea, c); },
// TODO: remaining cases
[&](const auto &) { return std::optional<evaluate::GenericExpr>{}; }},
x.u);
}
std::optional<evaluate::GenericExpr> ExpressionAnalyzer::Analyze(
const parser::Expr &x) {
return std::visit(
common::visitors{
[&](const parser::LiteralConstant &c) { return AnalyzeHelper(*this, c); },
// TODO: remaining cases
[&](const auto &) { return std::optional<evaluate::GenericExpr>{}; }}, [&](const auto &) { return std::optional<evaluate::GenericExpr>{}; }},
x.u); x.u);
} }

View File

@ -25,17 +25,28 @@ namespace Fortran::semantics {
class ExpressionAnalyzer { class ExpressionAnalyzer {
public: public:
ExpressionAnalyzer(parser::Messages &m, std::uint64_t dIK) using KindParam = std::int64_t;
: messages_{m}, defaultIntegerKind_{dIK} {} ExpressionAnalyzer(evaluate::FoldingContext &c, KindParam dIK)
: context_{c}, defaultIntegerKind_{dIK} {}
evaluate::FoldingContext &context() { return context_; }
KindParam defaultIntegerKind() const { return defaultIntegerKind_; }
template<typename M>
void Say(const M &msg) {
if (context_.messages != nullptr) {
context_.messages->Say(context_.at, msg);
}
}
// Performs semantic checking on an expression. If successful,
// returns its typed expression representation.
std::optional<evaluate::GenericExpr> Analyze(const parser::Expr &); std::optional<evaluate::GenericExpr> Analyze(const parser::Expr &);
std::optional<evaluate::GenericExpr> Analyze( KindParam Analyze(const std::optional<parser::KindParam> &, KindParam defaultKind, KindParam kanjiKind = -1 /* not allowed here */);
const parser::IntLiteralConstant &);
std::optional<evaluate::GenericExpr> Analyze(const parser::LiteralConstant &);
private: private:
parser::Messages &messages_; evaluate::FoldingContext context_;
const parser::CharBlock at_; KindParam defaultIntegerKind_{4};
std::uint64_t defaultIntegerKind_{4};
}; };
} // namespace Fortran::semantics } // namespace Fortran::semantics
#endif // FORTRAN_SEMANTICS_EXPRESSION_H_ #endif // FORTRAN_SEMANTICS_EXPRESSION_H_

View File

@ -42,7 +42,7 @@ leaves are concrete types:
DerivedTypeSpec DerivedTypeSpec
TypeSpec classes are immutable. For intrinsic types (except character) there TypeSpec classes are immutable. For intrinsic types (except character) there
are a limited number of instances -- one for each kind. is a limited number of instances -- one for each kind.
A DerivedTypeSpec is based on a DerivedTypeDef (from a derived type statement) A DerivedTypeSpec is based on a DerivedTypeDef (from a derived type statement)
with kind and len parameter values provided. with kind and len parameter values provided.