[m-dev.] pragma foreign_export_enum
jfischer at opturion.com
Tue Nov 28 11:58:15 AEDT 2017
On Tue, 28 Nov 2017, Zoltan Somogyi wrote:
> I have been working on making the infrastructure for type representations
> more flexible, since this is needed to enable more compact data representations.
Are there some concrete proposals for what these more compact data
> In the process, I have come across something odd that I need resolved.
> Section 14.5 of the language reference manual, for foreign_export_enum pragmas,
> states (near the end) that
> "It is an error if the mapping between constructors and symbolic names
> does not form a bijection. A program can contain multiple ‘pragma foreign_export_enum’
> declarations for a single Mercury type. The implementation is not required to check
> that the symbolic names generated by separate ‘pragma foreign_export_enum’
> declarations are unique."
> The second sentence is a bit ambiguous; it does not say whether you can have
> code like this:
> :- pragma foreign_export_enum("C", type_foo/0, [prefix("FOO_")]).
> :- pragma foreign_export_enum("C", type_foo/0, [prefix("BAR_")]).
> or whether having two foreign_export_enum (fee for short) pragmas for the
> same type is allowed ONLY if they are for different target languages.
The intention was that you may have multiple foreign_export_enum pragmas
for the same type as above. If you do, then the corresponding symbols
in each set are synonyms.
The bit about the bijection was intended to refer to the mapping
established by each fee pragma _in isolation_; they are not meant be
> As it happens, the implementation allows more than one fee pragma for
> a type even if they are for the same target language.
> However, this contradicts the first sentence, because given the
> pragmas above, any function symbol in type_foo will have TWO separate
> representations in C: one starting with FOO_ and one starting with BAR_.
> Such a mapping cannot be a bijection.
The FOO_* symbols must be bijective with the constructors of the type
and the BAR_* symbols must bijective with the constructors of the type.
Each symbol FOO_x and BAR_x for a Mercury constructor x is a synonym.
> I think that what we actually WANT to require is that for any generated
> symbolic name in any given target language, there should be at most one
> function symbol of ANY Mercury type that maps to that symbolic name.
> This means we require only surjection, not bijection, but we require it
> over all types with fee pragmas at once, not for each type separately.
> Does anyone disagree?
> If I am right, then the compiler COULD build a map from target language/
> symbolic name pairs back to type_ctor/cons_id pairs, and then check whether
> it maps any key to more than one value. Of course, it can build that map only
> for the fee pragmas in the module currently being compiled, and thus
> wouldn't be able to detect clashes between fee pragmas in different modules,
> but some error checking is better than none.
> Or, we could simply say that simply having more than one fee pragma
> for a given type/language pair in a module is an error. Can anyone
> think of a reasonable use case for such duplicates? I can't, yet
> I don't see any reason to forbid it, due to the following.
If the foreign language API exported by a Mercury module is
transitioning from one set of names to another, being able to provide
both sets is useful for backwards compatability with existing foreign
code. (In the absence of allowing multiple fee pragmas, such backwards
compatibility would need to be defined by hand in the foreign code.)
> Having both a foreign_enum and a foreign_export_enum pragma
> for the same type and the same language is required to be allowed by
> tests/hard_coded/exported_foreign_enum.m, though I can't see anything
> in the reference manual that expressly allows it. And having both
> will generate more than one target language name for a given constructor,
> just like multiple fee pragmas would.
Note that fee pragams are not restricted to the modules defining their
subject Mercury type. That again, was intentional: if I have a third
party library, I may want to export the constructors of one of its
enumeration types to a foreign language even though the library code
itself does not do that. As a user of the third party libray I don't
care if the enumeration is defined via a foreign_enum pragma or not.
(In principle I may not even now that a Mercury enumeration is a
foreign enum, although in practice I could dig into the interface
files and find out.)
More information about the developers