better type inference for numerical types; prerequisitive for version 1

This commit is contained in:
Andreas Rumpf 2018-02-02 09:29:05 +01:00
commit bd1dfa4b38
4 changed files with 73 additions and 3 deletions

View file

@ -235,3 +235,6 @@ styledEcho "Red on Green.", resetStyle
- ``\n`` is now only the single line feed character like in most - ``\n`` is now only the single line feed character like in most
other programming languages. The new platform specific newline escape sequence is other programming languages. The new platform specific newline escape sequence is
written as ``\p``. This change only affects the Windows platform. 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.

View file

@ -913,6 +913,25 @@ proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
else: else:
return false 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, proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
flags: TTypeRelFlags = {}): TTypeRelation = flags: TTypeRelFlags = {}): TTypeRelation =
# typeRel can be used to establish various relationships between types: # 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: elif x.kind == tyGenericParam:
result = isGeneric result = isGeneric
else: else:
result = typeRel(c, x, a) # check if it fits # Special type binding rule for numeric types.
if result > isGeneric: result = isGeneric # 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: of tyStatic:
let prev = PType(idTableGet(c.bindings, f)) let prev = PType(idTableGet(c.bindings, f))
if prev == nil: if prev == nil:

View file

@ -63,6 +63,8 @@ The following example shows a generic binary tree can be modelled:
for str in preorder(root): for str in preorder(root):
stdout.writeLine(str) stdout.writeLine(str)
The ``T`` is called a `generic type parameter `:idx:.
Is operator Is operator
----------- -----------
@ -710,3 +712,30 @@ definition):
But a ``bind`` is rarely useful because symbol binding from the definition But a ``bind`` is rarely useful because symbol binding from the definition
scope is the default. 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``

View 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))