@@ -1001,8 +1001,8 @@ class IsContiguousHelper
10011001 } else {
10021002 return Base::operator ()(ultimate); // use expr
10031003 }
1004- } else if (semantics::IsPointer (ultimate) ||
1005- semantics::IsAssumedShape (ultimate) || IsAssumedRank (ultimate)) {
1004+ } else if (semantics::IsPointer (ultimate) || IsAssumedShape (ultimate) ||
1005+ IsAssumedRank (ultimate)) {
10061006 return std::nullopt ;
10071007 } else if (ultimate.has <semantics::ObjectEntityDetails>()) {
10081008 return true ;
@@ -1282,9 +1282,21 @@ std::optional<bool> IsContiguous(const A &x, FoldingContext &context,
12821282 }
12831283}
12841284
1285+ std::optional<bool > IsContiguous (const ActualArgument &actual,
1286+ FoldingContext &fc, bool namedConstantSectionsAreContiguous,
1287+ bool firstDimensionStride1) {
1288+ auto *expr{actual.UnwrapExpr ()};
1289+ return expr &&
1290+ IsContiguous (
1291+ *expr, fc, namedConstantSectionsAreContiguous, firstDimensionStride1);
1292+ }
1293+
12851294template std::optional<bool > IsContiguous (const Expr<SomeType> &,
12861295 FoldingContext &, bool namedConstantSectionsAreContiguous,
12871296 bool firstDimensionStride1);
1297+ template std::optional<bool > IsContiguous (const ActualArgument &,
1298+ FoldingContext &, bool namedConstantSectionsAreContiguous,
1299+ bool firstDimensionStride1);
12881300template std::optional<bool > IsContiguous (const ArrayRef &, FoldingContext &,
12891301 bool namedConstantSectionsAreContiguous, bool firstDimensionStride1);
12901302template std::optional<bool > IsContiguous (const Substring &, FoldingContext &,
@@ -1434,4 +1446,177 @@ std::optional<parser::Message> CheckStatementFunction(
14341446 return StmtFunctionChecker{sf, context}(expr);
14351447}
14361448
1449+ // Helper class for checking differences between actual and dummy arguments
1450+ class CopyInOutExplicitInterface {
1451+ public:
1452+ explicit CopyInOutExplicitInterface (FoldingContext &fc,
1453+ const ActualArgument &actual,
1454+ const characteristics::DummyDataObject &dummyObj)
1455+ : fc_{fc}, actual_{actual}, dummyObj_{dummyObj} {}
1456+
1457+ // Returns true, if actual and dummy have different contiguity requirements
1458+ bool HaveContiguityDifferences () const {
1459+ // Check actual contiguity, unless dummy doesn't care
1460+ bool dummyTreatAsArray{dummyObj_.ignoreTKR .test (common::IgnoreTKR::Rank)};
1461+ bool actualTreatAsContiguous{
1462+ dummyObj_.ignoreTKR .test (common::IgnoreTKR::Contiguous) ||
1463+ IsSimplyContiguous (actual_, fc_)};
1464+ bool dummyIsExplicitShape{dummyObj_.type .IsExplicitShape ()};
1465+ bool dummyIsAssumedSize{dummyObj_.type .attrs ().test (
1466+ characteristics::TypeAndShape::Attr::AssumedSize)};
1467+ bool dummyIsPolymorphic{dummyObj_.type .type ().IsPolymorphic ()};
1468+ // type(*) with IGNORE_TKR(tkr) is often used to interface with C "void*".
1469+ // Since the other languages don't know about Fortran's discontiguity
1470+ // handling, such cases should require contiguity.
1471+ bool dummyIsVoidStar{dummyObj_.type .type ().IsAssumedType () &&
1472+ dummyObj_.ignoreTKR .test (common::IgnoreTKR::Type) &&
1473+ dummyObj_.ignoreTKR .test (common::IgnoreTKR::Rank) &&
1474+ dummyObj_.ignoreTKR .test (common::IgnoreTKR::Kind)};
1475+ // Explicit shape and assumed size arrays must be contiguous
1476+ bool dummyNeedsContiguity{dummyIsExplicitShape || dummyIsAssumedSize ||
1477+ (dummyTreatAsArray && !dummyIsPolymorphic) || dummyIsVoidStar ||
1478+ dummyObj_.attrs .test (
1479+ characteristics::DummyDataObject::Attr::Contiguous)};
1480+ return !actualTreatAsContiguous && dummyNeedsContiguity;
1481+ }
1482+
1483+ // Returns true, if actual and dummy have polymorphic differences
1484+ bool HavePolymorphicDifferences () const {
1485+ bool dummyIsAssumedRank{dummyObj_.type .attrs ().test (
1486+ characteristics::TypeAndShape::Attr::AssumedRank)};
1487+ bool actualIsAssumedRank{semantics::IsAssumedRank (actual_)};
1488+ bool dummyIsAssumedShape{dummyObj_.type .attrs ().test (
1489+ characteristics::TypeAndShape::Attr::AssumedShape)};
1490+ bool actualIsAssumedShape{semantics::IsAssumedShape (actual_)};
1491+ if ((actualIsAssumedRank && dummyIsAssumedRank) ||
1492+ (actualIsAssumedShape && dummyIsAssumedShape)) {
1493+ // Assumed-rank and assumed-shape arrays are represented by descriptors,
1494+ // so don't need to do polymorphic check.
1495+ } else if (!dummyObj_.ignoreTKR .test (common::IgnoreTKR::Type)) {
1496+ // flang supports limited cases of passing polymorphic to non-polimorphic.
1497+ // These cases require temporary of non-polymorphic type. (For example,
1498+ // the actual argument could be polymorphic array of child type,
1499+ // while the dummy argument could be non-polymorphic array of parent
1500+ // type.)
1501+ bool dummyIsPolymorphic{dummyObj_.type .type ().IsPolymorphic ()};
1502+ auto actualType{
1503+ characteristics::TypeAndShape::Characterize (actual_, fc_)};
1504+ bool actualIsPolymorphic{
1505+ actualType && actualType->type ().IsPolymorphic ()};
1506+ if (actualIsPolymorphic && !dummyIsPolymorphic) {
1507+ return true ;
1508+ }
1509+ }
1510+ return false ;
1511+ }
1512+
1513+ bool HaveArrayOrAssumedRankArgs () const {
1514+ bool dummyTreatAsArray{dummyObj_.ignoreTKR .test (common::IgnoreTKR::Rank)};
1515+ return IsArrayOrAssumedRank (actual_) &&
1516+ (IsArrayOrAssumedRank (dummyObj_) || dummyTreatAsArray);
1517+ }
1518+
1519+ bool PassByValue () const {
1520+ return dummyObj_.attrs .test (characteristics::DummyDataObject::Attr::Value);
1521+ }
1522+
1523+ bool HaveCoarrayDifferences () const {
1524+ return ExtractCoarrayRef (actual_) && dummyObj_.type .corank () == 0 ;
1525+ }
1526+
1527+ bool HasIntentOut () const { return dummyObj_.intent == common::Intent::Out; }
1528+
1529+ bool HasIntentIn () const { return dummyObj_.intent == common::Intent::In; }
1530+
1531+ static bool IsArrayOrAssumedRank (const ActualArgument &actual) {
1532+ return semantics::IsAssumedRank (actual) || actual.Rank () > 0 ;
1533+ }
1534+
1535+ static bool IsArrayOrAssumedRank (
1536+ const characteristics::DummyDataObject &dummy) {
1537+ return dummy.type .attrs ().test (
1538+ characteristics::TypeAndShape::Attr::AssumedRank) ||
1539+ dummy.type .Rank () > 0 ;
1540+ }
1541+
1542+ private:
1543+ FoldingContext &fc_;
1544+ const ActualArgument &actual_;
1545+ const characteristics::DummyDataObject &dummyObj_;
1546+ };
1547+
1548+ // If forCopyOut is false, returns if a particular actual/dummy argument
1549+ // combination may need a temporary creation with copy-in operation. If
1550+ // forCopyOut is true, returns the same for copy-out operation. For
1551+ // procedures with explicit interface, it's expected that "dummy" is not null.
1552+ // For procedures with implicit interface dummy may be null.
1553+ //
1554+ // Note that these copy-in and copy-out checks are done from the caller's
1555+ // perspective, meaning that for copy-in the caller need to do the copy
1556+ // before calling the callee. Similarly, for copy-out the caller is expected
1557+ // to do the copy after the callee returns.
1558+ bool MayNeedCopy (const ActualArgument *actual,
1559+ const characteristics::DummyArgument *dummy, FoldingContext &fc,
1560+ bool forCopyOut) {
1561+ if (!actual) {
1562+ return false ;
1563+ }
1564+ if (actual->isAlternateReturn ()) {
1565+ return false ;
1566+ }
1567+ const auto *dummyObj{dummy
1568+ ? std::get_if<characteristics::DummyDataObject>(&dummy->u )
1569+ : nullptr };
1570+ const bool forCopyIn = !forCopyOut;
1571+ if (!evaluate::IsVariable (*actual)) {
1572+ // Actual argument expressions that aren’t variables are copy-in, but
1573+ // not copy-out.
1574+ return forCopyIn;
1575+ }
1576+ if (dummyObj) { // Explict interface
1577+ CopyInOutExplicitInterface check{fc, *actual, *dummyObj};
1578+ if (forCopyOut && check.HasIntentIn ()) {
1579+ // INTENT(IN) dummy args never need copy-out
1580+ return false ;
1581+ }
1582+ if (forCopyIn && check.HasIntentOut ()) {
1583+ // INTENT(OUT) dummy args never need copy-in
1584+ return false ;
1585+ }
1586+ if (check.PassByValue ()) {
1587+ // Pass by value, always copy-in, never copy-out
1588+ return forCopyIn;
1589+ }
1590+ if (check.HaveCoarrayDifferences ()) {
1591+ return true ;
1592+ }
1593+ // Note: contiguity and polymorphic checks deal with array or assumed rank
1594+ // arguments
1595+ if (!check.HaveArrayOrAssumedRankArgs ()) {
1596+ return false ;
1597+ }
1598+ if (check.HaveContiguityDifferences ()) {
1599+ return true ;
1600+ }
1601+ if (check.HavePolymorphicDifferences ()) {
1602+ return true ;
1603+ }
1604+ } else { // Implicit interface
1605+ if (ExtractCoarrayRef (*actual)) {
1606+ // Coindexed actual args may need copy-in and copy-out with implicit
1607+ // interface
1608+ return true ;
1609+ }
1610+ if (!IsSimplyContiguous (*actual, fc)) {
1611+ // Copy-in: actual arguments that are variables are copy-in when
1612+ // non-contiguous.
1613+ // Copy-out: vector subscripts could refer to duplicate elements, can't
1614+ // copy out.
1615+ return !(forCopyOut && HasVectorSubscript (*actual));
1616+ }
1617+ }
1618+ // For everything else, no copy-in or copy-out
1619+ return false ;
1620+ }
1621+
14371622} // namespace Fortran::evaluate
0 commit comments