Skip to content

Commit 8e2885b

Browse files
committed
Support '_' as the 'discard identifier' for certain declarations
Allowed for method-local bindings and index variables. Method-local bindings named '_' are not added to scope Index variables named '_' do not get a parameter created for them
1 parent 937f891 commit 8e2885b

27 files changed

+997
-417
lines changed

RELEASENOTES-1.4.md

+20
Original file line numberDiff line numberDiff line change
@@ -367,3 +367,23 @@
367367
_ = throwing_method();
368368
(_, x, _) = method_with_multiple_return_values();
369369
```
370+
- '`_`' can no longer be used as the name for arbitrary declarations. Instead,
371+
it is now only permitted in particular contexts, and in these contexts, '`_`'
372+
will affect the declaration in a way suitable for when the declaration is
373+
_unused_. These contexts are:
374+
- Method-local bindings (e.g. variables and input parameters.)
375+
376+
When a method-local binding is given the name '`_`', it will not be added
377+
to scope. This is useful for e.g. unused method parameters.
378+
- Index variables for object arrays
379+
380+
When '`_`' is specified as an index variable, a parameter will not be
381+
created for it, meaning it cannot conflict with any other definition, and
382+
it cannot be referenced in the code in order to get the value of the index
383+
in question. It also isn't considered to conflict with any other
384+
definition that gives the index variable a different name. This is useful
385+
when defining an object array specification which does not depend on the
386+
index.
387+
388+
Note that as a consequence of these semantics, any reference to '`_`' in code
389+
will _always_ resolve to the discard reference.

doc/1.4/language.md

+38-7
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,18 @@ Identifiers
7878

7979
Identifiers in DML are defined as in C; an identifier may begin
8080
with a letter or underscore, followed by any number of letters,
81-
numbers, or underscores. Identifiers that begin with an underscore (`_`)
82-
are reserved by the DML language and standard library and should not
83-
be used.
81+
numbers, or underscores.
82+
83+
<a id="discard-identifier"/>
84+
Identifiers that begin with an underscore (`_`) are reserved by the DML language
85+
and standard library and should not be used, with the exception of the single
86+
underscore `_`; this is considered to be the *discard identifier*, and is only
87+
permitted as the name of a declaration in specific contexts, where it gives the
88+
declaration special semantics. Currently, these contexts are:
89+
* Method-local bindings, e.g. [local variables](#local-statements) &mdash;
90+
see that section for more information.
91+
* Index parameters for object arrays. See the documentation for the
92+
[`object` template](dml-builtins.html#object) for more information.
8493

8594
</dd><dt>
8695

@@ -3175,6 +3184,28 @@ local (bool a, int i) = m();
31753184
In the absence of explicit initializer expressions, a default
31763185
"all zero" initializer will be applied to each declared object.
31773186

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:
3194+
```
3195+
local (bool a, int _) = m();
3196+
// No conflicts since "_" is not added to scope
3197+
local (bool a, int _, float _) = returns_three_vals();
3198+
```
3199+
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.
3208+
31783209
### Session Statements
31793210
<pre>
31803211
session <em>type</em> <em>identifier</em> [= <em>initializer</em>];
@@ -4064,6 +4095,7 @@ independent method callback(int i, void *aux) {
40644095
```
40654096

40664097
### The Discard Reference (`_`)
4098+
<a id="discard-reference"/>
40674099
```
40684100
_
40694101
```
@@ -4072,10 +4104,6 @@ The discard reference *`_`* is an expression without any run-time representation
40724104
that may be used as the target of an assignment in order to explicitly discard
40734105
the result of an evaluated expression or return value of a method call.
40744106

4075-
For backwards compatibility reasons, `_` is not a keyword, but instead behaves
4076-
more closely as a global identifier. What this means is that declared
4077-
identifiers (e.g. local variables) are allowed to shadow it by being named `_`.
4078-
40794107
Example usage:
40804108
```
40814109
// Evaluate an expression and explicitly discard its result.
@@ -4089,6 +4117,9 @@ _ = throwing_method();
40894117
(_, x, _) = method_with_multiple_return_values();
40904118
```
40914119

4120+
The discard reference is related to the [discard
4121+
identifier](#discard-identifier), and have some use-cases in common.
4122+
40924123
### New Expressions
40934124

40944125
<pre>

lib/1.4/dml-builtins.dml

+14-1
Original file line numberDiff line numberDiff line change
@@ -498,12 +498,25 @@ which cannot be overridden:
498498
single field inside a register array, the value is the empty list.
499499

500500
* Each array has an *individual index parameter*, to make it possible
501-
to refer to both inner and outer indexes when arrays are nested
501+
to refer to both inner and outer indices when arrays are nested
502502
(cf. the `indices` parameter, above). The parameter name is
503503
specified in the array declaration; for instance, the declaration
504504
`register regs[i < 4][j < 11];` defines two index parameters, `i` and `j`.
505505
In this case, the `indices` parameter is `[i, j]`.
506506

507+
"\_" is also allowed as the name of one or more index parameters. If
508+
used, then it *won't* result in corresponding parameter definitions for those
509+
indices; in other words, if an index variable is named "\_", then it can't be
510+
accessed by that name (however, the `indices` parameter is unaffected and
511+
could be used instead.) It's also allowed to have an object array
512+
specification name an index parameter "\_" even if some other object array
513+
specification uses a different name (which *will* be given a corresponding
514+
parameter definition.) This is useful if a specific object array
515+
specification doesn't need to make use of some particular indices; or if the
516+
specification needs to support that the naming of the index variable may vary
517+
depending on what other specifications of the same object array are present
518+
in the device model.
519+
507520
The `object` template provides the non-overridable method `cancel_after()`,
508521
which cancels all pending events posted using `after` which are associated with
509522
the object (any events associated with subobjects are unaffected).

py/dml/ast.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,15 @@ def __setstate__(self, data):
4848
'case_dml12',
4949
'cast',
5050
'cdecl',
51+
'cdecl_maybe_discarded',
5152
'compound',
5253
'conditional',
5354
'constant',
5455
'continue',
5556
'default',
5657
'default_dml12',
5758
'delete',
58-
'discardref',
59+
'discard',
5960
'dml',
6061
'dml_typedef',
6162
'dowhile',

py/dml/c_backend.py

+40-35
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,8 @@ def generate_structfile(device, filename, outprefix):
249249
"%s(%s)" % (name,
250250
", ".join((["conf_object_t *_obj"]
251251
* (not func.independent))
252-
+ [t.declaration(n)
253-
for (n, t) in (
252+
+ [decl
253+
for (_, _, decl) in (
254254
func.cparams
255255
if func.independent
256256
else func.cparams[1:])]))))
@@ -718,7 +718,7 @@ def wrap_method(meth, wrapper_name, indices=()):
718718
is a port object
719719
"""
720720

721-
inparams = [t.declaration(p) for p, t in meth.inp]
721+
inparams = [p.declaration() for p in meth.inp]
722722
if not meth.outp:
723723
retvar = None
724724
rettype = TVoid()
@@ -749,7 +749,7 @@ def wrap_method(meth, wrapper_name, indices=()):
749749
None, None, None)).toc()
750750

751751
with LogFailure(meth.site, meth, indices):
752-
inargs = [mkLit(meth.site, v, t) for v, t in meth.inp]
752+
inargs = [mkLit(meth.site, p.c_ident, p.typ) for p in meth.inp]
753753
outargs = [mkLit(meth.site, v, t) for v, t in meth.outp]
754754
codegen_call(meth.site, meth,
755755
indices,
@@ -804,10 +804,10 @@ def generate_implement_method(device, ifacestruct, meth, indices):
804804
# currently impossible to implement a varargs interface
805805
# method in DML
806806
raise EMETH(meth.site, None, 'interface method is variadic')
807-
for ((mp, mt), it) in zip(meth.inp, iface_input_types):
808-
if not safe_realtype_unconst(mt).eq(safe_realtype_unconst(it)):
807+
for (mp, it) in zip(meth.inp, iface_input_types):
808+
if not safe_realtype_unconst(mp.typ).eq(safe_realtype_unconst(it)):
809809
raise EARGT(meth.site, 'implement', meth.name,
810-
mt, mp, it, 'method')
810+
mp.typ, mp.logref, it, 'method')
811811
if iface_num_outputs and dml.globals.dml_version != (1, 2):
812812
[(_, mt)] = meth.outp
813813
if not safe_realtype_unconst(mt).eq(
@@ -1355,15 +1355,15 @@ def generate_events(device):
13551355
def generate_reg_callback(meth, name):
13561356
dev_t = crep.structtype(dml.globals.device)
13571357
out('static bool\n')
1358-
params = [t.declaration(p) for p, t in meth.inp] + [
1358+
params = [p.declaration() for p in meth.inp] + [
13591359
TPtr(t).declaration(p) for p, t in meth.outp]
13601360
out('%s(void *_obj, const uint16 *indices, ' % (name,)
13611361
+ ', '.join(params) + ')\n')
13621362
out('{\n', postindent = 1)
13631363
out('%s *_dev = _obj;\n' % dev_t)
13641364
fail = ReturnFailure(meth.site)
13651365
with fail, crep.DeviceInstanceContext():
1366-
inargs = [mkLit(meth.site, n, t) for n, t in meth.inp]
1366+
inargs = [mkLit(meth.site, p.c_ident, p.typ) for p in meth.inp]
13671367
outargs = [mkLit(meth.site, "*" + n, t) for n, t in meth.outp]
13681368
code = [codegen_call(
13691369
meth.site, meth,
@@ -2029,48 +2029,50 @@ def generate_init(device, initcode, outprefix):
20292029
def generate_static_trampoline(func):
20302030
# static trampolines never need to be generated for independent methods
20312031
assert not func.independent
2032-
params = [("_obj", TPtr(TNamed("conf_object_t")))] + func.cparams[1:]
2033-
params_string = ('void' if not params
2034-
else ", ".join(t.declaration(n) for (n, t) in params))
2032+
2033+
params_string = ''.join(", " + decl for (_, _, decl) in func.cparams[1:])
20352034
start_function_definition(func.rettype.declaration(
2036-
"%s(%s)" % ("_trampoline" + func.get_cname(), params_string)))
2035+
"%s(conf_object_t *_obj%s)" % ("_trampoline" + func.get_cname(),
2036+
params_string)))
20372037
out("{\n", postindent=1)
20382038
out('ASSERT(_obj);\n')
20392039
out('ASSERT(SIM_object_class(_obj) == _dev_class);\n')
2040-
(name, typ) = func.cparams[0]
2041-
out("%s = (%s)_obj;\n" % (typ.declaration(name), typ.declaration("")))
2040+
(name, typ, decl) = func.cparams[0]
2041+
out("%s = (%s)_obj;\n" % (decl, typ.declaration("")))
20422042
out("%s%s(%s);\n" % ("" if func.rettype.void
20432043
else func.rettype.declaration("result") + " = ",
20442044
func.get_cname(),
2045-
", ".join(n for (n, t) in func.cparams)))
2045+
", ".join(n for (n, _, _) in func.cparams)))
20462046
output_dml_state_change(name)
20472047
if not func.rettype.void:
20482048
out("return result;\n")
20492049
out("}\n", preindent=-1)
20502050

20512051
def generate_extern_trampoline(exported_name, func):
2052-
params = (func.cparams if func.independent else
2053-
[("_obj", TPtr(TNamed("conf_object_t")))] + func.cparams[1:])
2054-
params_string = ('void' if not params
2055-
else ", ".join(t.declaration(n) for (n, t) in params))
2052+
cparams = list(func.cparams)
2053+
if not func.independent:
2054+
cparams[0] = (
2055+
'_obj', TPtr(TNamed("conf_object_t")),
2056+
TPtr(TNamed("conf_object_t")).declaration('_obj'))
2057+
params_string = ('void' if not cparams else ", ".join(
2058+
decl for (_, _, decl) in cparams))
20562059
out("extern %s\n" % (func.rettype.declaration(
20572060
"%s(%s)" % (exported_name, params_string))))
20582061
out("{\n", postindent=1)
20592062
out("%s%s(%s);\n" % ("return " * (not func.rettype.void),
20602063
"_trampoline" * (not func.independent)
20612064
+ func.get_cname(),
2062-
", ".join(n for (n, t) in params)))
2065+
", ".join(n for (n, _, _) in cparams)))
20632066
out("}\n", preindent=-1)
20642067

20652068
def generate_extern_trampoline_dml12(exported_name, func):
20662069
out("static UNUSED %s\n" % (func.rettype.declaration(
2067-
"%s(%s)" % (exported_name,
2068-
", ".join(t.declaration(n)
2069-
for (n, t) in func.cparams)))))
2070+
"%s(%s)" % (exported_name,", ".join(
2071+
decl for (_, _, decl) in func.cparams)))))
20702072
out("{\n", postindent=1)
20712073
out("%s%s(%s);\n" % ("" if func.rettype.void else "return ",
20722074
func.get_cname(),
2073-
", ".join(n for (n, t) in func.cparams)))
2075+
", ".join(n for (n, _, _) in func.cparams)))
20742076
out("}\n", preindent=-1)
20752077

20762078
def generate_each_in_table(trait, instances):
@@ -2204,7 +2206,7 @@ def generate_adjustor_thunk(traitname, name, inp, outp, throws, independent,
22042206
assert vtable_trait is def_path[-1]
22052207
implicit_inargs = vtable_trait.implicit_args()
22062208
preargs = crep.maybe_dev_arg(independent) + implicit_inargs
2207-
inargs = c_inargs(inp, outp, throws)
2209+
inargs = [(p.c_ident, p.typ) for p in inp] + c_extra_inargs(outp, throws)
22082210
out('(%s)\n{\n' % (", ".join(t.declaration(n) for (n, t) in (preargs
22092211
+ inargs))),
22102212
postindent=1)
@@ -2747,11 +2749,12 @@ def resolve_trait_param_values(node):
27472749

27482750
def generate_trait_trampoline(method, vtable_trait):
27492751
implicit_inargs = vtable_trait.implicit_args()
2750-
explicit_inargs = c_inargs(list(method.inp), method.outp, method.throws)
2751-
inparams = ", ".join(
2752-
t.declaration(n)
2753-
for (n, t) in (crep.maybe_dev_arg(method.independent) + implicit_inargs
2754-
+ explicit_inargs))
2752+
extra_inargs = c_extra_inargs(method.outp, method.throws)
2753+
inparams = ", ".join([t.declaration(n)
2754+
for (n, t) in (crep.maybe_dev_arg(method.independent)
2755+
+ implicit_inargs)]
2756+
+ [p.declaration() for p in method.inp]
2757+
+ [t.declaration(n) for (n, t) in extra_inargs])
27552758
rettype = c_rettype(method.outp, method.throws)
27562759

27572760
# guaranteed to exist; created by ObjTraits.mark_referenced
@@ -2772,7 +2775,8 @@ def generate_trait_trampoline(method, vtable_trait):
27722775
reduce(operator.mul, obj.dimsizes[dim + 1:], 1),
27732776
obj.dimsizes[dim]), TInt(32, False))
27742777
for dim in range(obj.dimensions)]
2775-
args = [mkLit(site, n, t) for (n, t) in explicit_inargs]
2778+
args = ([mkLit(site, p.c_ident, p.typ) for p in method.inp]
2779+
+ [mkLit(site, n, t) for (n, t) in extra_inargs])
27762780
call_expr = mkcall_method(site, func, indices)(args)
27772781
if not rettype.void:
27782782
out('return ')
@@ -3376,9 +3380,10 @@ def generate_cfile_body(device, footers, full_module, filename_prefix):
33763380
generated_funcs.add(func)
33773381
code = codegen_method_func(func)
33783382

3379-
specializations = [(n, 'undefined' if undefined(v) else v.value)
3380-
for (n, v) in func.inp
3381-
if isinstance(v, Expression)]
3383+
specializations = [(p.ident,
3384+
'undefined' if undefined(p.expr) else p.expr.value)
3385+
for p in func.inp
3386+
if p.ident is not None and p.expr is not None]
33823387

33833388
if gather_size_statistics:
33843389
ctx = StrOutput(lineno=output.current().lineno,

0 commit comments

Comments
 (0)