# Conversions and Promotions

C3 differs in some crucial respects when it comes to number conversions and promotions. These are the rules for C3:

- float to int conversions require a cast
- int to float conversions do not require a cast
- bool to float converts to 0.0 / 1.0
- widening float conversions are only conditionally allowed(*)
- narrowing conversions require a cast(*)
- widening int conversions are only conditionally allowed(*)
- signed <-> unsigned conversions of the same type do not require a cast.
- In conditionals float to bool
*do not*require a cast, any non zero float value considered true - Implicit conversion to bool only occurs in conditionals or when the value is enclosed in
`()`

e.g.`bool x = (1.0)`

or`if (1.0) { ... }`

C3 uses two’s complement arithmetic for all integer math.

*Please note: the abbreviations “lhs” for “left hand side” and “rhs” for “right hand side” are used in the text below.

## Target type

The left hand side of an assignment, or the parameter type in a call is known as the *target type* the target type is used for implicit widening and inferring struct initialization.

## Common arithmetic promotion

Like C, C3 uses implicit arithmetic promotion of integer and floating point variables before arithmetic operations:

- For any floating point type with a bitwidth smaller than 32 bits, widen to
`float`

. E.g.`f16 -> float`

- For an integer type smaller than the
*minimum arithmetic width*promote the value to a same signed integer of the*minimum arithmetic width*(this usually corresponds to a c int/uint). E.g.`ushort -> uint`

## Implicit narrowing

An expression with an integer type, may implicitly narrow to smaller integer type, and similarly a float type may implicitly narrow to less wide floating point type is determined from the following algorithm.

- Shifts and assign look at the lhs expression.
`++`

,`--`

,`~`

,`-`

,`!!`

,`!`

- check the inner type.`+`

,`-`

,`*`

,`/`

,`%`

,`^`

,`|`

,`&`

,`??`

,`?:`

- check both lhs and rhs.- Narrowing int/float cast, assume the type is the narrowed type.
- Widening int/float cast, look at the inner expression, ignoring the cast.
- In the case of any other cast, assume it is opaque and the type is that of the cast.
- In the case of an integer literal, instead of looking at the type, check that the integer would fit the type to narrow to.
- For .len access, allow narrowing to C int width.
- For all other expressions, check against the size of the type.

As rough guide: if all the sub expressions originally are small enough it’s ok to implicitly convert the result.

Examples

## Implicit widening

Unlike C, implicit widening will only happen on “simple expressions”: if the expression is a primary expression, or a unary operation on a primary expression.

For assignment, special rules hold. For an assignment to a binary expression, *if* its two subexpressions are “simple expressions” and the binary expression is `+`

, `-`

, `/`

, `*`

, allow an implicit promotion of the two sub expressions.

As a rule of thumb, if there are more than one possible conversion an explicit cast is needed.

Example:

## Maximum type

The *maximum type* is a concept used when unifying two or more types. The algorithm follows:

- First perform implicit promotion.
- If both types are the same, the maximum type is this type.
- If one type is a floating point type, and the other is an integer type, the maximum type is the floating point type. E.g.
`int + float -> float`

. - If both types are floating point types, the maximum type is the widest floating point type. E.g.
`float + double -> double`

. - If both types are integer types with the same signedness, the maximum type is the widest integer type of the two. E.g.
`uint + ulong -> ulong`

. - If both types are integer types with different signedness, the maximum type is a signed integer with the same bit width as the maximum integer type.
`ulong + int -> long`

- If at least one side is a struct or a pointer to a struct with an
`inline`

directive on a member, check recursively check if the type of the inline member can be used to find a maximum type (see below under sub struct conversions) - All other cases are errors.

## Substruct conversions

Substructs may be used in place of its parent structs in many cases. The rule is as follows:

- A substruct pointer may implicitly convert to a parent struct.
- A substruct
*value*may be implicitly assigned to a variable with the parent struct type, This will*truncate*the value, copying only the parent part of the substruct. However, a substruct value cannot be assigned its parent struct. - Substruct slices and arrays
*can not*be cast (implicitly or explicitly) to an array of the parent struct type.

## Pointer conversions

Pointer conversion between types usually need explicit casts. The exception is `void *`

which any type may implicitly convert *to* or *from*. Conversion rules from and to arrays are detailed under arrays

## Vector conversions

Conversion between underlying vector types need explicit conversions. They work
as regular conversions with one notable exception: converting a `true`

boolean
vector value into an int will yield a value with all bits set. So `bool[<2>] { true, false }`

converted to for example `char[<2>]`

will yield `{ 255, 0 }`

.

Vectors can also be cast to the corresponding array type, so for example: `char[<2>]`

<=> `char[2]`

.

## Binary conversions

### 1. Multiplication, division, remainder, subtraction / addition with both operands being numbers

These operations are only valid for integer and float types.

- Resolve the operands.
- Find the maximum type of the two operands.
- Promote both operands to the resulting type if both are simple expressions
- The resulting type of the expression is the resulting type.

### 2. Addition with left side being a pointer

- Resolve the operands.
- If the rhs is not an integer, this is an error.
- If the rhs has a bit width that exceeds isz, this is an error.
- The result of the expression is the lhs type.

### 3. Subtraction with lhs pointer and rhs integer

- Resolve the operands.
- If the right hand type has a bit width that exceeds isz, this is an error.
- The result of the expression is the left hand type.

### 4. Subtraction with both sides pointers

- Resolve the operands.
- If the either side is a
`void *`

, it is cast to the other type. - If the types of the sides are different, this is an error.
- The result of the expression is isz.
- If this result exceeds the target width, this is an error.

### 6. Bit operations `^`

`&`

`|`

These operations are only valid for integers and booleans.

- Resolve the operands.
- Find the maximum type of the two operands.
- Promote both operands to the maximum type if they are simple expressions.
- The result of the expression is the maximum type.

### 6. Shift operations `<<`

`>>`

These operations are only valid for integers.

- Resolve the operands.
- In safe mode, insert a trap to ensure that rhs >= 0 and rhs < bit width of the left hand side. 3The result of the expression is the lhs type.

### 7. Assignment operations `+=`

`-=`

`*=`

`*=`

`/=`

`%=`

`^=`

`|=`

`&=`

- Resolve the lhs.
- Resolve the right operand as an assignment rhs.
- The result of the expression is the lhs type.

### 8. Assignment shift `>>=`

`<<=`

- Resolve both operands
- In safe mode, insert a trap to ensure that rhs >= 0 and rhs < bit width of the left hand side.
- The result of the expression is the lhs type.

### 9. `&&`

and `||`

- Resolve both operands.
- Insert bool cast of both operands.
- The type is bool.

### 10. `<=`

`==`

`>=`

`!=`

- Resolve the operands, left to right.
- Find the maximum type of the two operands.
- Promote both operands to the maximum type.
- The type is bool.

## Unary conversions

### 1. Bit negate

- Resolve the inner operand.
- If the inner type is not an integer this is an error.
- The type is the inner type.

### 2. Boolean not

- Resolve the inner operand.
- The type is bool.

### 3. Negation

- Resolve the inner operand.
- If the type inner type is not a number this is an error.
- If the inner type is an unsigned integer, cast it to the same signed type.
- The type is the type of the result from (3)

### 4. `&`

and `&&`

- Resolve the inner operand.
- The type is a pointer to the type of the inner operand.

### 5. `*`

- Resolve the inner operand.
- If the operand is not a pointer, or is a
`void *`

pointer, this is an error. - The type is the pointee of the inner operand’s type.

Dereferencing 0 is implementation defined.

### 6. `++`

and `--`

- Resolve the inner operand.
- If the type is not a number, this is an error.
- The type is the same as the inner operand.

## Base expressions

### 1. Typed identifiers

- The type is that of the declaration.
- If the width of the type is less than that of the target type, widen to the target type.
- If the width of the type is greater than that of the target type, it is an error.

### 2. Constants and literals

- If the constant is an integer, it is assumed to be the
*arithmetic promotion width*and signed. If the suffix`u`

is added, it is assumed to be an unsigned number. If a suffix`ixx`

or`uxx`

is given then it is considered a an integer of that type width and signedness. It cannot be implicitly narrowed. - If the constant is a floating point value, it is assumed to be a
`double`

unless suffixed with`f`

which is then assumed to be a`float`

. If a bit width is given after`f`

, it is instead a floating point type of that width.