Expressions
Table of contents
- Overview
- Precedence
- Names
- Operators
- Suffix operators
- Conversions and casts
if
expressions- Numeric type literal expressions
- Alternatives considered
- References
Overview
Expressions are the portions of Carbon syntax that produce values. Because types in Carbon are values, this includes anywhere that a type is specified.
fn Foo(a: i32*) -> i32 {
return *a;
}
Here, the parameter type i32*
, the return type i32
, and the operand *a
of the return
statement are all expressions.
Precedence
Expressions are interpreted based on a partial precedence ordering. Expression components which lack a relative ordering must be disambiguated by the developer, for example by adding parentheses; otherwise, the expression will be invalid due to ambiguity. Precedence orderings will only be added when it’s reasonable to expect most developers to understand the precedence without parentheses.
The precedence diagram is defined thusly:
%%{init: {'themeVariables': {'fontFamily': 'monospace'}}}%%
graph BT
parens["(...)"]
braces["{...}"]
click braces "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/classes.md#literals"
unqualifiedName["x"]
click unqualifiedName "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/README.md#unqualified-names"
top((" "))
suffixOps{"x.y<br>
x.(...)<br>
x->y<br>
x->(...)<br>
x(...)<br>
x[y]"}
click suffixOps "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/README.md#suffix-operators"
constType["const T"]
click pointer-type "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/type_operators.md"
pointerType{"T*"}
click pointer-type "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/type_operators.md"
pointer{"*x<br>
&x<br>"}
click pointer "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/pointer.md"
negation["-x"]
click negation "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/arithmetic.md"
complement["^x"]
click complement "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/bitwise.md"
incDec["++x;<br>--x;"]
click incDec "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/assignment.md"
unary((" "))
as["x as T"]
click as "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/implicit_conversions.md"
multiplication>"x * y<br>
x / y"]
click multiplication "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/arithmetic.md"
addition>"x + y<br>
x - y"]
click addition "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/arithmetic.md"
modulo["x % y"]
click modulo "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/arithmetic.md"
bitwise_and>"x & y"]
bitwise_or>"x | y"]
bitwise_xor>"x ^ y"]
click bitwise_and "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/bitwise.md"
click bitwise_or "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/bitwise.md"
click bitwise_xor "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/bitwise.md"
shift["x << y<br>
x >> y"]
click shift "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/bitwise.md"
binaryOps((" "))
where["T where R"]
comparison["x == y<br>
x != y<br>
x < y<br>
x <= y<br>
x > y<br>
x >= y"]
click comparison "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/comparison_operators.md"
not["not x"]
click not "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/logical_operators.md"
logicalOperand((" "))
and>"x and y"]
click and "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/logical_operators.md"
or>"x or y"]
click or "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/logical_operators.md"
logicalExpression((" "))
if>"if x then y else z"]
click if "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/if.md"
insideParens["(...)"]
assignment["x = y;<br>x $= y;"]
click assignment "https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/assignment.md"
expressionStatement["x;"]
top --> parens & braces & unqualifiedName
suffixOps --> top
constType --> suffixOps
pointerType --> constType
pointer --> suffixOps
negation & complement & incDec --> pointer
unary --> pointerType & negation & complement
%% Use a longer arrow here to put `not` next to other unary operators
not ---> suffixOps
%% `as` at the same level as `where` and comparisons
as -----> unary
multiplication & modulo & bitwise_and & bitwise_or & bitwise_xor & shift --> unary
addition --> multiplication
binaryOps --> addition & modulo & bitwise_and & bitwise_or & bitwise_xor & shift
where --> binaryOps
comparison --> binaryOps
logicalOperand --> comparison & not
%% This helps group `and` and `or` together
classDef hidden display: none;
HIDDEN:::hidden ~~~ logicalOperand
and & or --> logicalOperand
logicalExpression --> as & where & and & or
if & expressionStatement --> logicalExpression
insideParens & assignment --> if
The diagram’s attributes are:
-
Each non-empty node represents a precedence group. Empty circles are used to simplify the graph, and do not represent a precedence group.
-
When an expression is composed from different precedence groups, the interpretation is determined by the precedence edges:
-
A precedence edge A –> B means that A is lower precedence than B, so A can contain B without parentheses. For example,
or --> not
means thatnot x or y
is treated as(not x) or y
. -
Precedence edges are transitive. For example,
or --> == --> as
means thator
is lower precedence thanas
.
-
-
When a binary operator expression is composed from a single precedence group, the interpretation is determined by the associativity of the precedence group:
graph TD non["Non-associative"] left>"Left associative"]
-
For example,
+
and-
are left-associative and in the same precedence group, soa + b + c - d
is treated as((a + b) + c) - d
. -
Note that in Carbon, we currently only have left-associative operators. Unlike C++ and other languages, assignment isn’t a right-associative operator, it uses its own statement.
-
-
When a unary operator expression is composed from a single precedence group, it can allow unparenthesized repetition or not:
graph TD non["Non-repeating"] repeating{"Repeating"}
This is analogous to associativity for binary operators.
Names
Unqualified names
An unqualified name is a word that is not a keyword and is not preceded by a period (.
).
TODO: Name lookup rules for unqualified names.
Qualified names and member access
A qualified name is a word that appears immediately after a period or rightward arrow. Qualified names appear in the following contexts:
- Designators:
.
word - Simple member access expressions: expression
.
word - Simple pointer member access expressions: expression
->
word
var x: auto = {.hello = 1, .world = 2};
^^^^^ ^^^^^ qualified name
^^^^^^ ^^^^^^ designator
x.hello = x.world;
^^^^^ ^^^^^ qualified name
^^^^^^^ ^^^^^^^ member access expression
x.hello = (&x)->world;
^^^^^ qualified name
^^^^^^^^^^^ pointer member access expression
Qualified names refer to members of an entity determined by the context in which the expression appears. For a member access, the entity is named by the expression preceding the period. In a struct literal, the entity is the struct type. For example:
package Foo;
namespace N;
fn N.F() {}
fn G() {
// Same as `(Foo.N).F()`.
// `Foo.N` names namespace `N` in package `Foo`.
// `(Foo.N).F` names function `F` in namespace `N`.
Foo.N.F();
}
// `.n` refers to the member `n` of `{.n: i32}`.
fn H(a: {.n: i32}) -> i32 {
// `a.n` is resolved to the member `{.n: i32}.n`,
// and names the corresponding subobject of `a`.
return a.n;
}
fn J() {
// `.n` refers to the member `n of `{.n: i32}`.
H({.n = 5 as i32});
}
Member access expressions associate left-to-right. If the member name is more complex than a single word, a compound member access expression can be used, with parentheses around the member name:
- expression
.
(
expression)
- expression
->
(
expression)
interface I { fn F[self: Self](); }
class X {}
impl X as I { fn F[self: Self]() {} }
// `x.I.F()` would mean `(x.I).F()`.
fn Q(x: X) { x.(I.F)(); }
Either simple or compound member access can be part of a pointer member access expression when an ->
is used instead of a .
, where expression ->
… is syntactic sugar for (
*
expression )
.
….
Operators
Most expressions are modeled as operators:
Category | Operator | Syntax | Function |
---|---|---|---|
Call | () (unary) | x(...) | Function call: the value returned by calling the function x . |
Call | [] (unary) | x[y] | Subscripting or indexing: returns the element y of x . |
Pointer | * (unary) | *x | Pointer dereference: the object pointed to by x . |
Pointer | & (unary) | &x | Address-of: a pointer to the object x . |
Arithmetic | - (unary) | -x | The negation of x . |
Bitwise | ^ (unary) | ^x | The bitwise complement of x . |
Arithmetic | + | x + y | The sum of x and y . |
Arithmetic | - (binary) | x - y | The difference of x and y . |
Arithmetic | * | x * y | The product of x and y . |
Arithmetic | / | x / y | x divided by y , or the quotient thereof. |
Arithmetic | % | x % y | x modulo y . |
Bitwise | & | x & y | The bitwise AND of x and y . |
Bitwise | \| | x \| y | The bitwise OR of x and y . |
Bitwise | ^ (binary) | x ^ y | The bitwise XOR of x and y . |
Bitwise | << | x << y | x bit-shifted left y places. |
Bitwise | >> | x >> y | x bit-shifted right y places. |
Conversion | as | x as T | Converts the value x to the type T . |
Comparison | == | x == y | Equality: true if x is equal to y . |
Comparison | != | x != y | Inequality: true if x is not equal to y . |
Comparison | < | x < y | Less than: true if x is less than y . |
Comparison | <= | x <= y | Less than or equal: true if x is less than or equal to y . |
Comparison | > | x > y | Greater than: true if x is greater than to y . |
Comparison | >= | x >= y | Greater than or equal: true if x is greater than or equal to y . |
Logical | and | x and y | A short-circuiting logical AND: true if both operands are true . |
Logical | or | x or y | A short-circuiting logical OR: true if either operand is true . |
Logical | not | not x | Logical NOT: true if the operand is false . |
The binary arithmetic and bitwise operators also have compound assignment forms. These are statements rather than expressions, and do not produce a value.
Suffix operators
These operators act like unary postfix operators for purposes of precedence:
- Member access operators, like
x.y
and the dereferencing variantx->y
, only have an expression on their left-hand side. The right-hand side is a name. - The compound member access operators,
x.(...)
andx->(...)
, have an expression as their second operand, but put that expression in parentheses and so it doesn’t participate in the precedence considerations of its first operand. - The indexing operator,
x[y]
, similarly puts its second operand in matching square brackets. - The call operator,
x(...)
, takes a comma-separated list of arguments, but again puts them in parentheses that clearly separate them for precedence purposes.
Conversions and casts
When an expression appears in a context in which an expression of a specific type is expected, implicit conversions are applied to convert the expression to the target type.
Expressions can also be converted to a specific type using an as
expression.
fn Bar(n: i32);
fn Baz(n: i64) {
// OK, same as Bar(n as i32)
Bar(n);
}
if
expressions
An if
expression chooses between two expressions.
fn Run(args: Span(StringView)) {
var file: StringView = if args.size() > 1 then args[1] else "/dev/stdin";
}
if
expressions are analogous to ?:
ternary expressions in C and C++.
Numeric type literal expressions
Carbon’s syntax provides a simple way to represent different types of integers and floating-point numbers. Each type is identified with a keyword-like syntax, prefixed with either i
, u
, or f
followed by a multiple of 8, representing the size in bits of the data type.
These are referred to as numeric type literals.
Alternatives considered
Other expression documents will list more alternatives; this lists alternatives not noted elsewhere.
References
Other expression documents will list more references; this lists references not noted elsewhere.
- Proposal #555: Operator precedence.