An incomplete, early, and in-progress overview of the language design. (#83)

Co-authored by: chandlerc

- Based on [PR 22](https://github.com/carbon-language/carbon-lang/pull/83)
- [Idea topic](https://forums.carbon-lang.dev/t/proposal-for-an-incomplete-rough-high-level-overview-ready-for-early-feedback/52)
- [RFC](https://forums.carbon-lang.dev/t/rfc-an-incomplete-early-and-in-progress-overview-of-the-language-design/73)
- [Decision announcement](https://forums.carbon-lang.dev/t/accepted-an-incomplete-early-and-in-progress-overview-of-the-language-design/110)

This proposal should be considered a starting point of the language design. It's not intended to be final; language details may change. This is intended to offer a reasonable starting point for:

- Example code.
- Conceptualizing Carbon at a high level.
- Reasonable, but not necessarily final, approaches to features in README.md.
  - If any idea is obviously bad, we can clean it up here.

This proposal is not intended to achieve:

- A whole language design.
  - This is way too much work for a single proposal; this is a skeletal framework only.
  - As we work on feature-specific designs, we may decide to use other approaches. That's fine: we only need somewhere to start.
  - The summaries in README.md may be expected to change over time.
- Feature-specific files aren't intended to be well-written or comprehensive. They are a quick jot of prior thoughts.
  - We want to avoid getting stuck on language details that we should consider
    more carefully regardless. If you're passionate about a feature, please feel
    free to start a new proposal for it.
  - Each and every aspect of the suggested overview should be subject to careful
    examination and justification before it becomes a settled plan of record.

Chandler started this with https://github.com/carbon-language/carbon-lang/pull/22. I've taken it over with the following changes:

- More of a directory hierarchy.
- Trying to thin out the main file (now README.md) to lighter summaries of features.
- Details/rationale/alternatives should be in feature-specific files.
  - Draft files are linked as references where added.

For an example of how we may proceed with feature-specific designs, see https://github.com/carbon-language/carbon-lang/pull/80. In this structure:

- docs/design/README.md mentions interoperability, with a light overview.
  - The light overview is not yet in https://github.com/carbon-language/carbon-lang/pull/80.
- docs/design/interoperability/README.md goes into more depth on interoperability, covering key points of the approach.
- Individual files in docs/design/interoperability/* go into more depth on interoperability.

Simple designs may not have a subdirectory. All current feature-specific designs do not -- they may be moved later.
This commit is contained in:
Jon Meow
2020-07-30 11:37:15 -07:00
committed by GitHub
parent b8be334ab3
commit d031f3cdab
21 changed files with 2334 additions and 0 deletions
+896
View File
@@ -0,0 +1,896 @@
# Language design overview
<!--
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
-->
## Table of contents
<!-- toc -->
- [Context and disclaimer](#context-and-disclaimer)
- [Example code](#example-code)
- [Basic syntax](#basic-syntax)
- [Code and comments](#code-and-comments)
- [Files, libraries, and packages](#files-libraries-and-packages)
- [Names and scopes](#names-and-scopes)
- [Naming conventions](#naming-conventions)
- [Aliases](#aliases)
- [Name lookup](#name-lookup)
- [Name lookup for common types](#name-lookup-for-common-types)
- [Expressions](#expressions)
- [Functions](#functions)
- [Blocks and statements](#blocks-and-statements)
- [Variables](#variables)
- [Lifetime and move semantics](#lifetime-and-move-semantics)
- [Control flow](#control-flow)
- [`if`/`else`](#ifelse)
- [`while`, `break`, and `continue`](#while-break-and-continue)
- [`return`](#return)
- [Types](#types)
- [Primitive types](#primitive-types)
- [Composite types](#composite-types)
- [Tuples](#tuples)
- [Variants](#variants)
- [Pointers and references](#pointers-and-references)
- [Arrays and slices](#arrays-and-slices)
- [User-defined types](#user-defined-types)
- [Structs](#structs)
- [Allocation, construction, and destruction](#allocation-construction-and-destruction)
- [Assignment, copying, and moving](#assignment-copying-and-moving)
- [Comparison](#comparison)
- [Implicit and explicit conversion](#implicit-and-explicit-conversion)
- [Inline type composition](#inline-type-composition)
- [Unions](#unions)
- [Pattern matching](#pattern-matching)
- [`match` control flow](#match-control-flow)
- [Pattern matching in local variables](#pattern-matching-in-local-variables)
- [Pattern matching as function overload resolution](#pattern-matching-as-function-overload-resolution)
- [Type abstractions](#type-abstractions)
- [Interfaces](#interfaces)
- [Generics](#generics)
- [Templates](#templates)
- [Types with template parameters](#types-with-template-parameters)
- [Functions with template parameters](#functions-with-template-parameters)
- [Overloading](#overloading)
- [Metaprogramming](#metaprogramming)
- [Execution abstractions](#execution-abstractions)
- [Abstract machine and execution model](#abstract-machine-and-execution-model)
- [Lambdas](#lambdas)
- [Co-routines](#co-routines)
- [Bidirectional interoperability with C/C++](#bidirectional-interoperability-with-cc)
<!-- tocstop -->
## Context and disclaimer
Eventually, this document hopes to provide a high-level overview of the design
of the Carbon language. It should summarize the key points across the different
aspects of the language design and link to more detailed and comprehensive
design documents to expand on specific aspects of the design. That means it
isn't and doesn't intend to be complete or stand on its own. Notably, it doesn't
attempt to provide detailed and comprehensive justification for design
decisions. Those should instead be provided by the dedicated and focused designs
linked to from here. However, it should provide an overarching view of the
design and a good basis for diving into specific details.
However, these are extremely early days for Carbon. Currently, this document
tries to capture two things:
1. Initial musings about what _might_ make sense as a basis for Carbon. These
are largely informed by idle discussions between C++ and Clang developers
over the years, and should not be given any particular weight.
2. A summary and snapshot of in-progress efforts to flesh out and motivate
specific designs for parts of the language.
The utility of capturing these at this early stage is primarily to give everyone
a reasonably consistent set of terminology and context as we begin fleshing out
concrete (and well justified) designs for each part of the language. In some
cases, it captures ideas that may be interesting to explore, but isn't meant to
overly anchor on them. Any ideas here need to be fully explored and justified
with a detailed analysis. The context of #1 (directly evolving C++, experience
building Clang, and experience working on C++ codebases including Clang and LLVM
themselves) is also important. It is both an important signal but also a bias.
### Example code
In order to keep example code consistent, we are making choices that may change
later. In particular, where `$` is shown in examples, it is a placeholder: `$`
is a well-known bad symbol due to international keyboard layouts, and will be
cleaned up during evolution.
## Basic syntax
### Code and comments
> References: [Lexical conventions](lexical_conventions.md)
>
> **TODO:** References need to be evolved.
- All source code is UTF-8 encoded text. For simplicity, no other encoding is
supported.
- Line comments look like `// ...`. However, they are required to be the only
non-whitespace on the line for readability.
- Block comments look like `//\{ ... //\}`, with each marker on its own line.
Nested block comments are supported using named regions. For example:
```carbon
live code
//\{
commented code
//\{ nested block
commented code in nested block
//\} nested block
//\}
live code
```
### Files, libraries, and packages
> References: [Files, libraries and packages](files_libraries_and_packages.md)
>
> **TODO:** References need to be evolved.
Carbon code is organized into files, libraries, and packages:
- A **file** is the unit of compilation.
- A **library** can be made up of multiple files, and is the unit whose public
interface can be imported.
- A **package** is a collection of one or more libraries, typically ones with
a single common source and with some close association.
A file belongs to precisely one library, and a library belongs to precisely one
package.
Files have a `.6c` extension. They must start with a declaration of their
package and library. They may import both other libraries from within their
package, as well as libraries from other packages. For example:
```carbon
// This is a file in the "Eucalyptus" library of the "Koala" package.
package Koala library Eucalyptus;
// Import the "Wombat" library from the "Widget" package.
import Widget library Wombat;
// Import the "Container" library from the "Koala" package.
import library Container;
```
### Names and scopes
> References: [Lexical conventions](lexical_conventions.md)
>
> **TODO:** References need to be evolved.
Various constructs introduce a named entity in Carbon. These can be functions,
types, variables, or other kinds of entities that we'll cover. A name in Carbon
is always formed out of an "identifier", or a sequence of letters, numbers, and
underscores which starts with a letter. As a regular expression, this would be
`/[a-zA-Z][a-zA-Z0-9_]*/`. Eventually we may add support for more unicode
characters as well.
#### Naming conventions
> References: [Naming conventions](naming_conventions.md)
>
> **TODO:** References need to be evolved.
Our current proposed naming convention are:
- `UpperCamelCase` for names of compile-time resolved constants, whether they
participate in the type system or not.
- `lower_snake_case` for keywords and names of run-time resolved values.
As a matter of style and consistency, we will follow these conventions where
possible and encourage convergence.
For example:
- An integer that is a compile-time constant sufficient to use in the
construction a compile-time array size, such as a template function
parameter, might be named `N`.
- A generic function parameter's value can't be used during type-checking, but
might still be named `N`, since it will be a constant available to the
compiler at code generation time.
- Functions and most types will be in `UpperCamelCase`.
- A type where only run-time type information queries are available would end
up as `lower_snake_case`.
- A keyword like `import` uses `lower_snake_case`.
#### Aliases
> References: [Aliases](aliases.md)
>
> **TODO:** References need to be evolved.
Carbon provides a facility to declare a new name as an alias for a value. This
is a fully general facility because everything is a value in Carbon, including
types.
For example:
```carbon
alias MyInt = Int;
```
This creates an alias called `MyInt` for whatever `Int` resolves to. Code
textually after this can refer to `MyInt`, and it will transparently refer to
`Int`.
#### Name lookup
> References: [Name lookup](name_lookup.md)
>
> **TODO:** References need to be evolved.
Names are always introduced into some scope which defines where they can be
referenced. Many of these scopes are themselves named. `namespace` is used to
introduce a dedicated named scope, and we traverse nested names in a uniform way
with `.`-separated names. Unqualified name lookup will always find a file-local
result, including aliases.
For example:
```carbon
package Koala library Eucalyptus;
namespace Leaf {
namespace Vein {
fn Count() -> Int;
}
}
```
`Count` may be referred to as:
- `Count` from within the `Vein` namespace.
- `Vein.Count` from within the `Leaf` namespace.
- `Leaf.Vein.Count` from within this file.
- `Koala.Leaf.Vein.Count` from any arbitrary location.
Note that libraries do **not** introduce a scope; they share the scope of their
package.
##### Name lookup for common types
> References: [Name lookup](name_lookup.md)
>
> **TODO:** References need to be evolved.
Common types that we expect to be used universally will be provided for every
file, including `Int` and `Bool`. These will likely be defined in a special
"prelude" package.
### Expressions
> References: [Lexical conventions](lexical_conventions.md) and
> [operators](operators.md)
>
> **TODO:** References need to be evolved.
Expressions describe some computed value. The simplest example would be a
literal number like `42`: an expression that computes the integer value 42.
Some common expressions in Carbon include:
- Literals: `42`, `3.1419`, `"Hello World!"`
- Operators:
- Increment and decrement: `++i`, `--j`
- These do not return any result.
- Unary negation: `-x`
- Arithmetic: `1 + 2`, `3 - 4`, `2 * 5`, `6 / 3`
- Bitwise: `2 & 3`, `2 | 4`, `3 ^ 1`, `~7`
- Bit shift: `1 << 3`, `8 >> 1`
- Comparison: `2 == 2`, `3 != 4`, `5 < 6`, `7 > 6`, `8 <= 8`, `8 >= 8`
- Logical: `a and b`, `c or d`
- Parenthesized expressions: `(7 + 8) * (3 - 1)`
### Functions
> References: [Functions](functions.md) and
> [syntactic conventions](syntactic_conventions.md)
>
> **TODO:** References need to be evolved.
Functions are the core unit of behavior. For example:
```carbon
fn Sum(Int: a, Int: b) -> Int;
```
Breaking this apart:
- `fn` is the keyword used to indicate a function.
- Its name is `Sum`.
- It accepts two `Int` parameters, `a` and `b`.
- It returns an `Int` result.
You would call this function like `Sum(1, 2)`.
### Blocks and statements
> References: [Blocks and statements](blocks_and_statements.md)
>
> **TODO:** References need to be evolved.
The body or definition of a function is provided by a block of code containing
statements. The body of a function is also a new, nested scope inside the
function's scope, meaning that parameter names are available.
Statements within a block are terminated by a semicolon. Each statement can,
among other things, be an expression.
For example, here is a function definition using a block of statements, one of
which is nested:
```carbon
fn Foo() {
Bar();
{
Baz();
}
}
```
### Variables
> References: [Variables](variables.md) and
> [syntactic conventions](syntactic_conventions.md)
>
> **TODO:** References need to be evolved.
Blocks introduce nested scopes and can contain local variable declarations that
work similarly to function parameters.
For example:
```carbon
fn Foo() {
var Int: x = 42;
}
```
Breaking this apart:
- `var` is the keyword used to indicate a variable.
- Its name is `x`.
- Its type is `Int`.
- It is initialized with the value `42`.
### Lifetime and move semantics
> References: TODO
>
> **TODO:** References need to be evolved.
### Control flow
> References: [Control flow](control_flow.md)
>
> **TODO:** References need to be evolved.
Blocks of statements are generally executed sequentially. However, statements
are the primary place where this flow of execution can be controlled.
#### `if`/`else`
> References: [Control flow](control_flow.md)
>
> **TODO:** References need to be evolved.
`if` and `else` are common flow control keywords, which can result in
conditional execution of statements.
For example:
```carbon
fn Foo(Int: x) {
if (x < 42) {
Bar();
} else if (x > 77) {
Baz();
}
}
```
Breaking the `Foo` function apart:
- `Bar()` is invoked if `x` is less than `42`.
- `Baz()` is invoked if `x` is greater than `77`.
- Nothing happens if `x` is between `42` and `77`.
#### `while`, `break`, and `continue`
> References: [Control flow](control_flow.md)
>
> **TODO:** References need to be evolved.
Loops will be supported with a low-level primitive `while` statement. `break`
will be a way to exit the `while` directly, while `continue` will skip the rest
of the current loop iteration.
For example:
```carbon
fn Foo() {
var Int: x = 0;
while (x < 42) {
if (ShouldStop()) break;
if (ShouldSkip(x)) {
++x;
continue;
}
Bar(x);
++x;
}
}
```
Breaking the `Foo` function apart:
- The while body is normally executed for all values of `x` in [0, 42).
- The increment of x at the end causes this.
- If `ShouldStop()` returns true, the `break` causes the `while` to exit
early.
- If `ShouldSkip()` returns true, the `continue` causes the `while` to restart
early.
- Otherwise, `Bar(x)` is called for values of `x` in [0, 42).
#### `return`
> References: [Control flow](control_flow.md)
>
> **TODO:** References need to be evolved.
The `return` statement ends the flow of execution within a function, returning
execution to the caller. If the function returns a value to the caller, that
value is provided by an expression in the return statement. This allows us to
complete the definition of our `Sum` function from earlier as:
```carbon
fn Sum(Int: a, Int: b) -> Int {
return a + b;
}
```
## Types
> References: [Primitive types](primitive_types.md), [tuples](tuples.md), and
> [structs](structs.md)
>
> **TODO:** References need to be evolved.
Carbon's core types are broken down into three categories:
- Primitive types
- Composite types
- User-defined types
The first two are intrinsic and directly built in the language. The last aspect
of types allows for defining new types.
Expressions compute values in Carbon, and these values are always strongly typed
much like in C++. However, an important difference from C++ is that types are
themselves modeled as values; specifically, compile-time constant values.
However, in simple cases this doesn't make much difference.
### Primitive types
> References: [Primitive types](primitive_types.md)
>
> **TODO:** References need to be evolved.
These types are fundamental to the language as they aren't either formed from or
modifying other types. They also have semantics that are defined from first
principles rather than in terms of other operations. These will be made
available through the [prelude package](#name-lookup-for-common-types).
Primitive types fall into the following categories:
- `Void` - a type with only one possible value: empty.
- `Bool` - a boolean type with two possible values: `True` and `False`.
- `Int` and `UInt` - signed and unsigned 64-bit integer types.
- Standard sizes are available, both signed and unsigned, including
`Int8`, `Int16`, `Int32`, `Int128`, and `Int256`.
- Overflow in either direction is an error.
- `Float64` - a floating point type with semantics based on IEEE-754.
- Standard sizes are available, including `Float16`, `Float32`, and
`Float128`.
- [`BFloat16`](primitive_types.md#bfloat16) is also provided.
- `String` - a byte sequence treated as containing UTF-8 encoded text.
- `StringView` - a read-only reference to a byte sequence treated as
containing UTF-8 encoded text.
### Composite types
#### Tuples
> References: [Tuples](tuples.md)
>
> **TODO:** References need to be evolved.
The primary composite type involves simple aggregation of other types as a
tuple. In formal type theory, tuples are product types.
An example use of tuples is:
```carbon
fn DoubleBoth(Int: x, Int: y) -> (Int, Int) {
return (2 * x, 2 * y);
}
```
Breaking this example apart:
- The return type is a tuple of two `Int` types.
- The expression uses tuple syntax to build a tuple of two `Int` values.
Both of these are expressions using the tuple syntax
`(<expression>, <expression>)`. The only difference is the type of the tuple
expression: one is a tuple of types, the other a tuple of values.
Element access uses subscript syntax:
```carbon
fn DoubleTuple((Int, Int): x) -> (Int, Int) {
return (2 * x[0], 2 * x[1]);
}
```
Tuples also support multiple indices and slicing to restructure tuple elements:
```carbon
// This reverses the tuple using multiple indices.
fn Reverse((Int, Int, Int): x) -> (Int, Int, Int) {
return x[2, 1, 0];
}
// This slices the tuple by extracting elements [0, 2).
fn RemoveLast((Int, Int, Int): x) -> (Int, Int) {
return x[0 .. 2];
}
```
#### Variants
> **TODO:** Needs a feature design and a high level summary provided inline.
#### Pointers and references
> **TODO:** Needs a feature design and a high level summary provided inline.
#### Arrays and slices
> **TODO:** Needs a feature design and a high level summary provided inline.
### User-defined types
#### Structs
> References: [Structs](structs.md)
>
> **TODO:** References need to be evolved.
`struct`s are a way for users to define their own data strutures or named
product types.
For example:
```carbon
struct Widget {
var Int: x;
var Int: y;
var Int: z;
var String: payload;
}
```
Breaking apart `Widget`:
- `Widget` has three `Int` members: `x`, `y`, and `z`.
- `Widget` has one `String` member: `payload`.
- Given an instance `dial`, a member can be referenced with `dial.paylod`.
More advanced `struct`s may be created:
```carbon
struct AdvancedWidget {
// Do a thing!
fn DoSomething(AdvancedWidget: self, Int: x, Int: y);
// A nested type.
struct Nestedtype {
// ...
}
private var Int: x;
private var Int: y;
}
fn Foo(AdvancedWidget: thing) {
thing.DoSomething(1, 2);
}
```
Breaking apart `AdvancedWidget`:
- `AdvancedWidget` has a public object method `DoSomething`.
- `DoSomething` explicitly indicates how the `AdvancedWidget` is passed to
it, and there is no automatic scoping - `self` must be specified as the
first input. The `self` name is also a keyword that explains how to
invoke this method on an object.
- `DoSomething` accepts `AdvancedWidget` _by value_, which is easily
expressed here along with other constraints on the object parameter.
- `AdvancedWidget` has two private data members: `x` and `y`.
- Private methods and data members are restricted to use by
`AdvancedWidget` only, providing a layer of easy validation of the most
basic interface constraints.
- `Nestedtype` is a nested type, and can be accessed as
`AdvancedWidget.Nestedtype`.
##### Allocation, construction, and destruction
> **TODO:** Needs a feature design and a high level summary provided inline.
##### Assignment, copying, and moving
> **TODO:** Needs a feature design and a high level summary provided inline.
##### Comparison
> **TODO:** Needs a feature design and a high level summary provided inline.
##### Implicit and explicit conversion
> **TODO:** Needs a feature design and a high level summary provided inline.
##### Inline type composition
> **TODO:** Needs a feature design and a high level summary provided inline.
#### Unions
> **TODO:** Needs a detailed design and a high level summary provided inline.
## Pattern matching
> References: [Pattern matching](pattern_matching.md)
>
> **TODO:** References need to be evolved.
The most prominent mechanism to manipulate and work with types in Carbon is
pattern matching. This may seem like a deviation from C++, but in fact this is
largely about building a clear, coherent model for a fundamental part of C++:
overload resolution.
### `match` control flow
> References: [Pattern matching](pattern_matching.md)
>
> **TODO:** References need to be evolved.
`match` is a control flow similar to `switch` of C/C++ and mirrors similar
constructs in other languages, such as Swift.
An example `match` is:
```carbon
fn Bar() -> (Int, (Float, Float));
fn Foo() -> Float {
match (Bar()...) {
case (42, (Float: x, Float: y)) => {
return x - y;
}
case (Int: p, (Float: x, Float: _)) if (p < 13) => {
return p * x;
}
case (Int: p, auto: _) if (p > 3) => {
return p * Pi;
}
default => {
return Pi;
}
}
}
```
Breaking apart this `match`:
- It accepts a value that will be inspected; in this case, the result of the
call to `Bar()`.
- It then will find the _first_ `case` that matches this value, and
execute that block.
- If none match, then it executes the default block.
- Each `case` pattern contains a value pattern, such as `(Int: p, auto: _)`,
followed by an optional boolean predicate introduced by the `if` keyword.
- The value pattern must first match, and then the predicate must also
evaluate to true for the overall `case` pattern to match.
- Using `auto` for a type will always match.
Value patterns may be composed of the following:
- An expression, such as `42`, whose value must be equal to match.
- An optional type, such as `Int`, followed by a `:` and an identifier to bind
the value.
- The special identifier `_` may be used to discard the value once
matched.
- A destructuring pattern containing a sequence of value patterns, such as
`(Float: x, Float: y)`, which match against tuples and tuple-like values by
recursively matching on their elements.
- An unwrapping pattern containing a nested value pattern which matches
against a variant or variant-like value by unwrapping it.
### Pattern matching in local variables
> References: [Pattern matching](pattern_matching.md)
>
> **TODO:** References need to be evolved.
Value patterns may be used when declaring local variables to conveniently
destructure them and do other type manipulations. However, the patterns must
match at compile time, so a boolean predicate cannot be used directly.
An example use is:
```carbon
fn Bar() -> (Int, (Float, Float));
fn Foo() -> Int {
var (Int: p, auto: _) = Bar();
return p;
}
```
To break this apart:
- The `Int` returned by `Bar()` matches and is bound to `p`, then returned.
- The `(Float, Float)` returned by `Bar()` matches and is discarded by
`auto: _`.
### Pattern matching as function overload resolution
> References: [Pattern matching](pattern_matching.md)
>
> **TODO:** References need to be evolved. Needs a detailed design and a high
> level summary provided inline.
## Type abstractions
### Interfaces
> **TODO:** Needs a feature design and a high level summary provided inline.
### Generics
> **TODO:** Needs a feature design and a high level summary provided inline.
### Templates
> References: [Templates](templates.md)
>
> **TODO:** References need to be evolved.
Carbon templates follow the same fundamental paradigm as C++ templates: they are
instantiated when called, resulting in late type checking, duck typing, and lazy
binding. Although generics are generally preferred, templates enable translation
of code between C++ and Carbon, and address some cases where the type checking
rigor of generics are problematic.
#### Types with template parameters
> References: [Templates](templates.md)
>
> **TODO:** References need to be evolved.
User-defined types may have template parameters. The resulting type-function may
be used to instantiate the parameterized definition with the provided arguments
in order to produce a complete type. For example:
```carbon
struct Stack(Type:$$ T) {
var Array(T): storage;
fn Push(T: value);
fn Pop() -> T;
}
```
Breaking apart the template use in `Stack`:
- `Stack` is a paremeterized type accepting a type `T`.
- `T` may be used within the definition of `Stack` anywhere a normal type
would be used, and will only be type checked on instantiation.
- `var Array(T)` instantiates a parameterized type `Array` when `Stack` is
instantiated.
#### Functions with template parameters
> References: [Templates](templates.md)
>
> **TODO:** References need to be evolved.
Both implicit and explicit function parameters in Carbon can be marked as
_template_ parameters. When called, the arguments to these parameters trigger
instantiation of the function definition, fully type checking and resolving that
definition after substituting in the provided (or computed if implicit)
arguments. The runtime call then passes the remaining arguments to the resulting
complete definition.
```carbon
fn Convert[Type:$$ T](T: source, Type:$$ U) -> U {
var U: converted = source;
return converted;
}
fn Foo(Int: i) -> Float {
// Instantiates with the `T` implicit argument set to `Int` and the `U`
// explicit argument set to `Float`, then calls with the runtime value `i`.
return Convert(i, Float);
}
```
Here we deduce one type parameter and explicitly pass another. It is not
possible to explicitly pass a deduced type parameter; instead the call site
should cast or convert the argument to control the deduction. In this particular
example, the explicit type is passed after a runtime parameter. While this makes
that type unavailable to the declaration of _that_ runtime parameter, it still
is a _template_ parameter and available to use as a type in the remaining parts
of the function declaration.
#### Overloading
> References: [Templates](templates.md)
>
> **TODO:** References need to be evolved.
An important feature of templates in C++ is the ability to customize how they
end up specialized for specific arguments. Because template parameters (whether
as type parameters or function parameters) are pattern matched, we expect to
leverage pattern matching techniques to provide "better match" definitions that
are selected analogously to specializations in C++ templates. When expressed
through pattern matching, this may enable things beyond just template parameter
specialization, but that is an area that we want to explore cautiously.
> **TODO:** lots more work to flesh this out needs to be done...
## Metaprogramming
> References: [Metaprogramming](metaprogramming.md)
>
> **TODO:** References need to be evolved. Needs a detailed design and a high
> level summary provided inline.
Carbon provides metaprogramming facilities that look similar to regular Carbon
code. These are structured, and do not offer arbitrary inclusion or
preprocessing of source text such as C/C++ does.
## Execution abstractions
Carbon provides some higher-order abstractions of program execution, as well as
the critical underpinnings of such abstractions.
### Abstract machine and execution model
> **TODO:** Needs a feature design and a high level summary provided inline.
### Lambdas
> **TODO:** Needs a feature design and a high level summary provided inline.
### Co-routines
> **TODO:** Needs a feature design and a high level summary provided inline.
## Bidirectional interoperability with C/C++
> References:
> [Bidirectional interoperability with C/C++](interoperability/README.md)
>
> **TODO:** References need to be evolved. Needs a detailed design and a high
> level summary provided inline.
+49
View File
@@ -0,0 +1,49 @@
# Aliases
<!--
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
-->
## Table of contents
<!-- toc -->
- [TODO](#todo)
- [Overview](#overview)
- [Alternatives](#alternatives)
<!-- tocstop -->
## TODO
This is a skeletal design, added to support [the overview](README.md). It should
not be treated as accepted by the core team; rather, it is a placeholder until
we have more time to examine this detail. Please feel welcome to rewrite and
update as appropriate.
## Overview
Naming is one of the things that most often requires careful management over
time -- things tend to get renamed and moved around.
Carbon provides a fully general name aliasing facility to declare a new name as
an alias for a value; everything is a value in Carbon. This is a fully general
facility because everything is a value in Carbon, including types.
For example:
```
alias MyInt = Int;
```
This creates an alias called `MyInt` for whatever `Int` resolves to. Code
textually after this can refer to `MyInt`, and it will transparently refer to
`Int`.
### Alternatives
The syntax here is not at all in a good state yet. We've considered a few
alternatives, but they all end up being confusing in some way. We need to figure
out a good and clean syntax that can be used here.
+51
View File
@@ -0,0 +1,51 @@
# Blocks and statements
<!--
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
-->
## Table of contents
<!-- toc -->
- [TODO](#todo)
- [Overview](#overview)
<!-- tocstop -->
## TODO
This is a skeletal design, added to support [the overview](README.md). It should
not be treated as accepted by the core team; rather, it is a placeholder until
we have more time to examine this detail. Please feel welcome to rewrite and
update as appropriate.
## Overview
The body or definition of a function is provided by a block of code containing
statements, much like in C or C++. The body of a function is also a new, nested
scope inside the function's scope (meaning that parameter names are available).
Statements within a block are terminated by a semicolon. Each statement can,
among other things, be an expression. Here is a trivial example of a function
definition using a block of statements:
```
fn Foo() {
Bar();
Baz();
}
```
Statements can also themselves be a block of statements, which provide scopes
and nesting:
```
fn Foo() {
Bar();
{
Baz();
}
}
```
+80
View File
@@ -0,0 +1,80 @@
# Control flow
<!--
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
-->
## Table of contents
<!-- toc -->
- [TODO](#todo)
- [Overview](#overview)
- [Open questions](#open-questions)
- [`if` blocks](#if-blocks)
- [`break` and `continue`](#break-and-continue)
<!-- tocstop -->
## TODO
This is a skeletal design, added to support [the overview](README.md). It should
not be treated as accepted by the core team; rather, it is a placeholder until
we have more time to examine this detail. Please feel welcome to rewrite and
update as appropriate.
At least summarize `if` and `else` to cover basics. Especially important to
surface the idea of using basic conditionals as both expressions and statements
to avoid needing conditional operators.
Looping is an especially interesting topic to explore as there are lots of
challenges posed by the C++ loop structure. Even C++ itself has been seeing
significant interest and pressure to improve its looping facilities.
## Overview
Blocks of statements are generally executed linearly. However, statements are
the primary place where this flow of execution can be controlled. Carbon's
control flow constructs are mostly similar to those in C, C++, and other
languages.
```
fn Foo(Int: x) {
if (x < 42) {
Bar();
} else if (x > 77) {
Baz();
}
}
```
Loops will at least be supported with a low-level primitive `while` statement,
with `break` and `continue` statements which work the same as in C++.
Last but not least, for the basics we need to include the `return` statement.
This statement ends the flow of execution within a function, returning it to the
caller. If the function returns a value to the caller, that value is provided by
an expression in the return statement. This allows us to complete the definition
of our `Sum` function from earlier as:
```
fn Sum(Int: a, Int: b) -> Int {
return a + b;
}
```
## Open questions
### `if` blocks
It is an open question whether a block is required or a single statement may be
nested in an `if` statement. Similarly, it is an open question whether `else if`
is a single keyword versus a nested `if` statement, and if it is a single
construct whether it should be spelled `elif` or something else.
### `break` and `continue`
If and how to support a "labeled break" or "labeled continue" is still a point
of open discussion.
@@ -0,0 +1,43 @@
# Files, libraries, and packages
<!--
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
-->
## Table of contents
<!-- toc -->
- [TODO](#todo)
- [Alternatives](#alternatives)
- [File extensions](#file-extensions)
<!-- tocstop -->
## TODO
This is a skeletal design, added to support [the overview](README.md). It should
not be treated as accepted by the core team; rather, it is a placeholder until
we have more time to examine this detail. Please feel welcome to rewrite and
update as appropriate.
## Alternatives
### File extensions
The use of `6c` as a short file extension or top-level CLI (with subcommands
below it similar to `git` or `go`) has some drawbacks. There are several other
possible extensions / commands:
- `cb`: This collides with several acronyms and may not be especially
memorable as referring to Carbon.
- `c6`: This seems a weird incorrect ordering of the atomic number and has a
bad (if _extremely_ obscure) Internet slang association (NSFW, use caution
if searching, as with too much Internet slang).
- `carbon`: This is an obvious and unsurprising choice, but also quite long.
This seems fairly easy for us to change as we go along, but we should at some
point do a formal proposal to gather other options and let the core team try to
find the set that they feel is close enough to be a bikeshed.
+63
View File
@@ -0,0 +1,63 @@
# Functions
<!--
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
-->
## Table of contents
<!-- toc -->
- [TODO](#todo)
- [Basic functions](#basic-functions)
<!-- tocstop -->
## TODO
This is a skeletal design, added to support [the overview](README.md). It should
not be treated as accepted by the core team; rather, it is a placeholder until
we have more time to examine this detail. Please feel welcome to rewrite and
update as appropriate.
## Basic functions
Programs written in Carbon, much like those written in other languages, are
primarily divided up into "functions" (or "procedures", "subroutines", or
"subprograms"). These are the core unit of behavior for the programming
language. Let's look at a simple example to understand how these work:
```
fn Sum(Int: a, Int: b) -> Int;
```
This declares a function called `Sum` which accepts two `Int` parameters, the
first called `a` and the second called `b`, and returns an `Int` result. C++
might declare the same thing:
```
std::int64_t Sum(std::int64_t a, std::int64_t b);
// Or with trailing return type syntax:
auto Sum(std::int64_t a, std::int64_t b) -> std::int64_t;
```
Let's look at how some specific parts of this work. The function declaration is
introduced with a keyword `fn` followed by the name of the function `Sum`. This
declares that name in the surrounding scope and opens up a new scope for this
function. We declare the first parameter as `Int: a`. The `Int` part is an
expression (here referring to a constant) that computes the type of the
parameter. The `:` marks the end of the type expression and introduces the
identifier for the parameter, `a`. The parameter names are introduced into the
function's scope and can be referenced immediately after they are introduced.
The return type is indicated with `-> Int`, where again `Int` is just an
expression computing the desired type. The return type can be completely omitted
in the case of functions which do not return a value.
Calling functions involves a new form of expression: `Sum(1, 2)` for example.
The first part, `Sum`, is an expression referring to the name of the function.
The second part, `(1, 2)` is a parenthesized list of arguments to the function.
The juxtaposition of one expression with parentheses forms the core of a call
expression, similar to a postfix operator.
+28
View File
@@ -0,0 +1,28 @@
# Bidirectional interoperability with C/C++
<!--
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
-->
## Table of contents
<!-- toc -->
- [TODO](#todo)
- [Overview](#overview)
<!-- tocstop -->
## TODO
This is a skeletal design, added to support [the overview](README.md). It should
not be treated as accepted by the core team; rather, it is a placeholder until
we have more time to examine this detail. Please feel welcome to rewrite and
update as appropriate.
## Overview
See the draft in
[PR 80](https://github.com/carbon-language/carbon-lang/pull/80).
+25
View File
@@ -0,0 +1,25 @@
# Lexical conventions
<!--
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
-->
## Table of contents
<!-- toc -->
- [TODO](#todo)
<!-- tocstop -->
## TODO
This is a skeletal design, added to support [the overview](README.md). It should
not be treated as accepted by the core team; rather, it is a placeholder until
we have more time to examine this detail. Please feel welcome to rewrite and
update as appropriate.
See [PR 17](https://github.com/carbon-language/carbon-lang/pull/17) for context
-- that proposal may replace this.
+33
View File
@@ -0,0 +1,33 @@
# Metaprogramming
<!--
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
-->
## Table of contents
<!-- toc -->
- [TODO](#todo)
- [Overview](#overview)
<!-- tocstop -->
## TODO
This is a skeletal design, added to support [the overview](README.md). It should
not be treated as accepted by the core team; rather, it is a placeholder until
we have more time to examine this detail. Please feel welcome to rewrite and
update as appropriate.
See
[carbon-proposals PR 89](https://github.com/carbon-language/carbon-proposals/pull/89)
for context -- that proposal may replace this.
## Overview
Carbon provides metaprogramming facilities that look similar to regular Carbon
code. These are structured, and do not offer inclusion or arbitrary
preprocessing of source text such as C/C++ does.
+91
View File
@@ -0,0 +1,91 @@
# Name lookup
<!--
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
-->
## Table of contents
<!-- toc -->
- [TODO](#todo)
- [Overview](#overview)
- [Unqualified name lookup](#unqualified-name-lookup)
- [Alternatives](#alternatives)
- [Name lookup for common, standard types](#name-lookup-for-common-standard-types)
- [Open questions](#open-questions)
- [Shadowing](#shadowing)
<!-- tocstop -->
## TODO
This is a skeletal design, added to support [the overview](README.md). It should
not be treated as accepted by the core team; rather, it is a placeholder until
we have more time to examine this detail. Please feel welcome to rewrite and
update as appropriate.
## Overview
Names are always introduced into some scope which defines where they can be
referenced. Many of these scopes are themselves named. Carbon has a special
facility for introducing a dedicated named scope just like C++, but we traverse
nested names in a uniform way with `.`-separated names:
```
namespace Foo {
namespace Bar {
alias ??? MyInt = Int;
}
}
fn F(Foo.Bar.MyInt: x);
```
Carbon packages are also namespaces so to get to an imported name from the
`Abseil` package you would write `Abseil.Foo`. The "top-level" file scope is
that of the Carbon package containing the file, meaning that there is no
"global" scope. Dedicated namespaces can be reopened within a package, but there
is no way to reopen a package without being a library and file _within_ that
package.
Note that libraries (unlike packages) do **not** introduce a scope, they share
the scope of their package. This is based on the observation that in practice, a
fairly coarse scoping tends to work best, with some degree of global registry to
establish a unique package name.
### Unqualified name lookup
Unqualified name lookup in Carbon will always find a file-local result, other
than the implicit "prelude" of importing and aliasing the fundamentals of the
standard library. There will be an explicit mention of the name in the file that
declares the name in the current or enclosing scope, which must also precede the
reference.
#### Alternatives
This implies that other names within your own package but not declared within
the file must be found via the package name. It isn't clear if this is the
desirable end state. We need to consider alternatives where names from the same
library or any library in the same package are made immediately visible within
the package scope for unqualified name lookup.
### Name lookup for common, standard types
The Carbon standard library is in the `Carbon` package. A very small subset of
this standard library is provided implicitly in every file's scope. This is
called the "prelude" package.
Names in the prelude package will be available without scoping names. For
example, `Bool` will be the commonly used name in code, even though the
underlying type may be `Carbon::Bool`. Also, no `import` will be necessary to
use `Bool`.
## Open questions
### Shadowing
We can probably disallow the use of shadowed unqualified names, but the actual
design for such needs to be thought through.
+65
View File
@@ -0,0 +1,65 @@
# Naming conventions
<!--
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
-->
## Table of contents
<!-- toc -->
- [TODO](#todo)
- [Overview](#overview)
<!-- tocstop -->
## TODO
This is a skeletal design, added to support [the overview](README.md). It should
not be treated as accepted by the core team; rather, it is a placeholder until
we have more time to examine this detail. Please feel welcome to rewrite and
update as appropriate.
## Overview
We would like to have widespread and consistent naming conventions across Carbon
code to the extent possible. This is for the same core reason as naming
conventions are provided in most major style guides. Even migrating existing C++
code at-scale presents a significant opportunity to converge even more broadly
and we're interested in pursuing this if viable.
Our current proposed naming convention, which we at least are attempting to
follow within Carbon documentation in order to keep code samples as consistent
as possible, is:
- `UpperCamelCase` for names of compile-time resolved constants, such that
they can participate in the type system and type checking of the program.
Comple-time constants fall into two categories:
- _Template_ constants that can be used in type checking, including
literals.
- _Generic_ constants whose value is not used in type checking, but will
be used as part of code generation.
- `lower_snake_case` for names of run-time resolved values.
As an example, an integer that is a compile-time constant sufficient to use in
the construction a compile-time array size might be named `N`, where an integer
that is not available as part of the type system would be named `n`, even if it
happened to be immutable or only take on a single value. Functions and most
types will be in `UpperCamelCase`, but a type where only run-time type
information queries are available would end up as `lower_snake_case`.
We only use `UpperCamelCase` and `lower_snake_case` (skipping other variations
on both snake-case and camel-case naming conventions) because these two have the
most significant visual separation. For example, the value of adding
`lowerCamelCase` for another set seems low given the small visual difference
provided; in particular, one-word identifiers would have no difference.
The rationale for the specific division between the two isn't a huge or
fundamental concept, but it stems from a convention in Ruby where constants are
named with a leading capital letter. The idea is that it mirrors the English
language capitalization of proper nouns: the name of a constant refers to a
_specific_ value that is precisely resolved at compile time, not just to _some_
value. For example, there are many different _shires_ in Britain, but Frodo
comes from the _Shire_ -- a specific fictional region.
+26
View File
@@ -0,0 +1,26 @@
# Operators
<!--
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
-->
## Table of contents
<!-- toc -->
- [TODO](#todo)
<!-- tocstop -->
## TODO
This is a skeletal design, added to support [the overview](README.md). It should
not be treated as accepted by the core team; rather, it is a placeholder until
we have more time to examine this detail. Please feel welcome to rewrite and
update as appropriate.
See
[carbon-proposals PR 5](https://github.com/carbon-language/carbon-proposals/pull/5)
for context -- that proposal may replace this.
+125
View File
@@ -0,0 +1,125 @@
# Pattern matching
<!--
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
-->
## Table of contents
<!-- toc -->
- [TODO](#todo)
- [Overview](#overview)
- [Pattern match control flow](#pattern-match-control-flow)
- [Pattern matching in local variables](#pattern-matching-in-local-variables)
- [Open questions](#open-questions)
- [Slice or array nested value pattern matching](#slice-or-array-nested-value-pattern-matching)
- [Generic/template pattern matching](#generictemplate-pattern-matching)
- [Pattern matching as function overload resolution](#pattern-matching-as-function-overload-resolution)
<!-- tocstop -->
## TODO
This is a skeletal design, added to support [the overview](README.md). It should
not be treated as accepted by the core team; rather, it is a placeholder until
we have more time to examine this detail. Please feel welcome to rewrite and
update as appropriate.
## Overview
The most prominent mechanism to manipulate and work with types in Carbon is
pattern matching. This may seem like a deviation from C++, but in fact this is
largely about building a clear, coherent model for a fundamental part of C++:
overload resolution.
### Pattern match control flow
The most powerful form and easiest to explain form of pattern matching is a
dedicated control flow construct that subsumes the `switch` of C and C++ into
something much more powerful, `match`. This is not a novel construct, and is
widely used in existing languages (Swift and Rust among others) and is currently
under active investigation for C++. Carbon's `match` can be used as follows:
```
fn Bar() -> (Int, (Float, Float));
fn Foo() -> Float {
match (Bar()) {
case (42, (Float: x, Float: y)) => {
return x - y;
}
case (Int: p, (Float: x, Float: _)) if (p < 13) => {
return p * x;
}
case (Int: p, auto: _) if (p > 3) => {
return p * Pi;
}
default => {
return Pi;
}
}
}
```
There is a lot going on here. First, let's break down the core structure of a
`match` statement. It accepts a value that will be inspected, here the result of
the call to `Bar()`. It then will find the _first_ `case` that matches this
value, and execute that block. If none match, then it executes the default
block.
Each `case` contains a pattern. The first part is a value pattern
(`(Int: p, auto: _)` for example) followed by an optional boolean predicate
introduced by the `if` keyword. The value pattern has to match, and then the
predicate has to evaluate to true for the overall pattern to match. Value
patterns can be composed of the following:
- An expression (`42` for example), whose value must be equal to match.
- An optional type (`Int` for example), followed by a `:` and either an
identifier to bind to the value or the special identifier `_` to discard the
value once matched.
- A destructuring pattern containing a sequence of value patterns
(`(Float: x, Float: y)`) which match against tuples and tuple like values by
recursively matching on their elements.
- An unwrapping pattern containing a nested value pattern which matches
against a variant or variant-like value by unwrapping it.
In order to match a value, whatever is specified in the pattern must match.
Using `auto` for a type will always match, making `auto: _` the wildcard
pattern.
### Pattern matching in local variables
Value patterns may be used when declaring local variables to conveniently
destructure them and do other type manipulations. However, the patterns must
match at compile time which is why the boolean predicate cannot be used
directly.
```
fn Bar() -> (Int, (Float, Float));
fn Foo() -> Int {
var (Int: p, auto: _) = Bar();
return p;
}
```
This extracts the first value from the result of calling `Bar()` and binds it to
a local variable named `p` which is then returned.
## Open questions
### Slice or array nested value pattern matching
An open question is how to effectively fit a "slice" or "array" pattern into
nested value pattern matching, or whether we shouldn't do so.
### Generic/template pattern matching
An open question is going beyond a simple "type" to things that support generics
and/or templates.
### Pattern matching as function overload resolution
Need to flesh out specific details of how overload selection leverages the
pattern matching machinery, what (if any) restrictions are imposed, etc.
+102
View File
@@ -0,0 +1,102 @@
# Primitive types
<!--
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
-->
## Table of contents
<!-- toc -->
- [TODO](#todo)
- [Overview](#overview)
- [Integers](#integers)
- [Floats](#floats)
- [BFloat16](#bfloat16)
- [Open questions](#open-questions)
- [Primitive types as code vs built-in](#primitive-types-as-code-vs-built-in)
- [String view vs owning string](#string-view-vs-owning-string)
- [Syntax for wrapping operations](#syntax-for-wrapping-operations)
- [Non-power-of-two sizes](#non-power-of-two-sizes)
<!-- tocstop -->
## TODO
This is a skeletal design, added to support [the overview](README.md). It should
not be treated as accepted by the core team; rather, it is a placeholder until
we have more time to examine this detail. Please feel welcome to rewrite and
update as appropriate.
## Overview
These types are fundamental to the language as they aren't either formed from or
modifying other types. They also have semantics that are defined from first
principles rather than in terms of other operations. These will be made
available through the [prelude package](README.md#name-lookup-for-common-types).
- `Void` - a type with only one possible value: empty.
- `Bool` - a boolean type with two possible values: `True` and `False`.
- `Int` and `UInt` - signed and unsigned 64-bit integer types.
- Standard sizes are available, both signed and unsigned, including
`Int8`, `Int16`, `Int32`, `Int128`, and `Int256`.
- Overflow in either direction is an error.
- `Float64` - a floating point type with semantics based on IEEE-754.
- Standard sizes are available, including `Float16`, `Float32`, and
`Float128`.
- [`BFloat16`](primitive_types.md#bfloat16) is also provided.
- `String` - a byte sequence treated as containing UTF-8 encoded text.
- `StringView` - a read-only reference to a byte sequence treated as
containing UTF-8 encoded text.
### Integers
Integer types can be either signed or unsigned, much like in C++. Signed
integers are represented using 2's complement and notionally modeled as
unbounded natural numbers. Overflow in either direction is an error. That
includes unsigned integers, differing from C++. The default size for both is
64-bits: `Int` and `UInt`. Specific sizes are also available, for example:
`Int8`, `Int16`, `Int32`, `Int128`, `UInt256`. Arbitrary powers of two above `8`
are supported for both (although perhaps we'll want to avoid _huge_ values for
implementation simplicity).
### Floats
Floating point types are based on the binary floating point formats provided by
IEEE-754. `Float16`, `Float32`, `Float64` and `Float128` correspond exactly to
those sized IEEE-754 formats, and have the semantics defined by IEEE-754.
### BFloat16
Carbon also supports the
`[BFloat16](https://en.wikipedia.org/wiki/Bfloat16_floating-point_format)`
format, a 16-bit truncation of a "binary32" IEEE-754 format floating point
number.
## Open questions
### Primitive types as code vs built-in
There are open questions about the extent to which these types should be defined
in Carbon code rather than special. Clearly they can't be directly implemented
w/o help, but it might still be useful to force the programmer-observed
interface to reside in code. However, this can cause difficulty with avoiding
the need to import things gratuitously.
### String view vs owning string
The right model of a string view vs. an owning string is still very much
unsettled.
### Syntax for wrapping operations
Open question around allowing special syntax for wrapping operations (even on
signed types) and/or requiring such syntax for wrapping operations on unsigned
types.
### Non-power-of-two sizes
Supporting non-power-of-two sizes is likely needed to have a clean model for
bitfields, but requires more details to be worked out around memory access.
+113
View File
@@ -0,0 +1,113 @@
# Structs
<!--
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
-->
## Table of contents
<!-- toc -->
- [TODO](#todo)
- [Overview](#overview)
- [Open questions](#open-questions)
- [`self` type](#self-type)
- [Default access control level](#default-access-control-level)
<!-- tocstop -->
## TODO
This is a skeletal design, added to support [the overview](README.md). It should
not be treated as accepted by the core team; rather, it is a placeholder until
we have more time to examine this detail. Please feel welcome to rewrite and
update as appropriate.
## Overview
Beyond simple tuples, Carbon of course allows defining named product types. This
is the primary mechanism for users to extend the Carbon type system and
fundamentally is deeply rooted in C++ and its history (C and Simula). We simply
call them `struct`s rather than other terms as it is both familiar to existing
programmers and accurately captures their essence: they are a mechanism for
structuring data:
```
struct Widget {
var Int: x;
var Int: y;
var Int: z;
var String: payload;
}
```
Most of the core features of structures from C++ remain present in Carbon, but
often using different syntax:
```
struct AdvancedWidget {
// Do a thing!
fn DoSomething(AdvancedWidget: self, Int: x, Int: y);
// A nested type.
struct NestedType {
// ...
}
private var Int: x;
private var Int: y;
}
fn Foo(AdvancedWidget: thing) {
thing.DoSomething(1, 2);
}
```
Here we provide a public object method and two private data members. The method
explicitly indicates how the object parameter is passed to it, and there is no
automatic scoping - you have to use `self` here. The `self` name is also a
keyword, though, that explains how to invoke this method on an object. This
member function accepts the object _by value_, which is easily expressed here
along with other constraints on the object parameter. Private members work the
same as in C++, providing a layer of easy validation of the most basic interface
constraints.
The type itself is a compile-time constant value. All name access is done with
the `.` notation. Constant members (including member types and member functions
which do not need an implicit object parameter) can be accessed via that
constant: `AdvancedWidget.NestedType`. Other members and member functions
needing an object parameter (or "methods") must be accessed from an object of
the type.
Some things in C++ are notably absent or orthogonally handled:
- No need for `static` functions, they simply don't take an initial `self`
parameter.
- No `static` variables because there are no global variables. Instead, can
have scoped constants.
## Open questions
### `self` type
Requiring the type of `self` makes method declarations quite verbose. Unclear
what is the best way to mitigate this, there are many options. One is to have a
special `Self` type.
It may be interesting to consider separating the `self` syntax from the rest of
the parameter pattern as it doesn't seem necessary to inject all of the special
rules (covariance vs. contravariance, special pointer handling) for `self` into
the general pattern matching system.
### Default access control level
The default access control level, and the options for access control, are pretty
large open questions. Swift and C++ (especially w/ modules) provide a lot of
options and a pretty wide space to explore here. If the default isn't right most
of the time, access control runs the risk of becoming a significant ceremony
burden that we may want to alleviate with grouped access regions instead of
per-entity specifiers. Grouped access regions have some other advantages in
terms of pulling the public interface into a specific area of the type.
+75
View File
@@ -0,0 +1,75 @@
# Syntactic conventions
<!--
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
-->
## Table of contents
<!-- toc -->
- [TODO](#todo)
- [Overview](#overview)
- [Alternatives](#alternatives)
- [Types before or after name](#types-before-or-after-name)
<!-- tocstop -->
## TODO
This is a skeletal design, added to support [the overview](README.md). It should
not be treated as accepted by the core team; rather, it is a placeholder until
we have more time to examine this detail. Please feel welcome to rewrite and
update as appropriate.
## Overview
Right now we expect variable syntax like: `Int: x`.
There are probably other syntactic conventions that can be added here, too.
## Alternatives
### Types before or after name
While we are currently keeping types first, matching C++, there is significant
uncertainty around the right approach here. While adding the colon improves the
grammar by unambiguously marking the transition from type to a declared
identifier, in essentially every other language with a colon in a similar
position, the identifier is first and the type follows. However, that ordering
would be very _inconsistent_ with C++.
One very important consideration here is the fundamental approach to type
inference. Languages which use the syntax `<identifier>: <type>` typically allow
completely omitting the colon and the type to signify inference. With C++,
inference is achieved with a placeholder keyword `auto`, and Carbon is currently
being consistent there as well with `auto: <identifier>`. For languages which
simply allow omission, this seems an intentional incentive to encourage
inference. On the other hand, there has been strong advocacy in the C++
community to not overly rely on inference and to write the explicit type
whenever convenient. Being consistent with the _ordering_ of identifier and type
may ultimately be less important than being consistent with the incentives and
approach to type inference. What should be the default that we teach? Teaching
to avoid inference unless it specifically helps readability by avoiding a
confusing or unhelpfully complex type name, and incentivizing that by requiring
`auto` or another placeholder, may cause as much or more inconsistency with
languages that use `<identifier>: <type>` as retaining the C++ ordering.
That said, all of this is largely unknown. It will require a significant
exploration of the trade-offs and consistency differences. It should also factor
in further development of pattern matching generally and whether that has an
influence on one or another approach. Last but not least, while this may seem
like something that people will get used to with time, it may be worthwhile to
do some user research to understand the likely reaction distribution, strength
of reaction, and any quantifiable impact these options have on measured
readability. We have only found one _very_ weak source of research that focused
on the _order_ question (rather than type inference vs. explicit types or other
questions in this space). That was a very limited PhD student's study of Java
programmers that seemed to indicate improved latency for recalling the type of a
given variable name with types on the left (as in C++). However, those results
are _far_ from conclusive.
**TODO**: Get a useful link to this PhD research (a few of us got a copy from
the professor directly).
+111
View File
@@ -0,0 +1,111 @@
# Templates
<!--
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
-->
## Table of contents
<!-- toc -->
- [TODO](#todo)
- [Overview](#overview)
- [Types with template parameters](#types-with-template-parameters)
- [Functions with template parameters](#functions-with-template-parameters)
- [Overloading](#overloading)
- [Constraining templates with interfaces](#constraining-templates-with-interfaces)
<!-- tocstop -->
## TODO
This is a skeletal design, added to support [the overview](README.md). It should
not be treated as accepted by the core team; rather, it is a placeholder until
we have more time to examine this detail. Please feel welcome to rewrite and
update as appropriate.
## Overview
Carbon templates follow the same fundamental paradigm as C++ templates: they are
instantiated, resulting in late type checking, duck typing, and lazy binding.
They both enable interoperability between Carbon and C++ and address some
(hopefully limited) use cases where the type checking rigor imposed by generics
isn't helpful.
### Types with template parameters
When parameterizing a user-defined type, the parameters can be marked as
template parameters. The resulting type-function will instantiate the
parameterized definition with the provided arguments to produce a complete type
when used. Note that only the parameters marked as having this template behavior
are subject to full instantiation -- other parameters will be type checked and
bound early to the extent possible. For example:
```
struct Stack(Type:$$ T) {
var Array(T): storage;
fn Push(T: value);
fn Pop() -> T;
}
```
This both defines a parameterized type (`Stack`) and uses one (`Array`). Within
the definition of the type, the template type parameter `T` can be used in all
of the places a normal type would be used, and it will only by type checked on
instantiation.
### Functions with template parameters
Both implicit and explicit function parameters in Carbon can be marked as
template parameters. When called, the arguments to these parameters trigger
instantiation of the function definition, fully type checking and resolving that
definition after substituting in the provided (or computed if implicit)
arguments. The runtime call then passes the remaining arguments to the resulting
complete definition.
```
fn Convert[Type:$$ T](T: source, Type:$$ U) -> U {
var U: converted = source;
return converted;
}
fn Foo(Int: i) -> Float {
// Instantiates with the `T` implicit argument set to `Int` and the `U`
// explicit argument set to `Float`, then calls with the runtime value `i`.
return Convert(i, Float);
}
```
Here we deduce one type parameter and explicitly pass another. It is not
possible to explicitly pass a deduced type parameter, instead the call site
should cast or convert the argument to control the deduction. The explicit type
is passed after a runtime parameter. While this makes that type unavailable to
the declaration of _that_ runtime parameter, it still is a template parameter
and available to use as a type even within the remaining parts of the function
declaration.
### Overloading
An important feature of templates in C++ is the ability to customize how they
end up specialized for specific types. Because template parameters (whether as
type parameters or function parameters) are pattern matched, we expect to
leverage pattern matching techniques to provide "better match" definitions that
are selected analogously to specializations in C++ templates. When expressed
through pattern matching, this may enable things beyond just template parameter
specialization, but that is an area that we want to explore cautiously.
### Constraining templates with interfaces
Because we consider only specific _parameters_ to be templated and they could be
individually migrated to a constrained interface using the
[generics system](README.md#generics), constraining templates themselves may be
less critical. Instead, we expect parameterized types and functions may use a
mixture of generic parameters and templated parameters based on where they are
constrained.
However, if there are still use cases, we would like to explore applying the
interface constraints of the generics system directly to template parameters
rather than create a new constraint system.
+124
View File
@@ -0,0 +1,124 @@
# Tuples
<!--
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
-->
## Table of contents
<!-- toc -->
- [TODO](#todo)
- [Overview](#overview)
- [Indices as compile-time constants](#indices-as-compile-time-constants)
- [Open questions](#open-questions)
- [Slicing ranges](#slicing-ranges)
- [Single-value tuples](#single-value-tuples)
- [Function pattern match](#function-pattern-match)
- [Type vs tuple of types](#type-vs-tuple-of-types)
<!-- tocstop -->
## TODO
This is a skeletal design, added to support [the overview](README.md). It should
not be treated as accepted by the core team; rather, it is a placeholder until
we have more time to examine this detail. Please feel welcome to rewrite and
update as appropriate.
## Overview
The primary composite type involves simple aggregation of other types as a tuple
(called a "product type" in formal type theory):
```
fn DoubleBoth(Int: x, Int: y) -> (Int, Int) {
return (2 * x, 2 * y);
}
```
This function returns a tuple of two integers represented by the type
`(Int, Int)`. The expression to return it uses a special tuple syntax to build a
tuple within an expression: `(<expression>, <expression>)`. This is actually the
same syntax in both cases. The return type is a tuple expression, and the first
and second elements are expressions referring to the `Int` type. The only
difference is the type of these expressions. Both are tuples, but one is a tuple
of types.
Element access uses subscript syntax:
```
fn Bar(Int: x, Int: y) -> Int {
var (Int, Int): t = (x, y);
return t[0] + t[1];
}
```
Tuples also support multiple indices and slicing to restructure tuple elements:
```
fn Baz(Int: x, Int: y, Int: z) -> (Int, Int) {
var (Int, Int, Int): t1 = (x, y, z);
var (Int, Int, Int): t2 = t1[(2, 1, 0)];
return t2[0 .. 2];
}
```
This code first reverses the tuple, and then extracts a slice using a half-open
range of indices.
### Indices as compile-time constants
In the example `t1[(2, 1, 0)]`, we will likely want to restrict these indices to
compile-time constants. Without that, run-time indexing would need to suddenly
switch to a variant-style return type to handle heterogeneous tuples. This would
both be surprising and complex for little or no value.
## Open questions
### Slicing ranges
The intent of `0 .. 2` is to be syntax for forming a sequence of indices based
on the half-open range [0, 2). There are a bunch of questions we'll need to
answer here:
- Is this valid anywhere? Only some places?
- What _is_ the sequence?
- If it is a tuple of indices, maybe that solves the above issue, and
unlike function call indexing with multiple indices is different from
indexing with a tuple of indexes.
- Do we need syntax for a closed range (`...` perhaps, unclear if that ends up
_aligned_ or in _conflict_ with other likely uses of `...` in pattern
matching)?
- All of these syntaxes are also very close to `0.2`, is that similarity of
syntax OK?
- Do we want to require the `..` to be surrounded by whitespace to
minimize that collision?
### Single-value tuples
This remains an area of active investigation. There are serious problems with
all approaches here. Without the collapse of one-tuples to scalars we need to
distinguish between a parenthesized expression (`(42)`) and a one tuple (in
Python or Rust, `(42,)`), and if we distinguish them then we cannot model a
function call as simply a function name followed by a tuple of arguments; one of
`f(0)` and `f(0,)` becomes a special case. With the collapse, we either break
genericity by forbidding `(42)[0]` from working, or it isn't clear what it means
to access a nested tuple's first element from a parenthesized expression:
`((1, 2))[0]`.
### Function pattern match
There are some interesting corner cases we need to expand on to fully and more
precisely talk about the exact semantic model of function calls and their
pattern match here, especially to handle variadic patterns and forwarding of
tuples as arguments. We are hoping for a purely type system answer here without
needing templates to be directly involved outside the type system as happens in
C++ variadics.
### Type vs tuple of types
Is `(Int, Int)` a type, a tuple of types, or is there even a difference between
the two? Is different syntax needed for these cases?
+76
View File
@@ -0,0 +1,76 @@
# Variables
<!--
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
-->
## Table of contents
<!-- toc -->
- [TODO](#todo)
- [Overview](#overview)
- [Declaring constants](#declaring-constants)
- [Alternatives](#alternatives)
- [Declaring constants](#declaring-constants-1)
- [Global variables](#global-variables)
<!-- tocstop -->
## TODO
This is a skeletal design, added to support [the overview](README.md). It should
not be treated as accepted by the core team; rather, it is a placeholder until
we have more time to examine this detail. Please feel welcome to rewrite and
update as appropriate.
## Overview
Blocks introduce nested scopes and can contain local variable declarations that
work similarly to function parameters.
For example:
```
fn Foo() {
var Int: x = 42;
}
```
This introduces a local variable named `x` into the block's scope. It has the
type `Int` and is initialized with the value `42`. These variable declarations
(and function declarations) have a lot more power than what we're covering just
yet, but this gives you the basic idea.
While there can be global constants, there are no global variables.
### Declaring constants
Constants will use template-like syntax for declarations. For example, a simple
integer constant looks like:
```carbon
var Int:$$ MyVal = 42;
```
## Alternatives
### Declaring constants
There is other syntax that could be used for declaring constants. There are
serious problems with the use of `const` in C++ as part of the type system.
Another alternative is `let` from Swift, although there are some questions
around how intuitive it is for this to introduce a constant. Another candidate
is `val` from Kotlin. Another thing we need to contend with is the surprise of
const and reference (semantic) types. At present we are leaning towards the
tempalte-like syntax for consistency within Carbon.
### Global variables
We are exploring several different ideas for how to design less bug-prone
patterns to replace the important use cases programmers still have for global
variables. We may be unable to fully address them, at least for migrated code,
and be forced to add some limited form of global variables back. We may also
discover that their convenience outweighs any improvements afforded.
+1
View File
@@ -30,5 +30,6 @@ request:
- [0051 - Goals](p0051.md)
- [0074 - Change comment/decision timelines in proposal process](p0074.md)
- [Decision](p0074-decision.md)
- [0083 - In-progress design overview](p0083.md)
<!-- endproposals -->
+157
View File
@@ -0,0 +1,157 @@
# In-progress design overview
<!--
Part of the Carbon Language, 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/83)
## Table of contents
<!-- toc -->
- [Problem](#problem)
- [Goals](#goals)
- [Background](#background)
- [Proposal](#proposal)
- [Alternatives considered](#alternatives-considered)
- [Single-file design overview](#single-file-design-overview)
- [No in-progress design overview](#no-in-progress-design-overview)
- [No overview of designs](#no-overview-of-designs)
<!-- tocstop -->
## Problem
We need some vaguely consistent shared understanding of the language design as a
whole. The goal is merely consistency in discussions, in-progress syntax, and
general approach. It in no way needs reliably to reflect the end state, either
desired or realized. Instead, it should evolve as each area matures and becomes
concrete, while providing an overarching overview that connects the language
together, and the consistent background that we all can refer back to during
discussions.
## Goals
This is intended to offer a reasonable starting point for:
- Example code.
- Conceptualizing Carbon at a high level.
- Reasonable, but not necessarily final, approaches to features in README.md.
- If any idea is obviously bad, we can clean it up here.
This proposal is not intended to achieve:
- A whole language design.
- This is way too much work for a single proposal; this is a skeletal
framework only.
- As we work on feature-specific designs, we may decide to use other
approaches. That's fine: we only need somewhere to start.
- The summaries in README.md may be expected to change over time.
- Feature-specific files aren't intended to be well-written or comprehensive.
They are a quick jot of prior thoughts.
- We want to avoid getting stuck on language details that we should
consider more carefully regardless. If you're passionate about a
feature, please feel free to start a new proposal for it.
- Each and every aspect of the suggested overview should be subject to
careful examination and justification before it becomes a settled plan
of record.
## Background
Many of the ideas here stem from discussions between several of the initial
people working on Carbon over several years. That doesn't make them good, but
may give some context on where they came from. They are also heavily informed by
the experience several of us have both working on the Clang C++ frontend and
several C++ codebases including those of LLVM and Clang themselves.
## Proposal
See [the language design overview document](/docs/design/README.md).
## Alternatives considered
### Single-file design overview
We also considered putting the full design overview in one file, as in
[PR 22](https://github.com/carbon-language/carbon-lang/pull/22). This is versus
the hierarchy proposed here.
Pros:
- All proposed changes are in one place.
- Easier for people to skim rationale and considered changes.
Cons:
- Encourages more single-file designs.
- A principle of the multi-file approach is that complex features may have
subdirectories with their own README.md and files for sub-features,
similar to the relationship between this design overview and features.
- Single-file designs may be harder to evolve long-term, as the volume of
information contained impedes reading.
### No in-progress design overview
The primary alternative is to avoid even having a draft or in-progress design of
this form until each constituent component is more thoroughly conceived and
considered.
Pros:
- Avoids anchoring design on approaches that haven't yet been fully explored.
- Avoids getting stuck on discussing details where a proposal isn't
fleshed out.
Cons:
- The lack of an overview can lead to significant confusion and
inconsistencies in discussion, hindering fleshing out details.
- An overview offers basic shaping of the language as a whole, even as it
evolves.
The compromise chosen is to have the in-progress design and simply work to
resist both anchoring and distraction stemming from it. We want to get the
benefits we can here while minimizing the cost.
### No overview of designs
The overview will result in content duplication from individual designs. At the
time of this proposal, this may be significant because individual designs are
not fleshed out, and should thus duplication should be expected to reduce over
time. However, it should be expected to remain as the duplication is fundamental
to having an overview.
This duplication could be addressed by removing the overview. Instead,
design/README.md could be restricted to listing existing designs, with no
additional content.
The proposed approach assumes that the proposed overviews offer significant
value for ramp-up.
Pros:
- Eliminates content duplication.
- A simple index is easier to maintain long-term, with less to become stale.
- It could be fully automated.
Cons:
- No quick way to get a high-level understanding.
- The overview is the only step before "reading every design".
- For example, we summarize common control flow keywords, so that readers
don't need to identify which documents they come from and what exists.
- Harder to show relationships between various features.
- While examples can show how designs relate, it may not be as obvious
from a simple link, even when reading the associated design.
- For example, lexical conventions come up as references for three
otherwise distinct sections. If we had a simple index of files, we
should expect users to need to read individual designs to understand
relationships.
- For example, we explain in brief the relationships between categories of
types.
- There's disagreement about whether the text of README.md offers any
utility:
[comment thread](https://github.com/carbon-language/carbon-lang/pull/83/files/25437de9e61b3a15e8ddde67b6297f1795922355..97da855dbe6023930e02473af46abea03af991e7#r444487049)