[flang] Represent NULL()

Original-commit: flang-compiler/f18@2c3368fb5f
Reviewed-on: https://github.com/flang-compiler/f18/pull/311
Tree-same-pre-rewrite: false
This commit is contained in:
peter klausler 2019-02-19 17:06:28 -08:00
parent 857da8cfe9
commit 402cc8c4e9
7 changed files with 88 additions and 25 deletions

View File

@ -146,6 +146,7 @@ std::ostream &ExpressionBase<RESULT>::AsFortran(std::ostream &o) const {
[&](const BOZLiteralConstant &x) { [&](const BOZLiteralConstant &x) {
o << "z'" << x.Hexadecimal() << "'"; o << "z'" << x.Hexadecimal() << "'";
}, },
[&](const NullPointer &) { o << "NULL()"; },
[&](const CopyableIndirection<Substring> &s) { s->AsFortran(o); }, [&](const CopyableIndirection<Substring> &s) { s->AsFortran(o); },
[&](const ImpliedDoIndex &i) { o << i.name.ToString(); }, [&](const ImpliedDoIndex &i) { o << i.name.ToString(); },
[&](const auto &x) { x.AsFortran(o); }, [&](const auto &x) { x.AsFortran(o); },
@ -201,11 +202,13 @@ std::optional<DynamicType> ExpressionBase<A>::GetType() const {
} else { } else {
return std::visit( return std::visit(
[](const auto &x) -> std::optional<DynamicType> { [](const auto &x) -> std::optional<DynamicType> {
if constexpr (!std::is_same_v<std::decay_t<decltype(x)>, using Ty = std::decay_t<decltype(x)>;
BOZLiteralConstant>) { if constexpr (std::is_same_v<Ty, BOZLiteralConstant> ||
std::is_same_v<Ty, NullPointer>) {
return std::nullopt; // typeless really means "no type"
} else {
return x.GetType(); return x.GetType();
} }
return std::nullopt; // typeless really means "no type"
}, },
derived().u); derived().u);
} }

View File

@ -98,12 +98,6 @@ public:
static Derived Rewrite(FoldingContext &, Derived &&); static Derived Rewrite(FoldingContext &, Derived &&);
}; };
// BOZ literal "typeless" constants must be wide enough to hold a numeric
// value of any supported kind of INTEGER or REAL. They must also be
// distinguishable from other integer constants, since they are permitted
// to be used in only a few situations.
using BOZLiteralConstant = typename LargestReal::Scalar::Word;
// Operations always have specific Fortran result types (i.e., with known // Operations always have specific Fortran result types (i.e., with known
// intrinsic type category and kind parameter value). The classes that // intrinsic type category and kind parameter value). The classes that
// represent the operations all inherit from this Operation<> base class // represent the operations all inherit from this Operation<> base class
@ -725,6 +719,18 @@ public:
common::MapTemplate<Expr, CategoryTypes<CAT>> u; common::MapTemplate<Expr, CategoryTypes<CAT>> u;
}; };
// BOZ literal "typeless" constants must be wide enough to hold a numeric
// value of any supported kind of INTEGER or REAL. They must also be
// distinguishable from other integer constants, since they are permitted
// to be used in only a few situations.
using BOZLiteralConstant = typename LargestReal::Scalar::Word;
// Null pointers without MOLD= arguments are typed by context.
struct NullPointer {
constexpr bool operator==(const NullPointer &) const { return true; }
constexpr int Rank() const { return 0; }
};
// A completely generic expression, polymorphic across all of the intrinsic type // A completely generic expression, polymorphic across all of the intrinsic type
// categories and each of their kinds. // categories and each of their kinds.
template<> class Expr<SomeType> : public ExpressionBase<SomeType> { template<> class Expr<SomeType> : public ExpressionBase<SomeType> {
@ -757,7 +763,7 @@ public:
} }
private: private:
using Others = std::variant<BOZLiteralConstant>; using Others = std::variant<BOZLiteralConstant, NullPointer>;
using Categories = common::MapTemplate<Expr, SomeCategory>; using Categories = common::MapTemplate<Expr, SomeCategory>;
public: public:

View File

@ -759,11 +759,14 @@ Expr<T> ExpressionBase<T>::Rewrite(FoldingContext &context, Expr<T> &&expr) {
return FoldOperation(context, std::move(x)); return FoldOperation(context, std::move(x));
} else if constexpr (std::is_same_v<T, SomeDerived>) { } else if constexpr (std::is_same_v<T, SomeDerived>) {
return FoldOperation(context, std::move(x)); return FoldOperation(context, std::move(x));
} else if constexpr (std::is_same_v<BOZLiteralConstant,
std::decay_t<decltype(x)>>) {
return std::move(expr);
} else { } else {
return Expr<T>{Fold(context, std::move(x))}; using Ty = std::decay_t<decltype(x)>;
if constexpr (std::is_same_v<Ty, BOZLiteralConstant> ||
std::is_same_v<Ty, NullPointer>) {
return std::move(expr);
} else {
return Expr<T>{Fold(context, std::move(x))};
}
} }
}, },
std::move(expr.u)); std::move(expr.u));
@ -789,6 +792,7 @@ struct ConstExprContext {
bool IsConstExpr(ConstExprContext &, const BOZLiteralConstant &) { bool IsConstExpr(ConstExprContext &, const BOZLiteralConstant &) {
return true; return true;
} }
bool IsConstExpr(ConstExprContext &, const NullPointer &) { return true; }
template<typename A> bool IsConstExpr(ConstExprContext &, const Constant<A> &) { template<typename A> bool IsConstExpr(ConstExprContext &, const Constant<A> &) {
return true; return true;
} }

View File

@ -1163,9 +1163,9 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
} }
} }
return {SpecificCall{ return std::make_optional<SpecificCall>(
SpecificIntrinsic{name, std::move(resultType), resultRank, attrs}, SpecificIntrinsic{name, std::move(resultType), resultRank, attrs},
std::move(rearranged)}}; std::move(rearranged));
} }
class IntrinsicProcTable::Implementation { class IntrinsicProcTable::Implementation {
@ -1254,9 +1254,9 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::Probe(
// Special cases of intrinsic functions // Special cases of intrinsic functions
if (call.name.ToString() == "null") { if (call.name.ToString() == "null") {
if (arguments.size() == 0) { if (arguments.size() == 0) {
// TODO: NULL() result type is determined by context return std::make_optional<SpecificCall>(
// Can pass that context in, or return a token distinguishing SpecificIntrinsic{"null"s}, std::move(arguments));
// NULL, or represent NULL as a new kind of top-level expression // TODO pmk work in progress - fold into NullPointer (where?)
} else if (arguments.size() > 1) { } else if (arguments.size() > 1) {
genericErrors.Say("too many arguments to NULL()"_err_en_US); genericErrors.Say("too many arguments to NULL()"_err_en_US);
} else if (arguments[0].has_value() && arguments[0]->keyword.has_value() && } else if (arguments[0].has_value() && arguments[0]->keyword.has_value() &&
@ -1264,10 +1264,16 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::Probe(
genericErrors.Say("unknown argument '%s' to NULL()"_err_en_US, genericErrors.Say("unknown argument '%s' to NULL()"_err_en_US,
arguments[0]->keyword->ToString().data()); arguments[0]->keyword->ToString().data());
} else { } else {
// TODO: Argument must be pointer, procedure pointer, or allocatable. Expr<SomeType> &mold{*arguments[0]->value};
// Characteristics, including dynamic length type parameter values, if (IsPointerOrAllocatable(mold)) {
// must be taken from the MOLD argument. return std::make_optional<SpecificCall>(
// TODO: set Attr::POINTER on NULL result SpecificIntrinsic{"null"s, mold.GetType(), mold.Rank(),
semantics::Attrs{semantics::Attr::POINTER}},
std::move(arguments));
} else {
genericErrors.Say("MOLD argument to NULL() must be a pointer "
"or allocatable"_err_en_US);
}
} }
} }
// No match // No match

View File

@ -347,6 +347,10 @@ std::optional<Expr<SomeType>> Negation(
messages.Say("BOZ literal cannot be negated"_err_en_US); messages.Say("BOZ literal cannot be negated"_err_en_US);
return NoExpr(); return NoExpr();
}, },
[&](NullPointer &&) {
messages.Say("NULL() cannot be negated"_err_en_US);
return NoExpr();
},
[&](Expr<SomeInteger> &&x) { return Package(-std::move(x)); }, [&](Expr<SomeInteger> &&x) { return Package(-std::move(x)); },
[&](Expr<SomeReal> &&x) { return Package(-std::move(x)); }, [&](Expr<SomeReal> &&x) { return Package(-std::move(x)); },
[&](Expr<SomeComplex> &&x) { return Package(-std::move(x)); }, [&](Expr<SomeComplex> &&x) { return Package(-std::move(x)); },
@ -501,7 +505,8 @@ std::optional<Expr<SomeType>> ConvertToNumeric(int kind, Expr<SomeType> &&x) {
return std::visit( return std::visit(
[=](auto &&cx) -> std::optional<Expr<SomeType>> { [=](auto &&cx) -> std::optional<Expr<SomeType>> {
using cxType = std::decay_t<decltype(cx)>; using cxType = std::decay_t<decltype(cx)>;
if constexpr (!std::is_same_v<cxType, BOZLiteralConstant>) { if constexpr (!std::is_same_v<cxType, BOZLiteralConstant> &&
!std::is_same_v<cxType, NullPointer>) {
if constexpr (IsNumericTypeCategory(ResultType<cxType>::category)) { if constexpr (IsNumericTypeCategory(ResultType<cxType>::category)) {
return std::make_optional( return std::make_optional(
Expr<SomeType>{ConvertToKind<TO>(kind, std::move(cx))}); Expr<SomeType>{ConvertToKind<TO>(kind, std::move(cx))});

View File

@ -20,6 +20,7 @@
#include "../common/idioms.h" #include "../common/idioms.h"
#include "../common/unwrap.h" #include "../common/unwrap.h"
#include "../parser/message.h" #include "../parser/message.h"
#include "../semantics/attr.h"
#include "../semantics/symbol.h" #include "../semantics/symbol.h"
#include <array> #include <array>
#include <optional> #include <optional>
@ -124,6 +125,10 @@ inline Expr<SomeType> AsGenericExpr(BOZLiteralConstant &&x) {
return Expr<SomeType>{std::move(x)}; return Expr<SomeType>{std::move(x)};
} }
inline Expr<SomeType> AsGenericExpr(NullPointer &&x) {
return Expr<SomeType>{std::move(x)};
}
Expr<SomeReal> GetComplexPart( Expr<SomeReal> GetComplexPart(
const Expr<SomeComplex> &, bool isImaginary = false); const Expr<SomeComplex> &, bool isImaginary = false);
@ -140,7 +145,8 @@ auto UnwrapExpr(B &x) -> common::Constify<A, B> * {
using Ty = std::decay_t<B>; using Ty = std::decay_t<B>;
if constexpr (std::is_same_v<A, Ty>) { if constexpr (std::is_same_v<A, Ty>) {
return &x; return &x;
} else if constexpr (std::is_same_v<Ty, BOZLiteralConstant>) { } else if constexpr (std::is_same_v<Ty, BOZLiteralConstant> ||
std::is_same_v<Ty, NullPointer>) {
return nullptr; return nullptr;
} else if constexpr (std::is_same_v<Ty, Expr<ResultType<A>>>) { } else if constexpr (std::is_same_v<Ty, Expr<ResultType<A>>>) {
return common::Unwrap<A>(x.u); return common::Unwrap<A>(x.u);
@ -521,5 +527,31 @@ struct TypeKindVisitor {
int kind; int kind;
VALUE value; VALUE value;
}; };
template<typename A> const semantics::Symbol *GetLastSymbol(const A &) {
return nullptr;
}
template<typename T>
const semantics::Symbol *GetLastSymbol(const Designator<T> &x) {
return x.GetLastSymbol();
}
template<typename T> const semantics::Symbol *GetLastSymbol(const Expr<T> &x) {
return std::visit([](const auto &y) { return GetLastSymbol(y); }, x.u);
}
template<typename A> semantics::Attrs GetAttrs(const A &x) {
if (const semantics::Symbol * symbol{GetLastSymbol(x)}) {
return symbol->attrs();
} else {
return {};
}
}
template<typename A> bool IsPointerOrAllocatable(const A &x) {
return GetAttrs(x).HasAny(
semantics::Attrs{semantics::Attr::POINTER, semantics::Attr::ALLOCATABLE});
}
} }
#endif // FORTRAN_EVALUATE_TOOLS_H_ #endif // FORTRAN_EVALUATE_TOOLS_H_

View File

@ -96,6 +96,7 @@ static std::optional<DataRef> ExtractDataRef(Expr<SomeType> &&expr) {
[](BOZLiteralConstant &&) -> std::optional<DataRef> { [](BOZLiteralConstant &&) -> std::optional<DataRef> {
return std::nullopt; return std::nullopt;
}, },
[](NullPointer &&) -> std::optional<DataRef> { return std::nullopt; },
[](auto &&catExpr) { return ExtractDataRef(std::move(catExpr)); }, [](auto &&catExpr) { return ExtractDataRef(std::move(catExpr)); },
}, },
std::move(expr.u)); std::move(expr.u));
@ -1644,6 +1645,9 @@ static MaybeExpr AnalyzeExpr(
[&](BOZLiteralConstant &&boz) { [&](BOZLiteralConstant &&boz) {
return operand; // ignore parentheses around typeless constants return operand; // ignore parentheses around typeless constants
}, },
[&](NullPointer &&boz) {
return operand; // ignore parentheses around NULL()
},
[&](Expr<SomeDerived> &&) { [&](Expr<SomeDerived> &&) {
// TODO: parenthesized derived type variable // TODO: parenthesized derived type variable
return operand; return operand;
@ -1669,6 +1673,9 @@ static MaybeExpr AnalyzeExpr(
std::visit( std::visit(
common::visitors{ common::visitors{
[](const BOZLiteralConstant &) {}, // allow +Z'1', it's harmless [](const BOZLiteralConstant &) {}, // allow +Z'1', it's harmless
[&](const NullPointer &) {
context.Say("+NULL() is not allowed"_err_en_US);
},
[&](const auto &catExpr) { [&](const auto &catExpr) {
TypeCategory cat{ResultType<decltype(catExpr)>::category}; TypeCategory cat{ResultType<decltype(catExpr)>::category};
if (cat != TypeCategory::Integer && cat != TypeCategory::Real && if (cat != TypeCategory::Integer && cat != TypeCategory::Real &&