https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117643

--- Comment #23 from kargls at comcast dot net ---
(In reply to anlauf from comment #20)
> (In reply to Jerry DeLisle from comment #19)
> > (In reply to kargls from comment #17)
> > > I suppose the error in check.cc(gfc_check_f_c_string) that starts
> > > with
> > > 
> > >     if (string->ts.type != BT_CHARACTER
> > >        || (string->ts.type == BT_CHARACTER
> > >     && (string->ts.kind != 1 || string->ts.is_c_interop != 1)))
> > >  
> > > can be suppressed for (string.expr_type == EXPR_CONSTANT &&
> > > string->ts.type == BT_CHARACTER && string->ts.kind == 1)
> 
> I am a little confused by the above logic.  So far I was under the
> impression that a string->ts.kind != 1 cannot be interoperable.  There
> is an existing PR (need to find it) that we illegally accept kind=4
> for BIND(C) procedures.  E.g.


18.2.3.9 F_C_STRING (STRING [, ASIS])
   ...
   STRING shall be a character scalar of kind C_CHAR.

If string->ts.type is not BT_CHARACTER. There is an argument mismatch.
So, issue an error.

If string->ts.type is BT_CHARACTER. Then, the kind must be 1 and
it should be marked as C interoperable.

   program foo
     use iso_c_binding, only : c_char, f_c_string
     implicit none
     character(kind=c_char,len=10) s1
     s1 = 'abc'
     if (len(f_c_string(s1)) /= 4) stop 1
   end program foo

(gdb) p *string
$1 = {expr_type = EXPR_VARIABLE, ts = {type = BT_CHARACTER, kind = 1,
      u = {derived = 0x804270390, cl = 0x804270390, pad = 69665680}, interface
= 0x0, 
      is_c_interop = 1, is_iso_c = 0, ...

The problem with f_c_string(c_char_'abc') is that the constant
has neither is_c_interop = 1 nor is_iso_c = 1 when it reaches 
gfc_check_f_c_string ().

(gdb) p *string
$3 = {expr_type = EXPR_CONSTANT, ts = {type = BT_CHARACTER, kind = 1,
      u = {derived = 0x804270510, cl = 0x804270510, pad = 69666064}, interface
= 0x0, 
      is_c_interop = 0, is_iso_c = 0, ...

I traced through primary.cc(match_string_constant), and is_c_interop
is set to 1 when c_char_'abc' is parsed.  Somewhere between parsing
the string constant and passing it to gfc_check_f_c_string, the expression
seems to have is_c_interop reset to 0.

I suspect that somewhere gfortran uses expr.cc(gfc_get_character_expr)
to copy the constant to a gfc_expr entity.  This function ignore ISO C
binding.

>   subroutine sub (s) bind(c)
>     character(kind=4,len=*), intent(in) :: s
>   end
> 
> is silently accepted although it should not.

Looks like a bug unrelated to the implementation of f_c_string().

> Of course we detect that
> when passed as argument to F_C_STRING, but other might scratch their head
> later when looking at the code...
> 
> > Of course after posting what I just did in Comment #18 I see Steve's comment
> > in 17. Maybe I have my logic all wrong on the error message. I saw the kind
> > = 1 and did not expect is_c_interop to be set since c_char_'string_constant'
> > is equivalent to 1_'string_constant'.
> 
> Other compilers seem to print identical values for KIND(1_"a") and
> KIND(c_char_"b"), as verified for NAG and Intel.
> 
> But we can check interoperability for variables (and in theory for functions
> returning character).
> 
> So if I come from the other side, which code to accept and which to diagnose,
> I tried:
> 
>   if (string->ts.type != BT_CHARACTER
>       || string->ts.kind != 1
>       || (string->expr_type == EXPR_VARIABLE && !string->ts.is_c_interop))
> 
> and threw it on:
> 
>   subroutine sub1 (s)
>     character(*), intent(in) :: s
>     print *, f_c_string (s)        ! detected
>     print *, f_c_string (trim(s))  ! not detected
>   end

With the logic in my original patch,

   program foo
     use iso_c_binding, only : c_char, f_c_string
     implicit none
     character(kind=c_char,len=10) s1
     s1 = 'abc'
     call sub1(s1)
     contains
      subroutine sub1 (s)
        character(*), intent(in) :: s
        print *, f_c_string (s)        ! detected
        print *, f_c_string (trim(s))  ! not detected
      end
   end program foo

f_c_string2.f90:10:26:

   10 |      print *, f_c_string (s)        ! detected
      |                          1
Error: 'string' argument of 'f_c_string' intrinsic at (1) shall have a type of
CHARACTER(KIND=C_CHAR)
f_c_string2.f90:11:26:

   11 |      print *, f_c_string (trim(s))  ! not detected
      |                          1
Error: 'string' argument of 'f_c_string' intrinsic at (1) shall have a type of
CHARACTER(KIND=C_CHAR)

Seems to detect the issue.

>   subroutine sub2 (s)
>     character(kind=c_char,len=*), intent(in) :: s
>     print *, f_c_string (s)        ! OK
>     print *, f_c_string (trim(s))  ! OK
>   end

   program foo
     use iso_c_binding, only : c_char, f_c_string
     implicit none
     character(kind=c_char,len=10) s1
     s1 = 'abc'
     call sub1(s1)
     contains
      subroutine sub1 (s)
        character(kind=c_char,len=*), intent(in) :: s
        print *, f_c_string (s)        ! detected
        print *, f_c_string (trim(s))  ! not detected
      end
   end program foo

Looks like a bug in the handling of trim(), which is likely present
with (all?) other functions that return a string.

  16.9.214 TRIM (STRING)
  ...
  Result Characteristics. Character with the same kind type parameter value as
STRING

IMHO, trim() should be setting is_c_interop if STRING has a kind
type parameter of c_char.  is_c_interop appears to be the only
way to distinguish kind('a') and kind(c_char_'a').

> I tried to extend to above conditions to
> .. (string->expr_type == EXPR_VARIABLE || string->expr_type ==
> EXPR_FUNCTION) ..
> but it appears that TRIM() keeps only the kind parameter, and when the
> argument
> is of kind C_CHAR the result string looses the interoperability attribute.
> 
> So we could ignore the issue with function results for the time being and
> use the above variant of the logic in gfc_check_f_c_string.
> 
> The two separate testcase could also be combined into one file as they share
> a lot of code but that's not really important.
> 
> What do you think?

I think the original logic is correct.  It is unfortunate that
kind('a') == kind(c_char_'a'), and the only way to determine 
if a string is interoperable is through the metadata is_c_interop
or is_iso_c.

Reply via email to