diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h --- a/flang/include/flang/Evaluate/tools.h +++ b/flang/include/flang/Evaluate/tools.h @@ -889,6 +889,10 @@ semantics::Attrs{semantics::Attr::POINTER, semantics::Attr::ALLOCATABLE}); } +// Like IsAllocatableOrPointer, but accepts pointer function results as being +// pointers. +bool IsAllocatableOrPointerObject(const Expr &, FoldingContext &); + // Procedure and pointer detection predicates bool IsProcedure(const Expr &); bool IsFunction(const Expr &); @@ -897,6 +901,10 @@ bool IsNullPointer(const Expr &); bool IsObjectPointer(const Expr &, FoldingContext &); +// Can Expr be passed as absent to an optional dummy argument. +// See 15.5.2.12 point 1 for more details. +bool MayBePassedAsAbsentOptional(const Expr &, FoldingContext &); + // Extracts the chain of symbols from a designator, which has perhaps been // wrapped in an Expr<>, removing all of the (co)subscripts. The // base object will be the first symbol in the result vector. diff --git a/flang/lib/Evaluate/fold-complex.cpp b/flang/lib/Evaluate/fold-complex.cpp --- a/flang/lib/Evaluate/fold-complex.cpp +++ b/flang/lib/Evaluate/fold-complex.cpp @@ -41,6 +41,15 @@ // CMPLX(X [, KIND]) with complex X return Fold(context, ConvertToType(std::move(*x))); } else { + if (args.size() >= 2 && args[1].has_value()) { + // Do not fold CMPLX with an Y argument that may be absent at runtime + // into a complex constructor so that lowering can deal with the + // optional aspect (there is no optional aspect with the complex + // constructor). + if (MayBePassedAsAbsentOptional(*args[1]->UnwrapExpr(), context)) { + return Expr{std::move(funcRef)}; + } + } // CMPLX(X [, Y [, KIND]]) with non-complex X Expr re{std::move(*args[0].value().UnwrapExpr())}; Expr im{args.size() >= 2 && args[1].has_value() diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp --- a/flang/lib/Evaluate/tools.cpp +++ b/flang/lib/Evaluate/tools.cpp @@ -1078,6 +1078,24 @@ return std::nullopt; } +bool IsAllocatableOrPointerObject( + const Expr &expr, FoldingContext &context) { + const semantics::Symbol *sym{UnwrapWholeSymbolOrComponentDataRef(expr)}; + return (sym && semantics::IsAllocatableOrPointer(*sym)) || + evaluate::IsObjectPointer(expr, context); +} + +bool MayBePassedAsAbsentOptional( + const Expr &expr, FoldingContext &context) { + const semantics::Symbol *sym{UnwrapWholeSymbolOrComponentDataRef(expr)}; + // 15.5.2.12 1. is pretty clear that an unallocated allocatable/pointer actual + // may be passed to a non-allocatable/non-pointer optional dummy. Note that + // other compilers (like nag, nvfortran, ifort, gfortran and xlf) seems to + // ignore this point in intrinsic contexts (e.g CMPLX argument). + return (sym && semantics::IsOptional(*sym)) || + IsAllocatableOrPointerObject(expr, context); +} + } // namespace Fortran::evaluate namespace Fortran::semantics {