tools: ynl-gen-c: keep the type values of a nest in spec order

Family._load_nested_set_nest() collects the type-value names of a
nest-type-value attribute into a set and Struct.set_inherited() renders
them sorted, while TypeNestTypeValue._attr_get() passes them to the
parse helper in the order the spec lists them.  For nlctrl's

  type-value: [policy-id, attr-id]

that makes the generated helper take (attr_id, policy_id) and the caller
pass (policy_id, attr_id).  Both are __u32 so nothing complains, and
every C YNL user of the nlctrl policy dump gets the two swapped.

Two attributes nesting the same attribute set with the type values in
a different order now fail set_inherited() instead of comparing equal.
No spec does that today, but better safe than sorry.

Nesting an attribute set which is also used as a sub-message gets its own
check: it used to be caught by the list-versus-set type mismatch, which
the ordering fix removes, and without it the generator emits a call
to the sub-message parser with the selector argument missing.
Again, no spec does this today.

nlctrl is the only spec affected, discovered by an LLM scan of the spec.

Reviewed-by: Nicolai Buchwitz <nb@tipi-net.de>
Link: https://patch.msgid.link/20260924223245.530132-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-09-24 15:32:45 -07:00
parent bd8e2c16f1
commit c20d84c6de

View File

@ -986,7 +986,8 @@ class Struct:
self.family = family
self.space_name = space_name
self.attr_set = family.attr_sets[space_name]
# Use list to catch comparisons with empty sets
# Stored by reference, _load_nested_set_nest() fills the list in
# after constructing us.
self._inherited = inherited if inherited is not None else []
self.inherited = []
self.fixed_header = None
@ -1038,9 +1039,11 @@ class Struct:
return self.attr_list
def set_inherited(self, new_inherited):
if self.submsg:
raise Exception("Nesting a sub-message as an attribute set not supported")
if self._inherited != new_inherited:
raise Exception("Inheriting different members not supported")
self.inherited = [c_lower(x) for x in sorted(self._inherited)]
raise Exception("Inheriting different members, or a different order, not supported")
self.inherited = [c_lower(x) for x in self._inherited]
def external_selectors(self):
sels = []
@ -1394,7 +1397,7 @@ class Family(SpecFamily):
pns_key_list.append(name)
def _load_nested_set_nest(self, spec):
inherit = set()
inherit = []
nested = spec['nested-attributes']
if nested not in self.root_sets:
if nested not in self.pure_nested_structs:
@ -1407,9 +1410,9 @@ class Family(SpecFamily):
if 'type-value' in spec:
if nested in self.root_sets:
raise Exception("Inheriting members to a space used as root not supported")
inherit.update(set(spec['type-value']))
inherit.extend(spec['type-value'])
elif spec['type'] == 'indexed-array':
inherit.add('idx')
inherit.append('idx')
self.pure_nested_structs[nested].set_inherited(inherit)
return nested