Skip to content

Commit dc3cf35

Browse files
committed
abstract method decl; explicit_method_decls provisional feature
1 parent f095d46 commit dc3cf35

File tree

11 files changed

+603
-167
lines changed

11 files changed

+603
-167
lines changed

py/dead_dml_methods.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,14 @@ def traverse_ast(ast):
9191
if any(stmt.kind == 'error' for stmt in body.args[0]):
9292
# poisoned method, apparently meant to be dead
9393
ignored = True
94-
yield (ast.site.lineno, ast.args[4].lineno, ast.args[0], ignored)
94+
yield (ast.site.lineno, ast.args[5].lineno, ast.args[0], ignored)
9595
elif ast.kind == 'sharedmethod':
96-
body = ast.args[6]
96+
body = ast.args[7]
9797
if body is None:
9898
# abstract method, no code generated
9999
return
100100
assert body.kind == 'compound'
101-
yield (ast.site.lineno, ast.args[7].lineno, ast.args[0], False)
101+
yield (ast.site.lineno, ast.args[8].lineno, ast.args[0], False)
102102
elif ast.kind in {'toplevel_if', 'hashif'}:
103103
(_, t, f) = ast.args
104104
for block in [t, f]:

py/dml/dmlparse.py

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ def object_method_noinparams(t):
546546
body = t[6]
547547
t[0] = ast.method(site(t), name,
548548
(inp, outp, throws, [], body),
549-
t[5], t[2], lex_end_site(t, -1))
549+
t[5], t[2], False, lex_end_site(t, -1))
550550

551551

552552
@prod_dml12
@@ -573,7 +573,7 @@ def object_method(t):
573573
body = t[10]
574574
t[0] = ast.method(site(t), name,
575575
(inp, outp, throws, [], body),
576-
t[9], t[2], lex_end_site(t, -1))
576+
t[9], t[2], False, lex_end_site(t, -1))
577577

578578

579579
def method_qualifiers_check(site, qualifiers, inp, outp, throws, default):
@@ -598,33 +598,58 @@ def method_qualifiers_check(site, qualifiers, inp, outp, throws, default):
598598
"startup methods may not be declared 'default'"))
599599
return (inp, outp)
600600

601+
@prod_dml14
602+
def maybe_colon_yes(t):
603+
'''maybe_colon : COLON'''
604+
if not site(t).provisional_enabled(provisional.explicit_method_decls):
605+
report(ESYNTAX(site(t), ':', "expected '=' or 'default'"))
606+
t[0] = False
607+
else:
608+
t[0] = True
609+
610+
@prod
611+
def maybe_colon_no(t):
612+
'''maybe_colon : '''
613+
t[0] = False
601614

602615
@prod_dml14
603616
def object_method(t):
604-
'''method : method_qualifiers METHOD objident method_params_typed maybe_default compound_statement'''
617+
'''method : method_qualifiers METHOD objident method_params_typed maybe_colon maybe_default compound_statement'''
605618
name = t[3]
606619
(inp, outp, throws) = t[4]
607-
body = t[6]
620+
body = t[7]
621+
(inp, outp) = method_qualifiers_check(site(t), t[1], inp, outp, throws,
622+
t[6])
623+
t[0] = ast.method(site(t), name,
624+
(inp, outp, throws, t[1], body),
625+
t[6], False, t[5], lex_end_site(t, -1))
626+
627+
@prod_dml14
628+
def object_method_abstract(t):
629+
'''method : method_qualifiers METHOD objident method_params_typed SEMI'''
630+
name = t[3]
631+
(inp, outp, throws) = t[4]
632+
body = None
608633
(inp, outp) = method_qualifiers_check(site(t), t[1], inp, outp, throws,
609-
t[5])
634+
False)
610635
t[0] = ast.method(site(t), name,
611636
(inp, outp, throws, t[1], body),
612-
t[5], False, lex_end_site(t, -1))
637+
True, False, None, site(t, 5))
613638

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

629654

630655
@prod_dml12
@@ -724,12 +749,13 @@ def template_statement_obj(t):
724749
@prod_dml14
725750
def template_statement_shared_method(t):
726751
'''template_stmt : SHARED method_qualifiers METHOD shared_method'''
727-
(name, (inp, outp, throws), overridable, body, rbrace_site) = t[4]
752+
(name, (inp, outp, throws), overridable, explicit_decl, body,
753+
rbrace_site) = t[4]
728754
default = overridable and body is not None
729755
(inp, outp) = method_qualifiers_check(site(t), t[2], inp, outp, throws,
730756
default)
731757
t[0] = [ast.sharedmethod(site(t), name, inp, outp, throws, t[2],
732-
overridable, body, rbrace_site)]
758+
overridable, explicit_decl, body, rbrace_site)]
733759

734760
@prod_dml14
735761
def template_statement_shared_hook(t):
@@ -759,23 +785,24 @@ def method_qualifiers(t):
759785
@prod_dml12
760786
def trait_method(t):
761787
'''trait_method : METHOD shared_method'''
762-
(name, (inp, outp, throws), overridable, body, rbrace_site) = t[2]
788+
(name, (inp, outp, throws), overridable, explicit_decl, body,
789+
rbrace_site) = t[2]
763790
t[0] = ast.sharedmethod(site(t), name, inp, outp, throws, [], overridable,
764-
body, rbrace_site)
791+
explicit_decl, body, rbrace_site)
765792

766793
@prod
767794
def shared_method_abstract(t):
768795
'''shared_method : ident method_params_typed SEMI'''
769-
t[0] = (t[1], t[2], True, None, site(t, 3))
796+
t[0] = (t[1], t[2], True, False, None, site(t, 3))
770797
@prod
771798
def shared_method_default(t):
772-
'''shared_method : ident method_params_typed DEFAULT compound_statement'''
773-
t[0] = (t[1], t[2], True, t[4], lex_end_site(t, -1))
799+
'''shared_method : ident method_params_typed maybe_colon DEFAULT compound_statement'''
800+
t[0] = (t[1], t[2], True, t[3], t[5], lex_end_site(t, -1))
774801

775802
@prod
776803
def shared_method_final(t):
777-
'''shared_method : ident method_params_typed compound_statement'''
778-
t[0] = (t[1], t[2], False, t[3], lex_end_site(t, -1))
804+
'''shared_method : ident method_params_typed maybe_colon compound_statement'''
805+
t[0] = (t[1], t[2], False, t[3], t[4], lex_end_site(t, -1))
779806

780807
@prod_dml12
781808
def trait_param(t):

py/dml/messages.py

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,9 +198,9 @@ def log(self):
198198

199199
class EAMETH(DMLError):
200200
"""
201-
An abstract method cannot override another method.
201+
A shared abstract method cannot override another method.
202202
"""
203-
fmt = "abstract method %s overrides existing method"
203+
fmt = "shared abstract method %s overrides existing method"
204204

205205
def __init__(self, site, prev_site, name):
206206
DMLError.__init__(self, site, name)
@@ -247,6 +247,13 @@ def log(self):
247247
self.print_site_message(
248248
self.decl_site, "abstract declaration")
249249

250+
class EABSMETH(DMLError):
251+
"""
252+
An (abstractly) declared method never has any definition made for it.
253+
"""
254+
version = "1.4"
255+
fmt = "declared method %s is never implemented"
256+
250257
class EIMPORT(DMLError):
251258
"""
252259
The file to imported could not be found. Use the `-I`
@@ -1134,7 +1141,7 @@ class EAUTOPARAM(DMLError):
11341141
library, and they may not be overridden."""
11351142
fmt = "bad declaration of automatic parameter '%s'"
11361143

1137-
class ENOVERRIDE(DMLError):
1144+
class ENOVERRIDEPARAM(DMLError):
11381145
"""When the `explict_param_decls` provisional feature is enabled, parameter
11391146
definitions written using `=` and `default` are only accepted if the
11401147
parameter has already been declared.
@@ -1154,7 +1161,7 @@ def log(self):
11541161
"enabled by the explicit_param_decls provisional feature")
11551162

11561163

1157-
class EOVERRIDE(DMLError):
1164+
class EOVERRIDEPARAM(DMLError):
11581165
"""When the `explict_param_decls` provisional feature is enabled,
11591166
any parameter declared via `:=` or `:default` may not already
11601167
have been declared. This means `:=` or `:default` syntax can't be used
@@ -1271,6 +1278,42 @@ def log(self):
12711278
if self.othersite:
12721279
self.print_site_message(self.othersite, "conflicting definition")
12731280

1281+
class ENOVERRIDEMETH(DMLError):
1282+
"""When the `explict_method_decls` provisional feature is enabled, method
1283+
definitions written using `{ ... }` and `default { ... }` are only accepted
1284+
if the method has already been declared.
1285+
1286+
To declare and define a new method not already declared, use the `:{ ... }`
1287+
or `:default { ... }` syntax.
1288+
"""
1289+
fmt = ("method '%s' not declared previously."
1290+
" To declare and define a new method, use the ':%s{...}' syntax.")
1291+
1292+
def log(self):
1293+
from . import provisional
1294+
DMLError.log(self)
1295+
prov_site = self.site.provisional_enabled(
1296+
provisional.explicit_method_decls)
1297+
self.print_site_message(
1298+
prov_site,
1299+
"enabled by the explicit_method_decls provisional feature")
1300+
1301+
class EOVERRIDEMETH(DMLError):
1302+
"""When the `explict_method_decls` provisional feature is enabled,
1303+
any method declared via `:{ ... }` or `:default { ... }` may not already
1304+
have been declared. This means `:{ ... }` or `:default { ... }` syntax
1305+
can't be used to override existing parameter declarations (not even those
1306+
lacking a definition of the parameter.)
1307+
"""
1308+
fmt = ("the method '%s' has already been declared "
1309+
+ "(':%s{ ... }' syntax may not be used for method overrides)")
1310+
def __init__(self, site, other_site, name, token):
1311+
super().__init__(site, name, token)
1312+
self.other_site = other_site
1313+
def log(self):
1314+
DMLError.log(self)
1315+
self.print_site_message(self.other_site, "existing declaration")
1316+
12741317
class EIMPLMEMBER(DMLError):
12751318
"""
12761319
A method in an `implement` object corresponds to a struct member

py/dml/provisional.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,60 @@ class explicit_param_decls(ProvisionalFeature):
8484
short = "Require := syntax for defining new params"
8585
stable = True
8686

87+
@feature
88+
class explicit_method_decls(ProvisionalFeature):
89+
'''
90+
This feature extends the DML syntax for methods to distinguish between an
91+
intent to declare a new method, and an intent to override an existing
92+
method/provide a definition for an abstract method.
93+
This distinction allows DML to capture misspelled parameter overrides as
94+
compile errors.
95+
96+
This provisional feature introduces new syntax for the following purposes:
97+
* Abstract method declarations
98+
```
99+
method m(...) [-> (...)] [throws];
100+
```
101+
102+
This declaration establishes a requirement that a method of that name and
103+
signature is defined in the same object as the abstract method
104+
declaration. This is similar to the existing abstract `shared` method
105+
declaration, but unlike abstract `shared` methods have no restrictions
106+
or semantic implications beyond that (except for the fact that it
107+
declares the method to exist.) In other words, it's semantically
108+
analagous to untyped abstract parameter declarations (`param p;`).
109+
110+
* Simultaneously declaring and defining a new method
111+
```
112+
method m(...) [-> (...)] [throws] :{ ... }
113+
method m(...) [-> (...)] [throws] :default { ... }
114+
```
115+
116+
DMLC rejects a declaration of this form if the method has already been
117+
declared, because this form signifies that the declaration was not
118+
intended as an override.
119+
120+
`explicit_metod_decls` also changes the meaning of the traditional form
121+
of method definitions (e.g. `method m() {}` or `method m() default {}`)
122+
such that DMLC will reject them if the method has not been declared
123+
previously (either abstractly or with an overridable definition.)
124+
125+
In some rare cases, you may need to declare a method without
126+
knowing if it's an override or a new declaration. In this case, one
127+
can accompany an overriding definition (e.g. `method m() {}`
128+
or `method() default {}`) with an abstract method declaration (e.g.
129+
`method m();`) in the same scope/rank. This marks that the method
130+
definition may either be for a previously declared method or a new method
131+
entirely, and no error will be printed.
132+
133+
Enabling the `explicit_method_decls` feature in a file only affects
134+
the method definitions specified in that file; in other words, it will not
135+
require other files to use the `:{` syntax in order to declare novel
136+
methods.
137+
'''
138+
short = "Require :{ ... } syntax for defining new methods"
139+
stable = False
140+
87141

88142
@feature
89143
class simics_util_vect(ProvisionalFeature):

0 commit comments

Comments
 (0)