[m-dev.] for discussion: foreign_export_enum syntax

Julien Fischer juliensf at csse.unimelb.edu.au
Mon Jul 2 17:43:03 AEST 2007


On Mon, 2 Jul 2007, Jonathan Morgan wrote:

> On 6/27/07, Julien Fischer <juliensf at csse.unimelb.edu.au> wrote:
>> I have been working on the change to allow Mercury enumeration constants
>> to be exported to foreign languages.  The majority of the changes to
>> the compiler were made last year; what remains undecided is the
>> concrete syntax.  Below is change to the reference manual that describes
>> the current version of this new feature.  Here is a small example of
>> how it would look and work in the case where C is the foreign language.
>
> I've been considering bringing this up again in the last couple of
> weeks, so you've saved me the trouble of doing that.

Glad to be of help!

>> Example
>> -------
>> 
>> :- type foo
>>                 --->    foo
>>                 ;               bar
>>                 ;               baz.
>> 
>>         :- pragma foreign_export_enum("C", foo, [bar - "BAR],
>>                 [prefix("MR_"]).
>> 
>> would cause the following declaration to be generated in the .mh file.
>> 
>>         enum {
>>                         MR_foo = 0,
>>                         MR_BAR = 1,
>>                         MR_baz = 2
>>         };
>
> Is there any particular reason for choosing enums over #define's?

Not particularly.

> I would expect #define's to be a better option as they do not specify
> any type for the resultant constant, whereas an enum specifies an enum
> type for the constant, which is probably different from the standard
> Mercury type (MR_Word?).

Identifiers used as enumeration constants in C have type int.

> While this may not be a problem with C's
> weak type system, it could potentially cause problems with C++ or
> C/C++ compilers (are there any plans to support a C++ compiler with
> any of the high-level C grades?).

Not in the immediate future (see comments in mlds_to_c.m if you are
interested in a C++ backend), but since we want the code the in the .mh
files to be compatible with C++ for the purposes of interoperability, we
should probably use #defines.

>> +For each foreign language there is a default mapping between the name
>> +of a Mercury constructor and its symbolic name in the language @var{Lang}.
>> +This default mapping is not required to map every valid constructor name
>> +to a valid name in language @var{Lang}; where it does not the programmer
>> +must specify a valid symbolic name.
>> +The programmer may also choose to map a constructor to a symbolic name
>> +that differs from the one supplied by the default mapping for language
>> + at var{Lang}.
>> + at var{Overrides} is a list of pairs of constructor names and strings of
>> +the following form:
>> +
>> + at example
>> +[consI - "symbolI", ..., consJ - "symbolJ"]
>> + at end example
>> +
>> +This can be used to provide either a valid symbolic name where the
>> +default mapping does not, or to override a valid symbolic name
>> +generated by the default mapping.
>
> Do you have to specify an empty list if there are no renamings? i

No, you should be able to omit it in that case; unless you have attributes.
(That might seem confusing but it's the same thing we do with mutable
declarations; maybe it would be better to swap the position of those two
arguments?)

> (I can imagine that requirement being potentially confusing if most uses
> of the pragma didn't do any renaming).  An alternative (for if renaming
> was scarce) would be to make each renaming a separate attribute (e.g.
> rename(x, "X")), though I don't think that it would really help.

Another choice, would be to make overrides into an attribute and have
something like:

 	:- pragma foreign_export_enum("C",
 		foo,
 		[
 			prefix("MR_"),
 			overrides([foo - "FOO", bar - "BAR"])
 		]).

I'm not so keen on that one, but I guess I could live with it.

...

>> +The argument @var{Attributes} is a list of optional attributes.
>> +If empty, it may be omitted from the @samp{pragma foreign_export_enum}
>> +declaration.
>> +The following attribute must be supported by all Mercury implementations.
>> +
>> + at table @asis
>> +
>> + at item @samp{prefix(Prefix)}
>> +Prefix each symbolic name, regardless of how it was generated, with
>> +the string @var{Prefix}.
>> +This occurs @emph{before} the validity of the symbolic name in the
>> +foreign language is checked, i.e. the effect of the @samp{prefix}
>> +attribute may cause an otherwise valid symbolic name to become invalid or
>> +vice versa.
>
> If it occurred before the validity check is done, shouldn't its
> validity also be checked? (and thus it shouldn't cause the name to be
> invalid)

That would probably work for C, however I'm haven't though too much
about how you could break it using other languages.  Another point
to consider here is that there may be other attributes that could
affect what the eventual symbolic name is.  I wanted the rules concerning
these things to be simple, e.g.

 	(1) take default or supplied name
 	(2) apply stuff from attributes
 	(3) check for validity

Note that the above specification does not prohibit the compiler from
performing further checks, e.g. checking the validity of a prefix separately
and emitting a more specific error message (indeed such behaviour would
be useful), it just specifies the minimum requirement for every language
that enumerations can exported to.  (Most probably the version for C
we will check the validity of the prefix separately; that will be mentioned
in the language specific information for this feature though.)

> Is there a requirement that there is only one prefix, or can I have
> multiple prefixes?

No, there should be at most one prefix attribute per pragma 
foreign_export_enum declaration.  I'll fix that.

>> + at end table
>> +
>> +It is an error for a single constructor to be mapped to two different 
>> symbolic
>> +names by a single @samp{pragma foreign_export_enum} declaration.
>> +A program can contain multiple @samp{pragma foreign_export_enum}
>> +declarations for a single Mercury type.
>> +
>> +A module may contain @samp{pragma foreign_export_enum} declarations that
>> +refer to imported types, subject to the usual visibility restrictions.
>> +
>> + at c -----------------------------------------------------------------------
>> +
>>   @node Adding foreign declarations
>>   @section Adding foreign declarations
>
> How are single-value enumerations handled?  The original version of
> this proposal defined an enumeration as requiring at least two
> constructors,

So does this one, but I accidently deleted that restriction when rewriting
the proposal.  (A better fix may to define `unit dummy type' in the types
section and say that they cannot be foreign_export_enum'd - since defining
this will simplify the reference manual in a few other spots as 
well.)

> but I can see no real reason for preventing single-value
> enumerations from being exported (if I understand correctly, all the
> dummy type optimisation does is to substitute the single acceptable
> value as a constant in places where it is needed, so there is a valid
> value to be exported).  Just FYI, Gtk+ has a few single value
> enumerations.

I forget the original reason for that restriction; it may have something
to do with the fact that the compiler handles dummy types differently
from other enumerations.  I'll take another look at it when I fix the
implementation up.

> I've also been considering an implementation for foreign_import_enum,
> if we were to have such a thing.

This has come up several times before (every time foreign_export_enum
has been discussed in fact) - I've never really seen a proposal for
it that I have been entirely satisfied with.

> I assume that it would look
> something like:
>
> :- pragma foreign_import_enum("C", my_enum, "MyEnum", [a -
> "MY_ENUM_A", b - "MY_ENUM_B"]).
>
> [something of a cross between a foreign type declaration and a type
> constructor declaration].
>
> and I've come up with the following couple of ideas:
>
> 1. Represent the imported type as a foreign type, and provide
> functions to access the valid constants.
>
> 2. Create a new Mercury type for the imported type, and marshal
> between the Mercury type and the foreign type whenever going into or
> out of a foreign code layer (e.g. at foreign_proc and foreign_export
> boundaries).

What about foreign_procs or foreign_exports with polymorphic arguments? 
How is the compiler to decide whether to call the marshalling code?

> While neither of these ideas are necessarily perfect, they each offer
> different trade-offs.  The first option is fairly efficient (no
> marshalling costs), but potentially requires a function call for
> creating any value in Mercury.

Compiling with --intermodule-optimization enabled should result in most
of those functions being inlined though.  (Some of the library bindings
in extras do things this way.)

> The second option allows switching on the type in Mercury code, but
> requires conversions on every foreign language call.  Neither option
> supports overlapping enumeration values very well (which happens in quite
> a few libraries).
>
> A potential problem with this declaration as is is that it doesn't
> support a good distinction between whether the constructors for the
> type are public or private.  With an ordinary type declaration, this
> would be determined by whether the type definition was in the
> interface section or the implementation section, but it is not usual
> to put foreign declarations of this sort in Mercury interfaces (as
> they expose implementation details).  Also, the constants used in the
> definition will need to be accessible to the generated code (probably
> through foreign_decl clauses).

And it's a little bit tricker than that if the things in foreign constants
foreign_import_enum pragma are #defines, since we then need to make sure
that the appropriate #include is available to an importing module.
(For library bindings if you do not allow the constants to be #defines
then I think this particular feature loses a lot of its appeal.)

Thanks for the feedback!

Cheers,
Julien.
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to:       mercury-developers at csse.unimelb.edu.au
Administrative Queries: owner-mercury-developers at csse.unimelb.edu.au
Subscriptions:          mercury-developers-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the developers mailing list