This bug has been around a long time. The attached is the first of two patches.

Several test cases added. Some modified where restrictions changed.

Regression tested on x86_64.

OK for mainline?

Regards,

Jerry

---

 Fortran: allow character(len=*), value dummy [PR49802]

Fortran 2003 C558 prohibited assumed-length character dummies with
VALUE, but Fortran 2008 removed that restriction.  gfortran was still
rejecting it, and also rejected character dummies with VALUE whose
length is a non-constant specified-length expression (e.g. another
dummy argument).  This patch has three parts:

  resolve.cc: Relax the constraint to allow character(len=*) with VALUE,
  using gfc_notify_std(GFC_STD_F2008) so -std=f2003 still rejects it.
  The same Fortran 2008 allowance is extended to character dummies with
  VALUE and a specified but non-constant length.  The C-interop checks
  are consolidated into a single "must have length one" condition (which
  also covers, and rejects, the assumed-length and non-constant-length
  cases since C has no equivalent), checked ahead of the Fortran 2008
  allowance so C-interop dummies always get the same diagnostic.

  trans-types.cc (gfc_sym_type): Assumed-length and non-constant-length
  VALUE character dummies now use byref=1, giving them a pointer-based
  ABI consistent with the callee's VLA parameter passing.  Without this,
  the callee compiled as a pointer-receiving function while the caller
  packed bytes by value into registers, causing an ABI mismatch.

  trans-expr.cc (conv_dummy_value): For assumed-length or non-constant-
  length CHARACTER VALUE dummies, generate a caller-side copy (VLA alloca
  + memcpy) and pass its address, so the callee operates on the copy and
  VALUE semantics are preserved.  This lives in conv_dummy_value, the
  common dummy-value conversion routine, rather than only in
  gfc_conv_procedure_call's argument-walking loop, so the copy is made on
  every path that converts a VALUE character dummy, not just direct
  procedure-call arguments.

Assisted by: Claude Sonnet 4.6

        PR fortran/49802

gcc/fortran/ChangeLog:

        * resolve.cc: Allow character(len=*) VALUE from Fortran 2008,
        and likewise a specified but non-constant length; use
        gfc_notify_std for F2003 compatibility, and consolidate the
        C-interop length checks into a single "must have length one"
        check (covering assumed and non-constant length too) ordered
        ahead of the Fortran 2008 allowance.
        * trans-expr.cc (conv_dummy_value): For assumed-length or
        non-constant-length CHARACTER VALUE dummies, make a caller-side
        copy via DECL_EXPR alloca and gfc_build_memcpy_call, then pass
        its address.
        * trans-types.cc (gfc_sym_type): Use byref=1 for assumed-length
        or non-constant-length VALUE character dummies so the ABI uses
        pointer passing.

gcc/testsuite/ChangeLog:

        * gfortran.dg/value_5.f90: Compile under -std=f2003 so the
        Fortran 2008 assumed-length VALUE relaxation is exercised as a
        rejection (foo4), and update the C-interop bar4 error
        expectation to match the consolidated diagnostic; the F2008
        acceptance case is covered separately by pr49802.f90 and
        pr49802_1.f90.
        * gfortran.dg/pr49802.f90: New test - run test for correctness
        with assumed-length VALUE character dummies.
        * gfortran.dg/pr49802_1.f90: New test - compile/error test for
        -std=f2003 rejection.
        * gfortran.dg/pr49802_2.f90: New test - run test for correctness
        with a non-constant specified-length VALUE character dummy.
---
From 45f27e33268e9f880992b43252725d5f1994104c Mon Sep 17 00:00:00 2001
From: Jerry DeLisle <[email protected]>
Date: Fri, 12 Jun 2026 10:27:40 -0700
Subject: [PATCH] Fortran: allow character(len=*), value dummy [PR49802]

Fortran 2003 C558 prohibited assumed-length character dummies with
VALUE, but Fortran 2008 removed that restriction.  gfortran was still
rejecting it, and also rejected character dummies with VALUE whose
length is a non-constant specified-length expression (e.g. another
dummy argument).  This patch has three parts:

  resolve.cc: Relax the constraint to allow character(len=*) with VALUE,
  using gfc_notify_std(GFC_STD_F2008) so -std=f2003 still rejects it.
  The same Fortran 2008 allowance is extended to character dummies with
  VALUE and a specified but non-constant length.  The C-interop checks
  are consolidated into a single "must have length one" condition (which
  also covers, and rejects, the assumed-length and non-constant-length
  cases since C has no equivalent), checked ahead of the Fortran 2008
  allowance so C-interop dummies always get the same diagnostic.

  trans-types.cc (gfc_sym_type): Assumed-length and non-constant-length
  VALUE character dummies now use byref=1, giving them a pointer-based
  ABI consistent with the callee's VLA parameter passing.  Without this,
  the callee compiled as a pointer-receiving function while the caller
  packed bytes by value into registers, causing an ABI mismatch.

  trans-expr.cc (conv_dummy_value): For assumed-length or non-constant-
  length CHARACTER VALUE dummies, generate a caller-side copy (VLA alloca
  + memcpy) and pass its address, so the callee operates on the copy and
  VALUE semantics are preserved.  This lives in conv_dummy_value, the
  common dummy-value conversion routine, rather than only in
  gfc_conv_procedure_call's argument-walking loop, so the copy is made on
  every path that converts a VALUE character dummy, not just direct
  procedure-call arguments.

Assisted by: Claude Sonnet 4.6

	PR fortran/49802

gcc/fortran/ChangeLog:

	* resolve.cc: Allow character(len=*) VALUE from Fortran 2008,
	and likewise a specified but non-constant length; use
	gfc_notify_std for F2003 compatibility, and consolidate the
	C-interop length checks into a single "must have length one"
	check (covering assumed and non-constant length too) ordered
	ahead of the Fortran 2008 allowance.
	* trans-expr.cc (conv_dummy_value): For assumed-length or
	non-constant-length CHARACTER VALUE dummies, make a caller-side
	copy via DECL_EXPR alloca and gfc_build_memcpy_call, then pass
	its address.
	* trans-types.cc (gfc_sym_type): Use byref=1 for assumed-length
	or non-constant-length VALUE character dummies so the ABI uses
	pointer passing.

gcc/testsuite/ChangeLog:

	* gfortran.dg/value_5.f90: Compile under -std=f2003 so the
	Fortran 2008 assumed-length VALUE relaxation is exercised as a
	rejection (foo4), and update the C-interop bar4 error
	expectation to match the consolidated diagnostic; the F2008
	acceptance case is covered separately by pr49802.f90 and
	pr49802_1.f90.
	* gfortran.dg/pr49802.f90: New test - run test for correctness
	with assumed-length VALUE character dummies.
	* gfortran.dg/pr49802_1.f90: New test - compile/error test for
	-std=f2003 rejection.
	* gfortran.dg/pr49802_2.f90: New test - run test for correctness
	with a non-constant specified-length VALUE character dummy.
---
 gcc/fortran/resolve.cc                  | 26 ++++++++++++++++++++--
 gcc/fortran/trans-expr.cc               | 26 ++++++++++++++++++++++
 gcc/fortran/trans-types.cc              |  6 ++++-
 gcc/testsuite/gfortran.dg/pr49802.f90   | 29 +++++++++++++++++++++++++
 gcc/testsuite/gfortran.dg/pr49802_1.f90 |  9 ++++++++
 gcc/testsuite/gfortran.dg/pr49802_2.f90 | 17 +++++++++++++++
 gcc/testsuite/gfortran.dg/value_5.f90   | 10 +++++++--
 7 files changed, 118 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/pr49802.f90
 create mode 100644 gcc/testsuite/gfortran.dg/pr49802_1.f90
 create mode 100644 gcc/testsuite/gfortran.dg/pr49802_2.f90

diff --git a/gcc/fortran/resolve.cc b/gcc/fortran/resolve.cc
index c326bc4f3d1..dffa0501858 100644
--- a/gcc/fortran/resolve.cc
+++ b/gcc/fortran/resolve.cc
@@ -18757,7 +18757,7 @@ skip_interfaces:
   if (sym->attr.value && sym->ts.type == BT_CHARACTER)
     {
       gfc_charlen *cl = sym->ts.u.cl;
-      if (!cl || !cl->length || cl->length->expr_type != EXPR_CONSTANT)
+      if (!cl)
 	{
 	  gfc_error ("Character dummy variable %qs at %L with VALUE "
 		     "attribute must have constant length",
@@ -18765,14 +18765,36 @@ skip_interfaces:
 	  return;
 	}
 
+      /* C interoperable character dummies must have length one; this
+	 also rules out assumed and non-constant length, so check it
+	 ahead of the Fortran 2008 assumed-length allowance below.  */
       if (sym->ts.is_c_interop
-	  && mpz_cmp_si (cl->length->value.integer, 1) != 0)
+	  && (!cl->length
+	      || cl->length->expr_type != EXPR_CONSTANT
+	      || mpz_cmp_si (cl->length->value.integer, 1) != 0))
 	{
 	  gfc_error ("C interoperable character dummy variable %qs at %L "
 		     "with VALUE attribute must have length one",
 		     sym->name, &sym->declared_at);
 	  return;
 	}
+
+      /* Assumed-length character with VALUE is valid from Fortran 2008
+	 (Fortran 2003 C558 prohibited it).  */
+      if (!cl->length
+	  && !gfc_notify_std (GFC_STD_F2008, "Assumed-length character "
+			      "dummy variable %qs at %L with VALUE attribute",
+			      sym->name, &sym->declared_at))
+	return;
+
+      /* Likewise for a specified but non-constant length (e.g. another
+	 dummy argument used as the length).  */
+      if (cl->length && cl->length->expr_type != EXPR_CONSTANT
+	  && !gfc_notify_std (GFC_STD_F2008, "Character dummy variable "
+			      "%qs at %L with VALUE attribute and "
+			      "non-constant length",
+			      sym->name, &sym->declared_at))
+	return;
     }
 
   if (sym->ts.type == BT_DERIVED && !sym->attr.is_iso_c
diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc
index 7bbfaa51ef9..a8c5f34cbf1 100644
--- a/gcc/fortran/trans-expr.cc
+++ b/gcc/fortran/trans-expr.cc
@@ -6739,6 +6739,32 @@ conv_dummy_value (gfc_se * parmse, gfc_expr * e, gfc_symbol * fsym,
       return;
     }
 
+  /* Assumed-length or non-constant-length CHARACTER VALUE: the dummy uses
+     pointer-based passing (byref=1 in gfc_sym_type), so make a caller-side
+     copy here to satisfy VALUE semantics -- changes to the dummy must not
+     propagate back to the actual argument.  Fall through afterwards so the
+     OPTIONAL handling below still runs.  */
+  if (fsym->ts.type == BT_CHARACTER
+      && (!fsym->ts.u.cl || !fsym->ts.u.cl->length
+	  || fsym->ts.u.cl->length->expr_type != EXPR_CONSTANT))
+    {
+      gfc_conv_string_parameter (parmse);
+      tree len = fold_convert (gfc_charlen_type_node, parmse->string_length);
+      tree chartype = gfc_get_character_type_len (fsym->ts.kind, len);
+      tree val_copy = gfc_create_var (chartype, "val_copy");
+      tmp = fold_build1_loc (input_location, DECL_EXPR, chartype, val_copy);
+      gfc_add_expr_to_block (&parmse->pre, tmp);
+      tmp = gfc_build_memcpy_call (
+	fold_convert (pvoid_type_node,
+		      gfc_build_addr_expr (NULL_TREE, val_copy)),
+	fold_convert (pvoid_type_node, parmse->expr),
+	fold_convert (size_type_node, len));
+      gfc_add_expr_to_block (&parmse->pre, tmp);
+      parmse->expr = fold_convert (
+	build_pointer_type (gfc_get_char_type (fsym->ts.kind)),
+	gfc_build_addr_expr (NULL_TREE, val_copy));
+    }
+
   /* Truncate a too long constant character actual argument.  */
   if (gfc_const_length_character_type_p (&fsym->ts)
       && e->expr_type == EXPR_CONSTANT
diff --git a/gcc/fortran/trans-types.cc b/gcc/fortran/trans-types.cc
index 5bccea960aa..1c35e114db9 100644
--- a/gcc/fortran/trans-types.cc
+++ b/gcc/fortran/trans-types.cc
@@ -2516,7 +2516,11 @@ gfc_sym_type (gfc_symbol * sym, bool is_bind_c)
   else
     type = gfc_typenode_for_spec (&sym->ts, sym->attr.codimension);
 
-  if (sym->attr.dummy && !sym->attr.function && !sym->attr.value
+  if (sym->attr.dummy && !sym->attr.function
+      && (!sym->attr.value
+	  || (sym->ts.type == BT_CHARACTER
+	      && (!sym->ts.u.cl || !sym->ts.u.cl->length
+		  || sym->ts.u.cl->length->expr_type != EXPR_CONSTANT)))
       && !sym->pass_as_value)
     byref = 1;
   else
diff --git a/gcc/testsuite/gfortran.dg/pr49802.f90 b/gcc/testsuite/gfortran.dg/pr49802.f90
new file mode 100644
index 00000000000..1167c09bfb2
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/pr49802.f90
@@ -0,0 +1,29 @@
+! { dg-do run }
+! PR fortran/49802
+! character(len=*), value was rejected by gfortran despite being valid
+! from Fortran 2008 onwards.  Verify that it compiles and that VALUE
+! semantics are correct: modifications to the dummy do not affect the
+! actual argument, and len() returns the actual argument's length.
+
+program test
+  implicit none
+  character(len=10) :: str
+
+  str = "123456789"
+  call by_value (str)
+  if (str /= "123456789") stop 1
+
+contains
+
+  subroutine by_value (y)
+    character(len=*), value :: y
+    if (len (y) /= 10) stop 2
+    if (y /= "123456789 ") stop 3
+    y = "abcdefghij"
+    if (y /= "abcdefghij") stop 4
+    ! str is accessible via host association; VALUE must not let
+    ! the assignment to y propagate back.
+    if (str /= "123456789") stop 5
+  end subroutine
+
+end program
diff --git a/gcc/testsuite/gfortran.dg/pr49802_1.f90 b/gcc/testsuite/gfortran.dg/pr49802_1.f90
new file mode 100644
index 00000000000..b3650be9a36
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/pr49802_1.f90
@@ -0,0 +1,9 @@
+! { dg-do compile }
+! { dg-options "-std=f2003" }
+! PR fortran/49802
+! Fortran 2003 C558 prohibited assumed-length character with VALUE.
+! Verify that -std=f2003 rejects it.
+
+subroutine sub (y)  ! { dg-error "Assumed-length character" }
+  character(len=*), value :: y
+end subroutine
diff --git a/gcc/testsuite/gfortran.dg/pr49802_2.f90 b/gcc/testsuite/gfortran.dg/pr49802_2.f90
new file mode 100644
index 00000000000..5bc5edeba50
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/pr49802_2.f90
@@ -0,0 +1,17 @@
+! { dg-do run }
+! PR fortran/49802
+! character(len=n), value with a non-constant specified length (n is a
+! dummy argument) was rejected with "must have constant length".  Verify
+! that it compiles and that VALUE semantics hold.
+
+program test
+  implicit none
+  call sub_char1_n ("abc", 3)
+contains
+  subroutine sub_char1_n (x, n)
+    integer, intent(in)            :: n
+    character(len=n), value :: x
+    x(1:1) = "1"
+    if (x(1:1) /= "1") error stop 23
+  end subroutine sub_char1_n
+end program test
diff --git a/gcc/testsuite/gfortran.dg/value_5.f90 b/gcc/testsuite/gfortran.dg/value_5.f90
index 4b0dcefb340..32f22d509bb 100644
--- a/gcc/testsuite/gfortran.dg/value_5.f90
+++ b/gcc/testsuite/gfortran.dg/value_5.f90
@@ -1,9 +1,15 @@
 ! { dg-do compile }
+! { dg-options "-std=f2003" }
 ! Length of character dummy variable with VALUE attribute:
 ! - must be initialization expression or omitted
 ! - C interoperable: must be initialization expression of length one
 !   or omitted
 !
+! Compiled as -std=f2003 because Fortran 2008 relaxed C558 to allow
+! assumed-length character dummies with the VALUE attribute; that case
+! (foo4) and its rejection under -std=f2003 are exercised separately in
+! pr49802.f90 and pr49802_1.f90.
+!
 ! Contributed by Tobias Burnus
 program x
   implicit none
@@ -36,7 +42,7 @@ contains
     value :: a
   end subroutine foo3
 
-  subroutine foo4(a) ! { dg-error "VALUE attribute must have constant length" }
+  subroutine foo4(a) ! { dg-error "Assumed-length character" }
     character(*) :: a
     value :: a
   end subroutine foo4
@@ -60,7 +66,7 @@ contains
     value :: a
   end subroutine bar3
 
-  subroutine bar4(a) ! { dg-error "VALUE attribute must have constant length" }
+  subroutine bar4(a) ! { dg-error "VALUE attribute must have length one" }
     use iso_c_binding, only: c_char
     character(kind=c_char,len=*) :: a
     value :: a
-- 
2.54.0

Reply via email to