Skip to content

P3394R4 Annotations for Reflection #8041

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions source/basic.tex
Original file line number Diff line number Diff line change
Expand Up @@ -5590,6 +5590,7 @@
\item a structured binding\iref{dcl.struct.bind},
\item a function\iref{dcl.fct},
\item an enumerator\iref{dcl.enum},
\item an annotation\iref{dcl.attr.grammar},
\item a type alias\iref{dcl.typedef},
\item a type\iref{basic.types},
\item a class member\iref{class.mem},
Expand Down
78 changes: 73 additions & 5 deletions source/declarations.tex
Original file line number Diff line number Diff line change
Expand Up @@ -9360,7 +9360,7 @@

\pnum
\indextext{attribute!syntax and semantics}%
Attributes specify additional information for various source constructs
Attributes and annotations specify additional information for various source constructs
such as types, variables, names, contract assertions, blocks, or translation units.

\begin{bnf}
Expand All @@ -9371,6 +9371,7 @@
\begin{bnf}
\nontermdef{attribute-specifier}\br
\terminal{[} \terminal{[} \opt{attribute-using-prefix} attribute-list \terminal{]} \terminal{]}\br
\terminal{[} \terminal{[} annotation-list \terminal{]} \terminal{]}\br
alignment-specifier
\end{bnf}

Expand All @@ -9393,11 +9394,22 @@
attribute-list \terminal{,} attribute \terminal{...}
\end{bnf}

\begin{bnf}
\nontermdef{annotation-list}\br
annotation \opt{\terminal{...}}\br
annotation-list \terminal{,} annotation \opt{\terminal{...}}
\end{bnf}

\begin{bnf}
\nontermdef{attribute}\br
attribute-token \opt{attribute-argument-clause}
\end{bnf}

\begin{bnf}
\nontermdef{annotation}\br
\terminal{=} constant-expression
\end{bnf}

\begin{bnf}
\nontermdef{attribute-token}\br
identifier\br
Expand Down Expand Up @@ -9470,10 +9482,11 @@
In an \grammarterm{attribute-list}, an ellipsis may appear only if that
\grammarterm{attribute}'s specification permits it. An \grammarterm{attribute} followed
by an ellipsis is a pack expansion\iref{temp.variadic}.
An \grammarterm{attribute-specifier}
that contains no \grammarterm{attribute}{s} and no \grammarterm{alignment-specifier}
has no
effect. The order in which the \grammarterm{attribute-token}{s} appear in an
An \grammarterm{attribute-specifier} that contains
an \grammarterm{attribute-list} with no \grammarterm{attribute}s
and no \grammarterm{alignment-specifier}
has no effect.
The order in which the \grammarterm{attribute-token}{s} appear in an
\grammarterm{attribute-list} is not significant. If a
keyword\iref{lex.key}
or an alternative token\iref{lex.digraph} that satisfies the syntactic requirements
Expand All @@ -9484,6 +9497,10 @@
\grammarterm{attribute-token}. The \grammarterm{attribute-token} determines additional
requirements on the \grammarterm{attribute-argument-clause} (if any).

\pnum
An \grammarterm{annotation} followed by an ellipsis
is a pack expansion\iref{temp.variadic}.

\pnum
Each \grammarterm{attribute-specifier-seq} is said to \defn{appertain} to some entity or
statement, identified by the syntactic context
Expand Down Expand Up @@ -10181,5 +10198,56 @@
if their respective types are all empty.
\end{example}

\rSec2[dcl.attr.annotation]{Annotations}%
\indextext{attribute!annotations}

\pnum
An annotation may be applied to any declaration of a
type,
type alias,
variable,
function,
namespace,
enumerator,
\grammarterm{base-specifier}, or
non-static data member.

\pnum
Let $E$ be the expression
\tcode{std::meta::reflect_constant(\grammarterm{constant-expression})}.
$E$ shall be a constant expression;
the result of $E$ is the \defnadj{underlying}{constant} of the annotation.

\pnum
Each \grammarterm{annotation} produces a unique annotation.

\pnum
Substituting into an \grammarterm{annotation}
is not in the immediate context.
\begin{example}
\begin{codeblock}
[[=1]] void f();
[[=2, =3, =2]] void g();
void g [[=4, =2]] ();
\end{codeblock}
\tcode{f} has one annotation
and \tcode{g} has five annotations.
These can be queried with metafunctions
such as \tcode{std::\brk{}meta::\brk{}anno\-tations_of}\iref{meta.reflection.annotation}.
\end{example}
\begin{example}
\begin{codeblock}
template<class T>
[[=T::type()]] void f(T t);

void f(int);

void g() {
f(0); // OK
f('0'); // error, substituting into the annotation results in an invalid expression
}
\end{codeblock}
\end{example}

\indextext{attribute|)}%
\indextext{declaration|)}
1 change: 1 addition & 0 deletions source/expressions.tex
Original file line number Diff line number Diff line change
Expand Up @@ -7608,6 +7608,7 @@
\item represent values that are template-argument-equivalent\iref{temp.type},
\item represent the same object,
\item represent the same entity,
\item represent the same annotation\iref{dcl.attr.annotation},
\item represent the same direct base class relationship, or
\item represent equal data member descriptions\iref{class.mem.general},
\end{itemize}
Expand Down
137 changes: 132 additions & 5 deletions source/meta.tex
Original file line number Diff line number Diff line change
Expand Up @@ -2625,6 +2625,7 @@

consteval bool is_bit_field(info r);
consteval bool is_enumerator(info r);
consteval bool is_annotation(info r);

consteval bool is_const(info r);
consteval bool is_volatile(info r);
Expand Down Expand Up @@ -2912,6 +2913,10 @@
consteval info variant_alternative(size_t index, info type);

consteval strong_ordering type_order(info type_a, info type_b);

// \ref{meta.reflection.annotation}, annotation reflection
consteval vector<info> annotations_of(info item);
consteval vector<info> annotations_of_with_type(info item, info type);
}
\end{codeblock}

Expand Down Expand Up @@ -3243,6 +3248,7 @@
\returns
\tcode{true} if \tcode{r} represents a
value,
annotation,
object,
variable,
function whose type does not contain an undeduced placeholder type
Expand Down Expand Up @@ -3277,6 +3283,9 @@
non-static data member, or
unnamed bit-field,
then the type of what is represented by \tcode{r}.
\item
Otherwise, if \tcode{r} represents an annotation,
then \tcode{type_of(constant_of(r))}.
\item
Otherwise, if \tcode{r} represents
an enumerator $N$ of an enumeration $E$, then:
Expand Down Expand Up @@ -3358,19 +3367,26 @@

\begin{itemdescr}
\pnum
Let \tcode{\placeholder{R}} be a constant expression of type \tcode{info}
such that \tcode{\placeholder{R} == r} is \tcode{true}.
Let $R$ be a constant expression of type \tcode{info}
such that \tcode{$R$ == r} is \tcode{true}.
If \tcode{r} represents an annotation,
then let $C$ be its underlying constant.

\pnum
\constantwhen
\tcode{[: \placeholder{R} :]} is a valid
Either \tcode{r} represents an annotation or
\tcode{[: $R$ :]} is a valid
\grammarterm{splice-expression}\iref{expr.prim.splice}.

\pnum
\effects
Equivalent to:
\begin{codeblock}
return reflect_constant([: @\placeholder{R}@ :]);
if constexpr (is_annotation(@$R$@)) {
return @$C$@;
} else {
return reflect_constant([: @$R$@ :]);
}
\end{codeblock}
\begin{example}
\begin{codeblock}
Expand Down Expand Up @@ -3552,14 +3568,16 @@
\end{itemdescr}

\indexlibraryglobal{is_enumerator}%
\indexlibraryglobal{is_annotation}%
\begin{itemdecl}
consteval bool is_enumerator(info r);
consteval bool is_annotation(info r);
\end{itemdecl}

\begin{itemdescr}
\pnum
\returns
\tcode{true} if \tcode{r} represents an enumerator.
\tcode{true} if \tcode{r} represents an enumerator or annotation, respectively.
Otherwise, \tcode{false}.
\end{itemdescr}

Expand Down Expand Up @@ -5946,6 +5964,115 @@
represented by \tcode{dealias(t1)} and \tcode{dealias(t2)}, respectively.
\end{itemdescr}

\rSec2[meta.reflection.annotation]{Annotation reflection}

\indexlibraryglobal{annotations_of}%
\begin{itemdecl}
consteval vector<info> annotations_of(info item);
\end{itemdecl}

\begin{itemdescr}
\pnum
\constantwhen
\tcode{item} represents a
type,
type alias,
variable,
function,
namespace,
enumerator,
direct base class relationship, or
non-static data member.

%FIXME: it is highly unusual for this subclause to put Let paragraphs after Constant When
\pnum
Let $E$ be
\begin{itemize}
\item
the corresponding \grammarterm{base-specifier}
if \tcode{item} represents a direct base class relationship,
\item
otherwise, the entity represented by \tcode{item}.
\end{itemize}

\pnum
\returns
A \tcode{vector} containing all of the reflections $R$
representing each annotation applying to each declaration of $E$ that precedes either
some point in the evaluation context\iref{expr.const} or
a point immediately following the \grammarterm{class-specifier}
of the outermost class for which such a point is in a complete-class context.
For any two reflections $R_1$ and $R_2$ in the returned \tcode{vector},
if the annotation represented by $R_1$ precedes the annotation represented by $R_2$,
then $R_1$ appears before $R_2$.
If $R_1$ and $R_2$ represent annotations from the same translation unit $T$,
any element in the returned \tcode{vector} between $R_1$ and $R_2$
represents an annotation from $T$.
\begin{note}
The order in which two annotations appear is otherwise unspecified.
\end{note}
\begin{example}
\begin{codeblock}
[[=1]] void f();
[[=2, =3]] void g();
void g [[=4]] ();

static_assert(annotations_of(^^f).size() == 1);
static_assert(annotations_of(^^g).size() == 3);
static_assert([: constant_of(annotations_of(^^g)[0]) :] == 2);
static_assert(extract<int>(annotations_of(^^g)[1]) == 3);
static_assert(extract<int>(annotations_of(^^g)[2]) == 4);

struct Option { bool value; };

struct C {
[[=Option{true}]] int a;
[[=Option{false}]] int b;
};

static_assert(extract<Option>(annotations_of(^^C::a)[0]).value);
static_assert(!extract<Option>(annotations_of(^^C::b)[0]).value);

template<class T>
struct [[=42]] D { };

constexpr std::meta::info a1 = annotations_of(^^D<int>)[0];
constexpr std::meta::info a2 = annotations_of(^^D<char>)[0];
static_assert(a1 != a2);
static_assert(constant_of(a1) == constant_of(a2));

[[=1]] int x, y;
static_assert(annotations_of(^^x)[0] == annotations_of(^^y)[0]);
\end{codeblock}
\end{example}
\end{itemdescr}

\indexlibraryglobal{annotations_of_with_type}%
\begin{itemdecl}
consteval vector<info> annotations_of_with_type(info item, info type);
\end{itemdecl}

\begin{itemdescr}
\pnum
\constantwhen
\begin{itemize}
\item
\tcode{annotations_of(item)} is a constant expression and
\item
\tcode{dealias(type)} represents a type that is complete
from some point in the evaluation context.
\end{itemize}

\pnum
\returns
A \tcode{vector} containing each element \tcode{e} of \tcode{annotations_of(item)}
where
\begin{codeblock}
remove_const(type_of(e)) == remove_const(type)
\end{codeblock}
is \tcode{true}, preserving their order.
\end{itemdescr}

\rSec1[ratio]{Compile-time rational arithmetic}

\rSec2[ratio.general]{General}
Expand Down
6 changes: 4 additions & 2 deletions source/templates.tex
Original file line number Diff line number Diff line change
Expand Up @@ -3204,8 +3204,10 @@
\item In a \grammarterm{template-argument-list}\iref{temp.arg};
the pattern is a \grammarterm{template-argument}.

\item In an \grammarterm{attribute-list}\iref{dcl.attr.grammar}; the pattern is
an \grammarterm{attribute}.
\item In an \grammarterm{attribute-list}\iref{dcl.attr.grammar};
the pattern is an \grammarterm{attribute}.
In an \grammarterm{annotation-list};
the pattern is an \grammarterm{annotation}.

\item In an \grammarterm{alignment-specifier}\iref{dcl.align}; the pattern is
the \grammarterm{alignment-specifier} without the ellipsis.
Expand Down