forked from OSchip/llvm-project
[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:
parent
857da8cfe9
commit
402cc8c4e9
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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))});
|
||||||
|
|
|
@ -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_
|
||||||
|
|
|
@ -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 &&
|
||||||
|
|
Loading…
Reference in New Issue