All, First, I would like to thank both mikael and fx for providing help in my debugging of the in-lining in trans-intrinsic.cc. It seems I have forgotten much of what I once knew about trees.
I have attached a patch that implements F2023 F_C_STRING() to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117643 and to this email. This function is provided by ISO_C_BINDING, and the current implementation in-lines the function when the intrinsic module is used. On x86_64-*-freebsd, I have === gfortran Summary === # of expected passes 71796 # of unexpected failures 24 # of expected failures 274 # of unsupported tests 87 /usr/home/kargl/gcc/obj/gcc/gfortran version 15.0.0 20241217 (experimental) (GCC) The unexpected failures are all ASAN or LTO related. Jerryd has indicated that the patch bootstraps on x86_64_linux_gnu and also regression tests cleanly. The specific changes are * check.cc (gfc_check_f_c_string): Check arguments of f_c_string(). * gfortran.h: New symbol GFC_ISYM_F_C_STRING. * intrinsic.cc (add_functions): Add the ISO C Binding routine f_c_string(). Wrap nearby long line to less than 80 characters. * intrinsic.h: Prototype for gfc_check_f_c_string(). * iso-c-binding.def: Declare for ISO C Binding routine f_c_string(). * primary.cc (gfc_match_rvalue): Fix comment that has been untrue since 2011. Add ISOCBINDING_F_C_STRING to conditional. * trans-intrinsic.cc(conv_trim): Specialized version of trim() for f_c_string(). (conv_isocbinding_function): Inline implementation of f_c_string(). (gfc_conv_intrinsic_function): Use GFC_ISYM_F_C_STRING to trigger in-lining. * gfortran.dg/f_c_string1.f90: New testcase. -- Steve
diff --git a/gcc/fortran/check.cc b/gcc/fortran/check.cc index f10e665088d..9b717fb934c 100644 --- a/gcc/fortran/check.cc +++ b/gcc/fortran/check.cc @@ -1829,6 +1829,42 @@ gfc_check_image_status (gfc_expr *image, gfc_expr *team) } +/* Check the arguments for f_c_string. */ + +bool +gfc_check_f_c_string (gfc_expr *string, gfc_expr *asis) +{ + + if (gfc_invalid_null_arg (string)) + return false; + + if (!scalar_check (string, 0)) + return false; + + if (string->ts.type != BT_CHARACTER + || (string->ts.type == BT_CHARACTER + && (string->ts.kind != 1 || string->ts.is_c_interop != 1))) + { + gfc_error ("%qs argument of %qs intrinsic at %L shall have " + "a type of CHARACTER(KIND=C_CHAR)", + gfc_current_intrinsic_arg[0]->name, gfc_current_intrinsic, + &string->where); + return false; + } + + if (asis) + { + if (!type_check (asis, 1, BT_LOGICAL)) + return false; + + if (!scalar_check (asis, 1)) + return false; + } + + return true; +} + + bool gfc_check_failed_or_stopped_images (gfc_expr *team, gfc_expr *kind) { diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h index d08439019a3..4e82e7b36e5 100644 --- a/gcc/fortran/gfortran.h +++ b/gcc/fortran/gfortran.h @@ -508,6 +508,7 @@ enum gfc_isym_id GFC_ISYM_EXP, GFC_ISYM_EXPONENT, GFC_ISYM_EXTENDS_TYPE_OF, + GFC_ISYM_F_C_STRING, GFC_ISYM_FAILED_IMAGES, GFC_ISYM_FDATE, GFC_ISYM_FE_RUNTIME_ERROR, diff --git a/gcc/fortran/intrinsic.cc b/gcc/fortran/intrinsic.cc index a2e241280c3..c5e423e666a 100644 --- a/gcc/fortran/intrinsic.cc +++ b/gcc/fortran/intrinsic.cc @@ -3145,6 +3145,14 @@ add_functions (void) x, BT_UNKNOWN, 0, REQUIRED); make_from_module(); + add_sym_2 ("f_c_string", GFC_ISYM_F_C_STRING, CLASS_TRANSFORMATIONAL, + ACTUAL_NO, + BT_CHARACTER, dc, GFC_STD_F2023, + gfc_check_f_c_string, NULL, NULL, + stg, BT_CHARACTER, dc, REQUIRED, + "asis", BT_CHARACTER, dc, OPTIONAL); + make_from_module(); + add_sym_1 ("c_sizeof", GFC_ISYM_C_SIZEOF, CLASS_INQUIRY, ACTUAL_NO, BT_INTEGER, gfc_index_integer_kind, GFC_STD_F2008, gfc_check_c_sizeof, gfc_simplify_sizeof, NULL, @@ -3301,7 +3309,8 @@ add_functions (void) make_generic ("transpose", GFC_ISYM_TRANSPOSE, GFC_STD_F95); - add_sym_1 ("trim", GFC_ISYM_TRIM, CLASS_TRANSFORMATIONAL, ACTUAL_NO, BT_CHARACTER, dc, GFC_STD_F95, + add_sym_1 ("trim", GFC_ISYM_TRIM, CLASS_TRANSFORMATIONAL, ACTUAL_NO, + BT_CHARACTER, dc, GFC_STD_F95, gfc_check_trim, gfc_simplify_trim, gfc_resolve_trim, stg, BT_CHARACTER, dc, REQUIRED); diff --git a/gcc/fortran/intrinsic.h b/gcc/fortran/intrinsic.h index 61d85eedc69..640d1bc15eb 100644 --- a/gcc/fortran/intrinsic.h +++ b/gcc/fortran/intrinsic.h @@ -71,6 +71,7 @@ bool gfc_check_dshift (gfc_expr *, gfc_expr *, gfc_expr *); bool gfc_check_eoshift (gfc_expr *, gfc_expr *, gfc_expr *, gfc_expr *); bool gfc_check_dtime_etime (gfc_expr *); bool gfc_check_event_query (gfc_expr *, gfc_expr *, gfc_expr *); +bool gfc_check_f_c_string (gfc_expr *, gfc_expr *); bool gfc_check_failed_or_stopped_images (gfc_expr *, gfc_expr *); bool gfc_check_fgetputc (gfc_expr *, gfc_expr *); bool gfc_check_fgetput (gfc_expr *); diff --git a/gcc/fortran/iso-c-binding.def b/gcc/fortran/iso-c-binding.def index bad66b1dcbc..5ef4368222c 100644 --- a/gcc/fortran/iso-c-binding.def +++ b/gcc/fortran/iso-c-binding.def @@ -256,6 +256,9 @@ NAMED_FUNCTION (ISOCBINDING_LOC, "c_loc", NAMED_FUNCTION (ISOCBINDING_C_SIZEOF, "c_sizeof", \ GFC_ISYM_C_SIZEOF, GFC_STD_F2008) +NAMED_FUNCTION (ISOCBINDING_F_C_STRING, "f_c_string", \ + GFC_ISYM_F_C_STRING, GFC_STD_F2023) + #undef NAMED_INTCST #undef NAMED_UINTCST #undef NAMED_REALCST diff --git a/gcc/fortran/primary.cc b/gcc/fortran/primary.cc index ab49eac450f..25f97832401 100644 --- a/gcc/fortran/primary.cc +++ b/gcc/fortran/primary.cc @@ -4039,12 +4039,11 @@ gfc_match_rvalue (gfc_expr **result) } /* Check here for the existence of at least one argument for the - iso_c_binding functions C_LOC, C_FUNLOC, and C_ASSOCIATED. The - argument(s) given will be checked in gfc_iso_c_func_interface, - during resolution of the function call. */ + iso_c_binding functions C_LOC, C_FUNLOC, and C_ASSOCIATED. */ if (sym->attr.is_iso_c == 1 && (sym->from_intmod == INTMOD_ISO_C_BINDING && (sym->intmod_sym_id == ISOCBINDING_LOC + || sym->intmod_sym_id == ISOCBINDING_F_C_STRING || sym->intmod_sym_id == ISOCBINDING_FUNLOC || sym->intmod_sym_id == ISOCBINDING_ASSOCIATED))) { diff --git a/gcc/fortran/trans-intrinsic.cc b/gcc/fortran/trans-intrinsic.cc index 1b36ac6e5ac..2e5bd7b3b65 100644 --- a/gcc/fortran/trans-intrinsic.cc +++ b/gcc/fortran/trans-intrinsic.cc @@ -9999,11 +9999,39 @@ gfc_conv_intrinsic_loc (gfc_se * se, gfc_expr * expr) } -/* The following routine generates code for the intrinsic - functions from the ISO_C_BINDING module: - * C_LOC - * C_FUNLOC - * C_ASSOCIATED */ +/* Specialized trim for f_c_string. */ + +static void +conv_trim (gfc_se *tse, gfc_se *str) +{ + tree cond, plen, pvar, tlen, ttmp, tvar; + + tlen = gfc_create_var (gfc_charlen_type_node, "tlen"); + plen = gfc_build_addr_expr (NULL_TREE, tlen); + + tvar = gfc_create_var (pchar_type_node, "tstr"); + pvar = gfc_build_addr_expr (ppvoid_type_node, tvar); + + ttmp = build_call_expr_loc (input_location, gfor_fndecl_string_trim, 4, + plen, pvar, str->string_length, str->expr); + + gfc_add_expr_to_block (&tse->pre, ttmp); + + /* Free the temporary afterwards, if necessary. */ + cond = fold_build2_loc (input_location, GT_EXPR, logical_type_node, + tlen, build_int_cst (TREE_TYPE (tlen), 0)); + ttmp = gfc_call_free (tvar); + ttmp = build3_v (COND_EXPR, cond, ttmp, build_empty_stmt (input_location)); + gfc_add_expr_to_block (&tse->post, ttmp); + + tse->expr = tvar; + tse->string_length = tlen; +} + + +/* The following routine generates code for the intrinsic functions from + the ISO_C_BINDING module: C_LOC, C_FUNLOC, C_ASSOCIATED, and + F_C_STRING. */ static void conv_isocbinding_function (gfc_se *se, gfc_expr *expr) @@ -10078,6 +10106,139 @@ conv_isocbinding_function (gfc_se *se, gfc_expr *expr) not_null_expr, eq_expr); } } + else if (expr->value.function.isym->id == GFC_ISYM_F_C_STRING) + { + /* There are three cases: + f_c_string(string) -> trim(string) // c_null_char + f_c_string(string, .false.) -> trim(string) // c_null_char + f_c_string(string, .true.) -> string // c_null_char */ + + gfc_se lse, rse, tse; + tree len, tmp, var; + gfc_expr *string = arg->expr; + gfc_expr *asis = arg->next->expr; + gfc_expr *cnc; + + /* Convert string. */ + gfc_init_se (&lse, se); + gfc_conv_expr (&lse, string); + gfc_conv_string_parameter (&lse); + + /* Create a string for C_NULL_CHAR and convert it. */ + cnc = gfc_get_character_expr (gfc_default_character_kind, + &string->where, "\0", 1); + gfc_init_se (&rse, se); + gfc_conv_expr (&rse, cnc); + gfc_conv_string_parameter (&rse); + gfc_free_expr (cnc); + +#ifdef cnode +#undef cnode +#endif +#define cnode gfc_charlen_type_node + if (asis) + { + stmtblock_t block; + gfc_se asis_se, vse; + tree elen, evar, tlen, tvar; + tree else_branch, then_branch; + + elen = evar = tlen = tvar = NULL_TREE; + + /* f_c_string(string, .true.) -> string // c_null_char */ + + gfc_init_block (&block); + + gfc_add_block_to_block (&block, &lse.pre); + gfc_add_block_to_block (&block, &rse.pre); + + tlen = fold_build2_loc (input_location, PLUS_EXPR, cnode, + fold_convert (cnode, lse.string_length), + fold_convert (cnode, rse.string_length)); + + gfc_init_se (&vse, se); + tvar = gfc_conv_string_tmp (&vse, pchar_type_node, tlen); + gfc_add_block_to_block (&block, &vse.pre); + + tmp = build_call_expr_loc (input_location, gfor_fndecl_concat_string, + 6, tlen, tvar, + lse.string_length, lse.expr, + rse.string_length, rse.expr); + gfc_add_expr_to_block (&block, tmp); + + then_branch = gfc_finish_block (&block); + + /* f_c_string(string, .false.) = trim(string) // c_null_char */ + + gfc_init_block (&block); + + gfc_init_se (&tse, se); + conv_trim (&tse, &lse); + gfc_add_block_to_block (&block, &tse.pre); + gfc_add_block_to_block (&block, &rse.pre); + + elen = fold_build2_loc (input_location, PLUS_EXPR, cnode, + fold_convert (cnode, tse.string_length), + fold_convert (cnode, rse.string_length)); + + gfc_init_se (&vse, se); + evar = gfc_conv_string_tmp (&vse, pchar_type_node, elen); + gfc_add_block_to_block (&block, &vse.pre); + + tmp = build_call_expr_loc (input_location, gfor_fndecl_concat_string, + 6, elen, evar, + tse.string_length, tse.expr, + rse.string_length, rse.expr); + gfc_add_expr_to_block (&block, tmp); + + else_branch = gfc_finish_block (&block); + + gfc_init_se (&asis_se, se); + gfc_conv_expr (&asis_se, asis); + gfc_add_block_to_block (&se->pre, &asis_se.pre); + tmp = fold_build3_loc (input_location, COND_EXPR, void_type_node, + asis_se.expr, then_branch, else_branch); + + gfc_add_expr_to_block (&se->pre, tmp); + + var = fold_build3_loc (input_location, COND_EXPR, pchar_type_node, + asis_se.expr, tvar, evar); + gfc_add_expr_to_block (&se->pre, var); + + len = fold_build3_loc (input_location, COND_EXPR, cnode, + asis_se.expr, tlen, elen); + gfc_add_expr_to_block (&se->pre, len); + } + else + { + /* f_c_string(string) = trim(string) // c_null_char */ + + gfc_add_block_to_block (&se->pre, &lse.pre); + gfc_add_block_to_block (&se->pre, &rse.pre); + + gfc_init_se (&tse, se); + conv_trim (&tse, &lse); + gfc_add_block_to_block (&se->pre, &tse.pre); + gfc_add_block_to_block (&se->post, &tse.post); + + len = fold_build2_loc (input_location, PLUS_EXPR, cnode, + fold_convert (cnode, tse.string_length), + fold_convert (cnode, rse.string_length)); + + var = gfc_conv_string_tmp (se, pchar_type_node, len); + + tmp = build_call_expr_loc (input_location, gfor_fndecl_concat_string, + 6, len, var, + tse.string_length, tse.expr, + rse.string_length, rse.expr); + gfc_add_expr_to_block (&se->pre, tmp); + } + + se->expr = var; + se->string_length = len; + +#undef cnode + } else gcc_unreachable (); } @@ -11218,6 +11379,7 @@ gfc_conv_intrinsic_function (gfc_se * se, gfc_expr * expr) case GFC_ISYM_C_ASSOCIATED: case GFC_ISYM_C_FUNLOC: case GFC_ISYM_C_LOC: + case GFC_ISYM_F_C_STRING: conv_isocbinding_function (se, expr); break; diff --git a/gcc/testsuite/gfortran.dg/f_c_string1.f90 b/gcc/testsuite/gfortran.dg/f_c_string1.f90 new file mode 100644 index 00000000000..6ce86ce3647 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/f_c_string1.f90 @@ -0,0 +1,47 @@ +! +! { dg-do run } +! +program foo + + use iso_c_binding, only : c_null_char, c_char, f_c_string, c_size_t + + implicit none + + logical asis + character(len=6, kind=c_char) :: s1 + character(len=:, kind=c_char), allocatable :: s2 + + interface + ! + ! strlen() counts up to '\0', and excludes it from the count + ! + function strlen(s) bind(c,name="strlen") + import c_char, c_size_t + integer(c_size_t) strlen + character(len=1,kind=c_char), intent(in) :: s(*) + end function strlen + end interface + + s1 = 'abc ' + s2 = f_c_string(s1) + if (len_trim(s1) /= int(strlen(s2), 4)) stop 1 + + s1 = ' ghij ' + s2 = f_c_string(s1) + if (len_trim(s1) /= int(strlen(s2), 4)) stop 2 + + s2 = f_c_string(s1, .true.) + if (len(s1) /= int(strlen(s2), 4)) stop 3 + + s2 = f_c_string(s1, .false.) + if (len_trim(s1) /= int(strlen(s2), 4)) stop 4 + + asis = .true. + s2 = f_c_string(s1, asis) + if (len(s1) /= int(strlen(s2), 4)) stop 5 + + asis = .false. + s2 = f_c_string(s1, asis) + if (len_trim(s1) /= int(strlen(s2), 4)) stop 6 + +end program foo