This is a missing Fortran 2023 feature.
gcc/fortran/ChangeLog
* check.cc (gfc_check_c_f_strpointer): New.
* f95-lang.cc (gfc_init_builtin_functions): Add BUILT_IN_STRNLEN.
* gfortran.h (enum gfc_isym_id): Add GFC_ISYM_C_F_STRPOINTER.
* gfortran.texi (Interoperable Subroutines and Functions): Mention
f_c_string and c_f_strpointer.
* intrinsic.cc (add_subroutines): Add c_f_strpointer. Fix nearby
whitespace errors.
(sort_actual): Handle first argument to c_f_strpointer specially.
* intrinsic.h (gfc_check_c_f_strpointer): Declare.
* intrinsic.texi (C_F_STRPOINTER): New section. Add entry to menu
and cross-references from similar functions.
* iso-c-binding.def: Add c_f_strpointer.
* trans-intrinsic.cc (conv_isocbinding_subroutine_strpointer): New.
(gfc_conv_intrinsic_subroutine): Call it.
gcc/testsuite/ChangeLog
* gfortran.dg/c_f_strpointer-1.f90: New.
* gfortran.dg/c_f_strpointer-2.f90: New.
* gfortran.dg/c_f_strpointer-3.f90: New.
* gfortran.dg/c_f_strpointer-4.f90: New.
* gfortran.dg/c_f_strpointer-5.f90: New.
* gfortran.dg/c_f_strpointer-6.f90: New.
* gfortran.dg/c_f_strpointer-7.f90: New.
* gfortran.dg/c_f_strpointer-8.f90: New.
* gfortran.dg/c_f_strpointer-9.f90: New.
* gfortran.dg/c_f_strpointer-10.f90: New.
* gfortran.dg/pr108961.f90: Rename locally-defined c_f_strpointer.
Co-authored-by: Tobias Burnus <[email protected]>
---
gcc/fortran/check.cc | 142 ++++++++++++++++++
gcc/fortran/f95-lang.cc | 5 +
gcc/fortran/gfortran.h | 1 +
gcc/fortran/gfortran.texi | 7 +
gcc/fortran/intrinsic.cc | 48 +++++-
gcc/fortran/intrinsic.h | 1 +
gcc/fortran/intrinsic.texi | 82 ++++++++++
gcc/fortran/iso-c-binding.def | 2 +
gcc/fortran/trans-intrinsic.cc | 120 +++++++++++++++
.../gfortran.dg/c_f_strpointer-1.f90 | 30 ++++
.../gfortran.dg/c_f_strpointer-10.f90 | 39 +++++
.../gfortran.dg/c_f_strpointer-2.f90 | 33 ++++
.../gfortran.dg/c_f_strpointer-3.f90 | 37 +++++
.../gfortran.dg/c_f_strpointer-4.f90 | 18 +++
.../gfortran.dg/c_f_strpointer-5.f90 | 19 +++
.../gfortran.dg/c_f_strpointer-6.f90 | 20 +++
.../gfortran.dg/c_f_strpointer-7.f90 | 50 ++++++
.../gfortran.dg/c_f_strpointer-8.f90 | 11 ++
.../gfortran.dg/c_f_strpointer-9.f90 | 34 +++++
gcc/testsuite/gfortran.dg/pr108961.f90 | 4 +-
20 files changed, 697 insertions(+), 6 deletions(-)
create mode 100644 gcc/testsuite/gfortran.dg/c_f_strpointer-1.f90
create mode 100644 gcc/testsuite/gfortran.dg/c_f_strpointer-10.f90
create mode 100644 gcc/testsuite/gfortran.dg/c_f_strpointer-2.f90
create mode 100644 gcc/testsuite/gfortran.dg/c_f_strpointer-3.f90
create mode 100644 gcc/testsuite/gfortran.dg/c_f_strpointer-4.f90
create mode 100644 gcc/testsuite/gfortran.dg/c_f_strpointer-5.f90
create mode 100644 gcc/testsuite/gfortran.dg/c_f_strpointer-6.f90
create mode 100644 gcc/testsuite/gfortran.dg/c_f_strpointer-7.f90
create mode 100644 gcc/testsuite/gfortran.dg/c_f_strpointer-8.f90
create mode 100644 gcc/testsuite/gfortran.dg/c_f_strpointer-9.f90
diff --git a/gcc/fortran/check.cc b/gcc/fortran/check.cc
index c4d9901a82d..ad6f66015d7 100644
--- a/gcc/fortran/check.cc
+++ b/gcc/fortran/check.cc
@@ -6306,6 +6306,148 @@ gfc_check_c_f_procpointer (gfc_expr *cptr, gfc_expr
*fptr)
}
+/* Handle both forms of this intrinsic, differentiated by whether
+ the first argument is a scalar or array. */
+
+bool
+gfc_check_c_f_strpointer (gfc_expr *arg0, gfc_expr *fstrptr,
+ gfc_expr *nchars)
+{
+ bool arg0_is_scalar = false;
+ const char *arg0name = "cstrarray";
+
+ if (arg0->rank == 0)
+ {
+ arg0_is_scalar = true;
+ arg0name = "cstrptr";
+
+ /* cstrptr is a scalar of type c_ptr. It is an intent in argument
+ holding the C address of a contiguous array s of nchars characters.
+ Its value must not be the C address of a Fortran variable without
+ the target attribute. */
+ if (arg0->ts.type != BT_DERIVED
+ || arg0->ts.u.derived->from_intmod != INTMOD_ISO_C_BINDING
+ || arg0->ts.u.derived->intmod_sym_id != ISOCBINDING_PTR)
+ {
+ gfc_error ("%qs argument of %qs intrinsic at %L shall be "
+ "a scalar of type C_PTR",
+ arg0name, gfc_current_intrinsic, &arg0->where);
+ return false;
+ }
+
+ if (!nchars)
+ {
+ gfc_error ("%qs argument of %qs intrinsic shall be present "
+ "when the %qs argument at %L is a C_PTR",
+ gfc_current_intrinsic_arg[2]->name,
+ gfc_current_intrinsic, arg0name, &arg0->where);
+ return false;
+ }
+ }
+ else
+ {
+ /* arg0 is a rank-one character array of kind c_char and character
+ length one. It is an intent in argument. Its actual argument
+ must be simply contiguous and have the target attribute. */
+ if (arg0->rank != 1
+ || arg0->ts.type != BT_CHARACTER
+ || arg0->ts.kind != gfc_default_character_kind
+ || get_ul_from_cst_cl (arg0->ts.u.cl) != 1)
+ {
+ gfc_error ("%qs argument of %qs intrinsic at %L shall be "
+ "a rank-one character array of kind C_CHAR and "
+ "character length one",
+ arg0name, gfc_current_intrinsic, &arg0->where);
+ return false;
+ }
+ if (!gfc_is_simply_contiguous (arg0, true, false))
+ {
+ gfc_error ("%qs argument of %qs intrinsic at %L shall be "
+ "simply contiguous",
+ arg0name, gfc_current_intrinsic, &arg0->where);
+ return false;
+ }
+ if (!gfc_expr_attr (arg0).target)
+ {
+ gfc_error ("%qs argument of %qs intrinsic at %L shall have "
+ "the TARGET attribute",
+ arg0name, gfc_current_intrinsic, &arg0->where);
+ return false;
+ }
+
+ /* If cstrarray is assumed-size, nchars must be present. */
+ if (!nchars)
+ {
+ gfc_array_ref *ar = gfc_find_array_ref (arg0);
+ if (ar->as && ar->as->type == AS_ASSUMED_SIZE
+ && (ar->type == AR_FULL || ar->end[0] == nullptr))
+ {
+ gfc_error ("%qs argument of %qs intrinsic shall be present "
+ "when the %qs argument at %L is assumed-size",
+ gfc_current_intrinsic_arg[2]->name,
+ gfc_current_intrinsic, arg0name, &arg0->where);
+ return false;
+ }
+ }
+ }
+
+ /* fstrptr is a scalar deferred-length character pointer of kind c_char.
+ It is an intent out argument [...] */
+ if (fstrptr->rank != 0
+ || fstrptr->ts.type != BT_CHARACTER
+ || fstrptr->ts.kind != gfc_default_character_kind
+ || !fstrptr->ts.deferred
+ || !gfc_expr_attr (fstrptr).pointer)
+ {
+ gfc_error ("%qs argument of %qs intrinsic at %L shall be "
+ "a scalar deferred-length character pointer of kind C_CHAR",
+ gfc_current_intrinsic_arg[1]->name, gfc_current_intrinsic,
+ &fstrptr->where);
+ return false;
+ }
+ if (gfc_expr_attr (fstrptr).intent == INTENT_IN)
+ {
+ gfc_error ("%qs argument of %qs intrinsic at %L cannot be INTENT(IN)",
+ gfc_current_intrinsic_arg[1]->name, gfc_current_intrinsic,
+ &fstrptr->where);
+ return false;
+ }
+
+ /* For the array form: nchars is an optional integer scalar with intent in.
+ If nchars is present, its value must be nonnegative and not greater
+ than the size of cstrarray.
+ For the scalar form: nchars is an integer scalar with intent in. Its
+ value must be nonnegative. */
+ if (!nchars)
+ return true;
+ if (nchars->rank != 0 || nchars->ts.type != BT_INTEGER)
+ {
+ gfc_error ("%qs argument of %qs intrinsic at %L shall be "
+ "a scalar integer",
+ gfc_current_intrinsic_arg[2]->name, gfc_current_intrinsic,
+ &nchars->where);
+ return false;
+ }
+ if (nchars->expr_type != EXPR_CONSTANT)
+ return true;
+ if (!nonnegative_check (gfc_current_intrinsic_arg[2]->name, nchars))
+ return false;
+ if (!arg0_is_scalar)
+ {
+ mpz_t asize;
+ if (gfc_array_size (arg0, &asize)
+ && mpz_cmp (nchars->value.integer, asize) > 0)
+ {
+ gfc_error ("%qs at %L must not be greater than the size of %qs",
+ gfc_current_intrinsic_arg[2]->name, &nchars->where,
+ arg0name);
+ return false;
+ }
+ }
+
+ return true;
+}
+
bool
gfc_check_c_funloc (gfc_expr *x)
{
diff --git a/gcc/fortran/f95-lang.cc b/gcc/fortran/f95-lang.cc
index 45aab34865f..1cdc83500a9 100644
--- a/gcc/fortran/f95-lang.cc
+++ b/gcc/fortran/f95-lang.cc
@@ -1036,6 +1036,11 @@ gfc_init_builtin_functions (void)
gfc_define_builtin ("__builtin_realloc", ftype, BUILT_IN_REALLOC,
"realloc", ATTR_NOTHROW_LEAF_LIST);
+ ftype = build_function_type_list (size_type_node, pchar_type_node,
+ size_type_node, NULL_TREE);
+ gfc_define_builtin ("__builtin_strnlen", ftype, BUILT_IN_STRNLEN,
+ "strnlen", ATTR_PURE_NOTHROW_LEAF_LIST);
+
/* Type-generic floating-point classification built-ins. */
ftype = build_function_type (integer_type_node, NULL_TREE);
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index 6c45e9b1682..67b351347c4 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -501,6 +501,7 @@ enum gfc_isym_id
GFC_ISYM_C_ASSOCIATED,
GFC_ISYM_C_F_POINTER,
GFC_ISYM_C_F_PROCPOINTER,
+ GFC_ISYM_C_F_STRPOINTER,
GFC_ISYM_C_FUNLOC,
GFC_ISYM_C_LOC,
GFC_ISYM_C_SIZEOF,
diff --git a/gcc/fortran/gfortran.texi b/gcc/fortran/gfortran.texi
index 16553508a58..716e58cf1b3 100644
--- a/gcc/fortran/gfortran.texi
+++ b/gcc/fortran/gfortran.texi
@@ -3117,6 +3117,13 @@ example, we ignore the return value:
end
@end smallexample
+Fortran 2023 added two new intrinsic functions for converting between
+C and Fortran string representations: @code{f_c_string} transforms a
+Fortran string into a C string by appending a null character, and
+@code{c_f_strpointer} allows access to a null-terminated C string or
+simply contiguous array of @code{c_char} as a Fortran deferred-length
+character pointer.
+
The intrinsic procedures are described in @ref{Intrinsic Procedures}.
@node Working with C Pointers
diff --git a/gcc/fortran/intrinsic.cc b/gcc/fortran/intrinsic.cc
index 6ffd7237468..1c97af087d5 100644
--- a/gcc/fortran/intrinsic.cc
+++ b/gcc/fortran/intrinsic.cc
@@ -3957,14 +3957,27 @@ add_subroutines (void)
"fptr", BT_UNKNOWN, 0, REQUIRED, INTENT_OUT,
"shape", BT_INTEGER, di, OPTIONAL, INTENT_IN,
"lower", BT_INTEGER, di, OPTIONAL, INTENT_IN);
- make_from_module();
+ make_from_module ();
add_sym_2s ("c_f_procpointer", GFC_ISYM_C_F_PROCPOINTER, CLASS_IMPURE,
BT_UNKNOWN, 0, GFC_STD_F2003, gfc_check_c_f_procpointer,
NULL, NULL,
"cptr", BT_VOID, 0, REQUIRED, INTENT_IN,
"fptr", BT_UNKNOWN, 0, REQUIRED, INTENT_OUT);
- make_from_module();
+ make_from_module ();
+
+ /* This represents both forms of the intrinsic; the one with the
+ signature given here, and the one that accepts a scalar for the
+ first argument with name "cstrptr" instead of "cstrarray".
+ This is handled by special-casing in sort_actual as well as
+ in the check function. */
+ add_sym_3s ("c_f_strpointer", GFC_ISYM_C_F_STRPOINTER, CLASS_IMPURE,
+ BT_UNKNOWN, 0, GFC_STD_F2023, gfc_check_c_f_strpointer,
+ NULL, NULL,
+ "cstrarray", BT_VOID, dc, REQUIRED, INTENT_IN,
+ "fstrptr", BT_UNKNOWN, dc, REQUIRED, INTENT_OUT,
+ "nchars", BT_INTEGER, di, OPTIONAL, INTENT_IN);
+ make_from_module ();
/* Internal subroutine for emitting a runtime error. */
@@ -4516,6 +4529,7 @@ sort_actual (const char *name, gfc_actual_arglist **ap,
{
gfc_actual_arglist *actual, *a;
gfc_intrinsic_arg *f;
+ bool is_c_f_strpointer = false;
remove_nullargs (ap);
actual = *ap;
@@ -4536,7 +4550,9 @@ sort_actual (const char *name, gfc_actual_arglist **ap,
return true;
/* ALLOCATED has two mutually exclusive keywords, but only one
- can be present at time and neither is optional. */
+ can be present at time and neither is optional. Likewise
+ C_F_STRPOINTER, but since that subroutine has multiple arguments
+ it has to be handled in the keywords loop below. */
if (strcmp (name, "allocated") == 0)
{
if (!a)
@@ -4605,9 +4621,32 @@ whoops:
keywords:
/* Associate the remaining actual arguments, all of which have
to be keyword arguments. */
+ is_c_f_strpointer = strcmp (name, "c_f_strpointer") == 0;
for (; a; a = a->next)
{
int idx;
+
+ /* Special case C_F_STRPOINTER. The first argument can either
+ be an array named "cstrarray" or a scalar named "cstrptr". */
+ if (is_c_f_strpointer)
+ {
+ idx = 0;
+ if (strcmp (a->name, "cstrarray") == 0)
+ {
+ if (a->expr->rank != 0)
+ goto got_keyword;
+ gfc_error ("Array entity required at %L", &a->expr->where);
+ return false;
+ }
+ else if (strcmp (a->name, "cstrptr") == 0)
+ {
+ if (a->expr->rank == 0)
+ goto got_keyword;
+ gfc_error ("Scalar entity required at %L", &a->expr->where);
+ return false;
+ }
+ }
+
FOR_EACH_VEC_ELT (dummy_args, idx, f)
if (strcmp (a->name, f->name) == 0)
break;
@@ -4623,10 +4662,11 @@ keywords:
return false;
}
+ got_keyword:
if (ordered_actual_args[idx] != NULL)
{
gfc_error ("Argument %qs appears twice in call to %qs at %L",
- f->name, name, where);
+ a->name, name, where);
return false;
}
ordered_actual_args[idx] = a;
diff --git a/gcc/fortran/intrinsic.h b/gcc/fortran/intrinsic.h
index 0b520f03332..ad0c54f2959 100644
--- a/gcc/fortran/intrinsic.h
+++ b/gcc/fortran/intrinsic.h
@@ -168,6 +168,7 @@ bool gfc_check_sizeof (gfc_expr *);
bool gfc_check_c_associated (gfc_expr *, gfc_expr *);
bool gfc_check_c_f_pointer (gfc_expr *, gfc_expr *, gfc_expr *, gfc_expr *);
bool gfc_check_c_f_procpointer (gfc_expr *, gfc_expr *);
+bool gfc_check_c_f_strpointer (gfc_expr *, gfc_expr *, gfc_expr *);
bool gfc_check_c_funloc (gfc_expr *);
bool gfc_check_c_loc (gfc_expr *);
bool gfc_check_c_sizeof (gfc_expr *);
diff --git a/gcc/fortran/intrinsic.texi b/gcc/fortran/intrinsic.texi
index f5a29606eb4..8a33fff68f9 100644
--- a/gcc/fortran/intrinsic.texi
+++ b/gcc/fortran/intrinsic.texi
@@ -106,6 +106,7 @@ Some basic guidelines for editing this document:
* @code{C_ASSOCIATED}: C_ASSOCIATED, Status of a C pointer
* @code{C_F_POINTER}: C_F_POINTER, Convert C into Fortran pointer
* @code{C_F_PROCPOINTER}: C_F_PROCPOINTER, Convert C into Fortran procedure
pointer
+* @code{C_F_STRPOINTER}: C_F_STRPOINTER, Convert C string to Fortran string
pointer
* @code{C_FUNLOC}: C_FUNLOC, Obtain the C address of a procedure
* @code{C_LOC}: C_LOC, Obtain the C address of an object
* @code{C_SIZEOF}: C_SIZEOF, Size in bytes of an expression
@@ -3418,6 +3419,7 @@ Fortran 2003 and later, with @var{LOWER} argument Fortran
2023 and later
@item @emph{See also}:
@ref{C_LOC}, @*
@ref{C_F_PROCPOINTER}
+@ref{C_F_STRPOINTER}
@end table
@@ -3476,6 +3478,86 @@ Fortran 2003 and later
@item @emph{See also}:
@ref{C_LOC}, @*
@ref{C_F_POINTER}
+@ref{C_F_STRPOINTER}
+@end table
+
+
+@node C_F_STRPOINTER
+@section @code{C_F_STRPOINTER} --- Convert C string into Fortran string pointer
+@fnindex C_F_STRPOINTER
+@cindex string, convert C to Fortran
+
+@table @asis
+@item @emph{Synopsis}:
+@multitable @columnfractions .80
+@item @code{CALL C_F_STRPOINTER(CSTRARRAY, FSTRPTR[, NCHARS])}
+@item @code{CALL C_F_STRPOINTER(CSTRPTR, FSTRPTR, NCHARS)}
+@end multitable
+
+@item @emph{Description}:
+@code{C_F_STRPOINTER(CSTRARRAY, FSTRPTR[, NCHARS])}
+pointer-associates the deferred-length character pointer
+@code{FSTRPTR} with the initial substring of the simply contiguous
+Fortran character array @code{STRARRAY}, up to the first null character,
+the length @code{NCHARS} if specified, or the actual size of @code{CSTRARRAY}.
+
+@code{CALL C_F_STRPOINTER(CSTRPTR, FSTRPTR, NCHARS)}
+pointer-associates the deferred-length array pointer @code{FSTRPTR} with the
+initial substring of the continguous array of characters pointed to by
+the C pointer @code{CSTRPTR}, up to the first null character or
+length @code{NCHARS}.
+
+@item @emph{Class}:
+Subroutine
+
+@item @emph{Arguments}:
+@multitable @columnfractions .15 .70
+@item @var{CSTRARRAY} @tab Rank-one character array of kind @code{C_CHAR}
+and character length one, which must be simply contiguous and have the
+@code{TARGET} attribute. It is @code{INTENT(IN)}.
+@item @var{CSTRPTR} @tab Scalar of the type @code{C_PTR}. It is
+@code{INTENT(IN)}.
+@item @var{FSTRPTR} @tab Scalar deferred-length character pointer of kind
+@code{C_CHAR}. It is @code{INTENT(OUT)}.
+@item @var{NCHARS} @tab (Optional) Integer scalar. It is @code{INTENT(IN)}.
+This argument can only be omitted for the @code{CSTRARRAY} form of the
+intrinsic, and only if @code{STRARRAY} is not assumed-size.
+@end multitable
+
+@item @emph{Example}:
+@smallexample
+program main
+
+ use iso_c_binding
+ implicit none
+
+ character (kind=c_char, len=1), dimension(15), target :: a
+ type(c_ptr) :: p
+ character (len=:, kind=c_char), pointer :: fp1, fp2
+
+ a = [ character :: 'h', 'e', 'l', 'l', 'o', ' ', &
+ 'w', 'o', 'r', 'l', 'd', '!', &
+ ' ', ' ', ' ']
+ ! give array a terminating null so its C string length is 12.
+ a(13) = C_NULL_CHAR
+
+ ! p is a C pointer to the the first character in the array
+ p = C_LOC (a(1))
+
+ ! Make both fp1 and fp2 point to a with Fortran string length 12.
+ call c_f_strpointer (p, fp1, 15)
+ call c_f_strpointer (a, fp2)
+end program main
+
+@end smallexample
+
+@item @emph{Standard}:
+Fortran 2023 and later.
+
+@item @emph{See also}:
+@ref{C_LOC}, @*
+@ref{C_F_POINTER}
+@ref{C_F_PROCPOINTER}
@end table
diff --git a/gcc/fortran/iso-c-binding.def b/gcc/fortran/iso-c-binding.def
index c7a67229273..041fcb2ff52 100644
--- a/gcc/fortran/iso-c-binding.def
+++ b/gcc/fortran/iso-c-binding.def
@@ -190,6 +190,8 @@ NAMED_SUBROUTINE (ISOCBINDING_F_POINTER, "c_f_pointer",
GFC_ISYM_C_F_POINTER, GFC_STD_F2003)
NAMED_SUBROUTINE (ISOCBINDING_F_PROCPOINTER, "c_f_procpointer",
GFC_ISYM_C_F_PROCPOINTER, GFC_STD_F2003)
+NAMED_SUBROUTINE (ISOCBINDING_F_STRPOINTER, "c_f_strpointer",
+ GFC_ISYM_C_F_STRPOINTER, GFC_STD_F2023)
NAMED_FUNCTION (ISOCBINDING_ASSOCIATED, "c_associated",
GFC_ISYM_C_ASSOCIATED, GFC_STD_F2003)
diff --git a/gcc/fortran/trans-intrinsic.cc b/gcc/fortran/trans-intrinsic.cc
index 391e8061db7..a18a6436062 100644
--- a/gcc/fortran/trans-intrinsic.cc
+++ b/gcc/fortran/trans-intrinsic.cc
@@ -10267,6 +10267,122 @@ conv_isocbinding_subroutine (gfc_code *code)
}
+/* The following routine generates code for both forms of the intrinsic
+ subroutine C_F_STRPOINTER from the ISO_C_BINDING module. */
+static tree
+conv_isocbinding_subroutine_strpointer (gfc_code *code)
+{
+ gfc_actual_arglist *arg = code->ext.actual;
+ gfc_expr *arg0 = arg->expr;
+ gfc_expr *fstrptr = arg->next->expr;
+ gfc_expr *nchars = arg->next->next->expr;
+ tree ptr;
+ tree size = NULL_TREE;
+ tree nc = NULL_TREE;
+ tree fstrptr_ptr, fstrptr_len;
+ stmtblock_t block;
+ gfc_init_block (&block);
+ gfc_se se0, se1, se2;
+ gfc_init_se (&se0, NULL);
+ gfc_init_se (&se1, NULL);
+ gfc_init_se (&se2, NULL);
+
+ /* arg0 can either be a simply contiguous rank-one character array,
+ or a scalar of type c_ptr that points to a contiguous array.
+ In the first case nchars may be omitted and defaults to the size
+ of the array. */
+ if (arg0->rank == 1)
+ {
+ gfc_array_ref *ar = gfc_find_array_ref (arg0);
+ if (ar->as && ar->as->type == AS_ASSUMED_SIZE
+ && (ar->type == AR_FULL || ar->end[0] == nullptr))
+ /* No size available. */
+ gfc_conv_array_parameter (&se0, arg0, true, NULL, NULL, NULL);
+ else
+ {
+ gfc_conv_array_parameter (&se0, arg0, true, NULL, NULL, &size);
+ gcc_assert (size);
+ }
+ ptr = se0.expr;
+ }
+ else if (arg0->rank == 0)
+ {
+ /* Scalar case. arg0 is a C pointer to the string, and the
+ nchars argument is required. */
+ gfc_conv_expr (&se0, arg0);
+ ptr = se0.expr;
+ /* We already issued a diagnostic for this in parsing. */
+ gcc_assert (nchars);
+ }
+ else
+ gcc_unreachable ();
+
+ /* Translate the fortran array pointer argument. AFAICT the
+ representation here is that this returns the pointer location in
+ se1.expr and there is a separate decl for the length.
+ Of course none of this is properly documented.... :-( */
+ gfc_conv_expr (&se1, fstrptr);
+ fstrptr_ptr = se1.expr;
+ gcc_assert (fstrptr->ts.u.cl && fstrptr->ts.u.cl->backend_decl);
+ fstrptr_len = fstrptr->ts.u.cl->backend_decl;
+
+ /* Translate nchars, if provided. If we have both the array size
+ and nchars, take the minimum value. NC is the tree expr to hold
+ the value. */
+ if (nchars)
+ {
+ gfc_conv_expr (&se2, nchars);
+ nc = se2.expr;
+ if (size)
+ nc = fold_build2_loc (input_location, MIN_EXPR,
+ TREE_TYPE (nc), nc, size);
+ /* Check for the case where an optional dummy parameter is
+ passed as the optional nchars argument. It's not supposed to
+ be omitted if we don't also have an array size; rather than
+ produce a run-time error, assume size 0. */
+ if (nchars->expr_type == EXPR_VARIABLE
+ && nchars->symtree->n.sym->attr.dummy
+ && nchars->symtree->n.sym->attr.optional)
+ {
+ tree present = gfc_conv_expr_present (nchars->symtree->n.sym);
+ nc = build3_loc (input_location, COND_EXPR,
+ TREE_TYPE (nc), present, nc,
+ size ? size : build_int_cst (TREE_TYPE (nc), 0));
+ }
+ }
+ else
+ {
+ gcc_assert (size);
+ nc = size;
+ }
+
+ /* Collect argument side-effect statements. */
+ gfc_add_block_to_block (&block, &se0.pre);
+ gfc_add_block_to_block (&block, &se1.pre);
+ gfc_add_block_to_block (&block, &se2.pre);
+
+ /* Generate a call to builtin_strnlen to get the C string length
+ for the output fstrptr. */
+ ptr = gfc_evaluate_now (ptr, &block);
+ size = build_call_expr_loc (input_location,
+ builtin_decl_explicit (BUILT_IN_STRNLEN), 2,
+ fold_convert (const_ptr_type_node, ptr),
+ fold_convert (size_type_node, nc));
+
+ /* Stuff the raw C char pointer PTR and actual length SIZE into fstrptr. */
+ gfc_add_modify (&block, fstrptr_ptr,
+ fold_convert (TREE_TYPE (fstrptr_ptr), ptr));
+ gfc_add_modify (&block, fstrptr_len,
+ fold_convert (gfc_charlen_type_node, size));
+
+ /* Collect argument cleanups. */
+ gfc_add_block_to_block (&block, &se2.post);
+ gfc_add_block_to_block (&block, &se1.post);
+ gfc_add_block_to_block (&block, &se0.post);
+
+ return gfc_finish_block (&block);
+}
+
/* Save and restore floating-point state. */
tree
@@ -13534,6 +13650,10 @@ gfc_conv_intrinsic_subroutine (gfc_code *code)
res = conv_isocbinding_subroutine (code);
break;
+ case GFC_ISYM_C_F_STRPOINTER:
+ res = conv_isocbinding_subroutine_strpointer (code);
+ break;
+
case GFC_ISYM_CAF_SEND:
res = conv_caf_send_to_remote (code);
break;
diff --git a/gcc/testsuite/gfortran.dg/c_f_strpointer-1.f90
b/gcc/testsuite/gfortran.dg/c_f_strpointer-1.f90
new file mode 100644
index 00000000000..cbdfd84f6a5
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/c_f_strpointer-1.f90
@@ -0,0 +1,30 @@
+! { dg-do run }
+program test
+
+ use iso_c_binding
+ implicit none
+
+ character (kind=c_char, len=1), dimension(15), target :: a
+ type(c_ptr) :: p
+ character (len=:, kind=c_char), pointer :: fp1, fp2
+
+ a = [ character :: 'h', 'e', 'l', 'l', 'o', ' ', &
+ 'w', 'o', 'r', 'l', 'd', '!', &
+ ' ', ' ', ' ']
+ a(13) = C_NULL_CHAR
+ p = C_LOC (a(1))
+
+ ! check length is correct
+ call c_f_strpointer (p, fp1, 15)
+ if (len (fp1) .ne. 12) stop 100
+ call c_f_strpointer (a, fp2)
+ if (len (fp2) .ne. 12) stop 101
+
+ ! check that fp1 and fp2 both point to the contents of array a.
+ if (fp1(1:1) .ne. 'h') stop 200
+ if (fp2(1:1) .ne. 'h') stop 201
+ a(1) = 'H'
+ if (fp1(1:1) .ne. 'H') stop 202
+ if (fp2(1:1) .ne. 'H') stop 203
+
+end program
diff --git a/gcc/testsuite/gfortran.dg/c_f_strpointer-10.f90
b/gcc/testsuite/gfortran.dg/c_f_strpointer-10.f90
new file mode 100644
index 00000000000..ac18336d240
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/c_f_strpointer-10.f90
@@ -0,0 +1,39 @@
+! { dg-do run }
+! Check handling of optional dummy nchars argument to c_f_strpointer
+! when its corresponding actual argument is an optional dummy that is
+! not present, and the C string argument has no size information (C
+! pointer or assumed-size array).
+! The Fortran spec says this is not allowed, but it's a runtime error
+! and the gfortran implementation assumes size 0 in this case rather than
+! diagnosing it.
+
+program test
+
+ use iso_c_binding
+ implicit none
+
+ character (kind=c_char, len=1), dimension(15), target :: a
+
+ a = [ character :: 'h', 'e', 'l', 'l', 'o', ' ', &
+ 'w', 'o', 'r', 'l', 'd', '!', &
+ ' ', ' ', ' ']
+ a(13) = C_NULL_CHAR
+ call doit (a, 12, 15)
+ call doit (a, 0)
+contains
+
+subroutine doit (aa, n, m)
+ character (kind=c_char, len=1), dimension(*), target, intent(inout) :: aa
+ integer, intent(in) :: n
+ integer, intent(in), optional :: m
+ character (len=:, kind=c_char), pointer :: fp
+ type(c_ptr) :: p
+
+ p = C_LOC (aa(1))
+ call c_f_strpointer (p, fp, m)
+ if (len(fp) .ne. n) stop 100
+ call c_f_strpointer (aa, fp, m)
+ if (len(fp) .ne. n) stop 200
+end subroutine
+
+end program
diff --git a/gcc/testsuite/gfortran.dg/c_f_strpointer-2.f90
b/gcc/testsuite/gfortran.dg/c_f_strpointer-2.f90
new file mode 100644
index 00000000000..b4a44db68c5
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/c_f_strpointer-2.f90
@@ -0,0 +1,33 @@
+! { dg-do run }
+program test
+
+ use iso_c_binding
+ implicit none
+
+ character (kind=c_char, len=1), dimension(15), target :: a
+
+ a = [ character :: 'h', 'e', 'l', 'l', 'o', ' ', &
+ 'w', 'o', 'r', 'l', 'd', '!', &
+ ' ', ' ', ' ']
+ a(13) = C_NULL_CHAR
+ call doit (a, 12, 15)
+ call doit (a(7:), 6, 9)
+
+contains
+
+subroutine doit (aa, n, m)
+ character (kind=c_char, len=1), dimension(*), target, intent(inout) :: aa
+ integer, intent(in) :: n, m
+ character (len=:, kind=c_char), pointer :: fp
+
+ ! check length is correct
+ call c_f_strpointer (aa, fp, m)
+ if (len (fp) .ne. n) stop 100
+
+ ! check that fp points to the contents of array aa.
+ if (fp(1:1) .ne. aa(1)) stop 101
+ aa(1) = '?'
+ if (fp(1:1) .ne. '?') stop 102
+end subroutine
+
+end program
diff --git a/gcc/testsuite/gfortran.dg/c_f_strpointer-3.f90
b/gcc/testsuite/gfortran.dg/c_f_strpointer-3.f90
new file mode 100644
index 00000000000..958145a3585
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/c_f_strpointer-3.f90
@@ -0,0 +1,37 @@
+! { dg-do compile }
+! Test that missing size argument is rejected.
+
+program test
+
+ use iso_c_binding
+ implicit none
+
+ character (kind=c_char, len=1), dimension(15), target :: a
+
+ a = [ character :: 'h', 'e', 'l', 'l', 'o', ' ', &
+ 'w', 'o', 'r', 'l', 'd', '!', &
+ ' ', ' ', ' ']
+ a(13) = C_NULL_CHAR
+ call doit (a, 12)
+
+contains
+
+subroutine doit (aa, n)
+ character (kind=c_char, len=1), dimension(*), target, intent(inout) :: aa
+ integer, intent(in) :: n
+ character (len=:, kind=c_char), pointer :: fp
+ type(c_ptr) :: p
+
+ p = C_LOC (aa(1))
+ call c_f_strpointer (p, fp) ! { dg-error ".nchars. argument of
.c_f_strpointer. intrinsic shall be present when the .cstrptr. argument at .1.
is a C_PTR" }
+ call c_f_strpointer (aa, fp) ! { dg-error ".nchars. argument of
.c_f_strpointer. intrinsic shall be present when the .cstrarray. argument at
.1. is assumed-size" }
+
+ ! These are all OK, they are known-size array sections of the assumed-size
+ ! array aa.
+ call c_f_strpointer (aa(:10), fp)
+ call c_f_strpointer (aa(:huge(1)), fp)
+ call c_f_strpointer (aa(5:10), fp)
+
+end subroutine
+
+end program
diff --git a/gcc/testsuite/gfortran.dg/c_f_strpointer-4.f90
b/gcc/testsuite/gfortran.dg/c_f_strpointer-4.f90
new file mode 100644
index 00000000000..e6268172f98
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/c_f_strpointer-4.f90
@@ -0,0 +1,18 @@
+! { dg-do compile }
+! Test that non-contiguous array section argument is rejected.
+program test
+
+ use iso_c_binding
+ implicit none
+
+ character (kind=c_char, len=1), dimension(15), target :: a
+ character (len=:, kind=c_char), pointer :: fp
+
+ a = [ character :: 'h', 'e', 'l', 'l', 'o', ' ', &
+ 'w', 'o', 'r', 'l', 'd', '!', &
+ ' ', ' ', ' ']
+ a(13) = C_NULL_CHAR
+
+ call c_f_strpointer (a(1:13), fp)
+ call c_f_strpointer (a(1:13:2), fp) ! { dg-error ".cstrarray. argument of
.c_f_strpointer. intrinsic at .1. shall be simply contiguous" }
+end program
diff --git a/gcc/testsuite/gfortran.dg/c_f_strpointer-5.f90
b/gcc/testsuite/gfortran.dg/c_f_strpointer-5.f90
new file mode 100644
index 00000000000..d9de7bf830b
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/c_f_strpointer-5.f90
@@ -0,0 +1,19 @@
+! { dg-do compile }
+! Test that multi-dimensional array arguments are rejected.
+program test
+
+ use iso_c_binding
+ implicit none
+
+ character (kind=c_char, len=1), target :: a(15), b(3,5)
+ character (len=:, kind=c_char), pointer :: fp
+
+ a = [ character :: 'h', 'e', 'l', 'l', 'o', ' ', &
+ 'w', 'o', 'r', 'l', 'd', '!', &
+ ' ', ' ', ' ']
+ a(13) = C_NULL_CHAR
+ b = reshape (a, shape (b))
+
+ call c_f_strpointer (a, fp)
+ call c_f_strpointer (b, fp) ! { dg-error ".cstrarray. argument of
.c_f_strpointer. intrinsic at .1. shall be a rank-one character array of kind
C_CHAR and character length one" }
+end program
diff --git a/gcc/testsuite/gfortran.dg/c_f_strpointer-6.f90
b/gcc/testsuite/gfortran.dg/c_f_strpointer-6.f90
new file mode 100644
index 00000000000..a90526af284
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/c_f_strpointer-6.f90
@@ -0,0 +1,20 @@
+! { dg-do compile }
+! Test that lack of target attribute and wrong-length character array are
+! rejected.
+program test
+
+ use iso_c_binding
+ implicit none
+
+ character (kind=c_char, len=1), dimension(15) :: a
+ character (kind=c_char, len=4), dimension(15), target :: b
+ character (len=:, kind=c_char), pointer :: fp
+
+ a = [ character :: 'h', 'e', 'l', 'l', 'o', ' ', &
+ 'w', 'o', 'r', 'l', 'd', '!', &
+ ' ', ' ', ' ']
+ a(13) = C_NULL_CHAR
+ b = a
+ call c_f_strpointer (a, fp) ! { dg-error ".cstrarray. argument of
.c_f_strpointer. intrinsic at .1. shall have the TARGET attribute" }
+ call c_f_strpointer (b, fp) ! { dg-error ".cstrarray. argument of
.c_f_strpointer. intrinsic at .1. shall be a rank-one character array of kind
C_CHAR and character length one" }
+end program
diff --git a/gcc/testsuite/gfortran.dg/c_f_strpointer-7.f90
b/gcc/testsuite/gfortran.dg/c_f_strpointer-7.f90
new file mode 100644
index 00000000000..b9531fc4c09
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/c_f_strpointer-7.f90
@@ -0,0 +1,50 @@
+! { dg-do run }
+! Test that c_f_strpointer works with strings/arrays of known length but
+! no null terminator character.
+
+module mod
+use iso_c_binding
+implicit none(type, external)
+type t
+ type(c_ptr) :: cptr
+ character(1, c_char) :: carr(10)
+end type t
+contains
+subroutine sub(x, y)
+ type(t), target :: x
+ character, pointer, contiguous, intent(in) :: y(:)
+ character(:), pointer :: fstr
+
+ call c_f_strpointer (x%cptr, fstr, 10)
+ print *, len(fstr), fstr
+ if (len(fstr) /= 10 .or. fstr /= "1234567890") stop 1
+
+ call c_f_strpointer (x%carr, fstr)
+ print *, len(fstr), fstr
+ if (len(fstr) /= 10 .or. fstr /= "abcdefghij") stop 2
+
+ call c_f_strpointer (y, fstr)
+ if (len(fstr) /= 10 .or. fstr /= "abcdefghij") stop 3
+
+ call c_f_strpointer (y(5:), fstr)
+ if (len(fstr) /= 6 .or. fstr /= "efghij") stop 4
+
+ call c_f_strpointer (x%carr(2:4), fstr)
+ if (len(fstr) /= 3 .or. fstr /= "bcd") stop 5
+end
+end module
+
+use mod
+implicit none
+character(10,c_char), target :: str10
+character(1,c_char), target :: arr10(10)
+
+type(t) :: arg
+
+str10 = '1234567890'
+arr10 = ['a','b','c','d','e','f','g','h','i', 'j']
+
+arg%cptr = c_loc(str10)
+arg%carr = arr10
+call sub(arg, arr10)
+end
diff --git a/gcc/testsuite/gfortran.dg/c_f_strpointer-8.f90
b/gcc/testsuite/gfortran.dg/c_f_strpointer-8.f90
new file mode 100644
index 00000000000..b8e8abe501f
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/c_f_strpointer-8.f90
@@ -0,0 +1,11 @@
+! { dg-do compile }
+! Test that c_f_strpointer rejects assumed-rank array argument.
+
+subroutine sub(y)
+ use iso_c_binding
+ implicit none (type, external)
+ character, pointer, contiguous, intent(in) :: y(..)
+ character(:), pointer :: fstr
+
+ call c_f_strpointer (y, fstr, 10) ! { dg-error "Assumed-rank argument at .1.
is only permitted as actual argument to intrinsic inquiry functions or to
RESHAPE." }
+end
diff --git a/gcc/testsuite/gfortran.dg/c_f_strpointer-9.f90
b/gcc/testsuite/gfortran.dg/c_f_strpointer-9.f90
new file mode 100644
index 00000000000..0e7043253b0
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/c_f_strpointer-9.f90
@@ -0,0 +1,34 @@
+! { dg-do compile }
+! Test that problems with the fstrptr argument are diagnosed.
+
+program test
+
+ use iso_c_binding
+ implicit none
+
+ character (kind=c_char, len=1), dimension(15), target :: a
+ character (len=:, kind=c_char), pointer :: fp
+
+ a = [ character :: 'h', 'e', 'l', 'l', 'o', ' ', &
+ 'w', 'o', 'r', 'l', 'd', '!', &
+ ' ', ' ', ' ']
+ a(13) = C_NULL_CHAR
+ call doit (a, 12, fp)
+
+contains
+
+subroutine doit (aa, n, fp1)
+ character (kind=c_char, len=1), dimension(*), target, intent(inout) :: aa
+ integer, intent(in) :: n
+ character (len=:, kind=c_char), pointer, intent(in) :: fp1
+ character (len=42, kind=c_char), pointer :: fp2
+ character (len=:, kind=c_char), allocatable :: fp3
+ type(c_ptr) :: p
+
+ p = C_LOC (aa(1))
+ call c_f_strpointer (p, fp1, n) ! { dg-error ".fstrptr. argument of
.c_f_strpointer. intrinsic at .1. cannot be INTENT.IN." }
+ call c_f_strpointer (p, fp2, n) ! { dg-error ".fstrptr. argument of
.c_f_strpointer. intrinsic at .1. shall be a scalar deferred-length character
pointer of kind C_CHAR" }
+ call c_f_strpointer (p, fp3, n) ! { dg-error ".fstrptr. argument of
.c_f_strpointer. intrinsic at .1. shall be a scalar deferred-length character
pointer of kind C_CHAR" }
+end subroutine
+
+end program
diff --git a/gcc/testsuite/gfortran.dg/pr108961.f90
b/gcc/testsuite/gfortran.dg/pr108961.f90
index 3e6c9df48bb..30eb502cbc4 100644
--- a/gcc/testsuite/gfortran.dg/pr108961.f90
+++ b/gcc/testsuite/gfortran.dg/pr108961.f90
@@ -5,7 +5,7 @@
module associate_ptr
use iso_c_binding
contains
- subroutine c_f_strpointer(cptr, ptr2)
+ subroutine my_c_f_strpointer(cptr, ptr2)
type(c_ptr), target, intent(in) :: cptr
character(kind=c_char,len=4), pointer :: ptr1
character(kind=c_char,len=:), pointer, intent(out) :: ptr2
@@ -21,6 +21,6 @@ program test_associate_ptr
character(kind=c_char,len=:), pointer :: ptr2
char_array = ['a', 'b', 'c', 'd', c_null_char, 'e', 'f']
! The first argument was providing a constant hidden string length => segfault
- call c_f_strpointer(c_loc(char_array), ptr2)
+ call my_c_f_strpointer(c_loc(char_array), ptr2)
if (ptr2 .ne. 'abcd') stop 2
end program
--
2.39.5