Type relations
==============

The following section defines several relations on types that are needed to
describe the type checking done by the compiler.


Type equality
-------------
Nim uses structural type equivalence for most types. Only for objects,
enumerations and distinct types name equivalence is used. The following
algorithm, *in pseudo-code*, determines type equality:

.. code-block:: nim
  proc typeEqualsAux(a, b: PType,
                     s: var HashSet[(PType, PType)]): bool =
    if (a,b) in s: return true
    incl(s, (a,b))
    if a.kind == b.kind:
      case a.kind
      of int, intXX, float, floatXX, char, string, cstring, pointer,
          bool, nil, void:
        # leaf type: kinds identical; nothing more to check
        result = true
      of ref, ptr, var, set, seq, openarray:
        result = typeEqualsAux(a.baseType, b.baseType, s)
      of range:
        result = typeEqualsAux(a.baseType, b.baseType, s) and
          (a.rangeA == b.rangeA) and (a.rangeB == b.rangeB)
      of array:
        result = typeEqualsAux(a.baseType, b.baseType, s) and
                 typeEqualsAux(a.indexType, b.indexType, s)
      of tuple:
        if a.tupleLen == b.tupleLen:
          for i in 0..a.tupleLen-1:
            if not typeEqualsAux(a[i], b[i], s): return false
          result = true
      of object, enum, distinct:
        result = a == b
      of proc:
        result = typeEqualsAux(a.parameterTuple, b.parameterTuple, s) and
                 typeEqualsAux(a.resultType, b.resultType, s) and
                 a.callingConvention == b.callingConvention

  proc typeEquals(a, b: PType): bool =
    var s: HashSet[(PType, PType)] = {}
    result = typeEqualsAux(a, b, s)

Since types are graphs which can have cycles, the above algorithm needs an
auxiliary set ``s`` to detect this case.


Type equality modulo type distinction
-------------------------------------

The following algorithm (in pseudo-code) determines whether two types
are equal with no respect to ``distinct`` types. For brevity the cycle check
with an auxiliary set ``s`` is omitted:

.. code-block:: nim
  proc typeEqualsOrDistinct(a, b: PType): bool =
    if a.kind == b.kind:
      case a.kind
      of int, intXX, float, floatXX, char, string, cstring, pointer,
          bool, nil, void:
        # leaf type: kinds identical; nothing more to check
        result = true
      of ref, ptr, var, set, seq, openarray:
        result = typeEqualsOrDistinct(a.baseType, b.baseType)
      of range:
        result = typeEqualsOrDistinct(a.baseType, b.baseType) and
          (a.rangeA == b.rangeA) and (a.rangeB == b.rangeB)
      of array:
        result = typeEqualsOrDistinct(a.baseType, b.baseType) and
                 typeEqualsOrDistinct(a.indexType, b.indexType)
      of tuple:
        if a.tupleLen == b.tupleLen:
          for i in 0..a.tupleLen-1:
            if not typeEqualsOrDistinct(a[i], b[i]): return false
          result = true
      of distinct:
        result = typeEqualsOrDistinct(a.baseType, b.baseType)
      of object, enum:
        result = a == b
      of proc:
        result = typeEqualsOrDistinct(a.parameterTuple, b.parameterTuple) and
                 typeEqualsOrDistinct(a.resultType, b.resultType) and
                 a.callingConvention == b.callingConvention
    elif a.kind == distinct:
      result = typeEqualsOrDistinct(a.baseType, b)
    elif b.kind == distinct:
      result = typeEqualsOrDistinct(a, b.baseType)


Subtype relation
----------------
If object ``a`` inherits from ``b``, ``a`` is a subtype of ``b``. This subtype
relation is extended to the types ``var``, ``ref``, ``ptr``:

.. code-block:: nim
  proc isSubtype(a, b: PType): bool =
    if a.kind == b.kind:
      case a.kind
      of object:
        var aa = a.baseType
        while aa != nil and aa != b: aa = aa.baseType
        result = aa == b
      of var, ref, ptr:
        result = isSubtype(a.baseType, b.baseType)

.. XXX nil is a special value!


Covariance
----------

Covariance in Nim can be introduced only though pointer-like types such
as ``ptr`` and ``ref``. Sequence, Array and OpenArray types, instantiated
with pointer-like types will be considered covariant if and only if they
are also immutable. The introduction of a ``var`` modifier or additional
``ptr`` or ``ref`` indirections would result in invariant treatment of
these types.

``proc`` types are currently always invariant, but future versions of Nim
may relax this rule.

User-defined generic types may also be covariant with respect to some of
their parameters. By default, all generic params are considered invariant,
but you may choose the apply the prefix modifier ``in`` to a parameter to
make it contravariant or ``out`` to make it covariant:

.. code-block:: nim
  type
    AnnotatedPtr[out T] =
      metadata: MyTypeInfo
      p: ref T

    RingBuffer[out T] =
      startPos: int
      data: seq[T]

    Action {.importcpp: "std::function<void ('0)>".} [in T] = object

When the designated generic parameter is used to instantiate a pointer-like
type as in the case of `AnnotatedPtr` above, the resulting generic type will
also have pointer-like covariance:

.. code-block:: nim
  type
    GuiWidget = object of RootObj
    Button = object of GuiWidget
    ComboBox = object of GuiWidget

  var
    widgetPtr: AnnotatedPtr[GuiWidget]
    buttonPtr: AnnotatedPtr[Button]

  ...

  proc drawWidget[T](x: AnnotatedPtr[GuiWidget]) = ...

  # you can call procs expecting base types by supplying a derived type
  drawWidget(buttonPtr)

  # and you can convert more-specific pointer types to more general ones
  widgetPtr = buttonPtr

Just like with regular pointers, covariance will be enabled only for immutable
values:

.. code-block:: nim
  proc makeComboBox[T](x: var AnnotatedPtr[GuiWidget]) =
    x.p = new(ComboBox)

  makeComboBox(buttonPtr) # Error, AnnotatedPtr[Button] cannot be modified
                          # to point to a ComboBox

On the other hand, in the `RingBuffer` example above, the designated generic
param is used to instantiate the non-pointer ``seq`` type, which means that
the resulting generic type will have covariance that mimics an array or
sequence (i.e. it will be covariant only when instantiated with ``ptr`` and
``ref`` types):

.. code-block:: nim

  type
    Base = object of RootObj
    Derived = object of Base

  proc consumeBaseValues(b: RingBuffer[Base]) = ...

  var derivedValues: RingBuffer[Derived]

  consumeBaseValues(derivedValues) # Error, Base and Derived values may differ
                                   # in size

  proc consumeBasePointers(b: RingBuffer[ptr Base]) = ...

  var derivedPointers: RingBuffer[ptr Derived]

  consumeBaseValues(derivedPointers) # This is legal

Please note that Nim will treat the user-defined pointer-like types as
proper alternatives to the built-in pointer types. That is, types such
as `seq[AnnotatedPtr[T]]` or `RingBuffer[AnnotatedPtr[T]]` will also be
considered covariant and you can create new pointer-like types by instantiating
other user-defined pointer-like types.

The contravariant parameters introduced with the ``in`` modifier are currently
useful only when interfacing with imported types having such semantics.


Convertible relation
--------------------
A type ``a`` is **implicitly** convertible to type ``b`` iff the following
algorithm returns true:

.. code-block:: nim
  # XXX range types?
  proc isImplicitlyConvertible(a, b: PType): bool =
    if isSubtype(a, b) or isCovariant(a, b):
      return true
    case a.kind
    of int:     result = b in {int8, int16, int32, int64, uint, uint8, uint16,
                               uint32, uint64, float, float32, float64}
    of int8:    result = b in {int16, int32, int64, int}
    of int16:   result = b in {int32, int64, int}
    of int32:   result = b in {int64, int}
    of uint:    result = b in {uint32, uint64}
    of uint8:   result = b in {uint16, uint32, uint64}
    of uint16:  result = b in {uint32, uint64}
    of uint32:  result = b in {uint64}
    of float:   result = b in {float32, float64}
    of float32: result = b in {float64, float}
    of float64: result = b in {float32, float}
    of seq:
      result = b == openArray and typeEquals(a.baseType, b.baseType)
    of array:
      result = b == openArray and typeEquals(a.baseType, b.baseType)
      if a.baseType == char and a.indexType.rangeA == 0:
        result = b = cstring
    of cstring, ptr:
      result = b == pointer
    of string:
      result = b == cstring

A type ``a`` is **explicitly** convertible to type ``b`` iff the following
algorithm returns true:

.. code-block:: nim
  proc isIntegralType(t: PType): bool =
    result = isOrdinal(t) or t.kind in {float, float32, float64}

  proc isExplicitlyConvertible(a, b: PType): bool =
    result = false
    if isImplicitlyConvertible(a, b): return true
    if typeEqualsOrDistinct(a, b): return true
    if isIntegralType(a) and isIntegralType(b): return true
    if isSubtype(a, b) or isSubtype(b, a): return true

The convertible relation can be relaxed by a user-defined type
`converter`:idx:.

.. code-block:: nim
  converter toInt(x: char): int = result = ord(x)

  var
    x: int
    chr: char = 'a'

  # implicit conversion magic happens here
  x = chr
  echo x # => 97
  # you can use the explicit form too
  x = chr.toInt
  echo x # => 97

The type conversion ``T(a)`` is an L-value if ``a`` is an L-value and
``typeEqualsOrDistinct(T, type(a))`` holds.


Assignment compatibility
------------------------

An expression ``b`` can be assigned to an expression ``a`` iff ``a`` is an
`l-value` and ``isImplicitlyConvertible(b.typ, a.typ)`` holds.


Overloading resolution
======================

In a call ``p(args)`` the routine ``p`` that matches best is selected. If
multiple routines match equally well, the ambiguity is reported at compiletime.

Every arg in args needs to match. There are multiple different categories how an
argument can match. Let ``f`` be the formal parameter's type and ``a`` the type
of the argument.

1. Exact match: ``a`` and ``f`` are of the same type.
2. Literal match: ``a`` is an integer literal of value ``v``
   and ``f`` is a signed or unsigned integer type and ``v`` is in ``f``'s
   range. Or:  ``a`` is a floating point literal of value ``v``
   and ``f`` is a floating point type and ``v`` is in ``f``'s
   range.
3. Generic match: ``f`` is a generic type and ``a`` matches, for
   instance ``a`` is ``int`` and ``f`` is a generic (constrained) parameter
   type (like in ``[T]`` or ``[T: int|char]``.
4. Subrange or subtype match: ``a`` is a ``range[T]`` and ``T``
   matches ``f`` exactly. Or: ``a`` is a subtype of ``f``.
5. Integral conversion match: ``a`` is convertible to ``f`` and ``f`` and ``a``
   is some integer or floating point type.
6. Conversion match: ``a`` is convertible to ``f``, possibly via a user
   defined ``converter``.

These matching categories have a priority: An exact match is better than a
literal match and that is better than a generic match etc. In the following
``count(p, m)`` counts the number of matches of the matching category ``m``
for the routine ``p``.

A routine ``p`` matches better than a routine ``q`` if the following
algorithm returns true::

  for each matching category m in ["exact match", "literal match",
                                  "generic match", "subtype match",
                                  "integral match", "conversion match"]:
    if count(p, m) > count(q, m): return true
    elif count(p, m) == count(q, m):
      discard "continue with next category m"
    else:
      return false
  return "ambiguous"


Some examples:

.. code-block:: nim
  proc takesInt(x: int) = echo "int"
  proc takesInt[T](x: T) = echo "T"
  proc takesInt(x: int16) = echo "int16"

  takesInt(4) # "int"
  var x: int32
  takesInt(x) # "T"
  var y: int16
  takesInt(y) # "int16"
  var z: range[0..4] = 0
  takesInt(z) # "T"


If this algorithm returns "ambiguous" further disambiguation is performed:
If the argument ``a`` matches both the parameter type ``f`` of ``p``
and ``g`` of ``q`` via a subtyping relation, the inheritance depth is taken
into account:

.. code-block:: nim
  type
    A = object of RootObj
    B = object of A
    C = object of B

  proc p(obj: A) =
    echo "A"

  proc p(obj: B) =
    echo "B"

  var c = C()
  # not ambiguous, calls 'B', not 'A' since B is a subtype of A
  # but not vice versa:
  p(c)

  proc pp(obj: A, obj2: B) = echo "A B"
  proc pp(obj: B, obj2: A) = echo "B A"

  # but this is ambiguous:
  pp(c, c)


Likewise for generic matches the most specialized generic type (that still
matches) is preferred:

.. code-block:: nim
  proc gen[T](x: ref ref T) = echo "ref ref T"
  proc gen[T](x: ref T) = echo "ref T"
  proc gen[T](x: T) = echo "T"

  var ri: ref int
  gen(ri) # "ref T"


Overloading based on 'var T'
----------------------------

If the formal parameter ``f`` is of type ``var T`` in addition to the ordinary
type checking, the argument is checked to be an `l-value`:idx:. ``var T``
matches better than just ``T`` then.

.. code-block:: nim
  proc sayHi(x: int): string =
    # matches a non-var int
    result = $x
  proc sayHi(x: var int): string =
    # matches a var int
    result = $(x + 10)

  proc sayHello(x: int) =
    var m = x # a mutable version of x
    echo sayHi(x) # matches the non-var version of sayHi
    echo sayHi(m) # matches the var version of sayHi

  sayHello(3) # 3
              # 13

Automatic dereferencing
-----------------------

If the `experimental mode <#pragmas-experimental-pragma>`_ is active and no other match
is found, the first argument ``a`` is dereferenced automatically if it's a
pointer type and overloading resolution is tried with ``a[]`` instead.

Automatic self insertions
-------------------------

Starting with version 0.14 of the language, Nim supports ``field`` as a
shortcut for ``self.field`` comparable to the `this`:idx: keyword in Java
or C++. This feature has to be explicitly enabled via a ``{.this: self.}``
statement pragma. This pragma is active for the rest of the module:

.. code-block:: nim
  type
    Parent = object of RootObj
      parentField: int
    Child = object of Parent
      childField: int

  {.this: self.}
  proc sumFields(self: Child): int =
    result = parentField + childField
    # is rewritten to:
    # result = self.parentField + self.childField

Instead of ``self`` any other identifier can be used too, but
``{.this: self.}`` will become the default directive for the whole language
eventually.

In addition to fields, routine applications are also rewritten, but only
if no other interpretation of the call is possible:

.. code-block:: nim
  proc test(self: Child) =
    echo childField, " ", sumFields()
    # is rewritten to:
    echo self.childField, " ", sumFields(self)
    # but NOT rewritten to:
    echo self, self.childField, " ", sumFields(self)


Lazy type resolution for untyped
--------------------------------

**Note**: An `unresolved`:idx: expression is an expression for which no symbol
lookups and no type checking have been performed.

Since templates and macros that are not declared as ``immediate`` participate
in overloading resolution it's essential to have a way to pass unresolved
expressions to a template or macro. This is what the meta-type ``untyped``
accomplishes:

.. code-block:: nim
  template rem(x: untyped) = discard

  rem unresolvedExpression(undeclaredIdentifier)

A parameter of type ``untyped`` always matches any argument (as long as there is
any argument passed to it).

But one has to watch out because other overloads might trigger the
argument's resolution:

.. code-block:: nim
  template rem(x: untyped) = discard
  proc rem[T](x: T) = discard

  # undeclared identifier: 'unresolvedExpression'
  rem unresolvedExpression(undeclaredIdentifier)

``untyped`` and ``varargs[untyped]`` are the only metatype that are lazy in this sense, the other
metatypes ``typed`` and ``typedesc`` are not lazy.


Varargs matching
----------------

See `Varargs`_.
