# `if`

expressions

## Table of contents

## Overview

An `if`

expression is an expression of the form:

`if`

condition`then`

value1`else`

value2

The *condition* is converted to a `bool`

value in the same way as the condition of an `if`

statement.

Note:These conversions have not yet been decided.

The *value1* and *value2* are implicitly converted to their common type, which is the type of the `if`

expression.

## Syntax

`if`

expressions have very low precedence, and cannot appear as the operand of any operator, except as the right-hand operand in an assignment. They can appear in other context where an expression is permitted, such as within parentheses, as the operand of a `return`

statement, as an initializer, or in a comma-separated list such as a function call.

The *value1* and *value2* expressions are arbitrary expressions, and can themselves be `if`

expressions. *value2* extends as far to the right as possible. An `if`

expression can be parenthesized if the intent is for *value2* to end earlier.

```
// OK, same as `if cond then (1 + 1) else (2 + (4 * 6))`
var a: i32 = if cond then 1 + 1 else 2 + 4 * 6;
// OK
var b: i32 = (if cond then 1 + 1 else 2) + 4 * 6;
```

An `if`

keyword at the start of a statement is always interpreted as an `if`

statement, never as an `if`

expression, even if it is followed eventually by a `then`

keyword.

## Semantics

The converted *condition* is evaluated. If it evaluates to `true`

, then the converted *value1* is evaluated and its value is the result of the expression. Otherwise, the converted *value2* is evaluated and its value is the result of the expression.

## Finding a common type

The common type of two types `T`

and `U`

is `(T as CommonType(U)).Result`

, where `CommonType`

is the `Carbon.CommonType`

constraint. `CommonType`

is notionally defined as follows:

```
constraint CommonType(U:! CommonTypeWith(Self)) {
extend CommonTypeWith(U) where .Result == U.Result;
}
```

The actual definition is a bit more complex than this, as described in symmetry.

The interface `CommonTypeWith`

is used to customize the behavior of `CommonType`

:

```
interface CommonTypeWith(U:! type) {
let Result:! type
where Self impls ImplicitAs(.Self) and
U impls ImplicitAs(.Self);
}
```

The implementation `A as CommonTypeWith(B)`

specifies the type that `A`

would like to result from unifying `A`

and `B`

as its `Result`

.

*Note:* It is required that both types implicitly convert to the common type. Some blanket `impl`

declaractions for `CommonTypeWith`

are provided as part of the prelude. These are described in the following sections.

*Note:* The same mechanism is expected to eventually be used to compute common types in other circumstances.

### Symmetry

The common type of `T`

and `U`

should always be the same as the common type of `U`

and `T`

. This is enforced in two steps:

- A
`SymmetricCommonTypeWith`

interface implicitly provides a`B as CommonTypeWith(A)`

implementation whenever one doesnâ€™t exist but an`A as CommonTypeWith(B)`

implementation exists. `CommonType`

is defined in terms of`SymmetricCommonTypeWith`

, and requires that both`A as SymmetricCommonTypeWith(B)`

and`B as SymmetricCommonTypeWith(A)`

produce the same type.

The interface `SymmetricCommonTypeWith`

is an implementation detail of the `CommonType`

constraint. It is defined and implemented as follows:

```
interface SymmetricCommonTypeWith(U:! type) {
let Result:! type
where Self impls ImplicitAs(.Self) and
U impls ImplicitAs(.Self);
}
match_first {
impl forall [T:! type, U:! CommonTypeWith(T)]
T as SymmetricCommonTypeWith(U) where .Result = U.Result {}
impl forall [U:! type, T:! CommonTypeWith(U)]
T as SymmetricCommonTypeWith(U) where .Result = T.Result {}
}
```

The `SymmetricCommonTypeWith`

interface is not exported, so users may not declare their own implementations of it, and only the two blanket `impl`

declarations above are used. The `CommonType`

constraint is then defined as follows:

```
constraint CommonType(U:! SymmetricCommonTypeWith(Self)) {
extend SymmetricCommonTypeWith(U) where .Result == U.Result;
}
```

When computing the common type of `T`

and `U`

, if only one of the types provides a `CommonTypeWith`

implementation, that determines the common type. If both types provide a `CommonTypeWith`

implementation and their `Result`

types are the same, that determines the common type. Otherwise, if both types provide implementations but their `Result`

types differ, there is no common type, and the `CommonType`

constraint is not met. For example, given:

```
// Implementation #1
impl forall [T:! type] MyX as CommonTypeWith(T) where .Result = MyX {}
// Implementation #2
impl forall [T:! type] MyY as CommonTypeWith(T) where .Result = MyY {}
```

`MyX as CommonTypeWith(MyY)`

will select #1, and `MyY as CommonTypeWith(MyX)`

will select #2, but the constraints on `MyX as CommonType(MyY)`

will not be met because result types differ.

### Same type

If `T`

is the same type as `U`

, the result is that type:

```
final impl forall [T:! type] T as CommonTypeWith(T) where .Result = T {}
```

*Note:* This rule is intended to be considered more specialized than the other rules in this document.

Because this `impl`

is declared `final`

, `T.(CommonType(T)).Result`

is always assumed to be `T`

, even in contexts where `T`

involves a symbolic binding and so the result would normally be an unknown type whose facet type is `type`

.

```
fn F[T:! Hashable](c: bool, x: T, y: T) -> HashCode {
// OK, type of `if` expression is `T`.
return (if c then x else y).Hash();
}
```

### Implicit conversions

If `T`

implicitly converts to `U`

, the common type is `U`

:

```
impl forall [T:! type, U:! ImplicitAs(T)]
T as CommonTypeWith(U) where .Result = T {}
```

*Note:* If an implicit conversion is possible in both directions, and no more specific implementation exists, the constraints on `T as CommonType(U)`

will not be met because `(T as CommonTypeWith(U)).Result`

and `(U as CommonTypeWith(T)).Result`

will differ. In order to define a common type for such a case, `CommonTypeWith`

implementations in both directions must be provided to override the blanket `impl`

declarations in both directions:

```
impl MyString as CommonTypeWith(YourString) where .Result = MyString {}
impl YourString as CommonTypeWith(MyString) where .Result = MyString {}
var my_string: MyString;
var your_string: YourString;
// The type of `also_my_string` is `MyString`.
var also_my_string: auto = if cond then my_string else your_string;
```

## Alternatives considered

- Provide no conditional expression
- Use
`cond ? expr1 : expr2`

, like in C and C++ syntax - Use
`if (cond) expr1 else expr2`

syntax - Use
`if (cond) then expr1 else expr2`

syntax - Allow
`1 + if cond then expr1 else expr2`

- Only require one
`impl`

to specify the common type if implicit conversions in both directions are possible - Introduce special rules for lvalue conditionals

## References

- Proposal #911: Conditional expressions.