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
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 @@ -546,7 +546,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 @@ -573,7 +573,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 @@ -598,33 +598,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'"))
Copy link
Contributor

Choose a reason for hiding this comment

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

'{' 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 @@ -724,12 +749,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 @@ -759,23 +785,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 @@ -1134,7 +1141,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 @@ -1154,7 +1161,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 @@ -1271,6 +1278,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