Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

as expressions #845

Merged
merged 19 commits into from
Oct 29, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
7 changes: 5 additions & 2 deletions docs/design/expressions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
## Table of contents

- [Overview](#overview)
- [Implicit conversions](#implicit-conversions)
- [Conversions and casts](#conversions-and-casts)

<!-- tocstop -->

Expand All @@ -29,12 +29,15 @@ fn Foo(a: i32*) -> i32 {
Here, the parameter type `i32*`, the return type `i32`, and the operand `*a` of
the `return` statement are all expressions.

## Implicit conversions
## Conversions and casts

When an expression appears in a context in which an expression of a specific
type is expected, [implicit conversions](implicit_conversions.md) are applied to
convert the expression to the target type.

Expressions can also be converted to a specific type using an
[`as` expression](as_expressions.md).
chandlerc marked this conversation as resolved.
Show resolved Hide resolved

```
fn Bar(n: i32);
fn Baz(n: i64) {
Expand Down
147 changes: 147 additions & 0 deletions docs/design/expressions/as_expressions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# `as` expressions

<!--
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
-->

<!-- toc -->

## Table of contents

- [Overview](#overview)
- [Built-in types](#built-in-types)
- [Data types](#data-types)
- [Compatible types](#compatible-types)
- [Pointer conversions](#pointer-conversions)
- [Extensibility](#extensibility)
- [Alternatives considered](#alternatives-considered)
- [References](#references)

<!-- tocstop -->

## Overview
geoffromer marked this conversation as resolved.
Show resolved Hide resolved

An expression of one type can be explicitly cast to another type by using an
`as` expression:

```
var n: i32 = Get();
var f: f32 = n as f32;
```

An `as` expression can be used to perform any implicit conversion, either when
the context does not imply a destination type or when it is valuable to a reader
of the code to make the conversion explicit. In addition, `as` expressions can
perform safe conversions that nonetheless should not be performed implicitly,
such as lossy conversions or conversions that lose capabilities or change the
way a type would be interpreted.

chandlerc marked this conversation as resolved.
Show resolved Hide resolved
As guidelines, an `as` conversion should be permitted when:

- The conversion is _safe_: it produces a well-defined output value for each
input value.
- The conversion is _unsurprising_: the resulting value is the expected value
in the destination type.

In cases where a cast is only defined for a subset of the possible inputs, an
`unsafe_as` expression can be used. An `unsafe_as` expression behaves like an
`as` expression, except that the domain of the conversion is narrower than the
entire input type, so the conversion is not safe as defined above.

It is possible for user-defined types to [extend](#extensibility) the set of
valid explicit casts that can be performed by `as` and `unsafe_as`. Such
extensions are expected to follow these guidelines.

## Built-in types

### Data types

In addition to the [implicit conversions](implicit_conversions.md#data-types),
the following numeric conversion is supported by `as`:
zygoloid marked this conversation as resolved.
Show resolved Hide resolved

- `iN`, `uN`, or `fN` -> `fM`, for any `N` and `M`. Values that cannot be
exactly represented are suitably rounded to one of the two nearest
representable values. Very large finite values may be rounded to an
infinity. NaN values are converted to NaN values.

- `iN` or `uN` -> `bool`. Zero converts to `false` and non-zero values convert
to `true`.
geoffromer marked this conversation as resolved.
Show resolved Hide resolved

- `bool` -> `iN` or `uN`. `false` converts to `0` and `true` converts to `1`.
geoffromer marked this conversation as resolved.
Show resolved Hide resolved

The following additional numeric conversions are supported by `unsafe_as`:

- `iN` or `uN` -> `iM` or `uM`, for any `N` and `M`. It is a programming error
if the source value cannot be represented in the destination type.

**TODO:** Once we have a two's complement truncation operation with defined
behavior on overflow, link to it from here as an alternative.

- `fN` -> `iM`, for any `N` and `M`. Values that cannot be exactly represented
are suitably rounded to one of the two nearest representable values. It is a
programming error if the source value does not round to an integer that can
be represented in the destination type.
chandlerc marked this conversation as resolved.
Show resolved Hide resolved

**Note:** The precise rounding rules for these conversions have not yet been
decided.

### Compatible types

The following conversion is supported by `as`:

- `T` -> `U` if `T` is
[cmopatible](../generics/terminology.md#compatible-types) with `U`.

**Future work:** We may need a mechanism to restrict which conversions between
adapters are permitted and which code can perform them. Some of the conversions
permitted by this rule may only be allowed in certain contexts.

### Pointer conversions

The following pointer conversion is supported by `unsafe_as`:

- `T*` -> `U*` if `U` is a subtype of `T`.

This cast converts in the opposite direction to the corresponding
[implicit conversion](implicit_conversions.md#pointer-conversions). It is a
programming error if the source pointer does not point to a `U` object.

**Note:** `unsafe_as` cannot convert between unrelated pointer types, because
there are no input values for which the conversion would produce a well-defined
output value. Separate facilities will be provided for reinterpreting memory as
a distinct type.

## Extensibility

Explicit casts can be defined for user-defined types such as
[classes](../classes.md) by implementing the `As` or `UnsafeAs` interface:

```
interface UnsafeAs(Dest:! Type) {
fn Convert[me: Self]() -> Dest;
}
interface As(Dest:! Type) extends UnsafeAs(Dest) {
// Inherited from UnsafeAs(Dest):
// fn Convert[me: Self]() -> Dest;
}
```

The expression `x as U` is rewritten to `x.(As(U).Convert)()`. The expression
`x unsafe_as U` is rewritten to `x.(UnsafeAs(U).Convert)()`.

**Future work:** Add a `TryAs` interface to test whether an input value is in
the domain of the conversion and convert it if so.
zygoloid marked this conversation as resolved.
Show resolved Hide resolved

## Alternatives considered

- [Do not distinguish between safe and unsafe casts](/docs/proposals/p0845.md#no-unsafe_as)
- [Do not distinguish between safe as and implicit conversions](/docs/proposals/p0845.md#as-only-performs-implicit-conversions)
zygoloid marked this conversation as resolved.
Show resolved Hide resolved
- [Allow `unsafe_as` to perform bit casts](/docs/proposals/p0845.md#unsafe_as-performs-bit-casts)

## References

- [Implicit conversions in C++](https://en.cppreference.com/w/cpp/language/implicit_conversion)
- Proposal
[#845: `as` expressions](https://github.com/carbon-language/carbon-lang/pull/845).
27 changes: 11 additions & 16 deletions docs/design/expressions/implicit_conversions.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,34 +248,29 @@ of `TT2`.
## Semantics

An implicit conversion of an expression `E` of type `T` to type `U`, when
permitted, always has the same meaning as the explicit cast expression `E as U`.
Moreover, such an implicit conversion is expected to exactly preserve the value.
For example, `(E as U) as T`, if valid, should be expected to result in the same
value as produced by `E`.

**Note:** The explicit cast expression syntax has not yet been decided. The use
of `E as T` in this document is provisional.
permitted, always has the same meaning as the
[explicit cast expression `E as U`](as_expressions.md). Moreover, such an
implicit conversion is expected to exactly preserve the value. For example,
`(E as U) as T`, if valid, should be expected to result in the same value as
produced by `E`.

## Extensibility

Implicit conversions can be defined for user-defined types such as
[classes](../classes.md) by implementing the `ImplicitAs` interface:
[classes](../classes.md) by implementing the `ImplicitAs` interface, which
extends
[the `As` interface used to implement `as` expressions](as_expressions.md#extensibility):

```
interface As(Dest:! Type) {
fn Convert[me: Self]() -> Dest;
interface ImplicitAs(Dest:! Type) extends As(Dest) {
// Inherited from As(Dest):
// fn Convert[me: Self]() -> Dest;
}
interface ImplicitAs(Dest:! Type) extends As(Dest) {}
```

When attempting to implicitly convert an expression `x` to type `U`, the
expression is rewritten to `x.(ImplicitAs(U).Convert)()`.

**Note:** The `As` interface is intended to be used as the implementation
vehicle for explicit casts: `x as U` would be rewritten as
`x.(As(U).Convert)()`. However, the explicit cast expression syntax has not yet
been decided, so this rewrite is provisional.

Note that implicit conversions are not transitive. Even if an
`impl A as ImplicitAs(B)` and an `impl B as ImplicitAs(C)` are both provided, an
expression of type `A` cannot be implicitly converted to type `C`. Allowing
Expand Down
2 changes: 1 addition & 1 deletion docs/design/generics/details.md
Original file line number Diff line number Diff line change
Expand Up @@ -1572,7 +1572,7 @@ addition to using the same data representation, they both implement one
interface, `Hashable`, and use the same implementation for that interface. The
one difference between them is that `Song as Hashable` may be implicitly
converted to `Song`, which implements interface `Printable`, and
`PlayableSong as Hashable` may be implicilty converted to `PlayableSong`, which
`PlayableSong as Hashable` may be implicitly converted to `PlayableSong`, which
implements interface `Media`. This means that it is safe to convert between
`HashMap(Song, Int) == HashMap(Song as Hashable, Int)` and
`HashMap(PlayableSong, Int) == HashMap(PlayableSong as Hashable, Int)` (though
Expand Down
47 changes: 47 additions & 0 deletions docs/design/lexical_conventions/words.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
## Table of contents

- [Overview](#overview)
- [Keywords](#keywords)
- [Alternatives considered](#alternatives-considered)
- [References](#references)

Expand All @@ -27,6 +28,52 @@ follow lexical conventions for identifiers based on
the precise rules are decided; see the
[Unicode source files](/proposals/p0142.md#characters-in-identifiers) proposal.

## Keywords
geoffromer marked this conversation as resolved.
Show resolved Hide resolved

The following words are interpreted as keywords:

- `abstract`
- `addr`
- `alias`
- `and`
- `api`
- `as`
- `auto`
- `base`
- `break`
- `case`
- `class`
- `continue`
- `default`
- `else`
- `extends`
- `external`
- `fn`
- `for`
- `friend`
- `if`
- `impl`
- `import`
- `interface`
- `let`
- `library`
- `match`
- `namespace`
- `not`
- `or`
- `override`
- `package`
- `partial`
- `private`
- `protected`
- `return`
- `returned`
- `unsafe_as`
- `var`
- `virtual`
- `where`
- `while`

## Alternatives considered

- [Character encoding: We could restrict words to ASCII.](/proposals/p0142.md#character-encoding-1)
Expand Down
1 change: 1 addition & 0 deletions proposals/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,6 @@ request:
- [0777 - Inheritance](p0777.md)
- [0820 - Implicit conversions](p0820.md)
- [0829 - One way principle](p0829.md)
- [0845 - `as` expressions](p0845.md)

<!-- endproposals -->
63 changes: 63 additions & 0 deletions proposals/p0845.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# `as` expressions

<!--
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/845)

<!-- toc -->

## Table of contents

- [Problem](#problem)
- [Background](#background)
- [Proposal](#proposal)
- [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals)
- [Alternatives considered](#alternatives-considered)
- [No `unsafe_as`](#no-unsafe_as)
- [`as` only performs implicit conversions](#as-only-performs-implicit-conversions)
- [`unsafe_as` performs bit casts](#unsafe_as-performs-bit-casts)

<!-- tocstop -->

## Problem

TODO: What problem are you trying to solve? How important is that problem? Who
is impacted by it?

## Background

TODO: Is there any background that readers should consider to fully understand
this problem and your approach to solving it?

## Proposal

See changes to the design.

## Rationale based on Carbon's goals
josh11b marked this conversation as resolved.
Show resolved Hide resolved

TODO: How does this proposal effectively advance Carbon's goals? Rather than
re-stating the full motivation, this should connect that motivation back to
Carbon's stated goals for the project or language. This may evolve during
review. Use links to appropriate goals, for example:

- [Community and culture](/docs/project/goals.md#community-and-culture)
- [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem)
- [Performance-critical software](/docs/project/goals.md#performance-critical-software)
- [Software and language evolution](/docs/project/goals.md#software-and-language-evolution)
- [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write)
- [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms)
- [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development)
- [Modern OS platforms, hardware architectures, and environments](/docs/project/goals.md#modern-os-platforms-hardware-architectures-and-environments)
- [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code)

## Alternatives considered

### No `unsafe_as`

### `as` only performs implicit conversions

### `unsafe_as` performs bit casts