Valid-initdeclor
Validate an initializer declarator.
- Signature
(valid-initdeclor initdeclor type storspecs table ienv)
→
(mv erp new-initdeclor return-types new-table)
- Arguments
- initdeclor — Guard (initdeclorp initdeclor).
- type — Guard (typep type).
- storspecs — Guard (stor-spec-listp storspecs).
- table — Guard (valid-tablep table).
- ienv — Guard (ienvp ienv).
- Returns
- new-initdeclor — Type (initdeclorp new-initdeclor).
- return-types — Type (type-setp return-types).
- new-table — Type (valid-tablep new-table).
Initializer declarators [C:6.7/1]
appear in declarations, after declaration specifiers.
The latter determine a type, passed as input,
which an initializer declarator may refine.
We also pass as input the storage class specifiers
extracted from the declaration specifiers.
We validate the declarator,
which returns the identifier and the possibly refined type.
Here the fundef-params-p flag is always nil,
because we are not in a function definition.
Then we validate the storage class specifiers,
which together with the identifier and final type
determine whether we are declaring a typedef,
and what the linkage and lifetime are.
If the typedef flag is t,
there must be no initializer,
because we are not declaring an object.
In this case, we add the typedef to the validation table.
The same typedef is allowed in the same scope [C:6.7/3],
under certain conditions that are inexpressible
in our current approximate type system,
but since our validation tables currently carry limited information
about typedef names (i.e. just that they are typedefs),
we allow the identifier to be present, as a typedef,
in the same (i.e. current) scope,
to avoid rejecting valid code.
If the typedef flag is nil,
the identifier may denote a function or an object.
The initializer may be present only if
the type is not that of a function,
because initializers only apply to objects,
and the type is not void,
because the type must be complete [C:6.7.9/3].
We validate the initializer if present;
we pass the type of the identifier,
so the initializer is checked against that.
We calculate the definition status for the declaration.
If we are declaring a function, the status is undefined,
because we do not have a function body here.
Otherwise, we are declaring an object.
If we are in a block scope,
the status is undefined if the object has external linkage
(because it refers to some external entity from the block),
while it is defined otherwise
(because the declaration allocates storage for the object).
If we are in a file scope, we follow [C:6.9.2/1] and [C:6.9.2/2]:
an initializer makes the status defined;
otherwise, the presence of extern makes the status undefined
and its absence makes the status defined.
This is slightly different from what [C:6.9.2/2] says,
which mentions no storage class specifier or static,
but it seems clear that this neglects to consider _Thread_local;
in fact, the wording in the newly released C23 standard is clearer.
We look up the identifier in the validation table.
If no information is found in the validation table,
we add the identifier to the table.
If the current declaration has no linkage,
or if the information in the table has no linkage
(which includes the case of the information in the table
being for a typedef or an enumeration constant),
the two must denote different entities,
and thus we ensure that the one in the table
is not in the same (i.e. current) scope [C:6.2.1/4] [C:6.7/3];
if the checks pass, we add the identifier to the table.
If instead both have internal or external linkage,
they must refer to the same entity,
and so we ensure that the types are the same.
We also need to check that the linkages are the same,
in which case we still add the identifier to the table,
to record that it is also declared in the current scope;
in this case, it is not clear whether we should suitable combine
the old and new definition statuses,
but for now we just use the newest.
It is an error if the current linkage is internal
while the one in the table is external.
The situation where the current one is external
while the one in the table is internal cannot happen,
because as defined in valid-stor-spec-list,
the current one would ``inherit''
the internal linkage from the previous one.
For now we ignore the optional assembler name specifier,
as well as any attribute specifiers.