|
| 1 | +# Numeric Literal Semantics |
| 2 | + |
| 3 | +<!-- |
| 4 | +Part of the Carbon Language project, under the Apache License v2.0 with LLVM |
| 5 | +Exceptions. See /LICENSE for license information. |
| 6 | +SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 7 | +--> |
| 8 | + |
| 9 | +> **STATUS:** Up-to-date on 23-Aug-2022. |
| 10 | +
|
| 11 | +<!-- toc --> |
| 12 | + |
| 13 | +## Table of contents |
| 14 | + |
| 15 | +- [Overview](#overview) |
| 16 | + - [TODO](#todo) |
| 17 | + - [Numeric literal syntax](#numeric-literal-syntax) |
| 18 | + - [Defined Types](#defined-types) |
| 19 | + - [Implicit conversions](#implicit-conversions) |
| 20 | +- [Examples](#examples) |
| 21 | +- [Alternatives Considered](#alternatives-considered) |
| 22 | +- [References](#references) |
| 23 | + |
| 24 | +<!-- tocstop --> |
| 25 | + |
| 26 | +## Overview |
| 27 | + |
| 28 | +Numeric Literals are defined on Wikipedia |
| 29 | +[here](<https://en.wikipedia.org/wiki/Literal_(computer_programming)>). |
| 30 | + |
| 31 | +In Carbon, numeric literals have a type derived from their value. Two integer |
| 32 | +literals have the same type if and only if they represent the same integer. Two |
| 33 | +real number literals have the same type if and only if they represent the same |
| 34 | +real number. |
| 35 | + |
| 36 | +That is: |
| 37 | + |
| 38 | +- For every integer, there is a type representing literals with that integer |
| 39 | + value. |
| 40 | +- For every rational number, there is a type representing literals with that |
| 41 | + real value. |
| 42 | +- The types for real numbers are distinct from the types for integers, even |
| 43 | + for real numbers that represent integers. For example, `1 / 2` results in |
| 44 | + `0`, due to integer arithmetic, whereas `1.0 / 2` results in `0.5`. This is |
| 45 | + due to `1` having an integral type, while `1.0` has a real number type, even |
| 46 | + though it represents the same numeric value. |
| 47 | + |
| 48 | +Primitive operators are available between numeric literals, and produce values |
| 49 | +with numeric literal types. For example, the type of `1 + 2` is the same as the |
| 50 | +type of `3`. |
| 51 | + |
| 52 | +Numeric types can provide conversions to support initialization from numeric |
| 53 | +literals. Because the value of the literal is carried in the type, a type-level |
| 54 | +decision can be made as to whether the conversion is valid. |
| 55 | + |
| 56 | +The integer types defined in the standard library permit conversion from integer |
| 57 | +literal types whose values are representable in the integer type. The |
| 58 | +floating-point types defined in the standard library permit conversion from |
| 59 | +integer and rational literal types whose values are between the minimum and |
| 60 | +maximum finite value representable in the floating-point type. |
| 61 | + |
| 62 | +### TODO |
| 63 | + |
| 64 | +This document needs to be updated once we have resolved how to reference things |
| 65 | +brought in by the prelude. `BigInt`, `Rational`, `IntLiteral`, and |
| 66 | +`FloatLiteral` will likely be accessed through a package prefix like |
| 67 | +`Carbon.BigInt` or `Core.BigInt`, and the [Defined Types](#defined-types) |
| 68 | +section will need to be updated to reflect those. |
| 69 | + |
| 70 | +### Numeric literal syntax |
| 71 | + |
| 72 | +Numeric Literal syntax is covered in the |
| 73 | +[numeric literal lexical conventions](lexical_conventions/numeric_literals.md) |
| 74 | +doc. Both Integer and Real-Number syntax is defined, with decimal, hexadecimal |
| 75 | +and binary integer literals, and decimal and hexadecimal real number literals. |
| 76 | + |
| 77 | +### Defined Types |
| 78 | + |
| 79 | +The following types are defined in the Carbon prelude: |
| 80 | + |
| 81 | +- An arbitrary-precision integer type. |
| 82 | + |
| 83 | + ``` |
| 84 | + class BigInt; |
| 85 | + ``` |
| 86 | +
|
| 87 | +- A rational type, parameterized by a type used for its numerator and |
| 88 | + denominator. |
| 89 | +
|
| 90 | + ``` |
| 91 | + class Rational(T:! Type); |
| 92 | + ``` |
| 93 | +
|
| 94 | + The exact constraints on `T` are not yet decided. |
| 95 | +
|
| 96 | +- A type representing integer literals. |
| 97 | +
|
| 98 | + ``` |
| 99 | + class IntLiteral(N:! BigInt); |
| 100 | + ``` |
| 101 | +
|
| 102 | +- A type representing floating-point literals. |
| 103 | +
|
| 104 | + ``` |
| 105 | + class FloatLiteral(X:! Rational(BigInt)); |
| 106 | + ``` |
| 107 | +
|
| 108 | +All of these types are usable during compilation. `BigInt` supports the same |
| 109 | +operations as `Int(n)`. `Rational(T)` supports the same operations as |
| 110 | +`Float(n)`. |
| 111 | +
|
| 112 | +The types `IntLiteral(n)` and `FloatLiteral(x)` also support primitive integer |
| 113 | +and floating-point operations such as arithmetic and comparison, but these |
| 114 | +operations are typically heterogeneous: for example, an addition between |
| 115 | +`IntLiteral(n)` and `IntLiteral(m)` produces a value of type |
| 116 | +`IntLiteral(n + m)`. |
| 117 | +
|
| 118 | +### Implicit conversions |
| 119 | +
|
| 120 | +`IntLiteral(n)` converts to any sufficiently large integer type, as if by: |
| 121 | +
|
| 122 | +``` |
| 123 | +impl forall [template N:! BigInt, template M:! BigInt] |
| 124 | + IntLiteral(N) as ImplicitAs(Carbon.Int(M)) |
| 125 | + if N >= Carbon.Int(M).MinValue as BigInt and N <= Carbon.Int(M).MaxValue as BigInt { |
| 126 | + ... |
| 127 | +} |
| 128 | +impl forall [template N:! BigInt, template M:! BigInt] |
| 129 | + IntLiteral(N) as ImplicitAs(Carbon.UInt(M)) |
| 130 | + if N >= Carbon.UInt(M).MinValue as BigInt and N <= Carbon.UInt(M).MaxValue as BigInt { |
| 131 | + ... |
| 132 | +} |
| 133 | +``` |
| 134 | +
|
| 135 | +The above is for exposition purposes only; various parts of this syntax are not |
| 136 | +yet decided. |
| 137 | +
|
| 138 | +Similarly, `IntLiteral(x)` and `FloatLiteral(x)` convert to any sufficiently |
| 139 | +large floating-point type, and produce the nearest representable floating-point |
| 140 | +value. |
| 141 | +
|
| 142 | +Conversions in which `x` lies exactly half-way between two values are rounded to |
| 143 | +the value in which the mantissa is even, as defined in the IEEE 754 standard and |
| 144 | +as was decided in |
| 145 | +[proposal #866](https://github.com/carbon-language/carbon-lang/pull/866). |
| 146 | +
|
| 147 | +Conversions in which `x` is outside the range of finite values of the |
| 148 | +floating-point type are rejected rather than saturating to the finite range or |
| 149 | +producing an infinity. |
| 150 | +
|
| 151 | +## Examples |
| 152 | +
|
| 153 | +```carbon |
| 154 | +// This is OK: the initializer is of the integer literal type with value |
| 155 | +// -2147483648 despite being written as a unary `-` applied to a literal. |
| 156 | +var x: i32 = -2147483648; |
| 157 | +
|
| 158 | +// This initializes y to 2^60. |
| 159 | +var y: i64 = 1 << 60; |
| 160 | +
|
| 161 | +// This forms a rational literal whose value is one third, and converts it to |
| 162 | +// the nearest representable value of type `f64`. |
| 163 | +var z: f64 = 1.0 / 3.0; |
| 164 | +
|
| 165 | +// This is an error: 300 cannot be represented in type `i8`. |
| 166 | +var c: i8 = 300; |
| 167 | +
|
| 168 | +fn F[template T:! Type](v: T) { |
| 169 | + var x: i32 = v * 2; |
| 170 | +} |
| 171 | +
|
| 172 | +// OK: x = 2_000_000_000. |
| 173 | +F(1_000_000_000); |
| 174 | +
|
| 175 | +// Error: 4_000_000_000 can't be represented in type `i32`. |
| 176 | +F(2_000_000_000); |
| 177 | +
|
| 178 | +// No storage required for the bound when it's of integer literal type. |
| 179 | +struct Span(template T:! Type, template BoundT:! Type) { |
| 180 | + var begin: T*; |
| 181 | + var bound: BoundT; |
| 182 | +} |
| 183 | +
|
| 184 | +// Returns 1, because 1.3 can implicitly convert to f32, even though conversion |
| 185 | +// to f64 might be a more exact match. |
| 186 | +fn G() -> i32 { |
| 187 | + match (1.3) { |
| 188 | + case _: f32 => { return 1; } |
| 189 | + case _: f64 => { return 2; } |
| 190 | + } |
| 191 | +} |
| 192 | +
|
| 193 | +// Can only be called with a literal 0. |
| 194 | +fn PassMeZero(_: IntLiteral(0)); |
| 195 | +
|
| 196 | +// Can only be called with integer literals in the given range. |
| 197 | +fn ConvertToByte[template N:! BigInt](_: IntLiteral(N)) -> i8 |
| 198 | + if N >= -128 and N <= 127 { |
| 199 | + return N as i8; |
| 200 | +} |
| 201 | +
|
| 202 | +// Given any int literal, produces a literal whose value is one higher. |
| 203 | +fn OneHigher(L: IntLiteral(template _:! BigInt)) -> auto { |
| 204 | + return L + 1; |
| 205 | +} |
| 206 | +// Error: 256 can't be represented in type `i8`. |
| 207 | +var v: i8 = OneHigher(255); |
| 208 | +``` |
| 209 | + |
| 210 | +## Alternatives Considered |
| 211 | + |
| 212 | +- [Use an ordinary integer or floating-point type for literals](/proposals/p0144.md#use-an-ordinary-integer-or-floating-point-type-for-literals) |
| 213 | +- [Use same type for all literals](/proposals/p0144.md#use-same-type-for-all-literals) |
| 214 | +- [Allow leading `-` in literal tokens](/proposals/p0144.md#allow-leading---in-literal-tokens) |
| 215 | +- [Forbidding floating-point ties](/proposals/p0866.md/#alternatives-considered) |
| 216 | + |
| 217 | +## References |
| 218 | + |
| 219 | +> - Proposal |
| 220 | +> [#143: Numeric literals](https://github.com/carbon-language/carbon-lang/pull/143) |
| 221 | +> - Proposal |
| 222 | +> [#144: Numeric literal semantics](https://github.com/carbon-language/carbon-lang/pull/144) |
| 223 | +> - Proposal |
| 224 | +> [#866: Allow ties in floating literals](https://github.com/carbon-language/carbon-lang/pull/866) |
| 225 | +> - Issue |
| 226 | +> [#1998: Make proposal for numeric type literal syntax](https://github.com/carbon-language/carbon-lang/issues/1998#issuecomment-1212644291) |
0 commit comments