pub struct Decl {Show 18 fields
pub name: Option<Symbol>,
pub ty: TypeId,
pub kind: DeclKind,
pub linkage: Linkage,
pub duration: StorageDuration,
pub state: Definition,
pub alignment: Option<u32>,
pub constant: bool,
pub retained: bool,
pub asm_label: Option<StrId>,
pub alias: Option<StrId>,
pub inline: Emission,
pub gnu_inline: bool,
pub init: Option<InitList>,
pub noreturn: bool,
pub visibility: Option<Visibility>,
pub params: DeclList,
pub body: Option<StmtId>,
}Expand description
An object or a function, as it was declared.
Fields§
§name: Option<Symbol>The name, absent for a compound literal and for a parameter that was not given one.
ty: TypeIdThe type, after the adjustments a declaration performs: an array parameter has already become a pointer, and a function parameter a function pointer.
kind: DeclKindWhether it is an object or a function.
linkage: LinkageWhether the name is shared with other translation units, and how.
duration: StorageDurationHow long the object lives.
state: DefinitionHow much of a definition this declaration is.
alignment: Option<u32>The alignment alignas asked for, absent when the type’s own alignment stands.
constant: boolWhether constexpr was written, which makes the object a named constant.
C23 6.6p8 puts a named constant of an integer type among the things an integer constant
expression may be built out of, and a member of one of a structure or union type with
it. That is the whole reason the keyword exists and it is why this is a fact about the
declaration rather than something a reader could work out: a const object with a
constant initializer is not one of them, so const int n = 1; int a[n]; is a variable
length array and the same two lines with constexpr are an array of one.
retained: boolWhether an attribute asks for this to exist where nothing in the file refers to it.
used, retain, constructor, destructor and alias each say that something reaches
the definition from where the compiler cannot see it, which is the only reason a program
ever writes one of them. Nothing else in the tree says that, and a static function
nothing refers to is not emitted, so this is how a program keeps one that has to be.
asm_label: Option<StrId>The symbol this name stands for in the object file, when a declaration of it wrote an assembler name of its own.
extern int f (int) __asm__ ("g"); says that f here is the symbol g, which is how
the C library redirects a name: open under _FILE_OFFSET_BITS=64 is declared this way
and reaches open64, and every _FORTIFY_SOURCE wrapper is the same trick. It is a fact
about the name rather than about one declaration of it, so it is kept where the
declarations of a name are merged, and the first one written is the one that stands.
alias: Option<StrId>The symbol this name is a second spelling of, when __attribute__((alias("target"))) was
written on a declaration of it.
A declaration with one of these defines the name rather than declaring it: nothing is
emitted for the declaration itself and the object file gets a second symbol pointing at
whatever the string names. extern int b __attribute__((alias("a"))); is how a program
gives a the name b, and weak, alias beside it is the form glibc writes so that a
program may define the name itself instead.
The string is the symbol the linker sees rather than an identifier this resolves, which
is why it is a StrId and not a Symbol. Whether anything
defines it is settled where the whole translation unit is known.
inline: EmissionWhether a definition of this name here is emitted, which inline is the only thing that
changes.
C 6.7.4p7: where every file-scope declaration of a function writes inline and none of
them writes extern, the definition in this unit is an inline definition, no external
definition is emitted for it, and a call goes to the definition some other unit holds.
One declaration without inline, or one with extern, makes the whole thing an external
definition again, which is why this is a fact about the name and is settled where the
declarations of a name are merged.
The two readings of inline swap over under Self::gnu_inline, where it is the
definition alone that decides and extern inline is the one that is not emitted.
gnu_inline: boolWhether this name is under GNU’s reading of inline rather than C’s.
__attribute__((__gnu_inline__)) asks for it by name, and the C89 dialects are under it
throughout, which is what __GNUC_GNU_INLINE__ tells a header. It is kept because the two
readings fold differently over the declarations of a name, and because gcc refuses a name
whose declarations disagree about which one they are under.
init: Option<InitList>The initializer, flattened, absent when there was none. An empty list is = {}, which
C23 added and which zero-initializes, and is not the same as no initializer at all.
noreturn: boolWhether control does not come back from a call to this function.
_Noreturn, __attribute__((noreturn)) and [[noreturn]] all say it and all land here.
What a caller does with it is put an unreachable after the call, so a program that tests
its allocation with if (!p) abort(); stops having a path where the block after the test is
reached carrying a null pointer. Nothing else in the compiler can work that out, because
what abort does belongs to abort.
A fact about the name rather than about one declaration of it, so one declaration saying it
is enough and the merge keeps it. That is the same rule Self::retained is under and it
is there for the same reason: the usual place to write it is a header, and the definition in
the file below writes nothing.
visibility: Option<Visibility>How far outside a shared library the name reaches, when a declaration of it said, and nothing when none did.
__attribute__((visibility("hidden"))) and the other three strings it takes. What is kept
here is only what was written, because the other way a name gets a visibility is
-fvisibility= and that is a fact about the compilation rather than about the declaration.
The two meet where the IR is built, which is also the only place that has both.
A fact about the name rather than about one declaration of it, like Self::asm_label,
and merged the way that one is: the first declaration to say something stands. gcc warns
and keeps the first when a later one disagrees, since the calls above it have already been
compiled against the answer it gave.
params: DeclListThe parameters of a function definition, in order, and empty for everything else.
A parameter is an object with automatic storage like any other, and the body refers to one the same way it refers to a local. What is different is that nothing in the body declares it, so without this there is no way to ask which objects a definition takes and in what order, which is the first question the walk to the IR has: the entry block’s parameters are these, in this order.
A declaration that is not a definition has none of these even when it was written with a
prototype, because int f(int a); declares no object called a. The types are in the
function type, which is where a call reads them.
body: Option<StmtId>The body of a function definition.