[flang-commits] [flang] [flang] Implement C_F_STRPOINTER (Fortran 2023) (PR #176973)
Caroline Newcombe via flang-commits
flang-commits at lists.llvm.org
Thu Feb 12 09:45:09 PST 2026
https://github.com/cenewcombe updated https://github.com/llvm/llvm-project/pull/176973
>From a9f441a00d2788148f378154c8c8ca71b91e6af9 Mon Sep 17 00:00:00 2001
From: Caroline Newcombe <caroline.newcombe at hpe.com>
Date: Wed, 14 Jan 2026 12:18:01 -0600
Subject: [PATCH 1/3] [flang] Implement C_F_STRPOINTER (Fortran 2023)
---
.../flang/Optimizer/Builder/IntrinsicCall.h | 1 +
flang/lib/Evaluate/intrinsics.cpp | 184 +++++++++++++++++-
flang/lib/Optimizer/Builder/IntrinsicCall.cpp | 99 ++++++++++
flang/module/__fortran_builtins.f90 | 3 +
flang/module/iso_c_binding.f90 | 4 +
.../test/Lower/Intrinsics/c_f_strpointer.f90 | 59 ++++++
flang/test/Semantics/c_f_strpointer.f90 | 46 +++++
7 files changed, 395 insertions(+), 1 deletion(-)
create mode 100644 flang/test/Lower/Intrinsics/c_f_strpointer.f90
create mode 100644 flang/test/Semantics/c_f_strpointer.f90
diff --git a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
index 56642a38cb1ca..6667a40c73c1d 100644
--- a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
+++ b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
@@ -231,6 +231,7 @@ struct IntrinsicLibrary {
llvm::ArrayRef<mlir::Value> args);
void genCFPointer(llvm::ArrayRef<fir::ExtendedValue>);
void genCFProcPointer(llvm::ArrayRef<fir::ExtendedValue>);
+ void genCFStrpointer(llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genCFunLoc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genCLoc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
template <mlir::arith::CmpIPredicate pred>
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index 1242e18ba2466..7cd9eae00a61d 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -2898,6 +2898,8 @@ class IntrinsicProcTable::Implementation {
SpecificCall HandleNull(ActualArguments &, FoldingContext &) const;
std::optional<SpecificCall> HandleC_F_Pointer(
ActualArguments &, FoldingContext &) const;
+ std::optional<SpecificCall> HandleC_F_Strpointer(
+ ActualArguments &, FoldingContext &) const;
std::optional<SpecificCall> HandleC_Loc(
ActualArguments &, FoldingContext &) const;
std::optional<SpecificCall> HandleC_Devloc(
@@ -2940,7 +2942,7 @@ bool IntrinsicProcTable::Implementation::IsIntrinsicSubroutine(
return true;
}
// special cases
- return name == "__builtin_c_f_pointer";
+ return name == "__builtin_c_f_pointer" || name == "__builtin_c_f_strpointer";
}
bool IntrinsicProcTable::Implementation::IsIntrinsic(
const std::string &name) const {
@@ -3256,6 +3258,184 @@ IntrinsicProcTable::Implementation::HandleC_F_Pointer(
}
}
+// Subroutine C_F_STRPOINTER from intrinsic module ISO_C_BINDING (18.2.3.5)
+// C_F_STRPOINTER(CSTRARRAY, FSTRPTR [,NCHARS]) or
+// C_F_STRPOINTER(CSTRPTR, FSTRPTR, NCHARS)
+std::optional<SpecificCall>
+IntrinsicProcTable::Implementation::HandleC_F_Strpointer(
+ ActualArguments &arguments, FoldingContext &context) const {
+ characteristics::Procedure::Attrs attrs;
+ attrs.set(characteristics::Procedure::Attr::Subroutine);
+ // The first argument can be either CSTRARRAY or CSTRPTR - we use a generic
+ // keyword since they're mutually exclusive
+ static const char *const keywords[]{
+ "cstrarray", "fstrptr", "nchars", nullptr};
+ characteristics::DummyArguments dummies;
+ if (CheckAndRearrangeArguments(arguments, context.messages(), keywords, 2)) {
+ CHECK(arguments.size() == 3);
+ const bool hasNchars{arguments[2].has_value()};
+
+ // Check first argument (CSTRARRAY or CSTRPTR) and optional third argument
+ // (NCHARS)
+ if (const auto *expr{arguments[0].value().UnwrapExpr()}) {
+ // General semantic checks will catch an actual argument that's not
+ // scalar.
+ const auto at{arguments[0]->sourceLocation()};
+ if (const auto type{expr->GetType()}) {
+ if (type->category() == TypeCategory::Derived &&
+ !type->IsPolymorphic() &&
+ (type->GetDerivedTypeSpec().typeSymbol().name() ==
+ "__builtin_c_ptr" ||
+ type->GetDerivedTypeSpec().typeSymbol().name() ==
+ "__builtin_c_devptr")) {
+ // First argument is C_PTR (CSTRPTR form)
+ if (!hasNchars) {
+ context.messages().Say(at,
+ "NCHARS= argument is required when CSTRPTR= appears in C_F_STRPOINTER()"_err_en_US);
+ }
+ characteristics::DummyDataObject cstrptr{
+ characteristics::TypeAndShape{*type}};
+ cstrptr.intent = common::Intent::In;
+ dummies.emplace_back("cstrptr"s, std::move(cstrptr));
+ } else if (type->category() == TypeCategory::Character) {
+ // First argument should be CSTRARRAY - rank-1 character array
+ if (type->kind() != 1) {
+ context.messages().Say(at,
+ "CSTRARRAY= argument to C_F_STRPOINTER() must be of kind C_CHAR"_err_en_US);
+ }
+ if (expr->Rank() != 1) {
+ context.messages().Say(at,
+ "CSTRARRAY= argument to C_F_STRPOINTER() must be a rank-one array"_err_en_US);
+ }
+ if (const auto len{type->GetCharLength()}) {
+ if (const auto constLen{ToInt64(*len)}) {
+ if (*constLen != 1) {
+ context.messages().Say(at,
+ "CSTRARRAY= argument to C_F_STRPOINTER() must have length type parameter equal to one"_err_en_US);
+ }
+ }
+ }
+ // Check if CSTRARRAY is assumed-size and NCHARS is absent
+ if (auto shape{GetShape(context, *expr)}) {
+ if (shape->size() == 1) {
+ const auto &extentExpr{(*shape)[0]};
+ const auto extentInt{ToInt64(extentExpr)};
+ if ((!extentInt || *extentInt < 0) && !hasNchars) {
+ context.messages().Say(at,
+ "NCHARS= argument is required when CSTRARRAY= is assumed-size in C_F_STRPOINTER()"_err_en_US);
+ }
+ }
+ }
+ // Check if NCHARS > size(CSTRARRAY) at compile time
+ if (hasNchars && arguments[2]) {
+ if (const auto *ncharsExpr{arguments[2]->UnwrapExpr()}) {
+ if (const auto ncharsVal{ToInt64(*ncharsExpr)}) {
+ if (const auto shape{GetShape(context, *expr)};
+ shape && shape->size() == 1) {
+ if (const auto arraySize{ToInt64((*shape)[0])};
+ arraySize && *arraySize > 0 && *ncharsVal > *arraySize) {
+ context.messages().Say(arguments[2]->sourceLocation(),
+ "NCHARS=%jd is greater than the size of CSTRARRAY=%jd in C_F_STRPOINTER()"_err_en_US,
+ static_cast<std::intmax_t>(*ncharsVal),
+ static_cast<std::intmax_t>(*arraySize));
+ }
+ }
+ }
+ }
+ }
+ characteristics::DummyDataObject cstrarray{
+ characteristics::TypeAndShape{*type, 1}};
+ cstrarray.intent = common::Intent::In;
+ cstrarray.attrs.set(characteristics::DummyDataObject::Attr::Target);
+ dummies.emplace_back("cstrarray"s, std::move(cstrarray));
+ } else {
+ context.messages().Say(at,
+ "First argument to C_F_STRPOINTER() must be a C_PTR or a rank-one character array of kind C_CHAR"_err_en_US);
+ }
+ }
+ }
+
+ // Check FSTRPTR argument - must be scalar deferred-length character pointer
+ if (const auto *expr{arguments[1].value().UnwrapExpr()}) {
+ const auto at{arguments[1]->sourceLocation()};
+ if (const auto type{expr->GetType()}) {
+ if (type->category() != TypeCategory::Character) {
+ context.messages().Say(at,
+ "FSTRPTR= argument to C_F_STRPOINTER() must be a character pointer"_err_en_US);
+ } else {
+ if (type->kind() != 1) {
+ context.messages().Say(at,
+ "FSTRPTR= argument to C_F_STRPOINTER() must be of kind C_CHAR"_err_en_US);
+ }
+ if (!type->HasDeferredTypeParameter()) {
+ context.messages().Say(at,
+ "FSTRPTR= argument to C_F_STRPOINTER() must have deferred length"_err_en_US);
+ }
+ }
+ if (ExtractCoarrayRef(*expr)) {
+ context.messages().Say(at,
+ "FSTRPTR= argument to C_F_STRPOINTER() may not be a coindexed object"_err_en_US);
+ }
+ characteristics::DummyDataObject fstrptr{
+ characteristics::TypeAndShape{*type, 0}};
+ fstrptr.intent = common::Intent::Out;
+ fstrptr.attrs.set(characteristics::DummyDataObject::Attr::Pointer);
+ dummies.emplace_back("fstrptr"s, std::move(fstrptr));
+ } else {
+ context.messages().Say(at,
+ "FSTRPTR= argument to C_F_STRPOINTER() must have a type"_err_en_US);
+ }
+ }
+
+ // Check NCHARS argument if present
+ if (hasNchars) {
+ if (const auto *expr{arguments[2].value().UnwrapExpr()}) {
+ const auto at{arguments[2]->sourceLocation()};
+ if (const auto type{expr->GetType()}) {
+ if (type->category() != TypeCategory::Integer) {
+ context.messages().Say(at,
+ "NCHARS= argument to C_F_STRPOINTER() must be an integer"_err_en_US);
+ }
+ }
+ if (expr->Rank() != 0) {
+ context.messages().Say(at,
+ "NCHARS= argument to C_F_STRPOINTER() must be a scalar"_err_en_US);
+ }
+ // Check for negative value if constant
+ if (const auto ncharsVal{ToInt64(*expr)}) {
+ if (*ncharsVal < 0) {
+ context.messages().Say(at,
+ "NCHARS= argument to C_F_STRPOINTER() must be non-negative"_err_en_US);
+ }
+ }
+ }
+ }
+ }
+ if (dummies.size() == 2) {
+ // Add NCHARS dummy
+ DynamicType ncharsType{TypeCategory::Integer, defaults_.sizeIntegerKind()};
+ if (arguments.size() >= 3 && arguments[2]) {
+ if (const auto type{arguments[2]->GetType()}) {
+ if (type->category() == TypeCategory::Integer) {
+ ncharsType = *type;
+ }
+ }
+ }
+ characteristics::DummyDataObject nchars{
+ characteristics::TypeAndShape{ncharsType}};
+ nchars.intent = common::Intent::In;
+ nchars.attrs.set(characteristics::DummyDataObject::Attr::Optional);
+ dummies.emplace_back("nchars"s, std::move(nchars));
+
+ return SpecificCall{
+ SpecificIntrinsic{"__builtin_c_f_strpointer"s,
+ characteristics::Procedure{std::move(dummies), attrs}},
+ std::move(arguments)};
+ } else {
+ return std::nullopt;
+ }
+}
+
// Function C_LOC(X) from intrinsic module ISO_C_BINDING (18.2.3.6)
std::optional<SpecificCall> IntrinsicProcTable::Implementation::HandleC_Loc(
ActualArguments &arguments, FoldingContext &context) const {
@@ -3538,6 +3718,8 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::Probe(
if (call.isSubroutineCall) {
if (call.name == "__builtin_c_f_pointer") {
return HandleC_F_Pointer(arguments, context);
+ } else if (call.name == "__builtin_c_f_strpointer") {
+ return HandleC_F_Strpointer(arguments, context);
} else if (call.name == "random_seed") {
int optionalCount{0};
for (const auto &arg : arguments) {
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index bf76a78e7e25d..c69a8372643e9 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -193,6 +193,12 @@ static constexpr IntrinsicHandler handlers[]{
&I::genCFProcPointer,
{{{"cptr", asValue}, {"fptr", asInquired}}},
/*isElemental=*/false},
+ {"c_f_strpointer",
+ &I::genCFStrpointer,
+ {{{"cstrptr_or_cstrarray", asValue},
+ {"fstrptr", asInquired},
+ {"nchars", asValue, handleDynamicOptional}}},
+ /*isElemental=*/false},
{"c_funloc", &I::genCFunLoc, {{{"x", asBox}}}, /*isElemental=*/false},
{"c_loc", &I::genCLoc, {{{"x", asBox}}}, /*isElemental=*/false},
{"c_ptr_eq", &I::genCPtrCompare<mlir::arith::CmpIPredicate::eq>},
@@ -3250,6 +3256,99 @@ void IntrinsicLibrary::genCFProcPointer(
fir::StoreOp::create(builder, loc, cptrBox, fptr);
}
+// C_F_STRPOINTER
+void IntrinsicLibrary::genCFStrpointer(
+ llvm::ArrayRef<fir::ExtendedValue> args) {
+ assert(args.size() == 3);
+
+ mlir::Value cStrAddr;
+ mlir::Value strLen;
+
+ const mlir::Value firstArg = fir::getBase(args[0]);
+ const mlir::Type firstArgType = fir::unwrapRefType(firstArg.getType());
+ const bool isCstrptr = mlir::isa<fir::RecordType>(firstArgType);
+
+ if (isCstrptr) {
+ // CSTRPTR form: Extract address from C_PTR
+ cStrAddr = fir::factory::genCPtrOrCFunptrValue(builder, loc, firstArg);
+
+ assert(isStaticallyPresent(args[2]));
+ mlir::Value nchars = fir::getBase(args[2]);
+ if (fir::isa_ref_type(nchars.getType())) {
+ strLen = fir::LoadOp::create(builder, loc, nchars);
+ } else {
+ strLen = nchars;
+ }
+ } else {
+ // CSTRARRAY form: Get address from CHARACTER array
+ if (const auto boxCharTy =
+ mlir::dyn_cast<fir::BoxCharType>(firstArg.getType())) {
+ const auto charTy = mlir::cast<fir::CharacterType>(boxCharTy.getEleTy());
+ const auto addrTy = builder.getRefType(charTy);
+ auto unboxed = fir::UnboxCharOp::create(
+ builder, loc, mlir::TypeRange{addrTy, builder.getIndexType()},
+ firstArg);
+ cStrAddr = unboxed.getResult(0);
+ } else if (mlir::isa<fir::BoxType>(firstArg.getType())) {
+ cStrAddr = fir::BoxAddrOp::create(builder, loc, firstArg);
+ } else {
+ cStrAddr = firstArg;
+ }
+
+ // Handle optional NCHARS argument
+ if (isStaticallyPresent(args[2])) {
+ mlir::Value nchars = fir::getBase(args[2]);
+ if (fir::isa_ref_type(nchars.getType())) {
+ strLen = fir::LoadOp::create(builder, loc, nchars);
+ } else {
+ strLen = nchars;
+ }
+ } else {
+ const mlir::Type i8PtrTy = builder.getRefType(builder.getIntegerType(8));
+ const mlir::Value strPtr = builder.createConvert(loc, i8PtrTy, cStrAddr);
+
+ const mlir::Type i64Ty = builder.getIntegerType(64);
+ const mlir::FunctionType strlenType =
+ mlir::FunctionType::get(builder.getContext(), {i8PtrTy}, {i64Ty});
+
+ mlir::func::FuncOp strlenFunc = builder.getNamedFunction("strlen");
+ if (!strlenFunc) {
+ strlenFunc = builder.createFunction(loc, "strlen", strlenType);
+ strlenFunc->setAttr(
+ fir::getSymbolAttrName(),
+ mlir::StringAttr::get(builder.getContext(), "strlen"));
+ }
+ auto call = fir::CallOp::create(builder, loc, strlenFunc, {strPtr});
+ strLen = call.getResult(0);
+ }
+ }
+
+ // Handle FSTRPTR (second argument)
+ const auto *fStrPtr = args[1].getBoxOf<fir::MutableBoxValue>();
+ assert(fStrPtr && "FSTRPTR must be a pointer");
+
+ const mlir::Value lenIdx =
+ builder.createConvert(loc, builder.getIndexType(), strLen);
+
+ const mlir::Type charPtrType = fir::PointerType::get(fir::CharacterType::get(
+ builder.getContext(), 1, fir::CharacterType::unknownLen()));
+ const mlir::Value charPtr = builder.createConvert(loc, charPtrType, cStrAddr);
+
+ const fir::CharBoxValue charBox{charPtr, lenIdx};
+ fir::factory::associateMutableBox(builder, loc, *fStrPtr, charBox,
+ /*lbounds=*/mlir::ValueRange{});
+
+ // CUDA synchronization if needed
+ if (auto declare = mlir::dyn_cast_or_null<hlfir::DeclareOp>(
+ fStrPtr->getAddr().getDefiningOp()))
+ if (declare.getMemref().getDefiningOp() &&
+ mlir::isa<fir::AddrOfOp>(declare.getMemref().getDefiningOp()))
+ if (cuf::isRegisteredDeviceAttr(declare.getDataAttr()) &&
+ !cuf::isCUDADeviceContext(builder.getRegion()))
+ fir::runtime::cuda::genSyncGlobalDescriptor(builder, loc,
+ declare.getMemref());
+}
+
// C_FUNLOC
fir::ExtendedValue
IntrinsicLibrary::genCFunLoc(mlir::Type resultType,
diff --git a/flang/module/__fortran_builtins.f90 b/flang/module/__fortran_builtins.f90
index 3f313f8ffbe8d..ca98272241518 100644
--- a/flang/module/__fortran_builtins.f90
+++ b/flang/module/__fortran_builtins.f90
@@ -31,6 +31,9 @@
intrinsic :: __builtin_f_c_string
public :: __builtin_f_c_string
+ intrinsic :: __builtin_c_f_strpointer
+ public :: __builtin_c_f_strpointer
+
intrinsic :: __builtin_show_descriptor
public :: __builtin_show_descriptor
diff --git a/flang/module/iso_c_binding.f90 b/flang/module/iso_c_binding.f90
index 9b4a3fe1c34c7..bd50b156a71e3 100644
--- a/flang/module/iso_c_binding.f90
+++ b/flang/module/iso_c_binding.f90
@@ -15,6 +15,7 @@ module iso_c_binding
c_funloc => __builtin_c_funloc, &
c_funptr => __builtin_c_funptr, &
c_f_pointer => __builtin_c_f_pointer, &
+ c_f_strpointer => __builtin_c_f_strpointer, &
c_loc => __builtin_c_loc, &
c_null_funptr => __builtin_c_null_funptr, &
c_null_ptr => __builtin_c_null_ptr, &
@@ -31,6 +32,9 @@ module iso_c_binding
public :: c_associated, c_funloc, c_funptr, c_f_pointer, c_loc, &
c_null_funptr, c_null_ptr, c_ptr, c_sizeof, f_c_string, &
+
+ public :: c_associated, c_funloc, c_funptr, c_f_pointer, c_f_strpointer, &
+ c_loc, c_null_funptr, c_null_ptr, c_ptr, c_sizeof, &
operator(==), operator(/=)
! Table 18.2 (in clause 18.3.1)
diff --git a/flang/test/Lower/Intrinsics/c_f_strpointer.f90 b/flang/test/Lower/Intrinsics/c_f_strpointer.f90
new file mode 100644
index 0000000000000..78d0c9a2e58a5
--- /dev/null
+++ b/flang/test/Lower/Intrinsics/c_f_strpointer.f90
@@ -0,0 +1,59 @@
+! RUN: bbc -emit-fir -hlfir=false %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-fir -flang-deprecated-no-hlfir %s -o - | FileCheck %s
+
+! Test intrinsic module procedure c_f_strpointer
+
+! CHECK-LABEL: func.func @_QPtest_cstrarray(
+! CHECK-SAME: %[[CSTRARRAY:.*]]: !fir.boxchar<1> {fir.bindc_name = "cstrarray", fir.target},
+! CHECK-SAME: %[[FSTRPTR:.*]]: !fir.ref<!fir.box<!fir.ptr<!fir.char<1,?>>>> {fir.bindc_name = "fstrptr"}
+subroutine test_cstrarray(cstrarray, fstrptr)
+ use iso_c_binding
+ character(len=1, kind=c_char), dimension(*), target, intent(in) :: cstrarray
+ character(len=:), pointer, intent(out) :: fstrptr
+ ! CHECK: %[[UNBOXED:.*]]:2 = fir.unboxchar %[[CSTRARRAY]]
+ ! CHECK: %[[CONVERTED:.*]] = fir.convert %[[UNBOXED]]#0 : (!fir.ref<!fir.char<1,?>>) -> !fir.ref<!fir.array<?x!fir.char<1>>>
+ ! CHECK: %[[NCHARS:.*]] = arith.constant 100 : i32
+ ! CHECK: %[[NCHARS_IDX:.*]] = fir.convert %[[NCHARS]] : (i32) -> index
+ ! CHECK: %[[PTR:.*]] = fir.convert %[[CONVERTED]] : (!fir.ref<!fir.array<?x!fir.char<1>>>) -> !fir.ptr<!fir.char<1,?>>
+ ! CHECK: %[[BOX:.*]] = fir.embox %[[PTR]] typeparams %[[NCHARS_IDX]] : (!fir.ptr<!fir.char<1,?>>, index) -> !fir.box<!fir.ptr<!fir.char<1,?>>>
+ ! CHECK: fir.store %[[BOX]] to %[[FSTRPTR]]
+ call c_f_strpointer(cstrarray, fstrptr, 100)
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_cstrarray_no_nchars(
+! CHECK-SAME: %[[FSTRPTR:.*]]: !fir.ref<!fir.box<!fir.ptr<!fir.char<1,?>>>> {fir.bindc_name = "fstrptr"}
+subroutine test_cstrarray_no_nchars(fstrptr)
+ use iso_c_binding
+ character(len=1, kind=c_char), dimension(100), target :: cstrarray
+ character(len=:), pointer, intent(out) :: fstrptr
+ ! CHECK: %[[CSTRARRAY:.*]] = fir.alloca !fir.array<100x!fir.char<1>> {bindc_name = "cstrarray"
+ ! CHECK: %[[I8PTR:.*]] = fir.convert %[[CSTRARRAY]] : (!fir.ref<!fir.array<100x!fir.char<1>>>) -> !fir.ref<i8>
+ ! CHECK: %[[STRLEN:.*]] = fir.call @strlen(%[[I8PTR]]) {{.*}} : (!fir.ref<i8>) -> i64
+ ! CHECK: %[[STRLEN_IDX:.*]] = fir.convert %[[STRLEN]] : (i64) -> index
+ ! CHECK: %[[PTR:.*]] = fir.convert %[[CSTRARRAY]] : (!fir.ref<!fir.array<100x!fir.char<1>>>) -> !fir.ptr<!fir.char<1,?>>
+ ! CHECK: %[[BOX:.*]] = fir.embox %[[PTR]] typeparams %[[STRLEN_IDX]] : (!fir.ptr<!fir.char<1,?>>, index) -> !fir.box<!fir.ptr<!fir.char<1,?>>>
+ ! CHECK: fir.store %[[BOX]] to %[[FSTRPTR]]
+ cstrarray = 'Hello' // c_null_char
+ call c_f_strpointer(cstrarray, fstrptr)
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_cstrptr(
+! CHECK-SAME: %[[CPTR:.*]]: !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>> {fir.bindc_name = "cptr"},
+! CHECK-SAME: %[[FSTRPTR:.*]]: !fir.ref<!fir.box<!fir.ptr<!fir.char<1,?>>>> {fir.bindc_name = "fstrptr"},
+! CHECK-SAME: %[[NCHARS:.*]]: !fir.ref<i32> {fir.bindc_name = "nchars"}
+subroutine test_cstrptr(cptr, fstrptr, nchars)
+ use iso_c_binding
+ type(c_ptr), intent(in) :: cptr
+ character(len=:), pointer, intent(out) :: fstrptr
+ integer, intent(in) :: nchars
+ ! CHECK: %[[NCHARS_LOAD:.*]] = fir.load %[[NCHARS]]
+ ! CHECK: %[[ADDR_REF:.*]] = fir.coordinate_of %[[CPTR]], __address
+ ! CHECK: %[[ADDR_VAL:.*]] = fir.load %[[ADDR_REF]] : !fir.ref<i64>
+ ! CHECK: %[[NCHARS_IDX:.*]] = fir.convert %[[NCHARS_LOAD]] : (i32) -> index
+ ! CHECK: %[[PTR:.*]] = fir.convert %[[ADDR_VAL]] : (i64) -> !fir.ptr<!fir.char<1,?>>
+ ! CHECK: %[[BOX:.*]] = fir.embox %[[PTR]] typeparams %[[NCHARS_IDX]] : (!fir.ptr<!fir.char<1,?>>, index) -> !fir.box<!fir.ptr<!fir.char<1,?>>>
+ ! CHECK: fir.store %[[BOX]] to %[[FSTRPTR]]
+ call c_f_strpointer(cptr, fstrptr, nchars)
+end subroutine
+
+end
diff --git a/flang/test/Semantics/c_f_strpointer.f90 b/flang/test/Semantics/c_f_strpointer.f90
new file mode 100644
index 0000000000000..4b401a20e30c4
--- /dev/null
+++ b/flang/test/Semantics/c_f_strpointer.f90
@@ -0,0 +1,46 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1 -pedantic
+! Enforce C_F_STRPOINTER semantics (18.2.3.5)
+
+program test
+ use iso_c_binding
+ type(c_ptr) :: cptr
+ character(len=:), pointer :: fstrptr
+ character(len=1, kind=c_char), dimension(100), target :: cstrarray
+ character(len=10), pointer :: fstrptr_not_deferred
+ integer :: nchars
+
+ ! Valid calls
+ call c_f_strpointer(cstrarray, fstrptr) ! ok
+ call c_f_strpointer(cstrarray, fstrptr, 50) ! ok with NCHARS
+ call c_f_strpointer(cptr, fstrptr, 100) ! ok with CSTRPTR form
+
+ ! Error: CSTRPTR form requires NCHARS
+ !ERROR: NCHARS= argument is required when CSTRPTR= appears in C_F_STRPOINTER()
+ call c_f_strpointer(cptr, fstrptr)
+
+ ! Error: FSTRPTR must have deferred length
+ !ERROR: FSTRPTR= argument to C_F_STRPOINTER() must have deferred length
+ call c_f_strpointer(cstrarray, fstrptr_not_deferred)
+
+ ! Error: NCHARS must be non-negative
+ !ERROR: NCHARS= argument to C_F_STRPOINTER() must be non-negative
+ call c_f_strpointer(cstrarray, fstrptr, -5)
+
+ ! Error: NCHARS greater than array size (compile-time check)
+ !ERROR: NCHARS=150 is greater than the size of CSTRARRAY=100 in C_F_STRPOINTER()
+ call c_f_strpointer(cstrarray, fstrptr, 150)
+
+end program
+
+subroutine test_assumed_size(cstrarray_assumed, fstrptr)
+ use iso_c_binding
+ character(len=1, kind=c_char), dimension(*), target, intent(in) :: cstrarray_assumed
+ character(len=:), pointer :: fstrptr
+
+ ! Error: Assumed-size requires NCHARS
+ !ERROR: NCHARS= argument is required when CSTRARRAY= is assumed-size in C_F_STRPOINTER()
+ call c_f_strpointer(cstrarray_assumed, fstrptr)
+
+ ! Valid: Assumed-size with NCHARS
+ call c_f_strpointer(cstrarray_assumed, fstrptr, 100)
+end subroutine
>From 69d9e8649fd9f31bd156e0edca3bd384cfce67a7 Mon Sep 17 00:00:00 2001
From: Caroline Newcombe <caroline.newcombe at hpe.com>
Date: Tue, 20 Jan 2026 16:45:01 -0600
Subject: [PATCH 2/3] [flang] Migrate c_f_strpointer test to HLFIR
---
.../test/Lower/Intrinsics/c_f_strpointer.f90 | 37 +++++++++----------
1 file changed, 18 insertions(+), 19 deletions(-)
diff --git a/flang/test/Lower/Intrinsics/c_f_strpointer.f90 b/flang/test/Lower/Intrinsics/c_f_strpointer.f90
index 78d0c9a2e58a5..152533007886b 100644
--- a/flang/test/Lower/Intrinsics/c_f_strpointer.f90
+++ b/flang/test/Lower/Intrinsics/c_f_strpointer.f90
@@ -1,58 +1,57 @@
-! RUN: bbc -emit-fir -hlfir=false %s -o - | FileCheck %s
-! RUN: %flang_fc1 -emit-fir -flang-deprecated-no-hlfir %s -o - | FileCheck %s
+! RUN: bbc -emit-hlfir %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s
! Test intrinsic module procedure c_f_strpointer
! CHECK-LABEL: func.func @_QPtest_cstrarray(
-! CHECK-SAME: %[[CSTRARRAY:.*]]: !fir.boxchar<1> {fir.bindc_name = "cstrarray", fir.target},
-! CHECK-SAME: %[[FSTRPTR:.*]]: !fir.ref<!fir.box<!fir.ptr<!fir.char<1,?>>>> {fir.bindc_name = "fstrptr"}
subroutine test_cstrarray(cstrarray, fstrptr)
use iso_c_binding
character(len=1, kind=c_char), dimension(*), target, intent(in) :: cstrarray
character(len=:), pointer, intent(out) :: fstrptr
- ! CHECK: %[[UNBOXED:.*]]:2 = fir.unboxchar %[[CSTRARRAY]]
- ! CHECK: %[[CONVERTED:.*]] = fir.convert %[[UNBOXED]]#0 : (!fir.ref<!fir.char<1,?>>) -> !fir.ref<!fir.array<?x!fir.char<1>>>
+ ! CHECK-DAG: %[[CSTRARRAY_DECL:.*]]:2 = hlfir.declare %{{.*}} {{.*}} {{{.*}}uniq_name = "_QFtest_cstrarrayEcstrarray"}
+ ! CHECK-DAG: %[[FSTRPTR_DECL:.*]]:2 = hlfir.declare %{{.*}} {{.*}} {{{.*}}uniq_name = "_QFtest_cstrarrayEfstrptr"}
! CHECK: %[[NCHARS:.*]] = arith.constant 100 : i32
! CHECK: %[[NCHARS_IDX:.*]] = fir.convert %[[NCHARS]] : (i32) -> index
- ! CHECK: %[[PTR:.*]] = fir.convert %[[CONVERTED]] : (!fir.ref<!fir.array<?x!fir.char<1>>>) -> !fir.ptr<!fir.char<1,?>>
+ ! CHECK: %[[PTR:.*]] = fir.convert %[[CSTRARRAY_DECL]]#1 : (!fir.ref<!fir.array<?x!fir.char<1>>>) -> !fir.ptr<!fir.char<1,?>>
! CHECK: %[[BOX:.*]] = fir.embox %[[PTR]] typeparams %[[NCHARS_IDX]] : (!fir.ptr<!fir.char<1,?>>, index) -> !fir.box<!fir.ptr<!fir.char<1,?>>>
- ! CHECK: fir.store %[[BOX]] to %[[FSTRPTR]]
+ ! CHECK: fir.store %[[BOX]] to %[[FSTRPTR_DECL]]#0
call c_f_strpointer(cstrarray, fstrptr, 100)
end subroutine
! CHECK-LABEL: func.func @_QPtest_cstrarray_no_nchars(
-! CHECK-SAME: %[[FSTRPTR:.*]]: !fir.ref<!fir.box<!fir.ptr<!fir.char<1,?>>>> {fir.bindc_name = "fstrptr"}
subroutine test_cstrarray_no_nchars(fstrptr)
use iso_c_binding
character(len=1, kind=c_char), dimension(100), target :: cstrarray
character(len=:), pointer, intent(out) :: fstrptr
- ! CHECK: %[[CSTRARRAY:.*]] = fir.alloca !fir.array<100x!fir.char<1>> {bindc_name = "cstrarray"
- ! CHECK: %[[I8PTR:.*]] = fir.convert %[[CSTRARRAY]] : (!fir.ref<!fir.array<100x!fir.char<1>>>) -> !fir.ref<i8>
+ ! CHECK-DAG: %[[CSTRARRAY_DECL:.*]]:2 = hlfir.declare %{{.*}} {{.*}} {{{.*}}uniq_name = "_QFtest_cstrarray_no_ncharsEcstrarray"}
+ ! CHECK-DAG: %[[FSTRPTR_DECL:.*]]:2 = hlfir.declare %{{.*}} {{.*}} {{{.*}}uniq_name = "_QFtest_cstrarray_no_ncharsEfstrptr"}
+ ! CHECK: hlfir.assign %{{.*}} to %[[CSTRARRAY_DECL]]#0
+ ! CHECK: %[[I8PTR:.*]] = fir.convert %[[CSTRARRAY_DECL]]#0 : (!fir.ref<!fir.array<100x!fir.char<1>>>) -> !fir.ref<i8>
! CHECK: %[[STRLEN:.*]] = fir.call @strlen(%[[I8PTR]]) {{.*}} : (!fir.ref<i8>) -> i64
! CHECK: %[[STRLEN_IDX:.*]] = fir.convert %[[STRLEN]] : (i64) -> index
- ! CHECK: %[[PTR:.*]] = fir.convert %[[CSTRARRAY]] : (!fir.ref<!fir.array<100x!fir.char<1>>>) -> !fir.ptr<!fir.char<1,?>>
+ ! CHECK: %[[PTR:.*]] = fir.convert %[[CSTRARRAY_DECL]]#0 : (!fir.ref<!fir.array<100x!fir.char<1>>>) -> !fir.ptr<!fir.char<1,?>>
! CHECK: %[[BOX:.*]] = fir.embox %[[PTR]] typeparams %[[STRLEN_IDX]] : (!fir.ptr<!fir.char<1,?>>, index) -> !fir.box<!fir.ptr<!fir.char<1,?>>>
- ! CHECK: fir.store %[[BOX]] to %[[FSTRPTR]]
+ ! CHECK: fir.store %[[BOX]] to %[[FSTRPTR_DECL]]#0
cstrarray = 'Hello' // c_null_char
call c_f_strpointer(cstrarray, fstrptr)
end subroutine
! CHECK-LABEL: func.func @_QPtest_cstrptr(
-! CHECK-SAME: %[[CPTR:.*]]: !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>> {fir.bindc_name = "cptr"},
-! CHECK-SAME: %[[FSTRPTR:.*]]: !fir.ref<!fir.box<!fir.ptr<!fir.char<1,?>>>> {fir.bindc_name = "fstrptr"},
-! CHECK-SAME: %[[NCHARS:.*]]: !fir.ref<i32> {fir.bindc_name = "nchars"}
subroutine test_cstrptr(cptr, fstrptr, nchars)
use iso_c_binding
type(c_ptr), intent(in) :: cptr
character(len=:), pointer, intent(out) :: fstrptr
integer, intent(in) :: nchars
- ! CHECK: %[[NCHARS_LOAD:.*]] = fir.load %[[NCHARS]]
- ! CHECK: %[[ADDR_REF:.*]] = fir.coordinate_of %[[CPTR]], __address
+ ! CHECK-DAG: %[[CPTR_DECL:.*]]:2 = hlfir.declare %{{.*}} {{.*}} {{{.*}}uniq_name = "_QFtest_cstrptrEcptr"}
+ ! CHECK-DAG: %[[FSTRPTR_DECL:.*]]:2 = hlfir.declare %{{.*}} {{.*}} {{{.*}}uniq_name = "_QFtest_cstrptrEfstrptr"}
+ ! CHECK-DAG: %[[NCHARS_DECL:.*]]:2 = hlfir.declare %{{.*}} {{.*}} {{{.*}}uniq_name = "_QFtest_cstrptrEnchars"}
+ ! CHECK: %[[NCHARS_LOAD:.*]] = fir.load %[[NCHARS_DECL]]#0
+ ! CHECK: %[[ADDR_REF:.*]] = fir.coordinate_of %[[CPTR_DECL]]#0, __address
! CHECK: %[[ADDR_VAL:.*]] = fir.load %[[ADDR_REF]] : !fir.ref<i64>
! CHECK: %[[NCHARS_IDX:.*]] = fir.convert %[[NCHARS_LOAD]] : (i32) -> index
! CHECK: %[[PTR:.*]] = fir.convert %[[ADDR_VAL]] : (i64) -> !fir.ptr<!fir.char<1,?>>
! CHECK: %[[BOX:.*]] = fir.embox %[[PTR]] typeparams %[[NCHARS_IDX]] : (!fir.ptr<!fir.char<1,?>>, index) -> !fir.box<!fir.ptr<!fir.char<1,?>>>
- ! CHECK: fir.store %[[BOX]] to %[[FSTRPTR]]
+ ! CHECK: fir.store %[[BOX]] to %[[FSTRPTR_DECL]]#0
call c_f_strpointer(cptr, fstrptr, nchars)
end subroutine
>From 8677adc1960d1ee316fab02522a3244e08090f5d Mon Sep 17 00:00:00 2001
From: Caroline Newcombe <caroline.newcombe at hpe.com>
Date: Wed, 21 Jan 2026 14:21:53 -0600
Subject: [PATCH 3/3] [flang] Fix keyword handling in C_F_STRPOINTER semantic
checks
---
.../flang/Optimizer/Builder/IntrinsicCall.h | 2 +-
flang/lib/Evaluate/intrinsics.cpp | 42 ++++++++++++++-----
flang/lib/Optimizer/Builder/IntrinsicCall.cpp | 4 +-
flang/test/Semantics/c_f_strpointer.f90 | 28 +++++++++++++
4 files changed, 63 insertions(+), 13 deletions(-)
diff --git a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
index 6667a40c73c1d..fd875b6822838 100644
--- a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
+++ b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
@@ -231,7 +231,7 @@ struct IntrinsicLibrary {
llvm::ArrayRef<mlir::Value> args);
void genCFPointer(llvm::ArrayRef<fir::ExtendedValue>);
void genCFProcPointer(llvm::ArrayRef<fir::ExtendedValue>);
- void genCFStrpointer(llvm::ArrayRef<fir::ExtendedValue>);
+ void genCFStrPointer(llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genCFunLoc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genCLoc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
template <mlir::arith::CmpIPredicate pred>
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index 7cd9eae00a61d..4186ca71651d8 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -3266,20 +3266,34 @@ IntrinsicProcTable::Implementation::HandleC_F_Strpointer(
ActualArguments &arguments, FoldingContext &context) const {
characteristics::Procedure::Attrs attrs;
attrs.set(characteristics::Procedure::Attr::Subroutine);
- // The first argument can be either CSTRARRAY or CSTRPTR - we use a generic
- // keyword since they're mutually exclusive
- static const char *const keywords[]{
- "cstrarray", "fstrptr", "nchars", nullptr};
+
+ // The first argument can be either CSTRARRAY or CSTRPTR (overloaded).
+ // Assign common internal keyword "cstr" for CheckAndRearrangeArguments.
+ std::optional<std::string> firstArgKeyword;
+ for (auto &arg : arguments) {
+ if (arg && arg->keyword()) {
+ auto kw{arg->keyword()->ToString()};
+ if (kw == "cstrarray" || kw == "cstrptr") {
+ if (!firstArgKeyword) {
+ firstArgKeyword = kw;
+ }
+ static const char cstrKeyword[] = "cstr";
+ arg->set_keyword(
+ parser::CharBlock{cstrKeyword, sizeof(cstrKeyword) - 1});
+ }
+ }
+ }
+
+ static const char *const keywords[]{"cstr", "fstrptr", "nchars", nullptr};
characteristics::DummyArguments dummies;
- if (CheckAndRearrangeArguments(arguments, context.messages(), keywords, 2)) {
+ if (CheckAndRearrangeArguments(arguments, context.messages(), keywords, 1)) {
CHECK(arguments.size() == 3);
const bool hasNchars{arguments[2].has_value()};
+ const int cCharKind = defaults_.GetDefaultKind(TypeCategory::Character);
// Check first argument (CSTRARRAY or CSTRPTR) and optional third argument
// (NCHARS)
if (const auto *expr{arguments[0].value().UnwrapExpr()}) {
- // General semantic checks will catch an actual argument that's not
- // scalar.
const auto at{arguments[0]->sourceLocation()};
if (const auto type{expr->GetType()}) {
if (type->category() == TypeCategory::Derived &&
@@ -3289,6 +3303,10 @@ IntrinsicProcTable::Implementation::HandleC_F_Strpointer(
type->GetDerivedTypeSpec().typeSymbol().name() ==
"__builtin_c_devptr")) {
// First argument is C_PTR (CSTRPTR form)
+ if (firstArgKeyword && *firstArgKeyword != "cstrptr") {
+ context.messages().Say(at,
+ "Keyword CSTRARRAY= cannot be used with a C_PTR argument; use CSTRPTR= instead"_err_en_US);
+ }
if (!hasNchars) {
context.messages().Say(at,
"NCHARS= argument is required when CSTRPTR= appears in C_F_STRPOINTER()"_err_en_US);
@@ -3299,7 +3317,11 @@ IntrinsicProcTable::Implementation::HandleC_F_Strpointer(
dummies.emplace_back("cstrptr"s, std::move(cstrptr));
} else if (type->category() == TypeCategory::Character) {
// First argument should be CSTRARRAY - rank-1 character array
- if (type->kind() != 1) {
+ if (firstArgKeyword && *firstArgKeyword != "cstrarray") {
+ context.messages().Say(at,
+ "Keyword CSTRPTR= cannot be used with a character array argument; use CSTRARRAY= instead"_err_en_US);
+ }
+ if (type->kind() != cCharKind) {
context.messages().Say(at,
"CSTRARRAY= argument to C_F_STRPOINTER() must be of kind C_CHAR"_err_en_US);
}
@@ -3327,7 +3349,7 @@ IntrinsicProcTable::Implementation::HandleC_F_Strpointer(
}
}
// Check if NCHARS > size(CSTRARRAY) at compile time
- if (hasNchars && arguments[2]) {
+ if (hasNchars) {
if (const auto *ncharsExpr{arguments[2]->UnwrapExpr()}) {
if (const auto ncharsVal{ToInt64(*ncharsExpr)}) {
if (const auto shape{GetShape(context, *expr)};
@@ -3363,7 +3385,7 @@ IntrinsicProcTable::Implementation::HandleC_F_Strpointer(
context.messages().Say(at,
"FSTRPTR= argument to C_F_STRPOINTER() must be a character pointer"_err_en_US);
} else {
- if (type->kind() != 1) {
+ if (type->kind() != cCharKind) {
context.messages().Say(at,
"FSTRPTR= argument to C_F_STRPOINTER() must be of kind C_CHAR"_err_en_US);
}
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index c69a8372643e9..d3c67395a08bd 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -194,7 +194,7 @@ static constexpr IntrinsicHandler handlers[]{
{{{"cptr", asValue}, {"fptr", asInquired}}},
/*isElemental=*/false},
{"c_f_strpointer",
- &I::genCFStrpointer,
+ &I::genCFStrPointer,
{{{"cstrptr_or_cstrarray", asValue},
{"fstrptr", asInquired},
{"nchars", asValue, handleDynamicOptional}}},
@@ -3257,7 +3257,7 @@ void IntrinsicLibrary::genCFProcPointer(
}
// C_F_STRPOINTER
-void IntrinsicLibrary::genCFStrpointer(
+void IntrinsicLibrary::genCFStrPointer(
llvm::ArrayRef<fir::ExtendedValue> args) {
assert(args.size() == 3);
diff --git a/flang/test/Semantics/c_f_strpointer.f90 b/flang/test/Semantics/c_f_strpointer.f90
index 4b401a20e30c4..94b219f621b66 100644
--- a/flang/test/Semantics/c_f_strpointer.f90
+++ b/flang/test/Semantics/c_f_strpointer.f90
@@ -13,11 +13,26 @@ program test
call c_f_strpointer(cstrarray, fstrptr) ! ok
call c_f_strpointer(cstrarray, fstrptr, 50) ! ok with NCHARS
call c_f_strpointer(cptr, fstrptr, 100) ! ok with CSTRPTR form
+ call c_f_strpointer(CSTRARRAY=cstrarray, FSTRPTR=fstrptr) ! ok with CSTRARRAY keyword
+ call c_f_strpointer(CSTRARRAY=cstrarray, FSTRPTR=fstrptr, NCHARS=50) ! ok with all keywords
+ call c_f_strpointer(CSTRPTR=cptr, FSTRPTR=fstrptr, NCHARS=50) ! ok with all keywords
! Error: CSTRPTR form requires NCHARS
!ERROR: NCHARS= argument is required when CSTRPTR= appears in C_F_STRPOINTER()
call c_f_strpointer(cptr, fstrptr)
+ ! Error: CSTRPTR form requires NCHARS (with explicit keyword)
+ !ERROR: NCHARS= argument is required when CSTRPTR= appears in C_F_STRPOINTER()
+ call c_f_strpointer(CSTRPTR=cptr, FSTRPTR=fstrptr)
+
+ ! Error: Wrong keyword for C_PTR argument
+ !ERROR: Keyword CSTRARRAY= cannot be used with a C_PTR argument; use CSTRPTR= instead
+ call c_f_strpointer(CSTRARRAY=cptr, FSTRPTR=fstrptr, NCHARS=10)
+
+ ! Error: Wrong keyword for character array argument
+ !ERROR: Keyword CSTRPTR= cannot be used with a character array argument; use CSTRARRAY= instead
+ call c_f_strpointer(CSTRPTR=cstrarray, FSTRPTR=fstrptr, NCHARS=50)
+
! Error: FSTRPTR must have deferred length
!ERROR: FSTRPTR= argument to C_F_STRPOINTER() must have deferred length
call c_f_strpointer(cstrarray, fstrptr_not_deferred)
@@ -30,6 +45,19 @@ program test
!ERROR: NCHARS=150 is greater than the size of CSTRARRAY=100 in C_F_STRPOINTER()
call c_f_strpointer(cstrarray, fstrptr, 150)
+ ! Error: Missing required argument FSTRPTR
+ !ERROR: Dummy argument 'fstrptr=' is absent and not OPTIONAL
+ call c_f_strpointer(cstrarray)
+
+ ! Error: Missing both required arguments
+ !ERROR: Dummy argument 'cstr=' is absent and not OPTIONAL
+ !ERROR: Dummy argument 'fstrptr=' is absent and not OPTIONAL
+ call c_f_strpointer()
+
+ ! Error: Too many arguments
+ !ERROR: Too many actual arguments (4 > 3)
+ call c_f_strpointer(cstrarray, fstrptr, 50, 999)
+
end program
subroutine test_assumed_size(cstrarray_assumed, fstrptr)
More information about the flang-commits
mailing list