mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-17 18:17:11 +00:00
better type inference for numerical types; prerequisitive for version 1
This commit is contained in:
@@ -235,3 +235,6 @@ styledEcho "Red on Green.", resetStyle
|
||||
- ``\n`` is now only the single line feed character like in most
|
||||
other programming languages. The new platform specific newline escape sequence is
|
||||
written as ``\p``. This change only affects the Windows platform.
|
||||
- Type inference for generic type parameters involving numeric types is now symetric. See
|
||||
[Generic type inference for numeric types](https://nim-lang.org/docs/manual.html#generics-generic-type-inference-fornumeric-types)
|
||||
for more information.
|
||||
|
||||
@@ -913,6 +913,25 @@ proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
|
||||
else:
|
||||
return false
|
||||
|
||||
proc maxNumericType(prev, candidate: PType): PType =
|
||||
let c = candidate.skipTypes({tyRange})
|
||||
template greater(s) =
|
||||
if c.kind in s: result = c
|
||||
case prev.kind
|
||||
of tyInt: greater({tyInt64})
|
||||
of tyInt8: greater({tyInt, tyInt16, tyInt32, tyInt64})
|
||||
of tyInt16: greater({tyInt, tyInt32, tyInt64})
|
||||
of tyInt32: greater({tyInt64})
|
||||
|
||||
of tyUInt: greater({tyUInt64})
|
||||
of tyUInt8: greater({tyUInt, tyUInt16, tyUInt32, tyUInt64})
|
||||
of tyUInt16: greater({tyUInt, tyUInt32, tyUInt64})
|
||||
of tyUInt32: greater({tyUInt64})
|
||||
|
||||
of tyFloat32: greater({tyFloat64, tyFloat128})
|
||||
of tyFloat64: greater({tyFloat128})
|
||||
else: discard
|
||||
|
||||
proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
|
||||
flags: TTypeRelFlags = {}): TTypeRelation =
|
||||
# typeRel can be used to establish various relationships between types:
|
||||
@@ -1602,9 +1621,16 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
|
||||
elif x.kind == tyGenericParam:
|
||||
result = isGeneric
|
||||
else:
|
||||
result = typeRel(c, x, a) # check if it fits
|
||||
if result > isGeneric: result = isGeneric
|
||||
|
||||
# Special type binding rule for numeric types.
|
||||
# See section "Generic type inference for numeric types" of the
|
||||
# manual for further details:
|
||||
let rebinding = maxNumericType(x.skipTypes({tyRange}), a)
|
||||
if rebinding != nil:
|
||||
put(c, f, rebinding)
|
||||
result = isGeneric
|
||||
else:
|
||||
result = typeRel(c, x, a) # check if it fits
|
||||
if result > isGeneric: result = isGeneric
|
||||
of tyStatic:
|
||||
let prev = PType(idTableGet(c.bindings, f))
|
||||
if prev == nil:
|
||||
|
||||
@@ -63,6 +63,8 @@ The following example shows a generic binary tree can be modelled:
|
||||
for str in preorder(root):
|
||||
stdout.writeLine(str)
|
||||
|
||||
The ``T`` is called a `generic type parameter `:idx:.
|
||||
|
||||
|
||||
Is operator
|
||||
-----------
|
||||
@@ -710,3 +712,30 @@ definition):
|
||||
|
||||
But a ``bind`` is rarely useful because symbol binding from the definition
|
||||
scope is the default.
|
||||
|
||||
|
||||
Generic type inference for numeric types
|
||||
----------------------------------------
|
||||
|
||||
A `numeric`:idx: type is any signed, unsigned integer type, floating point
|
||||
type or a subrange thereof. Let ``maxNumericType(T1, T2)`` be the "greater"
|
||||
type of ``T1`` and ``T2``, that is the type that uses more bits. For
|
||||
example ``maxNumericType(int32, int64) == int64``. ``maxNumericType`` is only
|
||||
defined for numeric types of the same class (signed, unsigned, floating point).
|
||||
``maxNumericType`` strips away subranges,
|
||||
``maxNumericType(subrangeof(int16), int8)`` produces ``int16`` not its
|
||||
subrange. The definition ``maxNumericType`` is extended to take a variable
|
||||
number of arguments in the obvious way;
|
||||
``maxNumericType(x, y, z) == maxNumericType(maxNumericType(x, y), z)``.
|
||||
|
||||
A generic type parameter ``T`` that is bound to multiple numeric types ``N1``,
|
||||
``N2``, ``N3``, ... during type checking is inferred to
|
||||
be ``maxNumericType(N1, N2, N3, ...)``. This special type inference rule ensures
|
||||
that the builtin arithmetic operators can be written in an intuitive way:
|
||||
|
||||
.. code-block:: nim
|
||||
proc `@`[T: int|int16|int32](x, y: T): T
|
||||
|
||||
4'i32 @ 6'i64 # inferred to be of type ``int64``
|
||||
|
||||
4'i64 @ 6'i32 # inferred to be of type ``int64``
|
||||
|
||||
12
tests/generics/tspecial_numeric_inference.nim
Normal file
12
tests/generics/tspecial_numeric_inference.nim
Normal file
@@ -0,0 +1,12 @@
|
||||
discard """
|
||||
output: '''int64
|
||||
int64'''
|
||||
"""
|
||||
|
||||
import typetraits
|
||||
|
||||
proc `@`[T: SomeInteger](x, y: T): T = x
|
||||
|
||||
echo(type(5'i64 @ 6'i32))
|
||||
|
||||
echo(type(5'i32 @ 6'i64))
|
||||
Reference in New Issue
Block a user