Skip to content

Commit

Permalink
Merge branch 'trunk' into lookup-multi
Browse files Browse the repository at this point in the history
  • Loading branch information
danakj committed Mar 5, 2025
2 parents fc0efe4 + e71d594 commit 3152dfc
Show file tree
Hide file tree
Showing 367 changed files with 4,089 additions and 3,744 deletions.
2 changes: 1 addition & 1 deletion bazel/cc_toolchains/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ selects.config_setting_group(
# Matches macOS platforms.
config_setting(
name = "is_macos",
constraint_values = ["@platforms//os:osx"],
constraint_values = ["@platforms//os:macos"],
)

# For use by rules.bzl.
Expand Down
10 changes: 5 additions & 5 deletions docs/design/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]() { ... }
}
```

Expand All @@ -1969,7 +1969,7 @@ or:
```carbon
class MyClass {
// Can modify `self` in the body.
destructor [addr self: Self*] { ... }
fn destroy[addr self: Self*]() { ... }
}
```

Expand Down
27 changes: 17 additions & 10 deletions docs/design/classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]() { ... }
}
```

Expand All @@ -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
Expand All @@ -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
Expand All @@ -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*]() { ... }
}
```

Expand Down Expand Up @@ -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)
Expand All @@ -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)
2 changes: 1 addition & 1 deletion docs/design/lexical_conventions/words.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ The following words are interpreted as keywords:
- `constraint`
- `continue`
- `default`
- `destructor`
- `destroy`
- `else`
- `export`
- `extend`
Expand Down
8 changes: 4 additions & 4 deletions docs/project/contribution_tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,11 @@ These tools are essential for work on Carbon.
- [Homebrew](https://brew.sh/) (for macOS)
- To upgrade versions of `brew` packages, it will be necessary to
periodically run `brew upgrade`.
- [`python3` and `pip3`](https://python.org)
- [Python](https://python.org)
- Carbon requires Python 3.9 or newer.
- To upgrade versions of `pip3` packages, it will be necessary to
periodically run `pip3 list --outdated`, then
`pip3 install -U <package>` to upgrade desired packages.
- To upgrade versions of pip-installed packages, it will be necessary
to periodically run `pipx list --outdated`, then
`pipx install -U <package>` to upgrade desired packages.
- When upgrading, version dependencies may mean packages _should_ be
outdated, and not be upgraded.
- Main tools
Expand Down
175 changes: 175 additions & 0 deletions proposals/p5017.md
Original file line number Diff line number Diff line change
@@ -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.
5 changes: 4 additions & 1 deletion scripts/target_determinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@ def log(s: str) -> None:


def filter_targets(bazel: Path, targets: str) -> str:
# Need to quote targets for inclusion in another query.
quoted_targets = "\n".join([f'"{t}"' for t in targets.splitlines()])

with tempfile.NamedTemporaryFile(mode="w+") as tmp:
query = (
f"let t = set({targets}) in "
f"let t = set({quoted_targets}) in "
"kind(rule, $t) except attr(tags, manual, $t)\n"
)
query_lines = query.splitlines()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ fn F() -> {} {
// CHECK:STDOUT: }
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
// CHECK:STDOUT: %return.patt: %empty_struct_type = return_slot_pattern
// CHECK:STDOUT: %return.param_patt: %empty_struct_type = out_param_pattern %return.patt, runtime_param0
// CHECK:STDOUT: %return.param_patt: %empty_struct_type = out_param_pattern %return.patt, call_param0
// CHECK:STDOUT: } {
// CHECK:STDOUT: %.loc13_12.1: %empty_struct_type = struct_literal ()
// CHECK:STDOUT: %.loc13_12.2: type = converted %.loc13_12.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
// CHECK:STDOUT: %return.param: ref %empty_struct_type = out_param runtime_param0
// CHECK:STDOUT: %return.param: ref %empty_struct_type = out_param call_param0
// CHECK:STDOUT: %return: ref %empty_struct_type = return_slot %return.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
Expand Down
4 changes: 2 additions & 2 deletions toolchain/check/testdata/alias/no_prelude/in_namespace.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ fn F() -> NS.a {
// CHECK:STDOUT: %b: ref %C = bind_name b, %.loc16_23.6
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
// CHECK:STDOUT: %return.patt: %C = return_slot_pattern
// CHECK:STDOUT: %return.param_patt: %C = out_param_pattern %return.patt, runtime_param0
// CHECK:STDOUT: %return.param_patt: %C = out_param_pattern %return.patt, call_param0
// CHECK:STDOUT: } {
// CHECK:STDOUT: %NS.ref: <namespace> = name_ref NS, file.%NS [concrete = file.%NS]
// CHECK:STDOUT: %a.ref: type = name_ref a, file.%a [concrete = constants.%C]
// CHECK:STDOUT: %return.param: ref %C = out_param runtime_param0
// CHECK:STDOUT: %return.param: ref %C = out_param call_param0
// CHECK:STDOUT: %return: ref %C = return_slot %return.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
Expand Down
4 changes: 2 additions & 2 deletions toolchain/check/testdata/alias/no_prelude/local.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ fn F() -> () {
// CHECK:STDOUT: }
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
// CHECK:STDOUT: %return.patt: %empty_tuple.type = return_slot_pattern
// CHECK:STDOUT: %return.param_patt: %empty_tuple.type = out_param_pattern %return.patt, runtime_param0
// CHECK:STDOUT: %return.param_patt: %empty_tuple.type = out_param_pattern %return.patt, call_param0
// CHECK:STDOUT: } {
// CHECK:STDOUT: %.loc11_12.1: %empty_tuple.type = tuple_literal ()
// CHECK:STDOUT: %.loc11_12.2: type = converted %.loc11_12.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
// CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param runtime_param0
// CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0
// CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
Expand Down
4 changes: 2 additions & 2 deletions toolchain/check/testdata/array/array_in_place.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ fn G() {
// CHECK:STDOUT: %Core.import = import Core
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
// CHECK:STDOUT: %return.patt: %tuple.type.189 = return_slot_pattern
// CHECK:STDOUT: %return.param_patt: %tuple.type.189 = out_param_pattern %return.patt, runtime_param0
// CHECK:STDOUT: %return.param_patt: %tuple.type.189 = out_param_pattern %return.patt, call_param0
// CHECK:STDOUT: } {
// CHECK:STDOUT: %int_32.loc11_12: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
// CHECK:STDOUT: %i32.loc11_12: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
Expand All @@ -59,7 +59,7 @@ fn G() {
// CHECK:STDOUT: %i32.loc11_22: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
// CHECK:STDOUT: %.loc11_25.1: %tuple.type.ff9 = tuple_literal (%i32.loc11_12, %i32.loc11_17, %i32.loc11_22)
// CHECK:STDOUT: %.loc11_25.2: type = converted %.loc11_25.1, constants.%tuple.type.189 [concrete = constants.%tuple.type.189]
// CHECK:STDOUT: %return.param: ref %tuple.type.189 = out_param runtime_param0
// CHECK:STDOUT: %return.param: ref %tuple.type.189 = out_param call_param0
// CHECK:STDOUT: %return: ref %tuple.type.189 = return_slot %return.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {}
Expand Down
Loading

0 comments on commit 3152dfc

Please sign in to comment.