[flang] Implement C_F_STRPOINTER (Fortran 2023) (#176973)

Implement C_F_STRPOINTER to associate a Fortran character pointer with a
C string.

This intrinsic has two forms:

C_F_STRPOINTER(CSTRARRAY, FSTRPTR [,NCHARS]): Associates FSTRPTR with a
C string array
C_F_STRPOINTER(CSTRPTR, FSTRPTR, NCHARS): Associates FSTRPTR with a
C_PTR pointing to a character string
Implementation includes semantic validation, FIR lowering, and
associated tests.

F2023 Standard: 18.2.3.5

AI Usage Disclosure: AI tools (Claude Sonnet 4.5) were used to assist
with implementation of this feature and test code generation. I have
reviewed, modified, and tested all AI-generated code.
This commit is contained in:
Caroline Newcombe 2026-02-12 12:42:13 -06:00 committed by GitHub
parent d18f6d086e
commit 4b109dc5ac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 443 additions and 3 deletions

View File

@ -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>

View File

@ -2898,6 +2898,8 @@ private:
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,206 @@ 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 (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, 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()}) {
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 (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);
}
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 (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);
}
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) {
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() != cCharKind) {
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 +3740,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) {

View File

@ -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,

View File

@ -31,6 +31,9 @@ module __fortran_builtins
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

View File

@ -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, &
@ -29,8 +30,8 @@ module iso_c_binding
! to be exported by this MODULE.
private
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, f_c_string, &
operator(==), operator(/=)
! Table 18.2 (in clause 18.3.1)

View File

@ -0,0 +1,58 @@
! 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(
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-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 %[[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_DECL]]#0
call c_f_strpointer(cstrarray, fstrptr, 100)
end subroutine
! CHECK-LABEL: func.func @_QPtest_cstrarray_no_nchars(
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-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_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_DECL]]#0
cstrarray = 'Hello' // c_null_char
call c_f_strpointer(cstrarray, fstrptr)
end subroutine
! CHECK-LABEL: func.func @_QPtest_cstrptr(
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-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_DECL]]#0
call c_f_strpointer(cptr, fstrptr, nchars)
end subroutine
end

View File

@ -0,0 +1,74 @@
! 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
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)
! 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)
! 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)
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