Skip to content

Commit 1c7fa5f

Browse files
committed
Permit discard identifier for layout members
1 parent 31eb618 commit 1c7fa5f

12 files changed

+219
-80
lines changed

RELEASENOTES-1.4.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@
370370
- `note 6` '`_`' can no longer be used as the name for arbitrary declarations.
371371
Instead, it is now only permitted in particular contexts, and in these
372372
contexts, '`_`' will affect the declaration in a way suitable for when the
373-
declaration is _unused_. These contexts are:
373+
declaration is _unused_ in some particular way. These contexts are:
374374
- Method-local bindings (e.g. variables and input parameters.)
375375
376376
When a method-local binding is given the name '`_`', it will not be added
@@ -384,6 +384,12 @@
384384
definition that gives the index variable a different name. This is useful
385385
when defining an object array specification which does not depend on the
386386
index.
387+
- Layout member names
388+
389+
When a layout member is given the name '`_`', that member will not be
390+
referencable within DML code, but will still affect the memory
391+
representation of the layout. This is useful to represent e.g. reserved
392+
or padding bytes.
387393
388394
Note that as a consequence of these semantics, any reference to '`_`' in code
389395
will _always_ resolve to the discard reference.

doc/1.4/language.md

+31-15
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ declaration special semantics. Currently, these contexts are:
9090
see that section for more information.
9191
* Index parameters for object arrays. See the documentation for the
9292
[`object` template](dml-builtins.html#object) for more information.
93+
* As the name of one or more members of a [layout type](#layouts).
9394

9495
</dd><dt>
9596

@@ -1480,6 +1481,7 @@ typedef struct { <em>member declarations</em> } <em>name</em>;
14801481

14811482
Layouts
14821483
</dt><dd>
1484+
<a id="layouts"/>
14831485

14841486
A layout is similar to a struct in many ways. The important
14851487
difference is that there is a well-defined mapping between a layout
@@ -1514,6 +1516,24 @@ integer members (and arrays of similar) are translated
15141516
to endian integers (or arrays of such) of similar size,
15151517
with endianness matching the layout. Layout and endian integer
15161518
members are accessed normally.
1519+
1520+
The *discard identifer* "\_" may be used as the name of any number of members
1521+
within a layout, making these *anonymous*. Anonymous layout members cannot be
1522+
referenced within DML code, but will still influence the underlying memory
1523+
representation of the layout in the same way as regular members.
1524+
This is useful to represent reserved or padding bytes, or bytes that the device
1525+
otherwise doesn't study or manipulate.
1526+
1527+
Note that when a compound initializer is given for a variable of layout type,
1528+
an initializer must still be given for each anonymous member:
1529+
```
1530+
local layout "little-endian" { uint32 x; uint32 _; uint32 y} = {1,0,2};
1531+
```
1532+
... unless designated initializers are used, in which case anonymous members
1533+
can (and must) be omitted:
1534+
```
1535+
local layout "little-endian" { uint32 x; uint32 _; uint32 y} = {.x = 1, .y = 2};
1536+
```
15171537
</dd><dt>
15181538

15191539
Bitfields
@@ -3184,27 +3204,23 @@ local (bool a, int i) = m();
31843204
In the absence of explicit initializer expressions, a default
31853205
"all zero" initializer will be applied to each declared object.
31863206

3187-
"\_" may be used as an identifier for local variables, as well as other
3188-
method-local bindings such as the method parameters, the bound identifier
3189-
in `foreach`/`#foreach`/`#select` statements, and message component parameters
3190-
of [hook-bound after statements](#hook-bound-after-statements). Any method-local
3191-
binding named "\_" *will not be added to scope.* This is useful for when
3192-
a method parameter is unused, or if you perform a method call where only a
3193-
subset of returned values are of interest:
3207+
The *discard identifier* "\_" may be used as an identifier for local variables,
3208+
as well as other method-local bindings such as the method parameters, the bound
3209+
identifier in `foreach`/`#foreach`/`#select` statements, and message component
3210+
parameters of [hook-bound after statements](#hook-bound-after-statements).
3211+
Any method-local binding named "\_" *will not be added to scope.* This is useful
3212+
for when a method parameter is unused, or if you perform a method call where
3213+
only a subset of returned values are of interest:
31943214
```
31953215
local (bool a, int _) = m();
31963216
// No conflicts since "_" is not added to scope
31973217
local (bool a, int _, float _) = returns_three_vals();
31983218
```
31993219

3200-
An alternative to this pattern is to leverage the
3201-
[discard reference](#discard-reference)
3202-
```
3203-
local bool a;
3204-
(a, _, _) = returns_three_vals();
3205-
```
3206-
... which does not require you to specify the types of the discarded values,
3207-
may require multiple lines.
3220+
An alternative to this pattern is to leverage the [discard
3221+
reference](#discard-reference) ``` local bool a; (a, _, _) =
3222+
returns_three_vals(); ``` ... which does not require you to specify the types of
3223+
the discarded values, may require multiple lines.
32083224

32093225
### Session Statements
32103226
<pre>

py/dml/codegen.py

+14-11
Original file line numberDiff line numberDiff line change
@@ -1487,17 +1487,20 @@ def eval_type(asttype, site, location, scope, extern=False, typename=None,
14871487
raise ELAYOUT(site, "extern layout not permitted,"
14881488
+ " use 'struct { }' instead")
14891489
endian, fields = info
1490-
members = {}
1491-
for (_, msite, name, type_ast) in fields:
1490+
member_decls = []
1491+
for (_, msite, ident, type_ast) in fields:
14921492
(member_struct_defs, member_type) = eval_type(
14931493
type_ast, msite, location, scope, False)
14941494
if isinstance(member_type, TFunction):
14951495
raise EFUNSTRUCT(msite)
1496-
members[name] = (msite, member_type)
1496+
member_decls.append((
1497+
msite,
1498+
ident.args[0] if ident.kind == 'variable' else None,
1499+
member_type))
14971500
struct_defs.extend(member_struct_defs)
1498-
if not members:
1501+
if not member_decls:
14991502
raise EEMPTYSTRUCT(site)
1500-
etype = TLayout(endian, members, label=typename)
1503+
etype = TLayout(endian, member_decls, label=typename)
15011504
struct_defs.append((site, etype))
15021505
elif tag == 'bitfields':
15031506
width, fields = info
@@ -1737,9 +1740,9 @@ def check_designated_initializers(site, etype, init_asts, allow_partial):
17371740
shallow_real_etype = safe_realtype_shallow(etype)
17381741
duplicates = set()
17391742
bad_fields = set()
1740-
remaining = set(shallow_real_etype.members)
1743+
remaining = set(shallow_real_etype.named_members)
17411744
for (field, init) in init_asts:
1742-
if field not in shallow_real_etype.members:
1745+
if field not in shallow_real_etype.named_members:
17431746
bad_fields.add(field)
17441747
elif field not in remaining:
17451748
duplicates.add(field)
@@ -1900,14 +1903,14 @@ def do_eval(etype, astinit):
19001903
init = tuple(do_eval(etype.base, e) for e in init_asts)
19011904
return CompoundInitializer(site, init)
19021905
elif isinstance(etype, TStruct):
1903-
if len(etype.members) != len(init_asts):
1906+
members = list(etype.members_qualified)
1907+
if len(members) != len(init_asts):
19041908
raise EDATAINIT(site, 'mismatched number of fields')
19051909
init = tuple(do_eval(mt, e)
1906-
for ((_, mt), e) in zip(etype.members_qualified,
1907-
init_asts))
1910+
for ((mn, mt), e) in zip(members, init_asts))
19081911
return CompoundInitializer(site, init)
19091912
elif isinstance(etype, TExternStruct):
1910-
if len(etype.members) != len(init_asts):
1913+
if len(etype.named_members) != len(init_asts):
19111914
raise EDATAINIT(site, 'mismatched number of fields')
19121915
init = {mn: do_eval(mt, e)
19131916
for ((mn, mt), e) in zip(etype.members_qualified,

py/dml/ctree_test.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1400,8 +1400,8 @@ def const_types(self):
14001400
types.TExternStruct({}, 'struct_t', 'struct_t'),
14011401
types.TStruct({'x': types.TBool()}, 'struct_label'),
14021402
types.TLayout(
1403-
'big-endian', {
1404-
'x': (site, types.TEndianInt(24, True, 'big-endian'))},
1403+
'big-endian', [(site, 'x',
1404+
types.TEndianInt(24, True, 'big-endian'))],
14051405
'struct_label'),
14061406
types.THook([]),
14071407
]

py/dml/dmlparse.py

+27-13
Original file line numberDiff line numberDiff line change
@@ -1482,7 +1482,13 @@ def typeof(t):
14821482
def check_struct_namecoll(member_decls):
14831483
sites_by_name = {}
14841484
for decl in member_decls:
1485-
(name, _) = decl.args
1485+
if decl.kind == 'cdecl':
1486+
(name, _) = decl.args
1487+
else:
1488+
(ident, _) = decl.args
1489+
if ident.kind == 'discard':
1490+
continue
1491+
(name,) = ident.args
14861492
if name in sites_by_name:
14871493
report(ENAMECOLL(decl.site, sites_by_name[name], name))
14881494
else:
@@ -1512,14 +1518,21 @@ def layout_decl(t):
15121518
field_names = set()
15131519
fields = []
15141520
for cdecl in t[4]:
1515-
(name, typ) = cdecl.args
1516-
if name in field_names:
1517-
while (name in field_names
1518-
or any(name == d.args[0] for d in t[4])):
1519-
name = '_' + name
1520-
cdecl = ast.cdecl(cdecl.site, name, typ)
1521+
(ident, typ) = cdecl.args
1522+
if ident.kind == 'variable':
1523+
(name,) = ident.args
1524+
if name in field_names:
1525+
while (name in field_names
1526+
or any(name == d.args[0].args[0]
1527+
for d in t[4]
1528+
if d.args[0].kind == 'variable')):
1529+
name = '_' + name
1530+
cdecl = ast.cdecl_maybe_discarded(
1531+
cdecl.site,
1532+
ast.variable(ident.site, name),
1533+
typ)
1534+
field_names.add(name)
15211535
fields.append(cdecl)
1522-
field_names.add(name)
15231536
else:
15241537
fields = t[4]
15251538
check_struct_namecoll(fields)
@@ -1536,7 +1549,7 @@ def layout(t):
15361549

15371550
@prod
15381551
def layout_decls(t):
1539-
'layout_decls : layout_decls named_cdecl SEMI'
1552+
'layout_decls : layout_decls named_cdecl_maybe_discarded SEMI'
15401553
t[0] = t[1] + (t[2],)
15411554

15421555
@prod
@@ -2757,10 +2770,11 @@ def objident_discard(t):
27572770
def discard_error(site):
27582771
report(ESYNTAX(site,
27592772
"_",
2760-
"'_' can only be used as an expression or as an unused "
2761-
+ "identifier for an index variable of an object array or "
2762-
+ "a method-local binding (e.g. local variable or method "
2763-
+ "parameter)"))
2773+
"can't use the name '_' (the discard identifier) in this "
2774+
+ "context. See the description of 'Identifiers' within "
2775+
+ "the Lexical Structure section of the DML 1.4 Reference "
2776+
+ "Manual for an overview of when '_' may be used as a "
2777+
+ "name."))
27642778

27652779
@prod
27662780
def ident_or_discard_ident(t):

py/dml/serialize.py

+33-8
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,25 @@ def prepare_array_de_serialization(site, t):
9999
dimsizes_expr = expr.mkLit(site, dimsizes_lit, TPtr(TInt(32, False)))
100100
return (base, dims, sizeof_base, dimsizes_expr)
101101

102+
def mkSubRefLit(site, expr, sub, typ, op):
103+
real_etype = safe_realtype_shallow(expr.ctype())
104+
105+
if isinstance(real_etype, TPtr):
106+
if op == '.':
107+
raise ENOSTRUCT(site, expr)
108+
basetype = real_etype.base
109+
real_basetype = safe_realtype(basetype)
110+
else:
111+
if op == '->':
112+
raise ENOPTR(site, expr)
113+
real_basetype = safe_realtype(etype)
114+
115+
real_basetype = real_basetype.resolve()
116+
117+
return ctree.StructMember(site, expr, sub,
118+
conv_const(real_basetype.const, typ), op)
119+
120+
102121
# This works on the assumption that args do not need to be hard-cast
103122
# to fit the actual fun signature
104123
def apply_c_fun(site, fun, args, rettype):
@@ -354,7 +373,7 @@ def map_dmltype_to_attrtype(site, dmltype):
354373
return 'f'
355374
if isinstance(real_type, TStruct):
356375
return '[%s]' % "".join([map_dmltype_to_attrtype(site, mt)
357-
for mt in real_type.members.values()])
376+
for (_, mt) in real_type.members])
358377
if isinstance(real_type, TArray):
359378
assert real_type.size.constant
360379
arr_attr_type = map_dmltype_to_attrtype(site, real_type.base)
@@ -375,7 +394,7 @@ def mark_for_serialization(site, dmltype):
375394
'''
376395
real_type = safe_realtype(dmltype)
377396
if isinstance(real_type, TStruct):
378-
for mt in real_type.members.values():
397+
for (_, mt) in real_type.members:
379398
mark_for_serialization(site, mt)
380399
elif isinstance(real_type, TArray):
381400
# Can only serialize constant-size arrays
@@ -496,9 +515,12 @@ def generate_serialize(real_type):
496515
in_arg_decl.toc()
497516
out_arg_decl.toc()
498517
if isinstance(real_type, TStruct):
499-
sources = ((ctree.mkSubRef(site, in_arg, name, "->"),
500-
safe_realtype(typ))
501-
for (name, typ) in real_type.members.items())
518+
sources = (
519+
(mkSubRefLit(
520+
site, in_arg, name or TStruct.anon_member_cident(i),
521+
typ, "->"),
522+
safe_realtype(typ))
523+
for (i, (name, typ)) in enumerate(real_type.members))
502524
serialize_sources_to_list(site, sources, out_arg)
503525
elif isinstance(real_type, TVector):
504526
raise ICE(site, "TODO: serialize vector")
@@ -619,9 +641,12 @@ def error_out(exc, msg):
619641
else ctree.mkCast(site, tmp_out_ref, TPtr(void)))
620642
cleanup.append(ctree.mkDelete(site, cleanup_ref))
621643
tmp_out_decl.toc()
622-
targets = tuple((ctree.mkSubRef(site, tmp_out_ref, name, "->"),
623-
conv_const(real_type.const, safe_realtype(typ)))
624-
for (name, typ) in real_type.members.items())
644+
targets = tuple(
645+
(mkSubRefLit(
646+
site, tmp_out_ref,
647+
name or TStruct.anon_member_cident(i), typ, "->"),
648+
conv_const(real_type.const, safe_realtype(typ)))
649+
for (i, (name, typ)) in enumerate(real_type.members))
625650
def error_out_at_index(_i, exc, msg):
626651
return error_out(exc, msg)
627652
deserialize_list_to_targets(site, in_arg, targets,

py/dml/structure.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ def type_deps(t, include_structs, expanded_typedefs):
325325
deps = []
326326
if include_structs:
327327
deps.append(t.label)
328-
for (mn, mt) in t.members.items():
328+
for (_, mt) in t.members:
329329
deps.extend(type_deps(mt, True, expanded_typedefs))
330330
return deps
331331
elif isinstance(t, TArray):

0 commit comments

Comments
 (0)