Prechádzať zdrojové kódy

Destructor syntax (#5017)

Fix destructor syntax ambiguity by switching to `fn destroy` mirroring
standard function syntax. This is a purely syntactic change, maintaining
destructor semantics.

This comes from leads question #4999

---------

Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
Jon Ross-Perkins 1 rok pred
rodič
commit
10a87c045a

+ 5 - 5
docs/design/README.md

@@ -1954,13 +1954,13 @@ names resolvable by the compiler, and don't act like forward declarations.
 #### Destructors
 
 A destructor for a class is custom code executed when the lifetime of a value of
-that type ends. They are defined with the `destructor` keyword followed by
-either `[self: Self]` or `[addr self: Self*]` (as is done with
-[methods](#methods)) and the block of code in the class definition, as in:
+that type ends. They are defined with `fn destroy` followed by either
+`[self: Self]` or `[addr self: Self*]` (as is done with [methods](#methods)) and
+the block of code in the class definition, as in:
 
 ```carbon
 class MyClass {
-  destructor [self: Self] { ... }
+  fn destroy[self: Self]() { ... }
 }
 ```
 
@@ -1969,7 +1969,7 @@ or:
 ```carbon
 class MyClass {
   // Can modify `self` in the body.
-  destructor [addr self: Self*] { ... }
+  fn destroy[addr self: Self*]() { ... }
 }
 ```
 

+ 17 - 10
docs/design/classes.md

@@ -1563,13 +1563,13 @@ it can lead to [slicing](https://en.wikipedia.org/wiki/Object_slicing).
 ### Destructors
 
 Every non-abstract type is _destructible_, meaning has a defined destructor
-function called when the lifetime of a value of that type ends, such as when a
+method called when the lifetime of a value of that type ends, such as when a
 variable goes out of scope. The destructor for a class may be customized using
-the `destructor` keyword:
+the `destroy` method:
 
 ```carbon
 class MyClass {
-  destructor [self: Self] { ... }
+  fn destroy[self: Self]() { ... }
 }
 ```
 
@@ -1578,12 +1578,12 @@ or:
 ```carbon
 class MyClass {
   // Can modify `self` in the body.
-  destructor [addr self: Self*] { ... }
+  fn destroy[addr self: Self*]() { ... }
 }
 ```
 
-If a class has no `destructor` declaration, it gets the default destructor,
-which is equivalent to `destructor [self: Self] { }`.
+If a class has no `destroy` method, it gets the default destructor, which is
+equivalent to `fn destroy[self: Self] { }`.
 
 The destructor for a class is run before the destructors of its data members.
 The data members are destroyed in reverse order of declaration. Derived classes
@@ -1601,9 +1601,9 @@ Destructors may be declared in class scope and then defined out-of-line:
 
 ```carbon
 class MyClass {
-  destructor [addr self: Self*];
+  fn destroy[addr self: Self*]();
 }
-destructor MyClass [addr self: Self*] { ... }
+fn MyClass.destroy[addr self: Self*]() { ... }
 ```
 
 It is illegal to delete an instance of a derived class through a pointer to one
@@ -1615,12 +1615,12 @@ must be `impl`:
 
 ```carbon
 base class MyBaseClass {
-  virtual destructor [addr self: Self*] { ... }
+  virtual fn destroy[addr self: Self*]() { ... }
 }
 
 class MyDerivedClass {
   extend base: MyBaseClass;
-  impl destructor [addr self: Self*] { ... }
+  impl fn destroy[addr self: Self*]() { ... }
 }
 ```
 
@@ -2285,9 +2285,15 @@ the type of `U.x`."
     -   [No unqualified lookup when defining outside a scope](/proposals/p2287.md#no-unqualified-lookup-when-defining-outside-a-scope)
 
 -   [#2760: Consistent `class` and `interface` syntax](https://github.com/carbon-language/carbon-lang/pull/2760)
+
     -   [Use `extends` instead of `extend`](/proposals/p2760.md#use-extends-instead-of-extend)
     -   [List base class in class declaration](/proposals/p2760.md#list-base-class-in-class-declaration)
 
+-   [#5017: Destructor syntax](https://github.com/carbon-language/carbon-lang/pull/5017)
+
+    -   [Destructor syntax options](/proposals/p5017.md#destructor-syntax-options)
+    -   [Destructor name options](/proposals/p5017.md#destructor-name-options)
+
 ## References
 
 -   [#257: Initialization of memory and variables](https://github.com/carbon-language/carbon-lang/pull/257)
@@ -2300,3 +2306,4 @@ the type of `U.x`."
 -   [#2107: Clarify rules around `Self` and `.Self`](https://github.com/carbon-language/carbon-lang/pull/2107)
 -   [#2287: Allow unqualified name lookup for class members](https://github.com/carbon-language/carbon-lang/pull/2287)
 -   [#2760: Consistent `class` and `interface` syntax](https://github.com/carbon-language/carbon-lang/pull/2760)
+-   [#5017: Destructor syntax](https://github.com/carbon-language/carbon-lang/pull/5017)

+ 1 - 1
docs/design/lexical_conventions/words.md

@@ -61,7 +61,7 @@ The following words are interpreted as keywords:
 -   `constraint`
 -   `continue`
 -   `default`
--   `destructor`
+-   `destroy`
 -   `else`
 -   `export`
 -   `extend`

+ 175 - 0
proposals/p5017.md

@@ -0,0 +1,175 @@
+# Destructor syntax
+
+<!--
+Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+Exceptions. See /LICENSE for license information.
+SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+-->
+
+[Pull request](https://github.com/carbon-language/carbon-lang/pull/5017)
+
+<!-- toc -->
+
+## Table of contents
+
+-   [Abstract](#abstract)
+-   [Problem](#problem)
+-   [Background](#background)
+-   [Proposal](#proposal)
+    -   [Not directly callable](#not-directly-callable)
+-   [Future work](#future-work)
+    -   [Extend syntax to allow explicit marking of _trivial_ destructors](#extend-syntax-to-allow-explicit-marking-of-trivial-destructors)
+    -   [Decide whether to desugar destructors to interfaces](#decide-whether-to-desugar-destructors-to-interfaces)
+    -   [Copy and move functions](#copy-and-move-functions)
+-   [Rationale](#rationale)
+-   [Alternatives considered](#alternatives-considered)
+    -   [Destructor syntax options](#destructor-syntax-options)
+    -   [Destructor name options](#destructor-name-options)
+
+<!-- tocstop -->
+
+## Abstract
+
+Fix destructor syntax ambiguity by switching to `fn destroy` mirroring standard
+function syntax. This is a purely syntactic change, maintaining destructor
+semantics.
+
+## Problem
+
+The
+[accepted destructor syntax](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/classes.md#destructors)
+includes out-of-line definitions such as:
+
+```carbon
+class MyClass {
+  destructor [addr self: Self*];
+}
+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?
+
+For comparison, note a generic might look like:
+
+```carbon
+class GenericClass[T:! type](N:! T) { ... }
+destructor GenericClass[T:! type](N:! T) [addr self: Self*] { ... }
+```
+
+The toolchain is able to parse this in constant time, but only because the lexer
+will pair brackets, so we can do lookahead at the bracket in `GenericClass[` for
+the closing `]`, and look past that for the `(` versus `{`. However, this is
+arbitrary lookahead and may be significantly less efficient in other parsers
+that people might want to use with Carbon, such as tree-sitter.
+
+## Background
+
+-   Proposal
+    [#1154: Destructors](https://github.com/carbon-language/carbon-lang/pull/1154)
+-   Leads question
+    [#4999: Out-of-line destructor syntax ambiguity](https://github.com/carbon-language/carbon-lang/issues/4999)
+-   [2025-02-25 Toolchain minutes](https://docs.google.com/document/d/1Iut5f2TQBrtBNIduF4vJYOKfw7MbS8xH_J01_Q4e6Rk/edit?resourcekey=0-mc_vh5UzrzXfU4kO-3tOjA&tab=t.0#heading=h.vootuzze8e8e)
+
+In particular, we are discussing destruction as possibly similar to copy and
+move syntax, and trying to create a consistency between the functions.
+
+## Proposal
+
+Destructor syntax will use standard function syntax, with `destroy` as a keyword
+for the function name.
+
+For example, in contrast with [problem examples](#problem):
+
+```carbon
+class MyClass {
+  fn destroy[addr self: Self*]();
+}
+fn MyClass.destroy[addr self: Self*]() { ... }
+
+class GenericClass[T:! type](N:! T) { ... }
+fn GenericClass[T:! type](N:! T).destroy[addr self: Self*]() { ... }
+```
+
+It is invalid to add other implicit or explicit parameters to the `destroy`
+function.
+
+### Not directly callable
+
+Although the syntax of `fn destroy` looks similar to a regular function, the
+functions are not designed to be directly callable. This does not add support
+for `my_var.destroy()`. See Proposal #1154, alternative
+[Allow functions to act as destructors](/proposals/p1154.md#allow-functions-to-act-as-destructors)
+for details.
+
+## Future work
+
+### Extend syntax to allow explicit marking of _trivial_ destructors
+
+Discussion has indicated potential utility in syntax to make the expectation of
+a trivial destructor _explicit_. This would allow a declarative way of ensuring
+no member accidentally caused a type to have non-trivial destruction.
+
+Still, this requires a further extension of syntax that isn't proposed at this
+time. Both determining syntax for such a feature and motivating it fully are
+left as future work.
+
+### Decide whether to desugar destructors to interfaces
+
+Under this proposal, `fn destroy` remains a special function. We may want to
+make it desugar to an interface implementation, but even if we do so, the terse
+destructor syntax seems likely to remain. There are concerns about the
+ergonomics of requiring an `impl` in order to add a destructor to a type, and
+decisions would need to be made for how virtual destructors should be handled.
+
+### Copy and move functions
+
+This proposal is set up for consistency with a possible `fn copy` and `fn move`,
+but those will be evaluated as part of copy and move semantics.
+
+## Rationale
+
+-   [Software and language evolution](/docs/project/goals.md#software-and-language-evolution)
+    -   Eliminates ambiguity in `destructor` syntax, by creating consistency
+        with `fn` syntax.
+    -   Claiming `destroy` as a keyword is considered to be a good balance.
+    -   Syntax choices, particularly with the keyword as a function name, should
+        not create a barrier for desugaring to an interface approach for
+        destructions.
+-   [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write)
+    -   Consistency with `fn` syntax should improve readability.
+    -   Features that impact data layout are consistently written like member
+        declarations.
+
+## Alternatives considered
+
+### Destructor syntax options
+
+The ambiguity between `destructor MyClass [...]` out-of-line destructor syntax
+and implicit parameters for generics is a sufficient barrier to change syntax.
+We do not want parsing Carbon to require arbitrary lookahead.
+
+`fn destroy` was preferred because it builds on existing `fn` syntax.
+
+Although adding a `.`, as in `destructor MyClass.[...]`, was brought up, it
+didn't present interesting advantages over `fn destroy`.
+
+### Destructor name options
+
+We expect more name conflicts with C++ code using the `destroy` keyword than
+with the `destructor` keyword, for example with
+[`std::allocator::destroy`](https://en.cppreference.com/w/cpp/memory/allocator/destroy),
+or visible
+[searching LLVM code](https://github.com/search?q=repository%3Allvm%2Fllvm-project+language%3Ac%2B%2B+symbol%3A%2F%28%3F-i%29%5Edestroy%24%2F&type=code).
+
+Still, the phrasing of `destroy`, particularly if we have `copy` and `move` to
+match, is preferred. Raw identifier syntax (`r#destroy`) is expected to be
+sufficient for name conflicts.
+
+`fn delete` was mentioned as an option reusing current keywords, but declined
+due to the "heap allocated" implication of `delete`.
+
+Non-keyword names were considered as part of proposal
+[#1154: Destructors](https://github.com/carbon-language/carbon-lang/pull/1154),
+and the trade-off considerations still apply.