-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
Summary of issue:
The accepted destructor syntax includes out-of-line definitions such as:
destructor MyClass [addr self: Self*] { ... }
The implicit parameter here could be interpreted as either an implicit parameter for MyClass
or an implicit parameter for the destructor. How should ambiguities like this be resolved?
Details:
The full example is:
class MyClass {
destructor [addr self: Self*];
}
destructor MyClass [addr self: Self*] { ... }
For comparison, note a generic might look like:
class GenericClass[T:! type](N:! T) { ... }
destructor GenericClass[T:! type](N:! T) [addr self: Self*] { ... }
Destructors were adopted in #1154, and 2021-08-03 notes are referenced there but don't seem to get into detail on the out-of-line syntax.
I can offer are a few options for resolving the ambiguity:
- Use arbitrary lookahead to see what is after the
[]
.
For the toolchain, in tokenization we will have found the closing ]
and we can parse differently based on whether the {
follows the ]
. Note this may have effects on error recovery, and while it would be efficient in the toolchain, may be more difficult to implement in third-party parsing systems, such as tree-sitter.
This option would also mean that generic types must always have explicit parameters if they have implicit parameters, which may affect other open questions. Because the {
would be disambiguating, it would also mean a typo of adding ()
(as in destructor MyClass [addr self: Self*]()
) would lead to diagnostic as a generic type, not incorrectly adding ()
after a destructor.
Note, I'm not pursuing this approach right now because I believe we don't want arbitrary lookahead to be required.
- Require a
.
before the destructor's implicit parameters, similar to qualified names.
The .
indicates a separation of name components. In the context of a destructor
introducer, we can treat the final name component in a special way.
For example:
destructor MyClass.[addr self: Self*] { ... }
destructor GenericClass[T:! type](N:! T).[addr self: Self*] { ... }
- Switch to a more
fn
-like declaration, usingdestructor
as the name.
This has benefits of aligning more with fn
parsing, and destructor
still cleanly disambiguates versus a regular function. In this approach, the destructor
keyword takes the place of the function name and can make the explicit parameters disallowed.
Note, I actually thought we'd discussed this approach in the past, but I can't find it from #1154.
For example:
class MyClass {
fn destructor[addr self: Self*];
}
fn MyClass.destructor[addr self: Self*] { ... }
Any other information that you want to share?
I have a half-working destructor parse, and it was specifically looking at making the qualified name optional that made me notice the challenge here.