fortran: C++ support for generating C prototypes

Message ID 20190511214704.5779-1-blomqvist.janne@gmail.com
State New
Headers show
Series
  • fortran: C++ support for generating C prototypes
Related show

Commit Message

Janne Blomqvist May 11, 2019, 9:47 p.m.
When generating C prototypes for Fortran procedures with the
-fc-prototypes and -fc-prototypes-external options, print a snippet
defining macros for complex types, and add C++ support by suppressing
mangling.

fortran/ChangeLog:

2019-05-12  Janne Blomqvist  <jb@gcc.gnu.org>

	* dump-parse-tree.c (get_c_type_name): Use macros for complex type
	names.
	* parse.c (gfc_parse_file): Define complex macros, add CPP support
	when printing C prototypes.

Ok for trunk?
---
 gcc/fortran/dump-parse-tree.c | 12 ++++++------
 gcc/fortran/parse.c           | 22 ++++++++++++++++++++++
 2 files changed, 28 insertions(+), 6 deletions(-)

-- 
2.17.1

Comments

Jakub Jelinek May 11, 2019, 10:02 p.m. | #1
On Sun, May 12, 2019 at 12:47:04AM +0300, Janne Blomqvist wrote:
> When generating C prototypes for Fortran procedures with the

> -fc-prototypes and -fc-prototypes-external options, print a snippet

> defining macros for complex types, and add C++ support by suppressing

> mangling.

> 

> fortran/ChangeLog:

> 

> 2019-05-12  Janne Blomqvist  <jb@gcc.gnu.org>

> 

> 	* dump-parse-tree.c (get_c_type_name): Use macros for complex type

> 	names.

> 	* parse.c (gfc_parse_file): Define complex macros, add CPP support

> 	when printing C prototypes.

> 

> Ok for trunk?


Is it correct to use macros in user namespace?  Shouldn't they be say __
prefixed, or even have __GFC_ or __GFORTRAN_ in them?

> --- a/gcc/fortran/dump-parse-tree.c

> +++ b/gcc/fortran/dump-parse-tree.c

> @@ -3143,11 +3143,11 @@ get_c_type_name (gfc_typespec *ts, gfc_array_spec *as, const char **pre,

>  	  else if (strcmp (*type_name, "size_t") == 0)

>  	    *type_name = "ssize_t";

>  	  else if (strcmp (*type_name, "float_complex") == 0)

> -	    *type_name = "float complex";

> +	    *type_name = "FLOAT_COMPLEX";

>  	  else if (strcmp (*type_name, "double_complex") == 0)

> -	    *type_name = "double complex";

> +	    *type_name = "DOUBLE_COMPLEX";

>  	  else if (strcmp (*type_name, "long_double_complex") == 0)

> -	    *type_name = "long double complex";

> +	    *type_name = "LONG_DOUBLE_COMPLEX";

>  

>  	  ret = T_OK;

>  	}

> @@ -3166,11 +3166,11 @@ get_c_type_name (gfc_typespec *ts, gfc_array_spec *as, const char **pre,

>  		  else if (strcmp (*type_name, "size_t") == 0)

>  		    *type_name = "ssize_t";

>  		  else if (strcmp (*type_name, "float_complex") == 0)

> -		    *type_name = "float complex";

> +		    *type_name = "FLOAT_COMPLEX";

>  		  else if (strcmp (*type_name, "double_complex") == 0)

> -		    *type_name = "double complex";

> +		    *type_name = "DOUBLE_COMPLEX";

>  		  else if (strcmp (*type_name, "long_double_complex") == 0)

> -		    *type_name = "long double complex";

> +		    *type_name = "LONG_DOUBLE_COMPLEX";

>  

>  		  ret = T_WARN;

>  		  break;

> diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c

> index 9d693595e20..8077da870b0 100644

> --- a/gcc/fortran/parse.c

> +++ b/gcc/fortran/parse.c

> @@ -6331,6 +6331,24 @@ done:

>        }

>  

>    /* Dump C prototypes.  */

> +  if (flag_c_prototypes || flag_c_prototypes_external)

> +    {

> +      fprintf (stdout,

> +	       _("#include <stddef.h>\n"

> +		 "#ifdef __cplusplus\n"

> +		 "#include <complex>\n"

> +		 "#define FLOAT_COMPLEX std::complex<float>\n"

> +		 "#define DOUBLE_COMPLEX std::complex<double>\n"

> +		 "#define LONG_DOUBLE_COMPLEX std::complex<long double>\n"

> +		 "extern \"C\" {\n"

> +		 "#else\n"

> +		 "#define FLOAT_COMPLEX float _Complex\n"

> +		 "#define DOUBLE_COMPLEX double _Complex\n"

> +		 "#define LONG_DOUBLE_COMPLEX long double _Complex\n"

> +		 "#endif\n\n"));

> +    }

> +

> +  /* First dump BIND(C) prototypes.  */

>    if (flag_c_prototypes)

>      {

>        for (gfc_current_ns = gfc_global_ns_list; gfc_current_ns;

> @@ -6342,6 +6360,10 @@ done:

>    if (flag_c_prototypes_external)

>      gfc_dump_external_c_prototypes (stdout);

>  

> +  if (flag_c_prototypes || flag_c_prototypes_external)

> +    fprintf (stdout,

> +	     _("\n#ifdef __cplusplus\n}\n#endif\n"));

> +

>    /* Do the translation.  */

>    translate_all_program_units (gfc_global_ns_list);

>  

> -- 

> 2.17.1


	Jakub
Thomas Koenig May 12, 2019, 8:06 a.m. | #2
Am 12.05.19 um 00:02 schrieb Jakub Jelinek:
> Is it correct to use macros in user namespace?  Shouldn't they be say __

> prefixed, or even have __GFC_ or __GFORTRAN_ in them?


I think Jakub has a point there -- something like __GFC_DOUBLE_COMPLEX
would probably be better.

So, OK with that change.

(I thought for a second about guarding about double inclusion, but
including prototypes twice is harmless, and this should be the
user's responsibility).

Regards

	Thomas
Janne Blomqvist May 12, 2019, 8:29 a.m. | #3
On Sun, May 12, 2019 at 11:06 AM Thomas Koenig <tkoenig@netcologne.de> wrote:
>

> Am 12.05.19 um 00:02 schrieb Jakub Jelinek:

> > Is it correct to use macros in user namespace?  Shouldn't they be say __

> > prefixed, or even have __GFC_ or __GFORTRAN_ in them?

>

> I think Jakub has a point there -- something like __GFC_DOUBLE_COMPLEX

> would probably be better.

>

> So, OK with that change.


Thanks, committed as r271106. I prefixed the macros with __GFORTRAN_,
as that seems clearer to users where those come from as gfc seems used
mostly in the compiler internals.

Any opinions whether it should be committed to 9 as well?

> (I thought for a second about guarding about double inclusion, but

> including prototypes twice is harmless, and this should be the

> user's responsibility).


I thought about adding include guards as well, but the problem is that
we don't know which name the end user wants to save the output as.

-- 
Janne Blomqvist
Thomas Koenig May 12, 2019, 9:24 a.m. | #4
Am 12.05.19 um 10:29 schrieb Janne Blomqvist:
> Thanks, committed as r271106. I prefixed the macros with __GFORTRAN_,

> as that seems clearer to users where those come from as gfc seems used

> mostly in the compiler internals.


Sounds good.

> Any opinions whether it should be committed to 9 as well?


Well, we just introduced this feature :-)  Your change makes it
more useful, and it's not like gcc-9 has a long history
with the current format.

So, OK from my side for gcc-9 as well.

I just hope that using this option will not keep people from
using the standard C interop feature...

Next task would be to generate prototypes for extended
C interop (but not today).

Regards

	Thomas
Janne Blomqvist May 12, 2019, 7:36 p.m. | #5
On Sun, May 12, 2019 at 11:29 AM Janne Blomqvist
<blomqvist.janne@gmail.com> wrote:
>

> On Sun, May 12, 2019 at 11:06 AM Thomas Koenig <tkoenig@netcologne.de> wrote:

> > (I thought for a second about guarding about double inclusion, but

> > including prototypes twice is harmless, and this should be the

> > user's responsibility).

>

> I thought about adding include guards as well, but the problem is that

> we don't know which name the end user wants to save the output as.


But, we can of course use the source file name instead of the usual
header name for the include guard. Like in the attached patch. Ok for
trunk and 9?



-- 
Janne Blomqvist
Jakub Jelinek May 12, 2019, 7:44 p.m. | #6
On Sun, May 12, 2019 at 10:36:09PM +0300, Janne Blomqvist wrote:
> On Sun, May 12, 2019 at 11:29 AM Janne Blomqvist

> <blomqvist.janne@gmail.com> wrote:

> >

> > On Sun, May 12, 2019 at 11:06 AM Thomas Koenig <tkoenig@netcologne.de> wrote:

> > > (I thought for a second about guarding about double inclusion, but

> > > including prototypes twice is harmless, and this should be the

> > > user's responsibility).

> >

> > I thought about adding include guards as well, but the problem is that

> > we don't know which name the end user wants to save the output as.

> 

> But, we can of course use the source file name instead of the usual

> header name for the include guard. Like in the attached patch. Ok for


There are many characters that can appear in filenames and can't appear in
macro names, /, <, >, :, ;, many others.  Either you need to replace them
all with _ but then you risk collisions, or add some way how to encode the
characters you can't put in directly (say _7b etc.), or instead use
_GFORTRAN_GUARD_<crc or sha256sum etc. of filename>

	Jakub
Thomas König May 12, 2019, 11:14 p.m. | #7
Hi Janne,

I do not think we need to add header guards.

The headers, as we emit them, contain prototypes only, so repeated inclusions
Should be harmless.

So. the potential disadvantage would be a teeny bit of compilation time vs the chance of header macro collision and resulting wrong code.

Filenames can be duplicates,
and eventually the responsibility should be the user‘s.

Finally, I want people to read the comment about this not being recommended 😉
Jakub Jelinek May 15, 2019, 1:41 p.m. | #8
On Sun, May 12, 2019 at 12:47:04AM +0300, Janne Blomqvist wrote:
> --- a/gcc/fortran/parse.c

> +++ b/gcc/fortran/parse.c

> @@ -6331,6 +6331,24 @@ done:

>        }

>  

>    /* Dump C prototypes.  */

> +  if (flag_c_prototypes || flag_c_prototypes_external)

> +    {

> +      fprintf (stdout,

> +	       _("#include <stddef.h>\n"

> +		 "#ifdef __cplusplus\n"

> +		 "#include <complex>\n"

> +		 "#define FLOAT_COMPLEX std::complex<float>\n"

> +		 "#define DOUBLE_COMPLEX std::complex<double>\n"

> +		 "#define LONG_DOUBLE_COMPLEX std::complex<long double>\n"

> +		 "extern \"C\" {\n"

> +		 "#else\n"

> +		 "#define FLOAT_COMPLEX float _Complex\n"

> +		 "#define DOUBLE_COMPLEX double _Complex\n"

> +		 "#define LONG_DOUBLE_COMPLEX long double _Complex\n"

> +		 "#endif\n\n"));


Two more things:
1) why the _() around the code snippet?  Do you expect translators
   to translate the C snippets to something else or what?
2) I don't think float _Complex is
   passed the same as std::complex<float> and similar for others;
   std::complex<float> is in libstdc++ a C++ class with with
   __complex__ float as its sole non-static data member and with non-trivial
   constructors; which means it is passed/returned via a hidden reference;
   when the argument is actually FLOAT_COMPLEX * or FLOAT_COMPLEX &,
   you except for aliasing don't have to care that much, but if
   that complex argument has VALUE attribute in Fortran and so the
   C prototype would be FLOAT_COMPLEX, then std::complex<float> is
   passed in the end as std::complex<float> &, while float _Complex
   the same as __complex__ float; and ditto for functions returning
   complex

	Jakub
Janne Blomqvist May 15, 2019, 7:41 p.m. | #9
On Wed, May 15, 2019 at 4:41 PM Jakub Jelinek <jakub@redhat.com> wrote:
>

> On Sun, May 12, 2019 at 12:47:04AM +0300, Janne Blomqvist wrote:

> > --- a/gcc/fortran/parse.c

> > +++ b/gcc/fortran/parse.c

> > @@ -6331,6 +6331,24 @@ done:

> >        }

> >

> >    /* Dump C prototypes.  */

> > +  if (flag_c_prototypes || flag_c_prototypes_external)

> > +    {

> > +      fprintf (stdout,

> > +            _("#include <stddef.h>\n"

> > +              "#ifdef __cplusplus\n"

> > +              "#include <complex>\n"

> > +              "#define FLOAT_COMPLEX std::complex<float>\n"

> > +              "#define DOUBLE_COMPLEX std::complex<double>\n"

> > +              "#define LONG_DOUBLE_COMPLEX std::complex<long double>\n"

> > +              "extern \"C\" {\n"

> > +              "#else\n"

> > +              "#define FLOAT_COMPLEX float _Complex\n"

> > +              "#define DOUBLE_COMPLEX double _Complex\n"

> > +              "#define LONG_DOUBLE_COMPLEX long double _Complex\n"

> > +              "#endif\n\n"));

>

> Two more things:

> 1) why the _() around the code snippet?  Do you expect translators

>    to translate the C snippets to something else or what?


Er, because originally I printed out these definitons as part of the
other fprintf call where a comment was printed, and when I moved it to
another location I forgot to remove the translation markers.

Committed r271261 and r271264 as obvious.

> 2) I don't think float _Complex is

>    passed the same as std::complex<float> and similar for others;

>    std::complex<float> is in libstdc++ a C++ class with with

>    __complex__ float as its sole non-static data member and with non-trivial

>    constructors; which means it is passed/returned via a hidden reference;

>    when the argument is actually FLOAT_COMPLEX * or FLOAT_COMPLEX &,

>    you except for aliasing don't have to care that much, but if

>    that complex argument has VALUE attribute in Fortran and so the

>    C prototype would be FLOAT_COMPLEX, then std::complex<float> is

>    passed in the end as std::complex<float> &, while float _Complex

>    the same as __complex__ float; and ditto for functions returning

>    complex


Ugh, I guess that's right. Any good way around it? Except print a
warning in the header that passing std::complex<> by value doesn't
work?

-- 
Janne Blomqvist
Jakub Jelinek May 16, 2019, 3:05 p.m. | #10
On Wed, May 15, 2019 at 10:41:06PM +0300, Janne Blomqvist wrote:
> > 2) I don't think float _Complex is

> >    passed the same as std::complex<float> and similar for others;

> >    std::complex<float> is in libstdc++ a C++ class with with

> >    __complex__ float as its sole non-static data member and with non-trivial

> >    constructors; which means it is passed/returned via a hidden reference;

> >    when the argument is actually FLOAT_COMPLEX * or FLOAT_COMPLEX &,

> >    you except for aliasing don't have to care that much, but if

> >    that complex argument has VALUE attribute in Fortran and so the

> >    C prototype would be FLOAT_COMPLEX, then std::complex<float> is

> >    passed in the end as std::complex<float> &, while float _Complex

> >    the same as __complex__ float; and ditto for functions returning

> >    complex

> 

> Ugh, I guess that's right. Any good way around it? Except print a

> warning in the header that passing std::complex<> by value doesn't

> work?


Perhaps we can use different macros for the two cases, define
__GFORTRAN_FLOAT_COMPLEX to std::complex<float> and
__GFORTRAN_FLOAT_COMPLEX_VALUE to __complex__ float and use the
former in the __GFORTRAN_FLOAT_COMPLEX * arguments and
the latter for VALUE dummy args and return value.

For the return value case, guess it will be fine, when one does
__complex__ float fortran_fn_ (void);
std::complex<float> ret = fortran_fn_ ();
it will work just fine, but for the arguments trying to
void fortran_sub_ (__complex__ float);
std::complex<float> f = 2.0f + 4.0fi;
fortran_sub_ (f);
will not work (but one will get at least errors).  One can use non-standard
fortran_sub_ (f.__rep ());

	Jakub

Patch

diff --git a/gcc/fortran/dump-parse-tree.c b/gcc/fortran/dump-parse-tree.c
index 54af5dfd50d..21305243522 100644
--- a/gcc/fortran/dump-parse-tree.c
+++ b/gcc/fortran/dump-parse-tree.c
@@ -3143,11 +3143,11 @@  get_c_type_name (gfc_typespec *ts, gfc_array_spec *as, const char **pre,
 	  else if (strcmp (*type_name, "size_t") == 0)
 	    *type_name = "ssize_t";
 	  else if (strcmp (*type_name, "float_complex") == 0)
-	    *type_name = "float complex";
+	    *type_name = "FLOAT_COMPLEX";
 	  else if (strcmp (*type_name, "double_complex") == 0)
-	    *type_name = "double complex";
+	    *type_name = "DOUBLE_COMPLEX";
 	  else if (strcmp (*type_name, "long_double_complex") == 0)
-	    *type_name = "long double complex";
+	    *type_name = "LONG_DOUBLE_COMPLEX";
 
 	  ret = T_OK;
 	}
@@ -3166,11 +3166,11 @@  get_c_type_name (gfc_typespec *ts, gfc_array_spec *as, const char **pre,
 		  else if (strcmp (*type_name, "size_t") == 0)
 		    *type_name = "ssize_t";
 		  else if (strcmp (*type_name, "float_complex") == 0)
-		    *type_name = "float complex";
+		    *type_name = "FLOAT_COMPLEX";
 		  else if (strcmp (*type_name, "double_complex") == 0)
-		    *type_name = "double complex";
+		    *type_name = "DOUBLE_COMPLEX";
 		  else if (strcmp (*type_name, "long_double_complex") == 0)
-		    *type_name = "long double complex";
+		    *type_name = "LONG_DOUBLE_COMPLEX";
 
 		  ret = T_WARN;
 		  break;
diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c
index 9d693595e20..8077da870b0 100644
--- a/gcc/fortran/parse.c
+++ b/gcc/fortran/parse.c
@@ -6331,6 +6331,24 @@  done:
       }
 
   /* Dump C prototypes.  */
+  if (flag_c_prototypes || flag_c_prototypes_external)
+    {
+      fprintf (stdout,
+	       _("#include <stddef.h>\n"
+		 "#ifdef __cplusplus\n"
+		 "#include <complex>\n"
+		 "#define FLOAT_COMPLEX std::complex<float>\n"
+		 "#define DOUBLE_COMPLEX std::complex<double>\n"
+		 "#define LONG_DOUBLE_COMPLEX std::complex<long double>\n"
+		 "extern \"C\" {\n"
+		 "#else\n"
+		 "#define FLOAT_COMPLEX float _Complex\n"
+		 "#define DOUBLE_COMPLEX double _Complex\n"
+		 "#define LONG_DOUBLE_COMPLEX long double _Complex\n"
+		 "#endif\n\n"));
+    }
+
+  /* First dump BIND(C) prototypes.  */
   if (flag_c_prototypes)
     {
       for (gfc_current_ns = gfc_global_ns_list; gfc_current_ns;
@@ -6342,6 +6360,10 @@  done:
   if (flag_c_prototypes_external)
     gfc_dump_external_c_prototypes (stdout);
 
+  if (flag_c_prototypes || flag_c_prototypes_external)
+    fprintf (stdout,
+	     _("\n#ifdef __cplusplus\n}\n#endif\n"));
+
   /* Do the translation.  */
   translate_all_program_units (gfc_global_ns_list);