Skip to content

Commit 58b51d9

Browse files
committed
Support '_' as a magic 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 d940bfa commit 58b51d9

20 files changed

+452
-89
lines changed

RELEASENOTES-1.4.docu

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,4 +533,27 @@
533533
_ = throwing_method();
534534
(_, x, _) = method_with_multiple_return_values();
535535
</pre></add-note></build-id>
536+
<build-id value="next"><add-note> '_' can no longer be used as the name for
537+
arbitrary declarations. Instead, it is now only permitted in particular
538+
contexts, and in these contexts, '_' will affect the declaration in a way
539+
suitable for when the declaration is <em>unused</em>.
540+
These contexts are:
541+
<dl>
542+
<dt>Method-local bindings (e.g. variables and input parameters)</dt>
543+
<dd>When a method-local binding is given the name '_', it will not
544+
be added to scope. This is useful for e.g. unused method
545+
parameters.</dd>
546+
<dt>Index variables for object arrays</dt>
547+
<dd>When '_' is specified as an index variable, a parameter will
548+
not be created for it, meaning it cannot conflict with any
549+
other definition, and it cannot be referenced in the code in
550+
order to get the value of the index in question.
551+
It also isn't considered to conflict with any other definition
552+
that gives the index variable a different name.
553+
This is useful when defining an object array specification which
554+
does not depend on the index.</dd>
555+
</dl>
556+
Note that as a consequence of these semantics, any reference to
557+
'<tt>_</tt>' in code will <em>always</em> resolve to the discard
558+
reference.<add-note></build-id>
536559
</rn>

doc/1.4/language.md

Lines changed: 39 additions & 7 deletions
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] for more information.
8493

8594
</dd><dt>
8695

@@ -3098,6 +3107,29 @@ method m() -> (bool, int) {
30983107
local (bool a, int i) = m();
30993108
```
31003109

3110+
3111+
"\_" may be used as an identifier for local variables, as well as other
3112+
method-local bindings such as the method parameters, and the bound identifier
3113+
in `foreach`/`#foreach`/`#select` statements, and message component paramters of
3114+
[hook-bound after statements](#hook-bound-after-statements). Any method-local
3115+
binding named "\_" *will not be added to scope.* This is useful for when
3116+
a method parameter is unused, or if you perform a method call where only a
3117+
subset of returned values are of interest:
3118+
```
3119+
local (bool a, int _) = m();
3120+
// No conflicts since "_" is not added to scope
3121+
local (bool a, int _, float _) = returns_three_vals();
3122+
```
3123+
3124+
An alternative to this pattern is to leverage the
3125+
[discard reference](#discard-reference)
3126+
```
3127+
local bool a;
3128+
(a, _, _) = returns_three_vals();
3129+
```
3130+
... which does not require you to specify the types of the discarded values,
3131+
but requires multiple lines.
3132+
31013133
### Session Statements
31023134
<pre>
31033135
session <em>type</em> <em>identifier</em> [= <em>initializer</em>];
@@ -3809,6 +3841,7 @@ independent method callback(int i, void *aux) {
38093841
```
38103842

38113843
### The Discard Reference (`_`)
3844+
<a id="discard-reference"/>
38123845
```
38133846
_
38143847
```
@@ -3817,10 +3850,6 @@ The discard reference *`_`* is an expression without any run-time representation
38173850
that may be used as the target of an assignment in order to explicitly discard
38183851
the result of an evaluated expression or return value of a method call.
38193852

3820-
For backwards compatibility reasons, `_` is not a keyword, but instead behaves
3821-
more closely as a global identifier. What this means is that declared
3822-
identifiers (e.g. local variables) are allowed to shadow it by being named `_`.
3823-
38243853
Example usage:
38253854
```
38263855
// Evaluate an expression and explicitly discard its result.
@@ -3834,6 +3863,9 @@ _ = throwing_method();
38343863
(_, x, _) = method_with_multiple_return_values();
38353864
```
38363865

3866+
The discard reference is related to the [discard
3867+
identifier](#discard-identifier), and have some use-cases in common.
3868+
38373869
### New Expressions
38383870

38393871
<pre>

lib/1.4/dml-builtins.dml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,11 +431,23 @@ which cannot be overridden:
431431
single field inside a register array, the value is the empty list.
432432

433433
* Each array has an *individual index parameter*, to make it possible
434-
to refer to both inner and outer indexes when arrays are nested
434+
to refer to both inner and outer indices when arrays are nested
435435
(cf. the `indices` parameter, above). The parameter name is
436436
specified in the array declaration; for instance, the declaration
437437
`register regs[i < 4][j < 11];` defines two index parameters, `i` and `j`.
438438
In this case, the `indices` parameter is `[i, j]`.
439+
"\_" is also allowed as the name of one or more index parameters. If
440+
used, then it *won't* result in corresponding parameter definitions for those
441+
indices; in other words, if an index variable is named "\_", then it can't be
442+
accessed by that name (however, the `indices` parameter is unaffected and
443+
could be used instead.) It's also allowed to have an object array
444+
specification name an index parameter "\_" even if some other object array
445+
specification uses a different name (which *will* be given a corresponding
446+
parameter definition.) This is useful if a specific object array
447+
specification doesn't need to make use of some particular indices; or if the
448+
specification needs to support that the naming of the index variable may vary
449+
depending on what other specifications of the same object array are present
450+
in the device model.
439451

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

py/dml/c_backend.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,15 @@ def wrap_method(meth, wrapper_name, indices=()):
720720
is a port object
721721
"""
722722

723-
inparams = [t.declaration(p) for p, t in meth.inp]
723+
inp = list(meth.inp)
724+
if meth.astcode.site.dml_version() != (1, 2):
725+
discarded = 0
726+
for (i, (n, t)) in enumerate(inp):
727+
if n == '_':
728+
inp[i] = (f'_unused_{discarded}', t)
729+
discarded += 1
730+
inparams = [t.declaration(p) + ' UNUSED'*(p.startswith('_unused_'))
731+
for p, t in inp]
724732
if not meth.outp:
725733
retvar = None
726734
rettype = TVoid()

py/dml/codegen.py

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -952,7 +952,7 @@ def declarations(scope):
952952
if sym.stmt:
953953
continue
954954
decl = sym_declaration(sym)
955-
if decl:
955+
if not decl.is_empty:
956956
decls.append(decl)
957957

958958
return decls
@@ -2036,19 +2036,20 @@ def mk_sym(name, typ, mkunique=not dml.globals.debuggable):
20362036
tgts, location, scope)
20372037
if method_invocation is not None and stmt.site.dml_version != (1, 2):
20382038
for sym in syms_to_add:
2039-
scope.add(sym)
2039+
if sym.name != '_':
2040+
scope.add(sym)
20402041
stmts.extend(sym_declaration(sym) for sym in tgt_syms)
20412042
stmts.append(method_invocation)
2042-
stmts.extend(sym_declaration(sym)
2043-
for sym in late_declared_syms)
2043+
stmts.extend(sym_declaration(sym) for sym in late_declared_syms)
20442044
else:
20452045
if len(tgts) != 1:
20462046
report(ERETLVALS(stmt.site, 1, len(tgts)))
20472047
else:
20482048
sym = syms_to_add[0]
20492049
sym.init = ExpressionInitializer(
20502050
codegen_expression(inits[0].args[0], location, scope))
2051-
scope.add(sym)
2051+
if not (sym.name == '_' and stmt.site.dml_version() != (1, 2)):
2052+
scope.add(sym)
20522053
stmts.append(sym_declaration(sym))
20532054
else:
20542055
# Initializer evaluation and variable declarations are done in separate
@@ -2057,9 +2058,13 @@ def mk_sym(name, typ, mkunique=not dml.globals.debuggable):
20572058
inits = [get_initializer(stmt.site, typ, init, location, scope)
20582059
for ((_, typ), init) in zip(decls, inits)]
20592060
for ((name, typ), init) in zip(decls, inits):
2060-
sym = scope.add_variable(
2061-
name, type = typ, site = stmt.site, init = init, stmt = True,
2062-
make_unique=not dml.globals.debuggable)
2061+
if not (name == '_' and stmt.site.dml_version() != (1, 2)):
2062+
sym = scope.add_variable(
2063+
name, type = typ, site = stmt.site, init = init,
2064+
stmt = True, make_unique=not dml.globals.debuggable)
2065+
else:
2066+
sym = mk_sym(name, typ)
2067+
20632068
stmts.append(sym_declaration(sym))
20642069

20652070
return stmts
@@ -2401,7 +2406,7 @@ def stmt_assign(stmt, location, scope):
24012406

24022407
lscope = Symtab(scope)
24032408
sym = lscope.add_variable(
2404-
'tmp', type=init_typ, site=init.site, init=init,
2409+
'_tmp', type=init_typ, site=init.site, init=init,
24052410
stmt=True)
24062411
init_expr = mkLocalVariable(init.site, sym)
24072412
stmts = [sym_declaration(sym)]
@@ -2437,7 +2442,7 @@ def stmt_assign(stmt, location, scope):
24372442
else:
24382443
init = eval_initializer(site, tgt.ctype(), src_ast, location,
24392444
scope, False)
2440-
name = 'tmp%d' % (i,)
2445+
name = '_tmp%d' % (i,)
24412446
sym = lscope.add_variable(
24422447
name, type=tgt.ctype(), site=tgt.site, init=init,
24432448
stmt=True)
@@ -2930,6 +2935,9 @@ def stmt_afteronhook(stmt, location, scope):
29302935

29312936
msg_comp_params = {}
29322937
for (idx, (mcp_site, mcp_name)) in enumerate(msg_comp_param_asts):
2938+
if mcp_name == '_':
2939+
continue
2940+
29332941
if mcp_name in msg_comp_params:
29342942
raise EDVAR(mcp_site, msg_comp_params[mcp_name][1],
29352943
mcp_name)
@@ -3079,7 +3087,8 @@ def stmt_select(stmt, location, scope):
30793087
clauses = []
30803088
for it in l:
30813089
condscope = Symtab(scope)
3082-
condscope.add(ExpressionSymbol(itername, it, stmt.site))
3090+
if not (itername == '_' and stmt.site.dml_version() != (1, 2)):
3091+
condscope.add(ExpressionSymbol(itername, it, stmt.site))
30833092
cond = as_bool(codegen_expression(
30843093
cond_ast, location, condscope))
30853094
if cond.constant and not cond.value:
@@ -3115,9 +3124,10 @@ def foreach_each_in(site, itername, trait, each_in,
31153124
body_ast, location, scope):
31163125
inner_scope = Symtab(scope)
31173126
trait_type = TTrait(trait)
3118-
inner_scope.add_variable(
3119-
itername, type=trait_type, site=site,
3120-
init=ForeachSequence.itervar_initializer(site, trait))
3127+
if itername != '_':
3128+
inner_scope.add_variable(
3129+
itername, type=trait_type, site=site,
3130+
init=ForeachSequence.itervar_initializer(site, trait))
31213131
context = GotoLoopContext()
31223132
with context:
31233133
inner_body = mkCompound(site, declarations(inner_scope)
@@ -3198,8 +3208,9 @@ def foreach_constant_list(site, itername, lst, statement, location, scope):
31983208
TInt(32, True))
31993209
for dim in range(len(items.dimsizes)))
32003210
loopscope = Symtab(scope)
3201-
loopscope.add(ExpressionSymbol(
3202-
itername, items.expr(loopvars), site))
3211+
if not (itername == '_' and site.dml_version() != (1, 2)):
3212+
loopscope.add(ExpressionSymbol(
3213+
itername, items.expr(loopvars), site))
32033214
stmt = codegen_statement(statement, location, loopscope)
32043215

32053216
if stmt.is_empty:
@@ -3584,6 +3595,7 @@ def codegen_inline(site, meth_node, indices, inargs, outargs,
35843595
# call is safe.
35853596
return codegen_call(site, meth_node, indices,
35863597
inargs, outargs)
3598+
pre = []
35873599
for (arg, (parmname, parmtype), argno) in zip(inargs, meth_node.inp,
35883600
list(range(len(inargs)))):
35893601
# Create an alias
@@ -3604,7 +3616,12 @@ def codegen_inline(site, meth_node, indices, inargs, outargs,
36043616
raise ECONSTP(site, parmname, "method call")
36053617
arg = coerce_if_eint(arg)
36063618

3607-
if inhibit_copyin or undefined(arg):
3619+
if (parmname == '_'
3620+
and meth_node.astcode.site.dml_version() != (1, 2)):
3621+
if not arg.constant:
3622+
pre.append(mkExpressionStatement(arg.site, arg,
3623+
explicit_discard=True))
3624+
elif inhibit_copyin or undefined(arg):
36083625
param_scope.add(ExpressionSymbol(parmname, arg, arg.site))
36093626
elif arg.constant and (
36103627
parmtype is None
@@ -3661,13 +3678,13 @@ def codegen_inline(site, meth_node, indices, inargs, outargs,
36613678
exit_handler = GotoExit_dml14(outargs)
36623679
with exit_handler:
36633680
code = codegen_statements(subs, location, param_scope)
3664-
decls = declarations(param_scope)
3681+
pre.extend(declarations(param_scope))
36653682
post = ([mkLabel(site, exit_handler.label)]
36663683
if exit_handler.used else [])
3667-
body = mkCompound(site, decls + code, rbrace_site)
3684+
body = mkCompound(site, pre + code, rbrace_site)
36683685
if meth_node.outp and body.control_flow().fallthrough:
36693686
report(ENORET(meth_node.astcode.site))
3670-
return mkInlinedMethod(site, meth_node, decls, code, post)
3687+
return mkInlinedMethod(site, meth_node, pre, code, post)
36713688

36723689
def c_rettype(outp, throws):
36733690
if throws:
@@ -3725,14 +3742,24 @@ def __init__(self, method, inp, outp, throws, independent, startup,
37253742

37263743
# rettype is the return type of the C function
37273744
self.rettype = c_rettype(outp, throws)
3745+
cparams = list(cparams)
3746+
3747+
if method.astcode.site.dml_version() != (1, 2):
3748+
discarded = 0
3749+
for (i, (n, t)) in enumerate(cparams):
3750+
if n == '_':
3751+
cparams[i] = (f'_unused_{discarded}', t)
3752+
discarded += 1
3753+
37283754
self.cparams = c_inargs(
3729-
implicit_params(method) + list(cparams), outp, throws)
3755+
implicit_params(method) + cparams, outp, throws)
37303756

37313757
@property
37323758
def prototype(self):
37333759
return self.rettype.declaration(
37343760
"%s(%s)" % (self.get_cname(),
37353761
", ".join([t.declaration(n)
3762+
+ ' UNUSED'*n.startswith('_unused_')
37363763
for (n, t) in self.cparams])))
37373764

37383765
def cfunc_expr(self, site):
@@ -3826,7 +3853,9 @@ def codegen_method_func(func):
38263853
if dml.globals.dml_version == (1, 2) and (
38273854
compat.dml12_misc not in dml.globals.enabled_compat):
38283855
check_varname(method.site, name)
3829-
if isinstance(e, Expression):
3856+
if (isinstance(e, Expression)
3857+
and not (name == '_'
3858+
and method.astcode.site.dml_version() != (1, 2))):
38303859
inlined_arg = (
38313860
mkInlinedParam(method.site, e, name, e.ctype())
38323861
if defined(e) else e)
@@ -3881,7 +3910,9 @@ def codegen_method(site, inp, outp, throws, independent, memoization, ast,
38813910
with (crep.DeviceInstanceContext() if not independent
38823911
else contextlib.nullcontext()):
38833912
for (arg, etype) in inp:
3884-
fnscope.add_variable(arg, type=etype, site=site, make_unique=False)
3913+
if not (arg == '_' and ast.site.dml_version() != (1, 2)):
3914+
fnscope.add_variable(arg, type=etype, site=site,
3915+
make_unique=False)
38853916
initializers = [get_initializer(site, parmtype, None, None, None)
38863917
for (_, parmtype) in outp]
38873918

0 commit comments

Comments
 (0)