Skip to content

Commit bc99ce0

Browse files
authored
[flang] Extension: Allow POINTER,INTENT(IN) passed objects (#172175)
ISO Fortran now accepts a non-pointer actual argument to associate with a dummy argument with the POINTER attribute if it is also INTENT(IN), so long as the actual argument is a valid target for the pointer. But passed-object dummy arguments still have a blanket prohibition against being pointers in the ISO standard. Relax that constraint in the case of INTENT(IN) so that passed objects can also benefit from the feature. Fixes #172157.
1 parent e47fc7b commit bc99ce0

File tree

11 files changed

+166
-11
lines changed

11 files changed

+166
-11
lines changed

flang/docs/Extensions.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,10 @@ end
474474
with an optional compilation-time warning. When executed, it
475475
is treated as an 'nX' positioning control descriptor that skips
476476
over the same number of characters, without comparison.
477+
* A passed-object dummy argument for a procedure binding is allowed
478+
to be a pointer so long as it is `INTENT(IN)`.
479+
(This extension is not yet supported for procedure pointer component
480+
interfaces.)
477481

478482
### Extensions supported when enabled by options
479483

flang/include/flang/Lower/CallInterface.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,11 +401,17 @@ class CallerInterface : public CallInterface<CallerInterface> {
401401
llvm_unreachable("getting host associated type in CallerInterface");
402402
}
403403

404+
std::optional<mlir::Value> getOriginalPassArg() const {
405+
return originalPassArg;
406+
}
407+
void setOriginalPassArg(mlir::Value x) { originalPassArg = x; }
408+
404409
private:
405410
/// Check that the input vector is complete.
406411
bool verifyActualInputs() const;
407412
const Fortran::evaluate::ProcedureRef &procRef;
408413
llvm::SmallVector<mlir::Value> actualInputs;
414+
std::optional<mlir::Value> originalPassArg;
409415
};
410416

411417
//===----------------------------------------------------------------------===//

flang/include/flang/Support/Fortran-features.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
5656
IgnoreIrrelevantAttributes, Unsigned, AmbiguousStructureConstructor,
5757
ContiguousOkForSeqAssociation, ForwardRefExplicitTypeDummy,
5858
InaccessibleDeferredOverride, CudaWarpMatchFunction, DoConcurrentOffload,
59-
TransferBOZ, Coarray)
59+
TransferBOZ, Coarray, PointerPassObject)
6060

6161
// Portability and suspicious usage warnings
6262
ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,

flang/lib/Lower/CallInterface.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ bool Fortran::lower::CallerInterface::requireDispatchCall() const {
103103
return true;
104104
}
105105
// calls with PASS attribute have the passed-object already set in its
106-
// arguments. Just check if their is one.
106+
// arguments. Just check if there is one.
107107
std::optional<unsigned> passArg = getPassArgIndex();
108108
if (passArg)
109109
return true;

flang/lib/Lower/ConvertCall.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -673,10 +673,13 @@ Fortran::lower::genCallOpAndResult(
673673
// passed object because interface mismatch issues may have inserted a
674674
// cast to the operand with a different declared type, which would break
675675
// later type bound call resolution in the FIR to FIR pass.
676+
mlir::Value passActual = caller.getInputs()[*passArg];
677+
if (std::optional<mlir::Value> original = caller.getOriginalPassArg())
678+
passActual = *original;
676679
dispatch = fir::DispatchOp::create(
677680
builder, loc, funcType.getResults(), builder.getStringAttr(procName),
678-
caller.getInputs()[*passArg], operands,
679-
builder.getI32IntegerAttr(*passArg), /*arg_attrs=*/nullptr,
681+
passActual, operands, builder.getI32IntegerAttr(*passArg),
682+
/*arg_attrs=*/nullptr,
680683
/*res_attrs=*/nullptr, procAttrs);
681684
} else {
682685
// NOPASS
@@ -1636,8 +1639,12 @@ void prepareUserCallArguments(
16361639
mlir::Location loc = callContext.loc;
16371640
bool mustRemapActualToDummyDescriptors = false;
16381641
fir::FirOpBuilder &builder = callContext.getBuilder();
1642+
std::optional<unsigned> passArg = caller.getPassArgIndex();
1643+
int argIndex = -1;
16391644
for (auto [preparedActual, arg] :
16401645
llvm::zip(loweredActuals, caller.getPassedArguments())) {
1646+
++argIndex;
1647+
bool thisIsPassArg = passArg && argIndex == static_cast<int>(*passArg);
16411648
mlir::Type argTy = callSiteType.getInput(arg.firArgument);
16421649
if (!preparedActual) {
16431650
// Optional dummy argument for which there is no actual argument.
@@ -1750,14 +1757,16 @@ void prepareUserCallArguments(
17501757
continue;
17511758
}
17521759
if (fir::isPointerType(argTy) &&
1753-
!Fortran::evaluate::IsObjectPointer(*expr)) {
1760+
(!Fortran::evaluate::IsObjectPointer(*expr) || thisIsPassArg)) {
17541761
// Passing a non POINTER actual argument to a POINTER dummy argument.
17551762
// Create a pointer of the dummy argument type and assign the actual
17561763
// argument to it.
17571764
auto dataTy = llvm::cast<fir::BaseBoxType>(fir::unwrapRefType(argTy));
17581765
fir::ExtendedValue actualExv = Fortran::lower::convertToAddress(
17591766
loc, callContext.converter, actual, callContext.stmtCtx,
17601767
hlfir::getFortranElementType(dataTy));
1768+
if (thisIsPassArg)
1769+
caller.setOriginalPassArg(fir::getBase(actualExv));
17611770
// If the dummy is an assumed-rank pointer, allocate a pointer
17621771
// descriptor with the actual argument rank (if it is not assumed-rank
17631772
// itself).

flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ class DesignateOpConversion
473473
if (designate.getComponent()) {
474474
mlir::Type baseRecordType = baseEntity.getFortranElementType();
475475
if (fir::isRecordWithTypeParameters(baseRecordType))
476-
TODO(loc, "hlfir.designate with a parametrized derived type base");
476+
TODO(loc, "hlfir.designate with a parameterized derived type base");
477477
fieldIndex = fir::FieldIndexOp::create(
478478
builder, loc, fir::FieldType::get(builder.getContext()),
479479
designate.getComponent().value(), baseRecordType,
@@ -499,7 +499,7 @@ class DesignateOpConversion
499499
return mlir::success();
500500
}
501501
TODO(loc,
502-
"addressing parametrized derived type automatic components");
502+
"addressing parameterized derived type automatic components");
503503
}
504504
baseEleTy = hlfir::getFortranElementType(componentType);
505505
shape = designate.getComponentShape();

flang/lib/Semantics/check-declarations.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2608,9 +2608,6 @@ void CheckHelper::CheckPassArg(
26082608
if (!passArg.has<ObjectEntityDetails>()) {
26092609
msg = "Passed-object dummy argument '%s' of procedure '%s'"
26102610
" must be a data object"_err_en_US;
2611-
} else if (passArg.attrs().test(Attr::POINTER)) {
2612-
msg = "Passed-object dummy argument '%s' of procedure '%s'"
2613-
" may not have the POINTER attribute"_err_en_US;
26142611
} else if (passArg.attrs().test(Attr::ALLOCATABLE)) {
26152612
msg = "Passed-object dummy argument '%s' of procedure '%s'"
26162613
" may not have the ALLOCATABLE attribute"_err_en_US;
@@ -2620,6 +2617,23 @@ void CheckHelper::CheckPassArg(
26202617
} else if (passArg.Rank() > 0) {
26212618
msg = "Passed-object dummy argument '%s' of procedure '%s'"
26222619
" must be scalar"_err_en_US;
2620+
} else if (passArg.attrs().test(Attr::POINTER)) {
2621+
if (context_.IsEnabled(common::LanguageFeature::PointerPassObject) &&
2622+
IsIntentIn(passArg)) {
2623+
if (proc.has<ProcBindingDetails>()) {
2624+
// Extension: allow a passed object to be an INTENT(IN) POINTER.
2625+
// Only works for TBPs, needs lowering work for proc ptr components.
2626+
Warn(common::LanguageFeature::PointerPassObject, name,
2627+
"Passed-object dummy argument '%s' of procedure '%s' that is an INTENT(IN) POINTER is not standard"_port_en_US,
2628+
*passName, name);
2629+
} else {
2630+
msg =
2631+
"Passed-object dummy argument '%s' of procedure '%s' used as procedure pointer component interface may not have the POINTER attribute"_err_en_US;
2632+
}
2633+
} else {
2634+
msg =
2635+
"Passed-object dummy argument '%s' of procedure '%s' may not have the POINTER attribute unless INTENT(IN)"_err_en_US;
2636+
}
26232637
}
26242638
if (msg) {
26252639
messages_.Say(name, std::move(*msg), passName.value(), name);

flang/test/Lower/bug172157-3.f90

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
!RUN: bbc -emit-fir %s -o - 2>&1 | FileCheck %s
2+
3+
module m
4+
type t
5+
integer :: n = 0
6+
contains
7+
procedure :: tbp => f
8+
end type
9+
contains
10+
function f(this)
11+
class(t), pointer, intent(in) :: this
12+
integer, pointer :: f
13+
f => this%n
14+
end
15+
end
16+
17+
subroutine test
18+
use m
19+
type(t), target :: xt
20+
class(t), pointer :: xp
21+
xp => xt
22+
xt%tbp() = 1
23+
xp%tbp() = 2
24+
end
25+
26+
! CHECK-LABEL: func @_QPtest(
27+
! CHECK: %[[C2_I32:.*]] = arith.constant 2 : i32
28+
! CHECK: %[[C1_I32:.*]] = arith.constant 1 : i32
29+
! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.box<!fir.ptr<i32>> {bindc_name = ".result"}
30+
! CHECK: %[[VAL_1:.*]] = fir.alloca !fir.class<!fir.ptr<!fir.type<_QMmTt{n:i32}>>>
31+
! CHECK: %[[VAL_2:.*]] = fir.alloca !fir.box<!fir.ptr<i32>> {bindc_name = ".result"}
32+
! CHECK: %[[VAL_3:.*]] = fir.alloca !fir.class<!fir.ptr<!fir.type<_QMmTt{n:i32}>>>
33+
! CHECK: %{{.*}} = fir.dummy_scope : !fir.dscope
34+
! CHECK: %[[VAL_5:.*]] = fir.alloca !fir.class<!fir.ptr<!fir.type<_QMmTt{n:i32}>>> {bindc_name = "xp", uniq_name = "_QFtestExp"}
35+
! CHECK: %[[VAL_6:.*]] = fir.zero_bits !fir.ptr<!fir.type<_QMmTt{n:i32}>>
36+
! CHECK: %[[VAL_7:.*]] = fir.embox %[[VAL_6]] : (!fir.ptr<!fir.type<_QMmTt{n:i32}>>) -> !fir.class<!fir.ptr<!fir.type<_QMmTt{n:i32}>>>
37+
! CHECK: fir.store %[[VAL_7]] to %[[VAL_5]] : !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMmTt{n:i32}>>>>
38+
! CHECK: %[[VAL_8:.*]] = fir.declare %[[VAL_5]] {fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFtestExp"} : (!fir.ref<!fir.class<!fir.ptr<!fir.type<_QMmTt{n:i32}>>>>) -> !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMmTt{n:i32}>>>>
39+
! CHECK: %[[VAL_9:.*]] = fir.alloca !fir.type<_QMmTt{n:i32}> {bindc_name = "xt", fir.target, uniq_name = "_QFtestExt"}
40+
! CHECK: %[[VAL_10:.*]] = fir.declare %[[VAL_9]] {fortran_attrs = #fir.var_attrs<target>, uniq_name = "_QFtestExt"} : (!fir.ref<!fir.type<_QMmTt{n:i32}>>) -> !fir.ref<!fir.type<_QMmTt{n:i32}>>
41+
! CHECK: %[[VAL_11:.*]] = fir.address_of(@_QQ_QMmTt.DerivedInit) : !fir.ref<!fir.type<_QMmTt{n:i32}>>
42+
! CHECK: fir.copy %[[VAL_11]] to %[[VAL_10]] no_overlap : !fir.ref<!fir.type<_QMmTt{n:i32}>>, !fir.ref<!fir.type<_QMmTt{n:i32}>>
43+
! CHECK: %[[VAL_12:.*]] = fir.embox %[[VAL_10]] : (!fir.ref<!fir.type<_QMmTt{n:i32}>>) -> !fir.box<!fir.ptr<!fir.type<_QMmTt{n:i32}>>>
44+
! CHECK: %[[VAL_13:.*]] = fir.convert %[[VAL_12]] : (!fir.box<!fir.ptr<!fir.type<_QMmTt{n:i32}>>>) -> !fir.class<!fir.ptr<!fir.type<_QMmTt{n:i32}>>>
45+
! CHECK: fir.store %[[VAL_13]] to %[[VAL_8]] : !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMmTt{n:i32}>>>>
46+
! CHECK: %[[VAL_14:.*]] = fir.embox %[[VAL_10]] : (!fir.ref<!fir.type<_QMmTt{n:i32}>>) -> !fir.class<!fir.ptr<!fir.type<_QMmTt{n:i32}>>>
47+
! CHECK: fir.store %[[VAL_14]] to %[[VAL_3]] : !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMmTt{n:i32}>>>>
48+
! CHECK: %[[VAL_15:.*]] = fir.call @_QMmPf(%[[VAL_3]]) fastmath<contract> : (!fir.ref<!fir.class<!fir.ptr<!fir.type<_QMmTt{n:i32}>>>>) -> !fir.box<!fir.ptr<i32>>
49+
! CHECK: fir.save_result %[[VAL_15]] to %[[VAL_2]] : !fir.box<!fir.ptr<i32>>, !fir.ref<!fir.box<!fir.ptr<i32>>>
50+
! CHECK: %[[VAL_16:.*]] = fir.declare %[[VAL_2]] {uniq_name = ".tmp.func_result"} : (!fir.ref<!fir.box<!fir.ptr<i32>>>) -> !fir.ref<!fir.box<!fir.ptr<i32>>>
51+
! CHECK: %[[VAL_17:.*]] = fir.load %[[VAL_16]] : !fir.ref<!fir.box<!fir.ptr<i32>>>
52+
! CHECK: %[[VAL_18:.*]] = fir.box_addr %[[VAL_17]] : (!fir.box<!fir.ptr<i32>>) -> !fir.ptr<i32>
53+
! CHECK: fir.store %[[C1_I32]] to %[[VAL_18]] : !fir.ptr<i32>
54+
! CHECK: %[[VAL_19:.*]] = fir.load %[[VAL_8]] : !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMmTt{n:i32}>>>>
55+
! CHECK: %[[VAL_20:.*]] = fir.rebox %[[VAL_19]] : (!fir.class<!fir.ptr<!fir.type<_QMmTt{n:i32}>>>) -> !fir.class<!fir.ptr<!fir.type<_QMmTt{n:i32}>>>
56+
! CHECK: fir.store %[[VAL_20]] to %[[VAL_1]] : !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMmTt{n:i32}>>>>
57+
! CHECK: %[[VAL_21:.*]] = fir.dispatch "tbp"(%[[VAL_19]] : !fir.class<!fir.ptr<!fir.type<_QMmTt{n:i32}>>>) (%[[VAL_1]] : !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMmTt{n:i32}>>>>) -> !fir.box<!fir.ptr<i32>> {pass_arg_pos = 0 : i32}
58+
! CHECK: fir.save_result %[[VAL_21]] to %[[VAL_0]] : !fir.box<!fir.ptr<i32>>, !fir.ref<!fir.box<!fir.ptr<i32>>>
59+
! CHECK: %[[VAL_22:.*]] = fir.declare %[[VAL_0]] {uniq_name = ".tmp.func_result"} : (!fir.ref<!fir.box<!fir.ptr<i32>>>) -> !fir.ref<!fir.box<!fir.ptr<i32>>>
60+
! CHECK: %[[VAL_23:.*]] = fir.load %[[VAL_22]] : !fir.ref<!fir.box<!fir.ptr<i32>>>
61+
! CHECK: %[[VAL_24:.*]] = fir.box_addr %[[VAL_23]] : (!fir.box<!fir.ptr<i32>>) -> !fir.ptr<i32>
62+
! CHECK: fir.store %[[C2_I32]] to %[[VAL_24]] : !fir.ptr<i32>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
!RUN: %python %S/test_errors.py %s %flang_fc1
2+
module m
3+
type t
4+
!ERROR: Passed-object dummy argument 'this' of procedure 'pp1' used as procedure pointer component interface may not have the POINTER attribute
5+
procedure(sub), pass, pointer :: pp1 => sub
6+
!ERROR: Passed-object dummy argument 'that' of procedure 'pp2' may not have the POINTER attribute unless INTENT(IN)
7+
procedure(sub), pass(that), pointer :: pp2 => sub
8+
contains
9+
procedure :: goodtbp => sub
10+
!ERROR: Passed-object dummy argument 'that' of procedure 'badtbp' may not have the POINTER attribute unless INTENT(IN)
11+
procedure, pass(that) :: badtbp => sub
12+
end type
13+
contains
14+
subroutine sub(this, that)
15+
class(t), pointer, intent(in) :: this
16+
class(t), pointer :: that
17+
end
18+
end
19+
20+
program test
21+
use m
22+
type(t) xnt
23+
type(t), target :: xt
24+
!ERROR: In assignment to object dummy argument 'this=', the target 'xnt' is not an object with POINTER or TARGET attributes
25+
call xnt%goodtbp(null())
26+
call xt%goodtbp(null()) ! ok
27+
end
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
!RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s
2+
module m
3+
type t
4+
integer :: n = 0
5+
contains
6+
procedure :: tbp => f
7+
end type
8+
contains
9+
function f(this)
10+
class(t), pointer, intent(in) :: this
11+
integer, pointer :: f
12+
f => this%n
13+
end
14+
end
15+
16+
program test
17+
use m
18+
type(t), target :: xt
19+
type(t), pointer :: xp
20+
xt%n = 1
21+
!CHECK: PRINT *, f(xt)
22+
print *, xt%tbp()
23+
!CHECK: f(xt)=2_4
24+
xt%tbp() = 2
25+
print *, xt%n
26+
xp => xt
27+
!CHECK: PRINT *, f(xp)
28+
print *, xp%tbp()
29+
!CHECK: f(xp)=3_4
30+
xp%tbp() = 3
31+
print *, xp%n
32+
print *, xt%n
33+
end

0 commit comments

Comments
 (0)