Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
be8587e
Expression form basics
geoffromer May 27, 2025
32c4e1c
Typo fix
geoffromer May 27, 2025
dcd79b6
Apply suggestions from code review
geoffromer May 29, 2025
5dcbfa2
Add proposal doc
geoffromer May 28, 2025
c02677b
Respond to reviewer comments
geoffromer May 29, 2025
e5128ce
Respond to reviewer comments
geoffromer May 30, 2025
fe90de1
Respond to reviewer comments
geoffromer Jun 4, 2025
7c5efcf
Apply suggestions from code review
geoffromer Jun 5, 2025
8e9803b
Apply suggestions from code review
geoffromer Jun 5, 2025
3ef17ee
Respond to reviewer comments
geoffromer Jun 5, 2025
a418308
Apply suggestions from code review
geoffromer Jun 26, 2025
10bc415
Apply suggestions from code review
geoffromer Jun 26, 2025
3a092a6
Respond to reviewer comments
geoffromer Jun 30, 2025
ea46717
Apply suggestions from code review
geoffromer Jul 22, 2025
41e6eec
Respond to reviewer comments
geoffromer Jul 22, 2025
9f4ec05
Merge branch 'trunk' into forms-proposal
geoffromer Aug 6, 2025
6bcbb95
Major restructuring
geoffromer Aug 7, 2025
97f31e6
Respond to reviewer comments
geoffromer Aug 7, 2025
76bc89e
Miscellaneous fixes
geoffromer Aug 20, 2025
73efff9
Add missing alternatives-considered
geoffromer Aug 22, 2025
dbe872b
Respond to reviewer comments
geoffromer Sep 9, 2025
f3b4735
Respond to reviewer comments
geoffromer Sep 15, 2025
5d59604
Apply suggestions from code review
geoffromer Sep 29, 2025
23cb28f
Respond to reviewer comments
geoffromer Sep 29, 2025
dff427f
Apply suggestions from code review
geoffromer Sep 29, 2025
5d6ecf0
Respond to reviewer comments
geoffromer Oct 2, 2025
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
14 changes: 8 additions & 6 deletions docs/design/classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -600,19 +600,21 @@ Assert(different_order.x == 3);
Assert(different_order.y == 2);
```

Initialization and assignment occur field-by-field. The order of fields is
determined from the target on the left side of the `=`. This rule matches what
we expect for classes with encapsulation more generally.
Initialization and assignment occur field-by-field. The overall order is that
the initializer is fully evaluated in the order it's written, and then each
field of the new object is initialized from the corresponding element of the
result, in the new object's field order. However, in some cases evaluation of
the initializer can directly initialize fields on the left-hand side, without
any intervening conversions. When that happens, the order of initialization of
those fields is determined by the evaluation order of the initializer. See
[here](values.md#type-conversions) for details.

**Open question:** What operations and in what order happen for assignment and
initialization?

- Is assignment just destruction followed by initialization? Is that
destruction completed for the whole object before initializing, or is it
interleaved field-by-field?
- When initializing to a literal value, is a temporary containing the literal
value constructed first or are the fields initialized directly? The latter
approach supports types that can't be moved or copied, such as mutex.
- Perhaps some operations are _not_ ordered with respect to each other?

### Operations performed field-wise
Expand Down
5 changes: 5 additions & 0 deletions docs/design/expressions/implicit_conversions.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- [Same type](#same-type)
- [Pointer conversions](#pointer-conversions)
- [Facet types](#facet-types)
- [Struct, tuple, and array types](#struct-tuple-and-array-types)
- [Consistency with `as`](#consistency-with-as)
- [Extensibility](#extensibility)
- [Alternatives considered](#alternatives-considered)
Expand Down Expand Up @@ -189,6 +190,10 @@ implicitly converted to the facet type `TT2` if `T`
[satisfies the requirements](../generics/details.md#subtyping-between-facet-types)
of `TT2`.

### Struct, tuple, and array types

See [here](/docs/design/values.md#type-conversions).

## Consistency with `as`

An implicit conversion of an expression `E` of type `T` to type `U`, when
Expand Down
47 changes: 20 additions & 27 deletions docs/design/expressions/member_access.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,17 +121,17 @@ A member access expression is processed using the following steps:
The process of _member resolution_ determines which member `M` a member access
expression is referring to.

For a simple member access, if the first operand is a type, facet, package, or
namespace, a search for the member name is performed in the first operand. If
the first operand is a form, the search is performed in its
[type component](/docs/design/values.md#expression-forms). Otherwise, a search
for the member name is performed in the type of the first operand. In either
case, the search must succeed. In the latter case, if the result is an instance
member, then [instance binding](#instance-binding) is performed on the first
operand.

Note that this means that forms never participate in simple member access,
except through their type components.
For a simple member access, if the first operand is a type, form, facet,
package, or namespace, a search for the member name is performed in the first
operand. Otherwise, a search for the member name is performed in the type of the
first operand. In either case, the search must succeed. In the latter case, if
the result is an instance member, then [instance binding](#instance-binding) is
performed on the first operand.

A search for a name within a form searches for the name in its
[type component](/docs/design/values.md#expression-forms). Note that this means
that the form of an expression never affects simple member access into that
expression, except through its type component.

For a compound member access, the second operand is evaluated as a compile-time
constant to determine the member being accessed. The evaluation is required to
Expand Down Expand Up @@ -193,8 +193,7 @@ class Bar {
If the first operand is a type, form, or facet, it must be a compile-time
constant. This disallows member access into a type except during compile-time,
see leads issue
[#1293](https://github.com/carbon-language/carbon-lang/issues/1293). If the
first operand is a form, lookup searches in its type component.
[#1293](https://github.com/carbon-language/carbon-lang/issues/1293).

Like the previous case, types (including
[facet types](/docs/design/generics/terminology.md#facet-type)) have member
Expand Down Expand Up @@ -229,6 +228,9 @@ class Avatar {
Simple member access `(Avatar as Cowboy).Draw` finds the `Cowboy.Draw`
implementation for `Avatar`, ignoring `Renderable.Draw`.

Similarly, a form has members, specifically the members of the form's type
component.

### Tuple indexing

Tuple types have member names that are *integer-literal*s, not *word*s.
Expand Down Expand Up @@ -735,20 +737,11 @@ of `x` and instance binding is performed if an instance member is found.
If instance binding is to be performed, the result of instance binding depends
on what instance member `M` was found:

- For a field member of a struct type, `x` is required to have that struct
type, and it is converted to an expression with
[struct form](/docs/design/values.md#expression-forms) by applying
[form decomposition](/docs/design/values.md#form-conversions) (if it doesn't
have a struct form already). Then, the form of `x.f` is the value of the
corresponding field of the struct form, and `x.f` evaluates to the
corresponding field of `x`.
- For an element member of a tuple type, `x` is required to have that tuple
type, and it is converted to a
[tuple form](/docs/design/values.md#expression-forms) by applying
[form decomposition](/docs/design/values.md#form-conversions) (if it doesn't
have a tuple form already). Then, the form of `x.f` is the value of the
corresponding element of the tuple form, and `x.f` evaluates to the
corresponding element of `x`.
- For a field member of a struct type or tuple type, `x` is required to have
that type. `x` is converted to a struct or tuple form by
[form decomposition](/docs/design/values.md#category-conversions), and the
`.f` element of the outcome of that conversion becomes the outcome of `x.f`.
All other elements are [discarded](/docs/design/values.md#form-conversions).
- For a field member in class `C`, `x` is required to be of type `C` or of a
type derived from `C`. The result is the corresponding subobject within `x`.
If `x` is an
Expand Down
5 changes: 5 additions & 0 deletions docs/design/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ possible syntaxes:
`fn Sleep(seconds: i64) -> ();`.
- `()` is similar to a `void` return type in C++.

> **TODO:** Update this section to cover return forms, as discussed
> [here](values.md#function-calls-and-returns).

### `return` statements

The [`return` statement](control_flow/return.md) is essential to function
Expand All @@ -104,6 +107,8 @@ When the return clause is provided, including when it is `-> ()`, the `return`
statement must have an expression that is convertible to the return type, and a
`return` statement must be used to end control flow of the function.

> **TODO:** Update this section to cover the form

## Function declarations

Functions may be declared separate from the definition by providing only a
Expand Down
119 changes: 72 additions & 47 deletions docs/design/pattern_matching.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,14 +129,16 @@ fn F() {

A name binding pattern is a pattern.

- _binding-pattern_ ::= _identifier_ `:` _expression_
- _binding-pattern_ ::= `ref`? (_identifier_ | `self`) `:` _expression_
- _binding-pattern_ ::= `template`? _identifier_ `:!` _expression_
- _proper-pattern_ ::= _binding-pattern_

A name binding pattern declares a _binding_ with a name specified by the
_identifier_, which can be used as an expression. If the binding pattern is
enclosed by a `var` pattern, it is a _reference binding pattern_, and otherwise
it is a _value binding pattern_, and the binding is a value expression.
prefixed with `ref` or enclosed by a `var` pattern, it is a _reference binding
pattern_, and otherwise it is a _value binding pattern_. A binding pattern
enclosed by a `var` pattern cannot have a `ref` prefix, because it would be
redundant.

A _variable binding pattern_ is a special kind of reference binding pattern,
which is the immediate subpattern of its enclosing `var` pattern.
Expand All @@ -151,19 +153,30 @@ If the pattern syntax uses `:` it is a _runtime binding pattern_. If it uses
pattern_ or a _template binding pattern_, depending on whether it is prefixed
with `template`.

The scrutinee expression is expected to have a
[primitive form](values.md#expression-forms) with the following components, and
its form is [converted](values.md#form-conversions) as needed to satisfy that
expectation:

- The type is expected to be _expression_.
- The category is expected to be "value" if the pattern is a value binding
pattern, or "durable reference" if it's a reference binding pattern
- The phase is expected to be "runtime", "symbolic", or "template" depending
on whether the pattern is a runtime, symbolic, or template binding pattern.

The binding is _bound_ to the converted scrutinee expression, which makes the
binding an alias for it, with the same form and value.
The binding declared by a binding pattern has a
[primitive form](values.md#expression-forms) with the following components:

- The type is _expression_.
- The category is "value" if the pattern is a value binding pattern, "owning
durable reference" if it's a variable binding pattern, or "non-owning
durable reference" if it's a non-variable reference binding pattern.
- The phase is "runtime", "symbolic", or "template" depending on whether the
pattern is a runtime, symbolic, or template binding pattern.

During pattern matching, the scrutinee is implicitly converted as needed to have
the same form, and then the binding is _bound_ to the result of these
conversions. This makes a runtime or template binding an alias for the converted
scrutinee expression, with the same form and value. Symbolic bindings are more
complex: the binding will have the same type, category, and phase as the
converted scrutinee expression, but its constant value is an opaque symbol
introduced by the binding, which the type system knows to be equal to the
converted scrutinee expression.

Note that there is no way to implicitly convert to a durable reference
expression from any other category, so the scrutinee of a reference binding
pattern must already be a durable reference. `var` pattern matching ensures that
this is the case for the bindings nested inside it, but for `ref` binding
patterns the user-provided scrutinee must meet this requirement itself.

```carbon
fn F() -> i32 {
Expand All @@ -178,14 +191,22 @@ fn F() -> i32 {
}
```

`self` can be used instead of an identifier only if the pattern is an implicit
parameter of a member function (optionally enclosed in a `var` pattern). This
marks the function as a method; during pattern matching, the parameter pattern
containing `self` is matched with the object that the method was invoked on.
Other than that, a `self` pattern behaves just like an ordinary binding pattern,
introducing a binding named `self` into scope, just as if `self` were an
identifier rather than a keyword.

#### Unused bindings

A syntax like a binding but with `_` in place of an identifier, or `unused`
before the name, can be used to ignore part of a value. Names that are qualified
with the `unused` keyword are visible for name lookup but uses are invalid,
including when they cause ambiguous name lookup errors. If attempted to be used,
a compiler error will be shown to the user, instructing them to either remove
the `unused` qualifier or remove the use.
before the name, [discards](values.md#form-conversions) the scrutinee. Names
that are qualified with the `unused` keyword are visible for name lookup but
uses are invalid, including when they cause ambiguous name lookup errors. If
attempted to be used, a compiler error will be shown to the user, instructing
them to either remove the `unused` qualifier or remove the use.

- _binding-pattern_ ::= `_` `:` _expression_
- _binding-pattern_ ::= `template`? `_` `:!` _expression_
Expand Down Expand Up @@ -280,13 +301,11 @@ scrutinee.
- _proper-pattern_ ::= `var` _proper-pattern_

The scrutinee is expected to have the same type as the resolved type of the
nested _proper-pattern_, and it is expected to be a runtime-phase initializing
expression. The scrutinee expression is converted as needed to satisfy those
expectations, and then used to initialize a newly-allocated object of that type.
The `var` pattern then matches if the nested _proper-pattern_ matches a durable
reference expression referring to that object. As a result, any reference
binding patterns within the nested pattern refer to portions of the
corresponding object rather than to the original scrutinee.
nested _proper-pattern_, and it is expected to be a runtime-phase owning
ephemeral reference expression. The scrutinee expression is converted as needed
to satisfy those expectations, and the `var` pattern takes ownership of the
referenced object, promotes it to an owning _durable_ reference expression, and
matches the nested _proper-pattern_ with it.

The lifetime of the allocated object extends to the end of scope of the `var`
pattern (that is the scope that any bindings declared within it would have).
Expand Down Expand Up @@ -326,13 +345,12 @@ A _tuple-pattern_ containing no commas is treated as grouping parens: the
contained _proper-pattern_ is matched directly against the scrutinee. Otherwise,
the behavior is as follows.

The scrutinee is expected to have a [tuple form](values.md#expression-forms)
with the same arity as the number of nested _proper_patterns_, and
[form decomposition](values.md#form-conversions) (but no other conversion) is
applied if necessary to satisfy that. Then, each nested _proper-pattern_ is
matched against `s.i`, where `s` is the converted scrutinee expression and `i`
is the 0-based index of the nested pattern within the tuple pattern. The tuple
pattern matches if all of these sub-matches succeed.
The scrutinee is required to be of tuple type, with the same arity as the number
of nested _proper-patterns_. It is converted to a tuple form by
[form decomposition](values.md#form-conversions), and then each nested
_proper-pattern_ in left-to-right order is matched against the corresponding
element of the converted scrutinee's [outcome](values.md#expression-forms). The
tuple pattern matches if all of these sub-matches succeed.

Note that a tuple pattern must contain at least one _proper-pattern_. Otherwise,
it is a tuple-valued expression. However, a tuple pattern and a corresponding
Expand Down Expand Up @@ -365,13 +383,17 @@ match ({.a = 1, .b = 2}) {
}
```

The scrutinee is expected to have a [struct form](values.md#expression-forms)
with the same set of field names as the pattern, and
[form decomposition](values.md#form-conversions) (but no other conversion) is
applied if necessary to satisfy that. Then, for each subpattern of the struct
pattern in left-to-right order, the subpattern is matched with `s.f`, where `s`
is the converted scrutinee expression and `f` is the field name associated with
the subpattern. The struct pattern matches if all of these sub-matches succeed.
The scrutinee is required to be of struct type, and every field name in the
pattern must be a field name in the scrutinee. It is converted to a struct form
by [form decomposition](values.md#form-conversions) and then, for each
subpattern of the struct pattern in left-to-right order, the subpattern is
matched with the same-named element of the converted scrutinee's
[outcome](values.md#expression-forms). If the scrutinee outcome has any field
names not present in the pattern, those sub-outcomes are
[discarded](values.md#form-conversions) in lexical order if the pattern has a
trailing `_`, or diagnosed as an error if it does not. The struct pattern
matches if all of these sub-matches succeed.

Note that the left-to-right order is consistent with the behavior of matching
against a struct-valued expression, where the expression pattern becomes the
left operand of the `==` and so determines the order in which `==` comparisons
Expand All @@ -386,6 +408,9 @@ match ({.a = 1, .b = 2}) {
}
```

Likewise, `ref a: T` is synonymous with `.a = ref a: T`, and `var a: T` is
synonymous with `.a = var a: T`.

If some fields should be ignored when matching, a trailing `, _` can be added to
specify this:

Expand Down Expand Up @@ -695,11 +720,11 @@ Using `auto` for a type will always match, making `_: auto` the wildcard
pattern.

If the scrutinee expression's [form](values.md#expression-forms) contains any
primitive forms with category "initializing", they are converted to ephemeral
reference expressions by [materialization](values.md#temporary-materialization)
before pattern matching begins, so that the result can be reused by multiple
`case`s. However, the objects created by `var` patterns are not reused by
multiple `case`s:
primitive forms with category "initializing", they are converted to non-owning
ephemeral reference expressions by
[materialization](values.md#temporary-materialization) before pattern matching
begins, so that the result can be reused by multiple `case`s. However, the
objects created by `var` patterns are not reused by multiple `case`s:

```carbon
class X {
Expand Down
9 changes: 9 additions & 0 deletions docs/design/tuples.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

- [Overview](#overview)
- [Element access](#element-access)
- [Conversion](#conversion)
- [Empty tuples](#empty-tuples)
- [Trailing commas and single-element tuples](#trailing-commas-and-single-element-tuples)
- [Tuple of types and tuple types](#tuple-of-types-and-tuple-types)
Expand Down Expand Up @@ -64,6 +65,14 @@ fn Choose(template N:! i32) -> i32 {
}
```

## Conversion

A tuple type `Source` can be converted to a tuple type `Dest` if they have the
same number of elements, and each element type of `Source` is convertible to the
corresponding element type of `Dest`, and the conversion is implicit if all of
the element type conversions are implicit. See
[here](values.md#type-conversions) for full details.

### Empty tuples

`()` is the empty tuple. This is used in other parts of the design, such as
Expand Down
Loading
Loading