Skip to content

Commit 1b99009

Browse files
committed
Permit discard identifier for layout members
1 parent 2f72728 commit 1b99009

File tree

13 files changed

+237
-93
lines changed

13 files changed

+237
-93
lines changed

RELEASENOTES-1.4.md

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@
363363
- `release 7 7122`
364364
- `release 6 6437`
365365
- `release 7 7123`
366-
- `note 6` Added the _discard reference_ '`_`' — a non-value expression
366+
- `note 6` Added the _discard reference_ "`_`" — a non-value expression
367367
which may be used as an assign target in order to explictly discard the result
368368
of an evaluated expression or return value of a method call (fixes
369369
SIMICS-21584.)
@@ -374,23 +374,30 @@
374374
_ = throwing_method();
375375
(_, x, _) = method_with_multiple_return_values();
376376
```
377-
- `note 6` '`_`' can no longer be used as the name for arbitrary declarations.
377+
- `note 6` "`_`" can no longer be used as the name for arbitrary declarations.
378378
Instead, it is now only permitted in particular contexts, and in these
379-
contexts, '`_`' will affect the declaration in a way suitable for when the
380-
declaration is _unused_. These contexts are:
379+
contexts, `_` will affect the declaration in a way suitable for when the
380+
declaration is _unused_ in some particular way. This feature is referred to
381+
as the _discard identifier_, and the contexts where it may be used are:
381382
- Method-local bindings (e.g. variables and input parameters.)
382383
383-
When a method-local binding is given the name '`_`', it will not be added
384+
When a method-local binding is given the name "`_`", it will not be added
384385
to scope. This is useful for e.g. unused method parameters.
385386
- Index variables for object arrays
386387
387-
When '`_`' is specified as an index variable, a parameter will not be
388+
When `_` is specified as an index variable, a parameter will not be
388389
created for it, meaning it cannot conflict with any other definition, and
389390
it cannot be referenced in the code in order to get the value of the index
390391
in question. It also isn't considered to conflict with any other
391392
definition that gives the index variable a different name. This is useful
392393
when defining an object array specification which does not depend on the
393394
index.
395+
- Layout member names
394396
395-
Note that as a consequence of these semantics, any reference to '`_`' in code
397+
When a layout member is given the name "`_`", that member will not be
398+
referencable within DML code, but will still affect the memory
399+
representation of the layout. This is useful to represent e.g. reserved
400+
or padding bytes.
401+
402+
Note that as a consequence of these semantics, any reference to `_` in code
396403
will _always_ resolve to the discard reference.

doc/1.4/language.md

Lines changed: 30 additions & 10 deletions
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

@@ -1537,6 +1538,7 @@ typedef struct { <em>member declarations</em> } <em>name</em>;
15371538

15381539
Layouts
15391540
</dt><dd>
1541+
<a id="layouts"/>
15401542

15411543
A layout is similar to a struct in many ways. The important
15421544
difference is that there is a well-defined mapping between a layout
@@ -1571,6 +1573,24 @@ integer members (and arrays of similar) are translated
15711573
to endian integers (or arrays of such) of similar size,
15721574
with endianness matching the layout. Layout and endian integer
15731575
members are accessed normally.
1576+
1577+
The *discard identifer* `_` may be used as the name of any number of members
1578+
within a layout, making these *anonymous*. Anonymous layout members cannot be
1579+
referenced within DML code, but will still influence the underlying memory
1580+
representation of the layout in the same way as regular members.
1581+
This is useful to represent reserved or padding bytes, or bytes that the device
1582+
otherwise doesn't study or manipulate.
1583+
1584+
Note that when a compound initializer is given for a variable of layout type,
1585+
an initializer must still be given for each anonymous member:
1586+
```
1587+
local layout "little-endian" { uint32 x; uint32 _; uint32 y} = {1,0,2};
1588+
```
1589+
... unless designated initializers are used, in which case anonymous members
1590+
can (and must) be omitted:
1591+
```
1592+
local layout "little-endian" { uint32 x; uint32 _; uint32 y} = {.x = 1, .y = 2};
1593+
```
15741594
</dd><dt>
15751595

15761596
Bitfields
@@ -3231,27 +3251,27 @@ local (bool a, int i) = m();
32313251
In the absence of explicit initializer expressions, a default
32323252
"all zero" initializer will be applied to each declared object.
32333253

3234-
"\_" may be used as an identifier for local variables, as well as other
3235-
method-local bindings such as the method parameters, the bound identifier
3236-
in `foreach`/`#foreach`/`#select` statements, and message component parameters
3237-
of [hook-bound after statements](#hook-bound-after-statements). Any method-local
3238-
binding named "\_" *will not be added to scope.* This is useful for when
3239-
a method parameter is unused, or if you perform a method call where only a
3240-
subset of returned values are of interest:
3254+
The *discard identifier* `_` may be used as an identifier for local variables,
3255+
as well as other method-local bindings such as the method parameters, the bound
3256+
identifier in `foreach`/`#foreach`/`#select` statements, and message component
3257+
parameters of [hook-bound after statements](#hook-bound-after-statements).
3258+
Any method-local binding named "`_`" *will not be added to scope*. This is
3259+
useful for when a method parameter is unused, or if you perform a method call
3260+
where only a subset of returned values are of interest:
32413261
```
32423262
local (bool a, int _) = m();
32433263
// No conflicts since "_" is not added to scope
32443264
local (bool a, int _, float _) = returns_three_vals();
32453265
```
32463266

3247-
An alternative to this pattern is to leverage the
3248-
[discard reference](#discard-reference)
3267+
An alternative to this pattern is to leverage the [discard
3268+
reference](#discard-reference):
32493269
```
32503270
local bool a;
32513271
(a, _, _) = returns_three_vals();
32523272
```
32533273
... which does not require you to specify the types of the discarded values,
3254-
may require multiple lines.
3274+
but may result in more lines of code.
32553275

32563276
### Session Statements
32573277
<pre>

lib/1.4/dml-builtins.dml

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -512,18 +512,18 @@ which cannot be overridden:
512512
`register regs[i < 4][j < 11];` defines two index parameters, `i` and `j`.
513513
In this case, the `indices` parameter is `[i, j]`.
514514

515-
"\_" is also allowed as the name of one or more index parameters. If
516-
used, then it *won't* result in corresponding parameter definitions for those
517-
indices; in other words, if an index variable is named "\_", then it can't be
518-
accessed by that name (however, the `indices` parameter is unaffected and
519-
could be used instead.) It's also allowed to have an object array
520-
specification name an index parameter "\_" even if some other object array
521-
specification uses a different name (which *will* be given a corresponding
522-
parameter definition.) This is useful if a specific object array
523-
specification doesn't need to make use of some particular indices; or if the
524-
specification needs to support that the naming of the index variable may vary
525-
depending on what other specifications of the same object array are present
526-
in the device model.
515+
The discard identifier `_` is also allowed as the name of one or more index
516+
parameters. If used, then it *won't* result in corresponding parameter
517+
definitions for those indices; in other words, if an index variable is named
518+
"`_`", then it can't be accessed by that name (however, the `indices`
519+
parameter is unaffected and could be used instead.) It's also allowed to have
520+
an object array specification name an index parameter "`_`" even if some
521+
other object array specification uses a different name (which *will* be given
522+
a corresponding parameter definition.) This is useful if a specific object
523+
array specification doesn't need to make use of some particular indices; or
524+
if the specification needs to support that the naming of the index variable
525+
may vary depending on what other specifications of the same object array are
526+
present in the device model.
527527

528528
The `object` template provides the non-overridable method `cancel_after()`,
529529
which cancels all pending events posted using `after` which are associated with

py/dml/codegen.py

Lines changed: 14 additions & 11 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,7 +1483,13 @@ def typeof(t):
14831483
def check_struct_namecoll(member_decls):
14841484
sites_by_name = {}
14851485
for decl in member_decls:
1486-
(name, _) = decl.args
1486+
if decl.kind == 'cdecl':
1487+
(name, _) = decl.args
1488+
else:
1489+
(ident, _) = decl.args
1490+
if ident.kind == 'discard':
1491+
continue
1492+
(name,) = ident.args
14871493
if name in sites_by_name:
14881494
report(ENAMECOLL(decl.site, sites_by_name[name], name))
14891495
else:
@@ -1513,14 +1519,21 @@ def layout_decl(t):
15131519
field_names = set()
15141520
fields = []
15151521
for cdecl in t[4]:
1516-
(name, typ) = cdecl.args
1517-
if name in field_names:
1518-
while (name in field_names
1519-
or any(name == d.args[0] for d in t[4])):
1520-
name = '_' + name
1521-
cdecl = ast.cdecl(cdecl.site, name, typ)
1522+
(ident, typ) = cdecl.args
1523+
if ident.kind == 'variable':
1524+
(name,) = ident.args
1525+
if name in field_names:
1526+
while (name in field_names
1527+
or any(name == d.args[0].args[0]
1528+
for d in t[4]
1529+
if d.args[0].kind == 'variable')):
1530+
name = '_' + name
1531+
cdecl = ast.cdecl_maybe_discarded(
1532+
cdecl.site,
1533+
ast.variable(ident.site, name),
1534+
typ)
1535+
field_names.add(name)
15221536
fields.append(cdecl)
1523-
field_names.add(name)
15241537
else:
15251538
fields = t[4]
15261539
check_struct_namecoll(fields)
@@ -1537,7 +1550,7 @@ def layout(t):
15371550

15381551
@prod
15391552
def layout_decls(t):
1540-
'layout_decls : layout_decls named_cdecl SEMI'
1553+
'layout_decls : layout_decls named_cdecl_maybe_discarded SEMI'
15411554
t[0] = t[1] + (t[2],)
15421555

15431556
@prod
@@ -2760,10 +2773,11 @@ def objident_discard(t):
27602773
def discard_error(site):
27612774
report(ESYNTAX(site,
27622775
"_",
2763-
"'_' can only be used as an expression or as an unused "
2764-
+ "identifier for an index variable of an object array or "
2765-
+ "a method-local binding (e.g. local variable or method "
2766-
+ "parameter)"))
2776+
"can't use the name '_' (the discard identifier) in this "
2777+
+ "context. See the description of 'Identifiers' within "
2778+
+ "the Lexical Structure section of the DML 1.4 Reference "
2779+
+ "Manual for an overview of when '_' may be used as a "
2780+
+ "name."))
27672781

27682782
@prod
27692783
def ident_or_discard_ident(t):

py/dml/serialize.py

Lines changed: 33 additions & 8 deletions
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

Lines changed: 1 addition & 1 deletion
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)