Fwd: [RFC] Proposal for tagged_by attribute to describe union discriminators

Flame Montgomery via Gcc <[email protected]> Tue, 28 Jul 2026 23:04:11 +1200
Newsgroups gmane.comp.gcc.devel
Message-ID <CADbr_TLd0OapR4QatWTYsMEZY9dDtDJDnjxRaOa6WJ_doAYdGw@mail.gmail.com>
---------- Forwarded message ---------
From: Ashton Warner <[email protected]>
Date: Tue, 28 Jul 2026, 11:01 pm
Subject: Re: [RFC] Proposal for tagged_by attribute to describe union
discriminators
To: Martin Uecker <[email protected]>


On Tue, Jul 28, 2026 at 09:29:07AM +0200, Martin Uecker wrote:
> Am Dienstag, dem 28.07.2026 um 08:31 +0200 schrieb Richard Biener:
> > On Tue, Jul 28, 2026 at 1:20=E2=80=AFAM Ashton Warner via Gcc <gcc@gcc.=
gnu.org>
wrote:
> > >
> > > On Thu, Jul 16, 2026 at 09:59:26AM +0200, Martin Uecker wrote:
> > > > Am Donnerstag, dem 16.07.2026 um 09:17 +1200 schrieb Ashton Warner
via Gcc:
> > > > > Hello,
> > > > >
> > > > > I would like to discuss the possibility of adding a GCC attribute
for
> > > > > describing tagged unions (discriminated unions) to improve static
> > > > > analysis diagnostics.
> > > > >
> > > > > The motivation is to allow programmers to explicitly describe a
> > > > > relationship between an enum discriminator and a union member. C
has a
> > > > > common pattern of representing variants using a struct containing
an
> > > > > enum and a union:
> > > > >
> > > > > enum num_type {
> > > > >   T_INT,
> > > > >   T_FLOAT,
> > > > > };
> > > > >
> > > > > struct number {
> > > > >   enum num_type type;
> > > > >
> > > > >   union {
> > > > >     int ival;
> > > > >     float fval;
> > > > >   };
> > > > > };
> > > > >
> > > > > I propose the GCC attribute __attribute__((tagged_by(...))) with
the
> > > > > following syntax:
> > > > >
> > > > > tagged_by(discriminator, mapping-list)
> > > > >
> > > > > where:
> > > > >
> > > > > - discriminator is an identifier
> > > > > - mapping-list is a comma-separated list of one or more mappings.
> > > > > - Each mapping has the form:
> > > > >     (enumerator, union-member)
> > > > >
> > > > > For example:
> > > > >
> > > > > struct number {
> > > > >   enum num_type type;
> > > > >
> > > > >   union {
> > > > >     int ival;
> > > > >     float fval;
> > > > >   } __attribute__((tagged_by(type,
> > > > >     (T_INT, ival),
> > > > >     (T_FLOAT, fval)
> > > > >   )));
> > > > > };
> > > > >
> > > > > The mapping is intentionally explicit rather than inferred from
the
> > > > > declaration order of enum values and union members. This avoids
> > > > > changing the meaning of the attribute if either the enum or union
> > > > > members are reordered.
> > > > >
> > > > > The attribute does not change the representation or runtime
behaviour
> > > > > of the union. It provides additional information that GCC can use
for
> > > > > diagnostics and static analysis.
> > > > >
> > > > > For example:
> > > > >
> > > > > struct number n;
> > > > >
> > > > > n.type =3D T_INT;
> > > > > n.fval =3D 1.0f;
> > > > >
> > > > > could produce a diagnostic because fval is not the member
associated
> > > > > with the current discriminator value.
> > > > >
> > > > > The implementation should diagnose invalid mappings, such as an
> > > > > enumerator that is not part of the discriminator's enum type or a
> > > > > member that does not exist in the union.
> > > > >
> > > > > The attribute is intended to provide semantic information in a
similar
> > > > > way to existing attributes such as counted_by, where the compiler
is
> > > > > given information about a relationship that already exists in the
> > > > > program.
> > > > >
> > > >
> > > > I agree with this proposal.  There is an existing enhancement
> > > > request: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=3D112840
> > > >
> > > > I like an attribute on the member more than the attribute on the
> > > > union type.
> > > >
> > > > > Open questions:
> > > > >
> > > > > - How should enumerators without an associated union member be
> > > > >   represented? One possibility is allowing a mapping without a
member,
> > > > >   for example (T_UNKNOWN), to explicitly indicate that an
enumerator
> > > > >   represents a valid discriminator state with no active union
member.
> > > > >   Enumerators that are not mentioned in the attribute could then
be
> > > > >   diagnosed.
> > > > > - Should diagnostics based on this attribute be implemented as
part of
> > > > >   existing warning infrastructure, -fanalyzer, or another analysi=
s
> > > > >   pass?
> > > > > - Could this information be useful to future runtime checking
tools?
> > > >
> > > > Yes, I think this would be useful for checking at run-time similar
> > > > to sanitizers.  This would be easy to implement.
> > > >
> > > > > - Are there existing GCC mechanisms that overlap with this
> > > > >   functionality?
> > > > > - Are there additional constraints or semantics that would be
needed
> > > > >   for GCC to implement this attribute?
> > > >
> > > > The issue similar to counted_by are the exact semantics for when
> > > > the constraints are in effect relative to when the members are
> > > > accessed / changed.
> > > >
> > > > Martin
> > >
> > >
> > > > I agree with this proposal.  There is an existing enhancement
> > > > request: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=3D112840
> > > >
> > > > I like an attribute on the member more than the attribute on the
> > > > union type.
> > >
> > > This proposal looks interesting and more readable than my initial
> > > proposal. The one semantics that is missing from this proposal is a
> > > system to allow the user to be notified when a union field does not
> > > contain a tag, or when there is no field for a given enum value.
> > >
> > > Would an attribute that enables warning on these issues be achievable=
?
> > >
> > > An example of this attribute
> > >
> > > struct number {
> > >   enum num_type type;
> > >
> > >   union {
> > >     int ival __attribute__((guard(.type =3D=3D T_INT));
> >
> > I also think that attributes on the individual fields are better, but
possibly
> > combined with an attribute on the union?
> >
> >    union {
> >       int ival __attribute__(tag_value(T_INT));
> >       float fval __attribute__((tag_value(T_FLOAT));
> >    } __attribute__((tagged_by(type)));
> >
> > ?
>
> I guess the question is whether you want to have a more constraint featur=
e
> or a more general, or both.

My current thoughts are that a more general feature would be better and
suit more developers and situations.

>
> I like "guard" because you can express a lot of different constraints
> and tagged unions are just one possible use case.
>

I agree with the "guard" statements and it should be able to implement
tagged unions easily while keeping the syntax available for more
complex relationships. For a tagged union with warnings I would prefer
to have a separate attribute that enables warning when enum values are
not used in the union guard statements.

union {
  int ival [[gnu::guard(.type =3D=3D T_INT)]];
  float fval [[gnu::guard(.type =3D=3D T_FLOAT || .type =3D=3D T_NUMBER)]];
} [[gnu::warn_unused_tag(enum type, T_UNKNOWN)]];

This would satisfy the use of T_INT, T_FLOAT, T_NUMBER for the
warn_unused_tag attribute despite T_FLOAT and T_NUMBER being in the
same guard statement.  If another value was added such as T_STR, a
warning would be incurred.

> >
> > In principle the QUAL_UNION facility would allow having different
> > tag members for different fields or even complex combined expressions
> > or constants.  Do we want/need
> >
> >   union {
> >       int ival __attribute__(tag_value(T_INT));
> >       float fval __attribute__(tag_value(T_FLOAT));
> >       char pad[sizeof(union)] __attribute__((tag_default));
> >    } __attribute__((tagged_by(type)));
> >
> > aka a fallback active element?  For QUAL_UNION it would be the
> > last member with a true qualifier.  The behavior when no field
> > is active isn't explicitly documented but you could read it to be
> > that the union is empty then.
>
> I agree that a default tag makes sense. If we add a more constraint
> version like this, I would would probably use an even shorter syntax
> and reuse the "default" keyword:
>
> enum num_type type;
> [[gnu::tagged_by(type)]] union {
>       [[gnu::tag(T_INT)]] int ival;
>       [[gnu::tag(T_FLOAT)] float fval;
>       [[gnu::tag(default)]] char pad[sizeof(union)];
> };
>
> If the underlying feature is QUAL_UNION and this changes the size of
> the union depending on the tag, I would not use attributes at all
> but introduce a new __tagged_union type or something.

If QUAL_UNION changes the size of the union, I would prefer to use a
different feature so that ABI size is clear and matches standard union
sizing. If there were a union that changed size depending on the tag, I
would also prefer introducing a new type.

Ashton

>
> Martin
>
> >
> > >     float fval;
> > >   } __attribute__((warn_untagged));
> > > };
> > >
> > > Warning: union <anonymous> member 'fval' is untagged. <warn_untagged>
> > >
> > >
> > > Another example showing unused enum field
> > >
> > > struct number {
> > >   enum num_type type;
> > >
> > >   union {
> > >     int ival __attribute__((guard(.type =3D=3D T_INT));
> > >   } __attribute__((warn_untagged(enum num_type, T_UNKNOWN)));
> > > };
> > >
> > > Warning: union <anonymous> missing enum value 'T_FLOAT'.
<warn_untagged>
> > >
> > >
> > > The above showing a warn_untagged that ignores the T_UNKNOWN value.
> > >
> > > The only issue I have with my current proposed syntax for
warn_untagged
> > > would be that it would be complex with the syntax of the guard
attribute
> > >
> > > Ashton