[libc] Enforce standard identifier validation in hdrgen (#198971)

Implemented validation for standard identifiers in the hdrgen tool to
catch typos and unknown standards. Aggregated standards from all entity
types (macros, types, etc.) for validation, addressing a TODO in
header.py.

Added llvm_libc_ext, stdc_ext, and llvm_libc_stdfix_ext to the canonical
identifiers. Standardised invalid or inconsistently formatted standards
in several YAML files.

This enforces the correctness of any provided standards field but does
not yet require that every entity has one (many entities still inherit
standards from the header level).

Assisted-by: Automated tooling, human reviewed.
This commit is contained in:
Jeff Bailey 2026-05-22 06:48:44 +00:00 committed by GitHub
parent 7d65f8e1bb
commit c20020ea73
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 70 additions and 33 deletions

View file

@ -153,7 +153,7 @@ functions:
arguments:
- type: float
- name: atanhf16
standatds:
standards:
- stdc
return_type: _Float16
arguments:
@ -765,7 +765,7 @@ functions:
guard: LIBC_TYPES_HAS_FLOAT128
- name: faddl
standards:
- faddl
- stdc
return_type: float
arguments:
- type: long double
@ -2203,13 +2203,15 @@ functions:
- type: float
- type: float
- name: powi
standards: llvm_libc_ext
standards:
- llvm_libc_ext
return_type: double
arguments:
- type: double
- type: int
- name: powif
standards: llvm_libc_ext
standards:
- llvm_libc_ext
return_type: float
arguments:
- type: float

View file

@ -218,7 +218,8 @@ functions:
arguments:
- type: void *
- name: pthread_getattr_np
standards: gnu
standards:
- gnu
return_type: int
arguments:
- type: pthread_t

View file

@ -21,13 +21,15 @@ functions:
arguments:
- type: size_t
- name: hcreate_r
standards: gnu
standards:
- gnu
return_type: int
arguments:
- type: size_t
- type: struct hsearch_data *
- name: hdestroy
standards: gnu
standards:
- gnu
return_type: void
arguments: []
- name: hdestroy_r
@ -44,7 +46,8 @@ functions:
- type: ENTRY
- type: ACTION
- name: hsearch_r
standards: gnu
standards:
- gnu
return_type: int
arguments:
- type: ENTRY
@ -116,14 +119,16 @@ functions:
- type: const posix_tnode *
- type: __action_fn_t
- name: twalk_r
standards: gnu
standards:
- gnu
return_type: void
arguments:
- type: const posix_tnode *
- type: __action_closure_fn_t
- type: void *
- name: tdestroy
standards: gnu
standards:
- gnu
return_type: void
arguments:
- type: posix_tnode *

View file

@ -435,7 +435,8 @@ functions:
- type: unsigned accum
guard: LIBC_COMPILER_HAS_FIXED_POINT
- name: sqrtulk
standards: llvm_libc_stdfix_ext
standards:
- llvm_libc_stdfix_ext
return_type: unsigned long accum
arguments:
- type: unsigned long accum

View file

@ -93,7 +93,8 @@ functions:
- type: size_t
- type: int
- name: munlock
standards: POSIX
standards:
- posix
return_type: int
arguments:
- type: void *

View file

@ -1,6 +1,7 @@
header: sys/syscall.h
header_template: syscall.h.def
standards: Linux
standards:
- linux
macros: []
types: []
enums: []

View file

@ -52,6 +52,9 @@ LIBRARY_DESCRIPTIONS = {
"linux": "Linux",
"uefi": "UEFI",
"svid": "SVID",
"stdc_ext": "Standard C Extension",
"llvm_libc_ext": "LLVM libc Extension",
"llvm_libc_stdfix_ext": "LLVM libc stdfix Extension",
}
HEADER_TEMPLATE = """\
@ -156,11 +159,27 @@ class HeaderFile:
)
def all_standards(self):
# FIXME: Only functions have the "standard" field, but all the entity
# types should have one too.
return set(self.standards).union(
*(filter(None, (f.standards for f in self.functions)))
)
standards = set(self.standards)
for collection in [
self.macros,
self.types,
self.enumerations,
self.objects,
self.functions,
]:
for item in collection:
if item.standards:
standards.update(item.standards)
descriptions = LIBRARY_DESCRIPTIONS | self.extra_standards
for standard in standards:
if standard not in descriptions:
# Provide a helpful error message with acceptable values.
expected = ", ".join(sorted(descriptions.keys()))
raise ValueError(
f"Standard '{standard}' is not one of the canonical "
f"identifiers: {expected}"
)
return standards
def includes(self):
return (
@ -190,6 +209,8 @@ class HeaderFile:
return "_LLVM_LIBC_" + "_".join(words)
def library_description(self):
# This also validates all standards.
standards = self.all_standards()
descriptions = LIBRARY_DESCRIPTIONS | self.extra_standards
# If the header itself is in standard C, just call it that.
if "stdc" in self.standards:
@ -198,7 +219,6 @@ class HeaderFile:
if "posix" in self.standards:
return descriptions["posix"]
# Otherwise, consider the standards for each symbol as well.
standards = self.all_standards()
# Otherwise, it's described by all those that apply, but ignoring
# "stdc" and "posix" since this is not a "stdc" or "posix" header.
return " / ".join(
@ -340,6 +360,9 @@ class HeaderFile:
"includes": sorted(str(file) for file in {COMMON_HEADER} | self.includes()),
}
def validate(self):
self.all_standards()
def emit_guard(self, content, current_guard, new_guard):
if current_guard != new_guard:
if current_guard is not None:

View file

@ -113,6 +113,7 @@ def main():
return 2
header.merge(merge_from_header)
header.validate()
assert header.name, f"`header: name.h` line is required in {yaml_file}"
return header

View file

@ -23,6 +23,7 @@ class Symbol:
def __init__(self, name):
assert name
self.name = name
self.standards = []
def __eq__(self, other):
return self.name == other.name

View file

@ -41,13 +41,13 @@ def yaml_to_classes(yaml_data, header_class, entry_points=None):
header.merge_yaml_files = yaml_data.get("merge_yaml_files", [])
for macro_data in yaml_data.get("macros", []):
header.add_macro(
Macro(
macro_data["macro_name"],
macro_data.get("macro_value"),
macro_data.get("macro_header"),
)
macro = Macro(
macro_data["macro_name"],
macro_data.get("macro_value"),
macro_data.get("macro_header"),
)
macro.standards = macro_data.get("standards", [])
header.add_macro(macro)
types = yaml_data.get("types", [])
sorted_types = sorted(types, key=lambda x: x["type_name"])
@ -65,12 +65,14 @@ def yaml_to_classes(yaml_data, header_class, entry_points=None):
f"macro_header '{type_guard}' was found in this file."
)
header.add_type(Type(type_name, type_guard))
typ = Type(type_name, type_guard)
typ.standards = type_data.get("standards", [])
header.add_type(typ)
for enum_data in yaml_data.get("enums", []):
header.add_enumeration(
Enumeration(enum_data["name"], enum_data.get("value", None))
)
enum = Enumeration(enum_data["name"], enum_data.get("value", None))
enum.standards = enum_data.get("standards", [])
header.add_enumeration(enum)
functions = yaml_data.get("functions", [])
if entry_points:
@ -122,10 +124,9 @@ def yaml_to_classes(yaml_data, header_class, entry_points=None):
objects = yaml_data.get("objects", [])
sorted_objects = sorted(objects, key=lambda x: x["object_name"])
for object_data in sorted_objects:
header.add_object(
Object(object_data["object_name"], object_data["object_type"])
)
obj = Object(object_data["object_name"], object_data["object_type"])
obj.standards = object_data.get("standards", [])
header.add_object(obj)
return header