[flang] Further work on NULL(MOLD=allocatable) (#129345)
Refine handling of NULL(...) in semantics to properly distinguish NULL(), NULL(objectPointer), NULL(procPointer), and NULL(allocatable) from each other in relevant contexts. Add IsNullAllocatable() and IsNullPointerOrAllocatable() utility functions. IsNullAllocatable() is true only for NULL(allocatable); it is false for a bare NULL(), which can be detected independently with IsBareNullPointer(). IsNullPointer() now returns false for NULL(allocatable). ALLOCATED(NULL(allocatable)) now works, and folds to .FALSE. These utilities were modified to accept const pointer arguments rather than const references; I usually prefer this style when the result should clearly be false for a null argument (in the C sense), and it helped me find all of their use sites in the code.
This commit is contained in:
parent
b2ba43a9c1
commit
79a25e11fe
@ -349,8 +349,8 @@ struct FunctionResult {
|
|||||||
|
|
||||||
// 15.3.1
|
// 15.3.1
|
||||||
struct Procedure {
|
struct Procedure {
|
||||||
ENUM_CLASS(
|
ENUM_CLASS(Attr, Pure, Elemental, BindC, ImplicitInterface, NullPointer,
|
||||||
Attr, Pure, Elemental, BindC, ImplicitInterface, NullPointer, Subroutine)
|
NullAllocatable, Subroutine)
|
||||||
using Attrs = common::EnumSet<Attr, Attr_enumSize>;
|
using Attrs = common::EnumSet<Attr, Attr_enumSize>;
|
||||||
Procedure(){};
|
Procedure(){};
|
||||||
Procedure(FunctionResult &&, DummyArguments &&, Attrs);
|
Procedure(FunctionResult &&, DummyArguments &&, Attrs);
|
||||||
|
@ -1059,9 +1059,11 @@ bool IsProcedurePointer(const Expr<SomeType> &);
|
|||||||
bool IsProcedure(const Expr<SomeType> &);
|
bool IsProcedure(const Expr<SomeType> &);
|
||||||
bool IsProcedurePointerTarget(const Expr<SomeType> &);
|
bool IsProcedurePointerTarget(const Expr<SomeType> &);
|
||||||
bool IsBareNullPointer(const Expr<SomeType> *); // NULL() w/o MOLD= or type
|
bool IsBareNullPointer(const Expr<SomeType> *); // NULL() w/o MOLD= or type
|
||||||
bool IsNullObjectPointer(const Expr<SomeType> &);
|
bool IsNullObjectPointer(const Expr<SomeType> *); // NULL() or NULL(objptr)
|
||||||
bool IsNullProcedurePointer(const Expr<SomeType> &);
|
bool IsNullProcedurePointer(const Expr<SomeType> *); // NULL() or NULL(procptr)
|
||||||
bool IsNullPointer(const Expr<SomeType> &);
|
bool IsNullPointer(const Expr<SomeType> *); // NULL() or NULL(pointer)
|
||||||
|
bool IsNullAllocatable(const Expr<SomeType> *); // NULL(allocatable)
|
||||||
|
bool IsNullPointerOrAllocatable(const Expr<SomeType> *); // NULL of any form
|
||||||
bool IsObjectPointer(const Expr<SomeType> &);
|
bool IsObjectPointer(const Expr<SomeType> &);
|
||||||
|
|
||||||
// Can Expr be passed as absent to an optional dummy argument.
|
// Can Expr be passed as absent to an optional dummy argument.
|
||||||
|
@ -100,9 +100,9 @@ template <bool INVARIANT>
|
|||||||
bool IsConstantExprHelper<INVARIANT>::IsConstantStructureConstructorComponent(
|
bool IsConstantExprHelper<INVARIANT>::IsConstantStructureConstructorComponent(
|
||||||
const Symbol &component, const Expr<SomeType> &expr) const {
|
const Symbol &component, const Expr<SomeType> &expr) const {
|
||||||
if (IsAllocatable(component)) {
|
if (IsAllocatable(component)) {
|
||||||
return IsNullObjectPointer(expr);
|
return IsNullObjectPointer(&expr);
|
||||||
} else if (IsPointer(component)) {
|
} else if (IsPointer(component)) {
|
||||||
return IsNullPointer(expr) || IsInitialDataTarget(expr) ||
|
return IsNullPointerOrAllocatable(&expr) || IsInitialDataTarget(expr) ||
|
||||||
IsInitialProcedureTarget(expr);
|
IsInitialProcedureTarget(expr);
|
||||||
} else {
|
} else {
|
||||||
return (*this)(expr);
|
return (*this)(expr);
|
||||||
@ -194,7 +194,7 @@ struct IsActuallyConstantHelper {
|
|||||||
const bool compIsConstant{(*this)(y)};
|
const bool compIsConstant{(*this)(y)};
|
||||||
// If an allocatable component is initialized by a constant,
|
// If an allocatable component is initialized by a constant,
|
||||||
// the structure constructor is not a constant.
|
// the structure constructor is not a constant.
|
||||||
if ((!compIsConstant && !IsNullPointer(y)) ||
|
if ((!compIsConstant && !IsNullPointerOrAllocatable(&y)) ||
|
||||||
(compIsConstant && IsAllocatable(sym))) {
|
(compIsConstant && IsAllocatable(sym))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -311,7 +311,9 @@ public:
|
|||||||
bool operator()(const ProcedureRef &x) const {
|
bool operator()(const ProcedureRef &x) const {
|
||||||
if (const SpecificIntrinsic * intrinsic{x.proc().GetSpecificIntrinsic()}) {
|
if (const SpecificIntrinsic * intrinsic{x.proc().GetSpecificIntrinsic()}) {
|
||||||
return intrinsic->characteristics.value().attrs.test(
|
return intrinsic->characteristics.value().attrs.test(
|
||||||
characteristics::Procedure::Attr::NullPointer);
|
characteristics::Procedure::Attr::NullPointer) ||
|
||||||
|
intrinsic->characteristics.value().attrs.test(
|
||||||
|
characteristics::Procedure::Attr::NullAllocatable);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -388,7 +390,7 @@ bool IsInitialProcedureTarget(const Expr<SomeType> &expr) {
|
|||||||
if (const auto *proc{std::get_if<ProcedureDesignator>(&expr.u)}) {
|
if (const auto *proc{std::get_if<ProcedureDesignator>(&expr.u)}) {
|
||||||
return IsInitialProcedureTarget(*proc);
|
return IsInitialProcedureTarget(*proc);
|
||||||
} else {
|
} else {
|
||||||
return IsNullProcedurePointer(expr);
|
return IsNullProcedurePointer(&expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -652,21 +652,18 @@ Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
|
|||||||
if (name == "all") {
|
if (name == "all") {
|
||||||
return FoldAllAnyParity(
|
return FoldAllAnyParity(
|
||||||
context, std::move(funcRef), &Scalar<T>::AND, Scalar<T>{true});
|
context, std::move(funcRef), &Scalar<T>::AND, Scalar<T>{true});
|
||||||
|
} else if (name == "allocated") {
|
||||||
|
if (IsNullAllocatable(args[0]->UnwrapExpr())) {
|
||||||
|
return Expr<T>{false};
|
||||||
|
}
|
||||||
} else if (name == "any") {
|
} else if (name == "any") {
|
||||||
return FoldAllAnyParity(
|
return FoldAllAnyParity(
|
||||||
context, std::move(funcRef), &Scalar<T>::OR, Scalar<T>{false});
|
context, std::move(funcRef), &Scalar<T>::OR, Scalar<T>{false});
|
||||||
} else if (name == "associated") {
|
} else if (name == "associated") {
|
||||||
bool gotConstant{true};
|
if (IsNullPointer(args[0]->UnwrapExpr()) ||
|
||||||
const Expr<SomeType> *firstArgExpr{args[0]->UnwrapExpr()};
|
(args[1] && IsNullPointer(args[1]->UnwrapExpr()))) {
|
||||||
if (!firstArgExpr || !IsNullPointer(*firstArgExpr)) {
|
return Expr<T>{false};
|
||||||
gotConstant = false;
|
|
||||||
} else if (args[1]) { // There's a second argument
|
|
||||||
const Expr<SomeType> *secondArgExpr{args[1]->UnwrapExpr()};
|
|
||||||
if (!secondArgExpr || !IsNullPointer(*secondArgExpr)) {
|
|
||||||
gotConstant = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return gotConstant ? Expr<T>{false} : Expr<T>{std::move(funcRef)};
|
|
||||||
} else if (name == "bge" || name == "bgt" || name == "ble" || name == "blt") {
|
} else if (name == "bge" || name == "bgt" || name == "ble" || name == "blt") {
|
||||||
static_assert(std::is_same_v<Scalar<LargestInt>, BOZLiteralConstant>);
|
static_assert(std::is_same_v<Scalar<LargestInt>, BOZLiteralConstant>);
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ Expr<SomeDerived> FoldOperation(
|
|||||||
for (auto &&[symbol, value] : std::move(structure)) {
|
for (auto &&[symbol, value] : std::move(structure)) {
|
||||||
auto expr{Fold(context, std::move(value.value()))};
|
auto expr{Fold(context, std::move(value.value()))};
|
||||||
if (IsPointer(symbol)) {
|
if (IsPointer(symbol)) {
|
||||||
if (IsNullPointer(expr)) {
|
if (IsNullPointer(&expr)) {
|
||||||
// Handle x%c when x designates a named constant of derived
|
// Handle x%c when x designates a named constant of derived
|
||||||
// type and %c is NULL() in that constant.
|
// type and %c is NULL() in that constant.
|
||||||
expr = Expr<SomeType>{NullPointer{}};
|
expr = Expr<SomeType>{NullPointer{}};
|
||||||
@ -86,9 +86,10 @@ Expr<SomeDerived> FoldOperation(
|
|||||||
// F2023: 10.1.12 (3)(a)
|
// F2023: 10.1.12 (3)(a)
|
||||||
// If comp-spec is not null() for the allocatable component the
|
// If comp-spec is not null() for the allocatable component the
|
||||||
// structure constructor is not a constant expression.
|
// structure constructor is not a constant expression.
|
||||||
isConstant &= IsNullPointer(expr);
|
isConstant &= IsNullAllocatable(&expr) || IsBareNullPointer(&expr);
|
||||||
} else {
|
} else {
|
||||||
isConstant &= IsActuallyConstant(expr) || IsNullPointer(expr);
|
isConstant &=
|
||||||
|
IsActuallyConstant(expr) || IsNullPointerOrAllocatable(&expr);
|
||||||
if (auto valueShape{GetConstantExtents(context, expr)}) {
|
if (auto valueShape{GetConstantExtents(context, expr)}) {
|
||||||
if (auto componentShape{GetConstantExtents(context, symbol)}) {
|
if (auto componentShape{GetConstantExtents(context, symbol)}) {
|
||||||
if (GetRank(*componentShape) > 0 && GetRank(*valueShape) == 0) {
|
if (GetRank(*componentShape) > 0 && GetRank(*valueShape) == 0) {
|
||||||
|
@ -247,8 +247,10 @@ ENUM_CLASS(Optionality, required,
|
|||||||
)
|
)
|
||||||
|
|
||||||
ENUM_CLASS(ArgFlag, none,
|
ENUM_CLASS(ArgFlag, none,
|
||||||
canBeNull, // actual argument can be NULL(with or without MOLD=)
|
canBeNullPointer, // actual argument can be NULL(with or without
|
||||||
canBeMoldNull, // actual argument can be NULL(with MOLD=)
|
// MOLD=pointer)
|
||||||
|
canBeMoldNull, // actual argument can be NULL(MOLD=any)
|
||||||
|
canBeNullAllocatable, // actual argument can be NULL(MOLD=allocatable)
|
||||||
defaultsToSameKind, // for MatchingDefaultKIND
|
defaultsToSameKind, // for MatchingDefaultKIND
|
||||||
defaultsToSizeKind, // for SizeDefaultKIND
|
defaultsToSizeKind, // for SizeDefaultKIND
|
||||||
defaultsToDefaultForResult, // for DefaultingKIND
|
defaultsToDefaultForResult, // for DefaultingKIND
|
||||||
@ -343,8 +345,10 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
|
|||||||
Rank::dimReduced, IntrinsicClass::transformationalFunction},
|
Rank::dimReduced, IntrinsicClass::transformationalFunction},
|
||||||
{"allocated", {{"scalar", AnyData, Rank::scalar}}, DefaultLogical,
|
{"allocated", {{"scalar", AnyData, Rank::scalar}}, DefaultLogical,
|
||||||
Rank::elemental, IntrinsicClass::inquiryFunction},
|
Rank::elemental, IntrinsicClass::inquiryFunction},
|
||||||
{"allocated", {{"array", AnyData, Rank::anyOrAssumedRank}}, DefaultLogical,
|
{"allocated",
|
||||||
Rank::elemental, IntrinsicClass::inquiryFunction},
|
{{"array", AnyData, Rank::anyOrAssumedRank, Optionality::required,
|
||||||
|
common::Intent::In, {ArgFlag::canBeNullAllocatable}}},
|
||||||
|
DefaultLogical, Rank::elemental, IntrinsicClass::inquiryFunction},
|
||||||
{"anint", {{"a", SameReal}, MatchingDefaultKIND}, KINDReal},
|
{"anint", {{"a", SameReal}, MatchingDefaultKIND}, KINDReal},
|
||||||
{"any", {{"mask", SameLogical, Rank::array}, OptionalDIM}, SameLogical,
|
{"any", {{"mask", SameLogical, Rank::array}, OptionalDIM}, SameLogical,
|
||||||
Rank::dimReduced, IntrinsicClass::transformationalFunction},
|
Rank::dimReduced, IntrinsicClass::transformationalFunction},
|
||||||
@ -353,10 +357,10 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
|
|||||||
{"asinh", {{"x", SameFloating}}, SameFloating},
|
{"asinh", {{"x", SameFloating}}, SameFloating},
|
||||||
{"associated",
|
{"associated",
|
||||||
{{"pointer", AnyPointer, Rank::anyOrAssumedRank, Optionality::required,
|
{{"pointer", AnyPointer, Rank::anyOrAssumedRank, Optionality::required,
|
||||||
common::Intent::In, {ArgFlag::canBeNull}},
|
common::Intent::In, {ArgFlag::canBeNullPointer}},
|
||||||
{"target", Addressable, Rank::anyOrAssumedRank,
|
{"target", Addressable, Rank::anyOrAssumedRank,
|
||||||
Optionality::optional, common::Intent::In,
|
Optionality::optional, common::Intent::In,
|
||||||
{ArgFlag::canBeNull}}},
|
{ArgFlag::canBeNullPointer}}},
|
||||||
DefaultLogical, Rank::elemental, IntrinsicClass::inquiryFunction},
|
DefaultLogical, Rank::elemental, IntrinsicClass::inquiryFunction},
|
||||||
{"atan", {{"x", SameFloating}}, SameFloating},
|
{"atan", {{"x", SameFloating}}, SameFloating},
|
||||||
{"atan", {{"y", OperandReal}, {"x", OperandReal}}, OperandReal},
|
{"atan", {{"y", OperandReal}, {"x", OperandReal}}, OperandReal},
|
||||||
@ -1892,9 +1896,9 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
|
|||||||
d.keyword);
|
d.keyword);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
if (!d.flags.test(ArgFlag::canBeNull)) {
|
if (!d.flags.test(ArgFlag::canBeNullPointer)) {
|
||||||
if (const auto *expr{arg->UnwrapExpr()}; expr && IsNullPointer(*expr)) {
|
if (const auto *expr{arg->UnwrapExpr()}; IsNullPointer(expr)) {
|
||||||
if (!IsBareNullPointer(expr) && IsNullObjectPointer(*expr) &&
|
if (!IsBareNullPointer(expr) && IsNullObjectPointer(expr) &&
|
||||||
d.flags.test(ArgFlag::canBeMoldNull)) {
|
d.flags.test(ArgFlag::canBeMoldNull)) {
|
||||||
// ok
|
// ok
|
||||||
} else {
|
} else {
|
||||||
@ -1905,6 +1909,14 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!d.flags.test(ArgFlag::canBeNullAllocatable) &&
|
||||||
|
IsNullAllocatable(arg->UnwrapExpr()) &&
|
||||||
|
!d.flags.test(ArgFlag::canBeMoldNull)) {
|
||||||
|
messages.Say(arg->sourceLocation(),
|
||||||
|
"A NULL() allocatable is not allowed for '%s=' intrinsic argument"_err_en_US,
|
||||||
|
d.keyword);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
if (d.flags.test(ArgFlag::notAssumedSize)) {
|
if (d.flags.test(ArgFlag::notAssumedSize)) {
|
||||||
if (auto named{ExtractNamedEntity(*arg)}) {
|
if (auto named{ExtractNamedEntity(*arg)}) {
|
||||||
if (semantics::IsAssumedSizeArray(named->GetLastSymbol())) {
|
if (semantics::IsAssumedSizeArray(named->GetLastSymbol())) {
|
||||||
@ -2862,14 +2874,15 @@ SpecificCall IntrinsicProcTable::Implementation::HandleNull(
|
|||||||
"MOLD= argument to NULL() must not be assumed-rank"_err_en_US);
|
"MOLD= argument to NULL() must not be assumed-rank"_err_en_US);
|
||||||
}
|
}
|
||||||
bool isProcPtrTarget{
|
bool isProcPtrTarget{
|
||||||
IsProcedurePointerTarget(*mold) && !IsNullObjectPointer(*mold)};
|
IsProcedurePointerTarget(*mold) && !IsNullObjectPointer(mold)};
|
||||||
if (isProcPtrTarget || IsAllocatableOrPointerObject(*mold)) {
|
if (isProcPtrTarget || IsAllocatableOrPointerObject(*mold)) {
|
||||||
characteristics::DummyArguments args;
|
characteristics::DummyArguments args;
|
||||||
std::optional<characteristics::FunctionResult> fResult;
|
std::optional<characteristics::FunctionResult> fResult;
|
||||||
|
bool isAllocatableMold{false};
|
||||||
if (isProcPtrTarget) {
|
if (isProcPtrTarget) {
|
||||||
// MOLD= procedure pointer
|
// MOLD= procedure pointer
|
||||||
std::optional<characteristics::Procedure> procPointer;
|
std::optional<characteristics::Procedure> procPointer;
|
||||||
if (IsNullProcedurePointer(*mold)) {
|
if (IsNullProcedurePointer(mold)) {
|
||||||
procPointer =
|
procPointer =
|
||||||
characteristics::Procedure::Characterize(*mold, context);
|
characteristics::Procedure::Characterize(*mold, context);
|
||||||
} else {
|
} else {
|
||||||
@ -2885,12 +2898,13 @@ SpecificCall IntrinsicProcTable::Implementation::HandleNull(
|
|||||||
fResult.emplace(std::move(*procPointer));
|
fResult.emplace(std::move(*procPointer));
|
||||||
}
|
}
|
||||||
} else if (auto type{mold->GetType()}) {
|
} else if (auto type{mold->GetType()}) {
|
||||||
// MOLD= object pointer
|
// MOLD= object pointer or allocatable
|
||||||
characteristics::TypeAndShape typeAndShape{
|
characteristics::TypeAndShape typeAndShape{
|
||||||
*type, GetShape(context, *mold)};
|
*type, GetShape(context, *mold)};
|
||||||
args.emplace_back(
|
args.emplace_back(
|
||||||
"mold"s, characteristics::DummyDataObject{typeAndShape});
|
"mold"s, characteristics::DummyDataObject{typeAndShape});
|
||||||
fResult.emplace(std::move(typeAndShape));
|
fResult.emplace(std::move(typeAndShape));
|
||||||
|
isAllocatableMold = IsAllocatableDesignator(*mold);
|
||||||
} else {
|
} else {
|
||||||
context.messages().Say(arguments[0]->sourceLocation(),
|
context.messages().Say(arguments[0]->sourceLocation(),
|
||||||
"MOLD= argument to NULL() lacks type"_err_en_US);
|
"MOLD= argument to NULL() lacks type"_err_en_US);
|
||||||
@ -2898,7 +2912,9 @@ SpecificCall IntrinsicProcTable::Implementation::HandleNull(
|
|||||||
if (fResult) {
|
if (fResult) {
|
||||||
fResult->attrs.set(characteristics::FunctionResult::Attr::Pointer);
|
fResult->attrs.set(characteristics::FunctionResult::Attr::Pointer);
|
||||||
characteristics::Procedure::Attrs attrs;
|
characteristics::Procedure::Attrs attrs;
|
||||||
attrs.set(characteristics::Procedure::Attr::NullPointer);
|
attrs.set(isAllocatableMold
|
||||||
|
? characteristics::Procedure::Attr::NullAllocatable
|
||||||
|
: characteristics::Procedure::Attr::NullPointer);
|
||||||
characteristics::Procedure chars{
|
characteristics::Procedure chars{
|
||||||
std::move(*fResult), std::move(args), attrs};
|
std::move(*fResult), std::move(args), attrs};
|
||||||
return SpecificCall{SpecificIntrinsic{"null"s, std::move(chars)},
|
return SpecificCall{SpecificIntrinsic{"null"s, std::move(chars)},
|
||||||
@ -3257,7 +3273,7 @@ static bool ApplySpecificChecks(SpecificCall &call, FoldingContext &context) {
|
|||||||
const auto &arg{call.arguments[0]};
|
const auto &arg{call.arguments[0]};
|
||||||
if (arg) {
|
if (arg) {
|
||||||
if (const auto *expr{arg->UnwrapExpr()}) {
|
if (const auto *expr{arg->UnwrapExpr()}) {
|
||||||
ok = evaluate::IsAllocatableDesignator(*expr);
|
ok = IsAllocatableDesignator(*expr) || IsNullAllocatable(expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
|
@ -1173,8 +1173,10 @@ auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result {
|
|||||||
if (call.arguments().size() >= 2) {
|
if (call.arguments().size() >= 2) {
|
||||||
return (*this)(call.arguments()[1]); // MASK=
|
return (*this)(call.arguments()[1]); // MASK=
|
||||||
}
|
}
|
||||||
} else if (intrinsic->characteristics.value().attrs.test(characteristics::
|
} else if (intrinsic->characteristics.value().attrs.test(
|
||||||
Procedure::Attr::NullPointer)) { // NULL(MOLD=)
|
characteristics::Procedure::Attr::NullPointer) ||
|
||||||
|
intrinsic->characteristics.value().attrs.test(
|
||||||
|
characteristics::Procedure::Attr::NullAllocatable)) { // NULL(MOLD=)
|
||||||
return (*this)(call.arguments());
|
return (*this)(call.arguments());
|
||||||
} else {
|
} else {
|
||||||
// TODO: shapes of other non-elemental intrinsic results
|
// TODO: shapes of other non-elemental intrinsic results
|
||||||
|
@ -929,7 +929,7 @@ bool IsPointer(const Expr<SomeType> &expr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool IsProcedurePointer(const Expr<SomeType> &expr) {
|
bool IsProcedurePointer(const Expr<SomeType> &expr) {
|
||||||
if (IsNullProcedurePointer(expr)) {
|
if (IsNullProcedurePointer(&expr)) {
|
||||||
return true;
|
return true;
|
||||||
} else if (const auto *funcRef{UnwrapProcedureRef(expr)}) {
|
} else if (const auto *funcRef{UnwrapProcedureRef(expr)}) {
|
||||||
if (const Symbol * proc{funcRef->proc().GetSymbol()}) {
|
if (const Symbol * proc{funcRef->proc().GetSymbol()}) {
|
||||||
@ -963,7 +963,7 @@ bool IsProcedurePointerTarget(const Expr<SomeType> &expr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool IsObjectPointer(const Expr<SomeType> &expr) {
|
bool IsObjectPointer(const Expr<SomeType> &expr) {
|
||||||
if (IsNullObjectPointer(expr)) {
|
if (IsNullObjectPointer(&expr)) {
|
||||||
return true;
|
return true;
|
||||||
} else if (IsProcedurePointerTarget(expr)) {
|
} else if (IsProcedurePointerTarget(expr)) {
|
||||||
return false;
|
return false;
|
||||||
@ -1030,15 +1030,15 @@ template <bool IS_PROC_PTR> struct IsNullPointerHelper {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool IsNullObjectPointer(const Expr<SomeType> &expr) {
|
bool IsNullObjectPointer(const Expr<SomeType> *expr) {
|
||||||
return IsNullPointerHelper<false>{}(expr);
|
return expr && IsNullPointerHelper<false>{}(*expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsNullProcedurePointer(const Expr<SomeType> &expr) {
|
bool IsNullProcedurePointer(const Expr<SomeType> *expr) {
|
||||||
return IsNullPointerHelper<true>{}(expr);
|
return expr && IsNullPointerHelper<true>{}(*expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsNullPointer(const Expr<SomeType> &expr) {
|
bool IsNullPointer(const Expr<SomeType> *expr) {
|
||||||
return IsNullObjectPointer(expr) || IsNullProcedurePointer(expr);
|
return IsNullObjectPointer(expr) || IsNullProcedurePointer(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1046,6 +1046,30 @@ bool IsBareNullPointer(const Expr<SomeType> *expr) {
|
|||||||
return expr && std::holds_alternative<NullPointer>(expr->u);
|
return expr && std::holds_alternative<NullPointer>(expr->u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct IsNullAllocatableHelper {
|
||||||
|
template <typename A> bool operator()(const A &) const { return false; }
|
||||||
|
template <typename T> bool operator()(const FunctionRef<T> &call) const {
|
||||||
|
const auto *intrinsic{call.proc().GetSpecificIntrinsic()};
|
||||||
|
return intrinsic &&
|
||||||
|
intrinsic->characteristics.value().attrs.test(
|
||||||
|
characteristics::Procedure::Attr::NullAllocatable);
|
||||||
|
}
|
||||||
|
template <typename T> bool operator()(const Parentheses<T> &x) const {
|
||||||
|
return (*this)(x.left());
|
||||||
|
}
|
||||||
|
template <typename T> bool operator()(const Expr<T> &x) const {
|
||||||
|
return common::visit(*this, x.u);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool IsNullAllocatable(const Expr<SomeType> *x) {
|
||||||
|
return x && IsNullAllocatableHelper{}(*x);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsNullPointerOrAllocatable(const Expr<SomeType> *x) {
|
||||||
|
return IsNullPointer(x) || IsNullAllocatable(x);
|
||||||
|
}
|
||||||
|
|
||||||
// GetSymbolVector()
|
// GetSymbolVector()
|
||||||
auto GetSymbolVectorHelper::operator()(const Symbol &x) const -> Result {
|
auto GetSymbolVectorHelper::operator()(const Symbol &x) const -> Result {
|
||||||
if (const auto *details{x.detailsIf<semantics::AssocEntityDetails>()}) {
|
if (const auto *details{x.detailsIf<semantics::AssocEntityDetails>()}) {
|
||||||
@ -1393,7 +1417,7 @@ bool IsAllocatableOrPointerObject(const Expr<SomeType> &expr) {
|
|||||||
const semantics::Symbol *sym{UnwrapWholeSymbolOrComponentDataRef(expr)};
|
const semantics::Symbol *sym{UnwrapWholeSymbolOrComponentDataRef(expr)};
|
||||||
return (sym &&
|
return (sym &&
|
||||||
semantics::IsAllocatableOrObjectPointer(&sym->GetUltimate())) ||
|
semantics::IsAllocatableOrObjectPointer(&sym->GetUltimate())) ||
|
||||||
evaluate::IsObjectPointer(expr);
|
evaluate::IsObjectPointer(expr) || evaluate::IsNullAllocatable(&expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsAllocatableDesignator(const Expr<SomeType> &expr) {
|
bool IsAllocatableDesignator(const Expr<SomeType> &expr) {
|
||||||
|
@ -370,7 +370,7 @@ static mlir::Value genStructureComponentInit(
|
|||||||
/*typeParams=*/mlir::ValueRange{} /*TODO*/);
|
/*typeParams=*/mlir::ValueRange{} /*TODO*/);
|
||||||
|
|
||||||
if (Fortran::semantics::IsAllocatable(sym)) {
|
if (Fortran::semantics::IsAllocatable(sym)) {
|
||||||
if (!Fortran::evaluate::IsNullPointer(expr)) {
|
if (!Fortran::evaluate::IsNullPointerOrAllocatable(&expr)) {
|
||||||
fir::emitFatalError(loc, "constant structure constructor with an "
|
fir::emitFatalError(loc, "constant structure constructor with an "
|
||||||
"allocatable component value that is not NULL");
|
"allocatable component value that is not NULL");
|
||||||
} else {
|
} else {
|
||||||
@ -414,7 +414,7 @@ static mlir::Value genStructureComponentInit(
|
|||||||
// must fall through to genConstantValue() below.
|
// must fall through to genConstantValue() below.
|
||||||
if (Fortran::semantics::IsBuiltinCPtr(sym) && sym.Rank() == 0 &&
|
if (Fortran::semantics::IsBuiltinCPtr(sym) && sym.Rank() == 0 &&
|
||||||
(Fortran::evaluate::GetLastSymbol(expr) ||
|
(Fortran::evaluate::GetLastSymbol(expr) ||
|
||||||
Fortran::evaluate::IsNullPointer(expr))) {
|
Fortran::evaluate::IsNullPointer(&expr))) {
|
||||||
// Builtin c_ptr and c_funptr have special handling because designators
|
// Builtin c_ptr and c_funptr have special handling because designators
|
||||||
// and NULL() are handled as initial values for them as an extension
|
// and NULL() are handled as initial values for them as an extension
|
||||||
// (otherwise only c_ptr_null/c_funptr_null are allowed and these are
|
// (otherwise only c_ptr_null/c_funptr_null are allowed and these are
|
||||||
|
@ -62,7 +62,7 @@ static void CheckImplicitInterfaceArg(evaluate::ActualArgument &arg,
|
|||||||
}
|
}
|
||||||
if (IsBOZLiteral(*expr)) {
|
if (IsBOZLiteral(*expr)) {
|
||||||
messages.Say("BOZ argument requires an explicit interface"_err_en_US);
|
messages.Say("BOZ argument requires an explicit interface"_err_en_US);
|
||||||
} else if (evaluate::IsNullPointer(*expr)) {
|
} else if (evaluate::IsNullPointerOrAllocatable(expr)) {
|
||||||
messages.Say(
|
messages.Say(
|
||||||
"Null pointer argument requires an explicit interface"_err_en_US);
|
"Null pointer argument requires an explicit interface"_err_en_US);
|
||||||
} else if (auto named{evaluate::ExtractNamedEntity(*expr)}) {
|
} else if (auto named{evaluate::ExtractNamedEntity(*expr)}) {
|
||||||
@ -783,7 +783,6 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
|
|||||||
// 15.5.2.6 -- dummy is ALLOCATABLE
|
// 15.5.2.6 -- dummy is ALLOCATABLE
|
||||||
bool dummyIsOptional{
|
bool dummyIsOptional{
|
||||||
dummy.attrs.test(characteristics::DummyDataObject::Attr::Optional)};
|
dummy.attrs.test(characteristics::DummyDataObject::Attr::Optional)};
|
||||||
bool actualIsNull{evaluate::IsNullPointer(actual)};
|
|
||||||
if (dummyIsAllocatable) {
|
if (dummyIsAllocatable) {
|
||||||
if (actualIsAllocatable) {
|
if (actualIsAllocatable) {
|
||||||
if (actualIsCoindexed && dummy.intent != common::Intent::In) {
|
if (actualIsCoindexed && dummy.intent != common::Intent::In) {
|
||||||
@ -791,7 +790,7 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
|
|||||||
"ALLOCATABLE %s must have INTENT(IN) to be associated with a coindexed actual argument"_err_en_US,
|
"ALLOCATABLE %s must have INTENT(IN) to be associated with a coindexed actual argument"_err_en_US,
|
||||||
dummyName);
|
dummyName);
|
||||||
}
|
}
|
||||||
} else if (actualIsNull) {
|
} else if (evaluate::IsBareNullPointer(&actual)) {
|
||||||
if (dummyIsOptional) {
|
if (dummyIsOptional) {
|
||||||
} else if (dummy.intent == common::Intent::Default &&
|
} else if (dummy.intent == common::Intent::Default &&
|
||||||
context.ShouldWarn(
|
context.ShouldWarn(
|
||||||
@ -808,6 +807,16 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
|
|||||||
}
|
}
|
||||||
// INTENT(OUT) and INTENT(IN OUT) cases are caught elsewhere as being
|
// INTENT(OUT) and INTENT(IN OUT) cases are caught elsewhere as being
|
||||||
// undefinable actual arguments.
|
// undefinable actual arguments.
|
||||||
|
} else if (evaluate::IsNullAllocatable(&actual)) {
|
||||||
|
if (dummyIsOptional) {
|
||||||
|
} else if (dummy.intent == common::Intent::Default &&
|
||||||
|
context.ShouldWarn(
|
||||||
|
common::UsageWarning::NullActualForDefaultIntentAllocatable)) {
|
||||||
|
messages.Say(
|
||||||
|
"A null allocatable should not be associated with allocatable %s without INTENT(IN)"_warn_en_US,
|
||||||
|
dummyName);
|
||||||
|
}
|
||||||
|
// INTENT(OUT) and INTENT(IN OUT) cases are caught elsewhere
|
||||||
} else {
|
} else {
|
||||||
messages.Say(
|
messages.Say(
|
||||||
"ALLOCATABLE %s must be associated with an ALLOCATABLE actual argument"_err_en_US,
|
"ALLOCATABLE %s must be associated with an ALLOCATABLE actual argument"_err_en_US,
|
||||||
@ -946,7 +955,7 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
|
|||||||
|
|
||||||
// NULL(MOLD=) checking for non-intrinsic procedures
|
// NULL(MOLD=) checking for non-intrinsic procedures
|
||||||
if (!intrinsic && !dummyIsAllocatableOrPointer && !dummyIsOptional &&
|
if (!intrinsic && !dummyIsAllocatableOrPointer && !dummyIsOptional &&
|
||||||
actualIsNull) {
|
evaluate::IsNullPointer(&actual)) {
|
||||||
messages.Say(
|
messages.Say(
|
||||||
"Actual argument associated with %s may not be null pointer %s"_err_en_US,
|
"Actual argument associated with %s may not be null pointer %s"_err_en_US,
|
||||||
dummyName, actual.AsFortran());
|
dummyName, actual.AsFortran());
|
||||||
@ -1091,6 +1100,8 @@ static void CheckProcedureArg(evaluate::ActualArgument &arg,
|
|||||||
characteristics::Procedure &argInterface{argProc->procedure.value()};
|
characteristics::Procedure &argInterface{argProc->procedure.value()};
|
||||||
argInterface.attrs.reset(
|
argInterface.attrs.reset(
|
||||||
characteristics::Procedure::Attr::NullPointer);
|
characteristics::Procedure::Attr::NullPointer);
|
||||||
|
argInterface.attrs.reset(
|
||||||
|
characteristics::Procedure::Attr::NullAllocatable);
|
||||||
if (!argProcSymbol || argProcSymbol->attrs().test(Attr::INTRINSIC)) {
|
if (!argProcSymbol || argProcSymbol->attrs().test(Attr::INTRINSIC)) {
|
||||||
// It's ok to pass ELEMENTAL unrestricted intrinsic functions.
|
// It's ok to pass ELEMENTAL unrestricted intrinsic functions.
|
||||||
argInterface.attrs.reset(
|
argInterface.attrs.reset(
|
||||||
@ -1105,6 +1116,8 @@ static void CheckProcedureArg(evaluate::ActualArgument &arg,
|
|||||||
} else {
|
} else {
|
||||||
argInterface.attrs.reset(
|
argInterface.attrs.reset(
|
||||||
characteristics::Procedure::Attr::NullPointer);
|
characteristics::Procedure::Attr::NullPointer);
|
||||||
|
argInterface.attrs.reset(
|
||||||
|
characteristics::Procedure::Attr::NullAllocatable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (interface.HasExplicitInterface()) {
|
if (interface.HasExplicitInterface()) {
|
||||||
@ -1161,7 +1174,7 @@ static void CheckProcedureArg(evaluate::ActualArgument &arg,
|
|||||||
"Actual argument associated with procedure %s is not a procedure"_err_en_US,
|
"Actual argument associated with procedure %s is not a procedure"_err_en_US,
|
||||||
dummyName);
|
dummyName);
|
||||||
}
|
}
|
||||||
} else if (IsNullPointer(*expr)) {
|
} else if (IsNullPointer(expr)) {
|
||||||
if (!dummyIsPointer &&
|
if (!dummyIsPointer &&
|
||||||
!dummy.attrs.test(
|
!dummy.attrs.test(
|
||||||
characteristics::DummyProcedure::Attr::Optional)) {
|
characteristics::DummyProcedure::Attr::Optional)) {
|
||||||
@ -1263,11 +1276,11 @@ static void CheckExplicitInterfaceArg(evaluate::ActualArgument &arg,
|
|||||||
IsBOZLiteral(*expr)) {
|
IsBOZLiteral(*expr)) {
|
||||||
// ok
|
// ok
|
||||||
} else if (object.type.type().IsTypelessIntrinsicArgument() &&
|
} else if (object.type.type().IsTypelessIntrinsicArgument() &&
|
||||||
evaluate::IsNullObjectPointer(*expr)) {
|
evaluate::IsNullObjectPointer(expr)) {
|
||||||
// ok, ASSOCIATED(NULL(without MOLD=))
|
// ok, ASSOCIATED(NULL(without MOLD=))
|
||||||
} else if (object.type.attrs().test(characteristics::
|
} else if (object.type.attrs().test(characteristics::
|
||||||
TypeAndShape::Attr::AssumedRank) &&
|
TypeAndShape::Attr::AssumedRank) &&
|
||||||
evaluate::IsNullObjectPointer(*expr) &&
|
evaluate::IsNullObjectPointer(expr) &&
|
||||||
(object.attrs.test(
|
(object.attrs.test(
|
||||||
characteristics::DummyDataObject::Attr::Allocatable) ||
|
characteristics::DummyDataObject::Attr::Allocatable) ||
|
||||||
object.attrs.test(
|
object.attrs.test(
|
||||||
@ -1280,7 +1293,7 @@ static void CheckExplicitInterfaceArg(evaluate::ActualArgument &arg,
|
|||||||
Attr::Pointer) ||
|
Attr::Pointer) ||
|
||||||
object.attrs.test(characteristics::
|
object.attrs.test(characteristics::
|
||||||
DummyDataObject::Attr::Optional)) &&
|
DummyDataObject::Attr::Optional)) &&
|
||||||
evaluate::IsNullObjectPointer(*expr)) {
|
evaluate::IsNullObjectPointer(expr)) {
|
||||||
// FOO(NULL(without MOLD=))
|
// FOO(NULL(without MOLD=))
|
||||||
if (object.type.type().IsAssumedLengthCharacter()) {
|
if (object.type.type().IsAssumedLengthCharacter()) {
|
||||||
messages.Say(
|
messages.Say(
|
||||||
@ -1299,7 +1312,8 @@ static void CheckExplicitInterfaceArg(evaluate::ActualArgument &arg,
|
|||||||
}
|
}
|
||||||
} else if (object.attrs.test(characteristics::DummyDataObject::
|
} else if (object.attrs.test(characteristics::DummyDataObject::
|
||||||
Attr::Allocatable) &&
|
Attr::Allocatable) &&
|
||||||
evaluate::IsNullPointer(*expr)) {
|
(evaluate::IsNullAllocatable(expr) ||
|
||||||
|
evaluate::IsBareNullPointer(expr))) {
|
||||||
if (object.intent == common::Intent::Out ||
|
if (object.intent == common::Intent::Out ||
|
||||||
object.intent == common::Intent::InOut) {
|
object.intent == common::Intent::InOut) {
|
||||||
messages.Say(
|
messages.Say(
|
||||||
@ -1573,13 +1587,13 @@ static void CheckAssociated(evaluate::ActualArguments &arguments,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!IsNullProcedurePointer(*targetExpr)) {
|
} else if (!IsNullProcedurePointer(targetExpr)) {
|
||||||
messages.Say(
|
messages.Say(
|
||||||
"POINTER= argument '%s' is a procedure pointer but the TARGET= argument '%s' is not a procedure or procedure pointer"_err_en_US,
|
"POINTER= argument '%s' is a procedure pointer but the TARGET= argument '%s' is not a procedure or procedure pointer"_err_en_US,
|
||||||
pointerExpr->AsFortran(), targetExpr->AsFortran());
|
pointerExpr->AsFortran(), targetExpr->AsFortran());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (IsVariable(*targetExpr) || IsNullPointer(*targetExpr)) {
|
} else if (IsVariable(*targetExpr) || IsNullPointer(targetExpr)) {
|
||||||
// Object pointer and target
|
// Object pointer and target
|
||||||
if (ExtractDataRef(*targetExpr)) {
|
if (ExtractDataRef(*targetExpr)) {
|
||||||
if (SymbolVector symbols{GetSymbolVector(*targetExpr)};
|
if (SymbolVector symbols{GetSymbolVector(*targetExpr)};
|
||||||
|
@ -381,7 +381,7 @@ bool DataInitializationCompiler<DSV>::InitElement(
|
|||||||
if (static_cast<std::size_t>(offsetSymbol.offset() + offsetSymbol.size()) >
|
if (static_cast<std::size_t>(offsetSymbol.offset() + offsetSymbol.size()) >
|
||||||
symbol.size()) {
|
symbol.size()) {
|
||||||
OutOfRangeError();
|
OutOfRangeError();
|
||||||
} else if (evaluate::IsNullPointer(*expr)) {
|
} else if (evaluate::IsNullPointer(expr)) {
|
||||||
// nothing to do; rely on zero initialization
|
// nothing to do; rely on zero initialization
|
||||||
return true;
|
return true;
|
||||||
} else if (isProcPointer) {
|
} else if (isProcPointer) {
|
||||||
@ -414,7 +414,7 @@ bool DataInitializationCompiler<DSV>::InitElement(
|
|||||||
GetImage().AddPointer(offsetSymbol.offset(), *expr);
|
GetImage().AddPointer(offsetSymbol.offset(), *expr);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (evaluate::IsNullPointer(*expr)) {
|
} else if (evaluate::IsNullPointer(expr)) {
|
||||||
exprAnalyzer_.Say("Initializer for '%s' must not be a pointer"_err_en_US,
|
exprAnalyzer_.Say("Initializer for '%s' must not be a pointer"_err_en_US,
|
||||||
DescribeElement());
|
DescribeElement());
|
||||||
} else if (evaluate::IsProcedureDesignator(*expr)) {
|
} else if (evaluate::IsProcedureDesignator(*expr)) {
|
||||||
@ -900,7 +900,7 @@ void ConstructInitializer(const Symbol &symbol,
|
|||||||
mutableProc.set_init(DEREF(procDesignator->GetSymbol()));
|
mutableProc.set_init(DEREF(procDesignator->GetSymbol()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
CHECK(evaluate::IsNullProcedurePointer(*expr));
|
CHECK(evaluate::IsNullProcedurePointer(&*expr));
|
||||||
mutableProc.set_init(nullptr);
|
mutableProc.set_init(nullptr);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -381,7 +381,7 @@ std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at,
|
|||||||
if (auto whyNotDataRef{WhyNotDefinable(at, scope, flags, *dataRef)}) {
|
if (auto whyNotDataRef{WhyNotDefinable(at, scope, flags, *dataRef)}) {
|
||||||
return whyNotDataRef;
|
return whyNotDataRef;
|
||||||
}
|
}
|
||||||
} else if (evaluate::IsNullPointer(expr)) {
|
} else if (evaluate::IsNullPointerOrAllocatable(&expr)) {
|
||||||
return parser::Message{
|
return parser::Message{
|
||||||
at, "'%s' is a null pointer"_err_en_US, expr.AsFortran()};
|
at, "'%s' is a null pointer"_err_en_US, expr.AsFortran()};
|
||||||
} else if (flags.test(DefinabilityFlag::PointerDefinition)) {
|
} else if (flags.test(DefinabilityFlag::PointerDefinition)) {
|
||||||
|
@ -2167,7 +2167,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(
|
|||||||
result.Add(*symbol, Fold(std::move(*value)));
|
result.Add(*symbol, Fold(std::move(*value)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (IsNullPointer(*value)) {
|
if (IsNullPointer(&*value)) {
|
||||||
if (IsAllocatable(*symbol)) {
|
if (IsAllocatable(*symbol)) {
|
||||||
if (IsBareNullPointer(&*value)) {
|
if (IsBareNullPointer(&*value)) {
|
||||||
// NULL() with no arguments allowed by 7.5.10 para 6 for
|
// NULL() with no arguments allowed by 7.5.10 para 6 for
|
||||||
@ -2175,7 +2175,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(
|
|||||||
result.Add(*symbol, Expr<SomeType>{NullPointer{}});
|
result.Add(*symbol, Expr<SomeType>{NullPointer{}});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (IsNullObjectPointer(*value)) {
|
if (IsNullObjectPointer(&*value)) {
|
||||||
AttachDeclaration(
|
AttachDeclaration(
|
||||||
Warn(common::LanguageFeature::
|
Warn(common::LanguageFeature::
|
||||||
NullMoldAllocatableComponentValue,
|
NullMoldAllocatableComponentValue,
|
||||||
@ -2200,8 +2200,11 @@ MaybeExpr ExpressionAnalyzer::Analyze(
|
|||||||
*symbol);
|
*symbol);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
} else if (IsNullAllocatable(&*value) && IsAllocatable(*symbol)) {
|
||||||
|
result.Add(*symbol, Expr<SomeType>{NullPointer{}});
|
||||||
|
continue;
|
||||||
} else if (const Symbol * pointer{FindPointerComponent(*symbol)};
|
} else if (const Symbol * pointer{FindPointerComponent(*symbol)};
|
||||||
pointer && pureContext) { // C1594(4)
|
pointer && pureContext) { // C1594(4)
|
||||||
if (const Symbol *
|
if (const Symbol *
|
||||||
visible{semantics::FindExternallyVisibleObject(
|
visible{semantics::FindExternallyVisibleObject(
|
||||||
*value, *pureContext)}) {
|
*value, *pureContext)}) {
|
||||||
@ -2522,10 +2525,13 @@ static bool CheckCompatibleArgument(bool isElemental,
|
|||||||
return common::visit(
|
return common::visit(
|
||||||
common::visitors{
|
common::visitors{
|
||||||
[&](const characteristics::DummyDataObject &x) {
|
[&](const characteristics::DummyDataObject &x) {
|
||||||
if (x.attrs.test(characteristics::DummyDataObject::Attr::Pointer) &&
|
if ((x.attrs.test(
|
||||||
|
characteristics::DummyDataObject::Attr::Pointer) ||
|
||||||
|
x.attrs.test(
|
||||||
|
characteristics::DummyDataObject::Attr::Allocatable)) &&
|
||||||
IsBareNullPointer(expr)) {
|
IsBareNullPointer(expr)) {
|
||||||
// NULL() without MOLD= is compatible with any dummy data pointer
|
// NULL() without MOLD= is compatible with any dummy data pointer
|
||||||
// but cannot be allowed to lead to ambiguity.
|
// or allocatable, but cannot be allowed to lead to ambiguity.
|
||||||
return true;
|
return true;
|
||||||
} else if (!isElemental && actual.Rank() != x.type.Rank() &&
|
} else if (!isElemental && actual.Rank() != x.type.Rank() &&
|
||||||
!x.type.attrs().test(
|
!x.type.attrs().test(
|
||||||
@ -3877,7 +3883,7 @@ MaybeExpr ExpressionAnalyzer::ExprOrVariable(
|
|||||||
}
|
}
|
||||||
if (result) {
|
if (result) {
|
||||||
if constexpr (std::is_same_v<PARSED, parser::Expr>) {
|
if constexpr (std::is_same_v<PARSED, parser::Expr>) {
|
||||||
if (!isNullPointerOk_ && IsNullPointer(*result)) {
|
if (!isNullPointerOk_ && IsNullPointerOrAllocatable(&*result)) {
|
||||||
Say(source,
|
Say(source,
|
||||||
"NULL() may not be used as an expression in this context"_err_en_US);
|
"NULL() may not be used as an expression in this context"_err_en_US);
|
||||||
}
|
}
|
||||||
@ -4396,15 +4402,11 @@ bool ArgumentAnalyzer::CheckAssignmentConformance() {
|
|||||||
|
|
||||||
bool ArgumentAnalyzer::CheckForNullPointer(const char *where) {
|
bool ArgumentAnalyzer::CheckForNullPointer(const char *where) {
|
||||||
for (const std::optional<ActualArgument> &arg : actuals_) {
|
for (const std::optional<ActualArgument> &arg : actuals_) {
|
||||||
if (arg) {
|
if (arg && IsNullPointerOrAllocatable(arg->UnwrapExpr())) {
|
||||||
if (const Expr<SomeType> *expr{arg->UnwrapExpr()}) {
|
context_.Say(
|
||||||
if (IsNullPointer(*expr)) {
|
source_, "A NULL() pointer is not allowed %s"_err_en_US, where);
|
||||||
context_.Say(
|
fatalErrors_ = true;
|
||||||
source_, "A NULL() pointer is not allowed %s"_err_en_US, where);
|
return false;
|
||||||
fatalErrors_ = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -184,7 +184,7 @@ bool PointerAssignmentChecker::Check(const SomeExpr &rhs) {
|
|||||||
if (!common::visit([&](const auto &x) { return Check(x); }, rhs.u)) {
|
if (!common::visit([&](const auto &x) { return Check(x); }, rhs.u)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (IsNullPointer(rhs)) {
|
if (IsNullPointer(&rhs)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (lhs_ && IsProcedure(*lhs_)) {
|
if (lhs_ && IsProcedure(*lhs_)) {
|
||||||
|
@ -6225,7 +6225,8 @@ bool DeclarationVisitor::Pre(const parser::ProcPointerInit &x) {
|
|||||||
const auto &null{DEREF(std::get_if<parser::NullInit>(&x.u))};
|
const auto &null{DEREF(std::get_if<parser::NullInit>(&x.u))};
|
||||||
Walk(null);
|
Walk(null);
|
||||||
if (auto nullInit{EvaluateExpr(null)}) {
|
if (auto nullInit{EvaluateExpr(null)}) {
|
||||||
if (!evaluate::IsNullPointer(*nullInit)) {
|
if (!evaluate::IsNullProcedurePointer(&*nullInit) &&
|
||||||
|
!evaluate::IsBareNullPointer(&*nullInit)) {
|
||||||
Say(null.v.value().source,
|
Say(null.v.value().source,
|
||||||
"Procedure pointer initializer must be a name or intrinsic NULL()"_err_en_US);
|
"Procedure pointer initializer must be a name or intrinsic NULL()"_err_en_US);
|
||||||
}
|
}
|
||||||
@ -8634,7 +8635,7 @@ void DeclarationVisitor::Initialization(const parser::Name &name,
|
|||||||
[&](const parser::NullInit &null) { // => NULL()
|
[&](const parser::NullInit &null) { // => NULL()
|
||||||
Walk(null);
|
Walk(null);
|
||||||
if (auto nullInit{EvaluateExpr(null)}) {
|
if (auto nullInit{EvaluateExpr(null)}) {
|
||||||
if (!evaluate::IsNullPointer(*nullInit)) { // C813
|
if (!evaluate::IsNullPointer(&*nullInit)) { // C813
|
||||||
Say(null.v.value().source,
|
Say(null.v.value().source,
|
||||||
"Pointer initializer must be intrinsic NULL()"_err_en_US);
|
"Pointer initializer must be intrinsic NULL()"_err_en_US);
|
||||||
} else if (IsPointer(ultimate)) {
|
} else if (IsPointer(ultimate)) {
|
||||||
@ -8684,7 +8685,7 @@ void DeclarationVisitor::PointerInitialization(
|
|||||||
ultimate.set(Symbol::Flag::InDataStmt, false);
|
ultimate.set(Symbol::Flag::InDataStmt, false);
|
||||||
} else if (auto *details{ultimate.detailsIf<ProcEntityDetails>()}) {
|
} else if (auto *details{ultimate.detailsIf<ProcEntityDetails>()}) {
|
||||||
// something like "REAL, EXTERNAL, POINTER :: p => t"
|
// something like "REAL, EXTERNAL, POINTER :: p => t"
|
||||||
if (evaluate::IsNullProcedurePointer(*expr)) {
|
if (evaluate::IsNullProcedurePointer(&*expr)) {
|
||||||
CHECK(!details->init());
|
CHECK(!details->init());
|
||||||
details->set_init(nullptr);
|
details->set_init(nullptr);
|
||||||
} else if (const Symbol *
|
} else if (const Symbol *
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
module m
|
module m
|
||||||
|
|
||||||
! Testing ASSOCATED
|
! Testing ASSOCIATED and ALLOCATED
|
||||||
integer, pointer :: int_pointer
|
integer, pointer :: int_pointer
|
||||||
integer, allocatable :: int_allocatable
|
integer, allocatable :: int_allocatable
|
||||||
logical, parameter :: test_Assoc1 = .not.(associated(null()))
|
logical, parameter :: test_Assoc1 = .not.(associated(null()))
|
||||||
@ -11,13 +11,10 @@ module m
|
|||||||
!WARN: because: 'NULL()' is a null pointer
|
!WARN: because: 'NULL()' is a null pointer
|
||||||
logical, parameter :: test_Assoc2 = .not.(associated(null(), null()))
|
logical, parameter :: test_Assoc2 = .not.(associated(null(), null()))
|
||||||
logical, parameter :: test_Assoc3 = .not.(associated(null(int_pointer)))
|
logical, parameter :: test_Assoc3 = .not.(associated(null(int_pointer)))
|
||||||
logical, parameter :: test_Assoc4 = .not.(associated(null(int_allocatable)))
|
logical, parameter :: test_Alloc1 = .not.(allocated(null(int_allocatable)))
|
||||||
!WARN: portability: POINTER= argument of ASSOCIATED() is required by some other compilers to be a valid left-hand side of a pointer assignment statement
|
!WARN: portability: POINTER= argument of ASSOCIATED() is required by some other compilers to be a valid left-hand side of a pointer assignment statement
|
||||||
!WARN: because: 'NULL()' is a null pointer
|
!WARN: because: 'NULL()' is a null pointer
|
||||||
logical, parameter :: test_Assoc5 = .not.(associated(null(), null(int_pointer)))
|
logical, parameter :: test_Assoc5 = .not. associated(null(), null(int_pointer))
|
||||||
!WARN: portability: POINTER= argument of ASSOCIATED() is required by some other compilers to be a valid left-hand side of a pointer assignment statement
|
|
||||||
!WARN: because: 'NULL()' is a null pointer
|
|
||||||
logical, parameter :: test_Assoc6 = .not.(associated(null(), null(int_allocatable)))
|
|
||||||
|
|
||||||
type A
|
type A
|
||||||
real(4) x
|
real(4) x
|
||||||
|
@ -25,7 +25,6 @@ subroutine test2
|
|||||||
l = associated(null(),i)
|
l = associated(null(),i)
|
||||||
end subroutine test2
|
end subroutine test2
|
||||||
! CHECK-LABEL: func.func @_QPtest2() {
|
! CHECK-LABEL: func.func @_QPtest2() {
|
||||||
! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.ptr<none>>
|
|
||||||
! CHECK: %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.ptr<i32>> {bindc_name = "i", uniq_name = "_QFtest2Ei"}
|
! CHECK: %[[VAL_1:.*]] = fir.alloca !fir.box<!fir.ptr<i32>> {bindc_name = "i", uniq_name = "_QFtest2Ei"}
|
||||||
! CHECK: %[[VAL_2:.*]] = fir.zero_bits !fir.ptr<i32>
|
! CHECK: %[[VAL_2:.*]] = fir.zero_bits !fir.ptr<i32>
|
||||||
! CHECK: %[[VAL_3:.*]] = fir.embox %[[VAL_2]] : (!fir.ptr<i32>) -> !fir.box<!fir.ptr<i32>>
|
! CHECK: %[[VAL_3:.*]] = fir.embox %[[VAL_2]] : (!fir.ptr<i32>) -> !fir.box<!fir.ptr<i32>>
|
||||||
@ -33,16 +32,8 @@ end subroutine test2
|
|||||||
! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[VAL_1]] {fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFtest2Ei"} : (!fir.ref<!fir.box<!fir.ptr<i32>>>) -> (!fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.ref<!fir.box<!fir.ptr<i32>>>)
|
! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[VAL_1]] {fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFtest2Ei"} : (!fir.ref<!fir.box<!fir.ptr<i32>>>) -> (!fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.ref<!fir.box<!fir.ptr<i32>>>)
|
||||||
! CHECK: %[[VAL_5:.*]] = fir.alloca !fir.logical<4> {bindc_name = "l", uniq_name = "_QFtest2El"}
|
! CHECK: %[[VAL_5:.*]] = fir.alloca !fir.logical<4> {bindc_name = "l", uniq_name = "_QFtest2El"}
|
||||||
! CHECK: %[[VAL_6:.*]]:2 = hlfir.declare %[[VAL_5]] {uniq_name = "_QFtest2El"} : (!fir.ref<!fir.logical<4>>) -> (!fir.ref<!fir.logical<4>>, !fir.ref<!fir.logical<4>>)
|
! CHECK: %[[VAL_6:.*]]:2 = hlfir.declare %[[VAL_5]] {uniq_name = "_QFtest2El"} : (!fir.ref<!fir.logical<4>>) -> (!fir.ref<!fir.logical<4>>, !fir.ref<!fir.logical<4>>)
|
||||||
! CHECK: %[[VAL_7:.*]] = fir.zero_bits !fir.ptr<none>
|
! CHECK: %false = arith.constant false
|
||||||
! CHECK: %[[VAL_8:.*]] = fir.embox %[[VAL_7]] : (!fir.ptr<none>) -> !fir.box<!fir.ptr<none>>
|
! CHECK: %[[VAL_15:.*]] = fir.convert %false : (i1) -> !fir.logical<4>
|
||||||
! CHECK: fir.store %[[VAL_8]] to %[[VAL_0]] : !fir.ref<!fir.box<!fir.ptr<none>>>
|
|
||||||
! CHECK: %[[VAL_9:.*]]:2 = hlfir.declare %[[VAL_0]] {uniq_name = ".tmp.null_box"} : (!fir.ref<!fir.box<!fir.ptr<none>>>) -> (!fir.ref<!fir.box<!fir.ptr<none>>>, !fir.ref<!fir.box<!fir.ptr<none>>>)
|
|
||||||
! CHECK: %[[VAL_10:.*]] = fir.load %[[VAL_4]]#1 : !fir.ref<!fir.box<!fir.ptr<i32>>>
|
|
||||||
! CHECK: %[[VAL_11:.*]] = fir.load %[[VAL_9]]#1 : !fir.ref<!fir.box<!fir.ptr<none>>>
|
|
||||||
! CHECK: %[[VAL_12:.*]] = fir.convert %[[VAL_11]] : (!fir.box<!fir.ptr<none>>) -> !fir.box<none>
|
|
||||||
! CHECK: %[[VAL_13:.*]] = fir.convert %[[VAL_10]] : (!fir.box<!fir.ptr<i32>>) -> !fir.box<none>
|
|
||||||
! CHECK: %[[VAL_14:.*]] = fir.call @_FortranAPointerIsAssociatedWith(%[[VAL_12]], %[[VAL_13]]) fastmath<contract> : (!fir.box<none>, !fir.box<none>) -> i1
|
|
||||||
! CHECK: %[[VAL_15:.*]] = fir.convert %[[VAL_14]] : (i1) -> !fir.logical<4>
|
|
||||||
! CHECK: hlfir.assign %[[VAL_15]] to %[[VAL_6]]#0 : !fir.logical<4>, !fir.ref<!fir.logical<4>>
|
! CHECK: hlfir.assign %[[VAL_15]] to %[[VAL_6]]#0 : !fir.logical<4>, !fir.ref<!fir.logical<4>>
|
||||||
! CHECK: return
|
! CHECK: return
|
||||||
! CHECK: }
|
! CHECK: }
|
||||||
|
@ -119,7 +119,8 @@ subroutine assoc()
|
|||||||
lvar = associated(intPointerVar1, (targetIntVar1))
|
lvar = associated(intPointerVar1, (targetIntVar1))
|
||||||
!ERROR: MOLD= argument to NULL() must be a pointer or allocatable
|
!ERROR: MOLD= argument to NULL() must be a pointer or allocatable
|
||||||
lVar = associated(null(intVar))
|
lVar = associated(null(intVar))
|
||||||
lVar = associated(null(intAllocVar)) !OK
|
!ERROR: A NULL() allocatable is not allowed for 'pointer=' intrinsic argument
|
||||||
|
lVar = associated(null(intAllocVar))
|
||||||
lVar = associated(null()) !OK
|
lVar = associated(null()) !OK
|
||||||
lVar = associated(null(intPointerVar1)) !OK
|
lVar = associated(null(intPointerVar1)) !OK
|
||||||
!PORTABILITY: POINTER= argument of ASSOCIATED() is required by some other compilers to be a valid left-hand side of a pointer assignment statement
|
!PORTABILITY: POINTER= argument of ASSOCIATED() is required by some other compilers to be a valid left-hand side of a pointer assignment statement
|
||||||
|
@ -12,10 +12,9 @@ program test
|
|||||||
!ERROR: Actual argument associated with INTENT(IN OUT) dummy argument 'a=' is not definable
|
!ERROR: Actual argument associated with INTENT(IN OUT) dummy argument 'a=' is not definable
|
||||||
!BECAUSE: 'null(mold=a)' is a null pointer
|
!BECAUSE: 'null(mold=a)' is a null pointer
|
||||||
call foo0(null(mold=a))
|
call foo0(null(mold=a))
|
||||||
!WARNING: A null pointer should not be associated with allocatable dummy argument 'a=' without INTENT(IN)
|
!WARNING: A null allocatable should not be associated with allocatable dummy argument 'a=' without INTENT(IN)
|
||||||
call foo1(null(mold=a))
|
call foo1(null(mold=a))
|
||||||
!PORTABILITY: Allocatable dummy argument 'a=' is associated with a null pointer
|
call foo2(null(mold=a)) ! ok
|
||||||
call foo2(null(mold=a))
|
|
||||||
call foo3(null(mold=a)) ! ok
|
call foo3(null(mold=a)) ! ok
|
||||||
contains
|
contains
|
||||||
subroutine foo0(a)
|
subroutine foo0(a)
|
||||||
|
@ -112,7 +112,10 @@ subroutine test
|
|||||||
dt4x = dt4(null(dt2x%pps0))
|
dt4x = dt4(null(dt2x%pps0))
|
||||||
call canbenull(null(), null()) ! fine
|
call canbenull(null(), null()) ! fine
|
||||||
call canbenull(null(mold=ip0), null(mold=rp0)) ! fine
|
call canbenull(null(mold=ip0), null(mold=rp0)) ! fine
|
||||||
call optionalAllocatable(null(mold=ip0)) ! fine
|
!ERROR: ALLOCATABLE dummy argument 'x=' must be associated with an ALLOCATABLE actual argument
|
||||||
|
call optionalAllocatable(null(mold=ip0))
|
||||||
|
call optionalAllocatable(null(mold=ia0)) ! fine
|
||||||
|
call optionalAllocatable(null()) ! fine
|
||||||
!ERROR: Null pointer argument requires an explicit interface
|
!ERROR: Null pointer argument requires an explicit interface
|
||||||
call implicit(null())
|
call implicit(null())
|
||||||
!ERROR: Null pointer argument requires an explicit interface
|
!ERROR: Null pointer argument requires an explicit interface
|
||||||
|
Loading…
x
Reference in New Issue
Block a user