Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions RELEASENOTES-1.4.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,3 +408,18 @@
overridable. See the documentation of these templates for more details.
- `release 7 7129`
- `release 6 6444`
- `note 6` Added non-`shared` abstract method declarations, e.g.
```
method m(uint8 a) -> (int);
```
Similarly to untyped abstract parameter declarations, an abstract method
declaration may be specified regardless of whether it's in the context of a
template definition, and regardless of what other declarations of the same
method exists (except that all declarations must share the same signature.)
- `note 6` Added a provisional feature `explicit_method_decls`, which can be
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we warn that the provisional feature is unstable? My interpretation of our view is that people are free to play around with it and give feedback, but that we strongly discourage any use in production code (maybe with the exception for Gustav, but he knows that already).

enabled per file by a statement `provisional explicit_method_decls;`.
This feature adds a syntax `method m() :{ ... }`, which signifies that the method
is *not* intended as an override. The existing syntax `method m() { ... }``
is re-purposed to signify that the method *is* intended as an override,
Inconsistent usage will trigger compile errors. This is useful to
catch misspelled overrides.
48 changes: 37 additions & 11 deletions doc/1.4/language.md
Original file line number Diff line number Diff line change
Expand Up @@ -1798,13 +1798,37 @@ handled:
* A method can only be overridden by another method if it is declared
`default`.

All declarations of the same method in an object must share the same signature:
every declaration must share input parameters, return value types, agree on
whether the method throws, and agree on every method qualifier except
`shared` (such as [`independent`](#independent-methods)).
Comment on lines +1803 to +1804
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shorter and more explicit to just spell out the list: independent, memoized, startup.


> [!NOTE]
> An overridable built-in method is defined by a template
> named as the object type. So, if you want to write a template that
> overrides the `read` method of a register, and want to make
> your implementation overridable, then your template must explicitly
> instantiate the `register` template using a statement `is
> register;`.
> Overridable built-in methods are often defined by a template named as the
> object type. For example, if you want to write a template that overrides the
> `read_register` method of a register, then your template must explicitly
> instantiate the `register` template using
> <code>template <em>name</em> is (register) { ... }</code>.

### Abstract method declarations
A method may be declared abstractly, imposing a requirement that some definition
of that method, by some part of the device model, is provided to the object the
abstract declaration is a member of. If that requirement is not satisfied then
the device model will be rejected by the compiler.

The following is an example of an abstract method declarations:
```
method m(uint32 a, bool b) -> (uint8, uint16) throws;
```

Similarly to [untyped abstract parameter declarations](#parameters-detailed), a
non-`shared` abstract method declaration may be specified regardless of what
other declarations of that method there are in the model, except that it is
still subject to the requirement that every declaration of the same method must
share the same signature. In contrast, [`shared` abstract method
declarations](#shared-methods) may only be specified if there is no previous
([lower or equal ranked](#resolution-of-overrides)) `shared` declaration of that
method.

### Calling Methods

Expand Down Expand Up @@ -2829,19 +2853,21 @@ incomplete description can be found in the [section on templates](#templates).
*dominates* the set if it has higher rank than all other
declarations in the set. Abstract `param` declarations (<code>param
<em>name</em>;</code> or <code>param <em>name</em> :
<em>type</em>;</code>) and abstract method definitions (<code>method
<em>type</em>;</code>) and abstract method declarations (<code>method
<em>name</em>(<em>args...</em>);</code>) are excluded here; they
cannot dominate a set, and a dominating declaration in a set does
not need to have higher declaration than any abstract `param` or
`method` declaration in the set.
* There may be any number of *untyped* abstract definitions of a
* There may be any number of *untyped* abstract declarations of a
parameter (<code>param <em>name</em>;</code>).
* There may be at most one *typed* abstract definition of a parameter
(<code>param <em>name</em> : <em>type</em>;</code>)
* There may be at most one abstract shared definition of a method. Any
* There may be any number of *non-shared* abstract declarations of a
method.
* There may be at most one abstract shared declaration of a method. Any
other *shared* definition of this method must have higher rank than
the abstract definition, but any rank is permitted for non-shared
definitions. For instance:
declarations. For instance:

```
template a {
Expand All @@ -2855,7 +2881,7 @@ incomplete description can be found in the [section on templates](#templates).
shared method m();
}
template bb is b {
// Error: abstract shared definition overrides non-abstract
// Error: abstract shared declaration overrides non-abstract
shared method m();
}
```
Expand Down
6 changes: 3 additions & 3 deletions py/dead_dml_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,14 @@ def traverse_ast(ast):
if any(stmt.kind == 'error' for stmt in body.args[0]):
# poisoned method, apparently meant to be dead
ignored = True
yield (ast.site.lineno, ast.args[4].lineno, ast.args[0], ignored)
yield (ast.site.lineno, ast.args[5].lineno, ast.args[0], ignored)
elif ast.kind == 'sharedmethod':
body = ast.args[6]
body = ast.args[7]
if body is None:
# abstract method, no code generated
return
assert body.kind == 'compound'
yield (ast.site.lineno, ast.args[7].lineno, ast.args[0], False)
yield (ast.site.lineno, ast.args[8].lineno, ast.args[0], False)
elif ast.kind in {'toplevel_if', 'hashif'}:
(_, t, f) = ast.args
for block in [t, f]:
Expand Down
63 changes: 45 additions & 18 deletions py/dml/dmlparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ def object_method_noinparams(t):
body = t[6]
t[0] = ast.method(site(t), name,
(inp, outp, throws, [], body),
t[5], t[2], lex_end_site(t, -1))
t[5], t[2], False, lex_end_site(t, -1))


@prod_dml12
Expand All @@ -578,7 +578,7 @@ def object_method(t):
body = t[10]
t[0] = ast.method(site(t), name,
(inp, outp, throws, [], body),
t[9], t[2], lex_end_site(t, -1))
t[9], t[2], False, lex_end_site(t, -1))


def method_qualifiers_check(site, qualifiers, inp, outp, throws, default):
Expand All @@ -603,33 +603,58 @@ def method_qualifiers_check(site, qualifiers, inp, outp, throws, default):
"startup methods may not be declared 'default'"))
return (inp, outp)

@prod_dml14
def maybe_colon_yes(t):
'''maybe_colon : COLON'''
if not site(t).provisional_enabled(provisional.explicit_method_decls):
report(ESYNTAX(site(t), ':', "expected '{' or 'default'"))
t[0] = False
else:
t[0] = True

@prod
def maybe_colon_no(t):
'''maybe_colon : '''
t[0] = False

@prod_dml14
def object_method(t):
'''method : method_qualifiers METHOD objident method_params_typed maybe_default compound_statement'''
'''method : method_qualifiers METHOD objident method_params_typed maybe_colon maybe_default compound_statement'''
name = t[3]
(inp, outp, throws) = t[4]
body = t[6]
body = t[7]
(inp, outp) = method_qualifiers_check(site(t), t[1], inp, outp, throws,
t[6])
t[0] = ast.method(site(t), name,
(inp, outp, throws, t[1], body),
t[6], False, t[5], lex_end_site(t, -1))

@prod_dml14
def object_method_abstract(t):
'''method : method_qualifiers METHOD objident method_params_typed SEMI'''
name = t[3]
(inp, outp, throws) = t[4]
body = None
(inp, outp) = method_qualifiers_check(site(t), t[1], inp, outp, throws,
t[5])
False)
t[0] = ast.method(site(t), name,
(inp, outp, throws, t[1], body),
t[5], False, lex_end_site(t, -1))
True, False, None, site(t, 5))

@prod_dml14
def object_inline_method(t):
'''method : INLINE METHOD objident method_params_maybe_untyped maybe_default compound_statement'''
'''method : INLINE METHOD objident method_params_maybe_untyped maybe_colon maybe_default compound_statement'''
name = t[3]
(inp, outp, throws) = t[4]
if all(typ for (_, asite, name, typ) in inp):
# inline annotation would have no effect for fully typed methods.
# We forbid it as a way to strongly discourage unneeded use of inline.
report(ESYNTAX(site(t, 2), 'inline',
'only use inline if there are untyped arguments'))
body = t[6]
body = t[7]
t[0] = ast.method(site(t), name,
(inp, outp, throws, [], body),
t[5], False, lex_end_site(t, -1))
t[6], False, t[5], lex_end_site(t, -1))


@prod_dml12
Expand Down Expand Up @@ -730,12 +755,13 @@ def template_statement_obj(t):
@prod_dml14
def template_statement_shared_method(t):
'''template_stmt : SHARED method_qualifiers METHOD shared_method'''
(name, (inp, outp, throws), overridable, body, rbrace_site) = t[4]
(name, (inp, outp, throws), overridable, explicit_decl, body,
rbrace_site) = t[4]
default = overridable and body is not None
(inp, outp) = method_qualifiers_check(site(t), t[2], inp, outp, throws,
default)
t[0] = [ast.sharedmethod(site(t), name, inp, outp, throws, t[2],
overridable, body, rbrace_site)]
overridable, explicit_decl, body, rbrace_site)]

@prod_dml14
def template_statement_shared_hook(t):
Expand Down Expand Up @@ -765,23 +791,24 @@ def method_qualifiers(t):
@prod_dml12
def trait_method(t):
'''trait_method : METHOD shared_method'''
(name, (inp, outp, throws), overridable, body, rbrace_site) = t[2]
(name, (inp, outp, throws), overridable, explicit_decl, body,
rbrace_site) = t[2]
t[0] = ast.sharedmethod(site(t), name, inp, outp, throws, [], overridable,
body, rbrace_site)
explicit_decl, body, rbrace_site)

@prod
def shared_method_abstract(t):
'''shared_method : ident method_params_typed SEMI'''
t[0] = (t[1], t[2], True, None, site(t, 3))
t[0] = (t[1], t[2], True, False, None, site(t, 3))
@prod
def shared_method_default(t):
'''shared_method : ident method_params_typed DEFAULT compound_statement'''
t[0] = (t[1], t[2], True, t[4], lex_end_site(t, -1))
'''shared_method : ident method_params_typed maybe_colon DEFAULT compound_statement'''
t[0] = (t[1], t[2], True, t[3], t[5], lex_end_site(t, -1))

@prod
def shared_method_final(t):
'''shared_method : ident method_params_typed compound_statement'''
t[0] = (t[1], t[2], False, t[3], lex_end_site(t, -1))
'''shared_method : ident method_params_typed maybe_colon compound_statement'''
t[0] = (t[1], t[2], False, t[3], t[4], lex_end_site(t, -1))

@prod_dml12
def trait_param(t):
Expand Down
51 changes: 47 additions & 4 deletions py/dml/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,9 @@ def log(self):

class EAMETH(DMLError):
"""
An abstract method cannot override another method.
A shared abstract method cannot override another method.
"""
fmt = "abstract method %s overrides existing method"
fmt = "shared abstract method %s overrides existing method"

def __init__(self, site, prev_site, name):
DMLError.__init__(self, site, name)
Expand Down Expand Up @@ -247,6 +247,13 @@ def log(self):
self.print_site_message(
self.decl_site, "abstract declaration")

class EABSMETH(DMLError):
"""
An (abstractly) declared method never has any definition made for it.
"""
version = "1.4"
fmt = "declared method %s is never implemented"

class EIMPORT(DMLError):
"""
The file to imported could not be found. Use the `-I`
Expand Down Expand Up @@ -1133,7 +1140,7 @@ class EAUTOPARAM(DMLError):
library, and they may not be overridden."""
fmt = "bad declaration of automatic parameter '%s'"

class ENOVERRIDE(DMLError):
class ENOVERRIDEPARAM(DMLError):
"""When the `explict_param_decls` provisional feature is enabled, parameter
definitions written using `=` and `default` are only accepted if the
parameter has already been declared.
Expand All @@ -1153,7 +1160,7 @@ def log(self):
"enabled by the explicit_param_decls provisional feature")


class EOVERRIDE(DMLError):
class EOVERRIDEPARAM(DMLError):
"""When the `explict_param_decls` provisional feature is enabled,
any parameter declared via `:=` or `:default` may not already
have been declared. This means `:=` or `:default` syntax can't be used
Expand Down Expand Up @@ -1270,6 +1277,42 @@ def log(self):
if self.othersite:
self.print_site_message(self.othersite, "conflicting definition")

class ENOVERRIDEMETH(DMLError):
"""When the `explict_method_decls` provisional feature is enabled, method
definitions written using `{ ... }` and `default { ... }` are only accepted
if the method has already been declared.

To declare and define a new method not already declared, use the `:{ ... }`
or `:default { ... }` syntax.
"""
fmt = ("method '%s' not declared previously."
" To declare and define a new method, use the ':%s{...}' syntax.")

def log(self):
from . import provisional
DMLError.log(self)
prov_site = self.site.provisional_enabled(
provisional.explicit_method_decls)
self.print_site_message(
prov_site,
"enabled by the explicit_method_decls provisional feature")

class EOVERRIDEMETH(DMLError):
"""When the `explict_method_decls` provisional feature is enabled,
any method declared via `:{ ... }` or `:default { ... }` may not already
have been declared. This means `:{ ... }` or `:default { ... }` syntax
can't be used to override existing parameter declarations (not even those
lacking a definition of the parameter.)
"""
fmt = ("the method '%s' has already been declared "
+ "(':%s{ ... }' syntax may not be used for method overrides)")
def __init__(self, site, other_site, name, token):
super().__init__(site, name, token)
self.other_site = other_site
def log(self):
DMLError.log(self)
self.print_site_message(self.other_site, "existing declaration")

class EIMPLMEMBER(DMLError):
"""
A method in an `implement` object corresponds to a struct member
Expand Down
54 changes: 54 additions & 0 deletions py/dml/provisional.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,60 @@ class explicit_param_decls(ProvisionalFeature):
short = "Require := syntax for defining new params"
stable = True

@feature
class explicit_method_decls(ProvisionalFeature):
'''
This feature extends the DML syntax for methods to distinguish between an
intent to declare a new method, and an intent to override an existing
method/provide a definition for an abstract method.
This distinction allows DML to capture misspelled parameter overrides as
compile errors.

This provisional feature introduces new syntax for the following purposes:
* Abstract method declarations
```
method m(...) [-> (...)] [throws];
```

This declaration establishes a requirement that a method of that name and
signature is defined in the same object as the abstract method
declaration. This is similar to the existing abstract `shared` method
declaration, but unlike abstract `shared` methods have no restrictions
or semantic implications beyond that (except for the fact that it
declares the method to exist.) In other words, it's semantically
analagous to untyped abstract parameter declarations (`param p;`).

* Simultaneously declaring and defining a new method
```
method m(...) [-> (...)] [throws] :{ ... }
method m(...) [-> (...)] [throws] :default { ... }
```

DMLC rejects a declaration of this form if the method has already been
declared, because this form signifies that the declaration was not
intended as an override.

`explicit_metod_decls` also changes the meaning of the traditional form
of method definitions (e.g. `method m() {}` or `method m() default {}`)
such that DMLC will reject them if the method has not been declared
previously (either abstractly or with an overridable definition.)

In some rare cases, you may need to declare a method without
knowing if it's an override or a new declaration. In this case, one
can accompany an overriding definition (e.g. `method m() {}`
or `method() default {}`) with an abstract method declaration (e.g.
`method m();`) in the same scope/rank. This marks that the method
definition may either be for a previously declared method or a new method
entirely, and no error will be printed.

Enabling the `explicit_method_decls` feature in a file only affects
the method definitions specified in that file; in other words, it will not
require other files to use the `:{` syntax in order to declare novel
methods.
'''
short = "Require :{ ... } syntax for defining new methods"
stable = False


@feature
class simics_util_vect(ProvisionalFeature):
Expand Down
Loading