mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-09 06:23:25 +00:00
implemented return type inference
Other fixes: * bind once is now the default for type classes as documented in the manual * fixes an issue in template overloading (erroneous ambiguity when different typedesc params were used)
This commit is contained in:
@@ -350,6 +350,8 @@ type
|
||||
# pass of semProcTypeNode performed after instantiation.
|
||||
# this won't be needed if we don't perform this redundant
|
||||
# second pass (stay tuned).
|
||||
tfRetType # marks return types in proc (used to detect type classes
|
||||
# used as return types for return type inference)
|
||||
tfAll, # type class requires all constraints to be met (default)
|
||||
tfAny, # type class requires any constraint to be met
|
||||
tfCapturesEnv, # whether proc really captures some environment
|
||||
|
||||
@@ -1048,8 +1048,20 @@ proc semAsgn(c: PContext, n: PNode): PNode =
|
||||
localError(a.info, errXCannotBeAssignedTo,
|
||||
renderTree(a, {renderNoComments}))
|
||||
else:
|
||||
n.sons[1] = semExprWithType(c, n.sons[1])
|
||||
n.sons[1] = fitNode(c, le, n.sons[1])
|
||||
var
|
||||
rhs = semExprWithType(c, n.sons[1])
|
||||
lhs = n.sons[0]
|
||||
if lhs.kind == nkSym and lhs.sym.kind == skResult and
|
||||
lhs.sym.typ.kind == tyGenericParam:
|
||||
if matchTypeClass(lhs.typ, rhs.typ):
|
||||
InternalAssert c.p.resultSym != nil
|
||||
lhs.typ = rhs.typ
|
||||
c.p.resultSym.typ = rhs.typ
|
||||
c.p.owner.typ.sons[0] = rhs.typ
|
||||
else:
|
||||
typeMismatch(n, lhs.typ, rhs.typ)
|
||||
|
||||
n.sons[1] = fitNode(c, le, rhs)
|
||||
fixAbstractType(c, n)
|
||||
asgnToResultVar(c, n, n.sons[0], n.sons[1])
|
||||
result = n
|
||||
|
||||
@@ -25,8 +25,13 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
|
||||
s.flags = s.flags + {sfUsed, sfFromGeneric}
|
||||
var t = PType(IdTableGet(pt, q.typ))
|
||||
if t == nil:
|
||||
LocalError(a.info, errCannotInstantiateX, s.name.s)
|
||||
t = errorType(c)
|
||||
if tfRetType in q.typ.flags:
|
||||
# keep the generic type and allow the return type to be bound
|
||||
# later by semAsgn in return type inference scenario
|
||||
t = q.typ
|
||||
else:
|
||||
LocalError(a.info, errCannotInstantiateX, s.name.s)
|
||||
t = errorType(c)
|
||||
elif t.kind == tyGenericParam:
|
||||
InternalError(a.info, "instantiateGenericParamList: " & q.name.s)
|
||||
elif t.kind == tyGenericInvokation:
|
||||
@@ -163,7 +168,6 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
result.typ = newTypeS(tyProc, c)
|
||||
rawAddSon(result.typ, nil)
|
||||
result.typ.callConv = fn.typ.callConv
|
||||
ParamsTypeCheck(c, result.typ)
|
||||
var oldPrc = GenericCacheGet(c, entry)
|
||||
if oldPrc == nil:
|
||||
c.generics.generics.add(entry)
|
||||
@@ -174,6 +178,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
if fn.kind != skTemplate:
|
||||
instantiateBody(c, n, result)
|
||||
sideEffectsCheck(c, result)
|
||||
ParamsTypeCheck(c, result.typ)
|
||||
else:
|
||||
result = oldPrc
|
||||
popInfoContext()
|
||||
|
||||
@@ -584,22 +584,20 @@ proc paramTypeClass(c: PContext, paramType: PType, procKind: TSymKind):
|
||||
result.typ = newTypeS(tyTypeDesc, c)
|
||||
result.typ.sons = paramType.sons
|
||||
of tyDistinct:
|
||||
# type T1 = distinct expr
|
||||
# type S1 = distinct Sortable
|
||||
# proc x(a, b: T1, c, d: S1)
|
||||
# This forces bindOnce behavior for the type class, equivalent to
|
||||
# proc x[T, S](a, b: T, c, d: S)
|
||||
result = paramTypeClass(c, paramType.lastSon, procKind)
|
||||
result.id = paramType.sym.name
|
||||
# disable the bindOnce behavior for the type class
|
||||
result.id = nil
|
||||
return
|
||||
of tyGenericBody:
|
||||
# type Foo[T] = object
|
||||
# proc x(a: Foo, b: Foo)
|
||||
result.typ = newTypeS(tyTypeClass, c)
|
||||
result.typ.addSonSkipIntLit(paramType)
|
||||
result.id = paramType.sym.name # bindOnce by default
|
||||
of tyTypeClass:
|
||||
result.typ = copyType(paramType, getCurrOwner(), false)
|
||||
else: nil
|
||||
# bindOnce by default
|
||||
if paramType.sym != nil: result.id = paramType.sym.name
|
||||
|
||||
proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||
paramType: PType, paramName: string,
|
||||
@@ -619,7 +617,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||
let s = SymtabGet(c.tab, paramTypId)
|
||||
# tests/run/tinterf triggers this:
|
||||
if s != nil: result = s.typ
|
||||
else:
|
||||
else:
|
||||
LocalError(info, errCannotInstantiateX, paramName)
|
||||
result = errorType(c)
|
||||
else:
|
||||
@@ -684,8 +682,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue
|
||||
for j in countup(0, length-3):
|
||||
var arg = newSymG(skParam, a.sons[j], c)
|
||||
var finalType = liftParamType(c, kind, genericParams, typ, arg.name.s,
|
||||
arg.info).skipIntLit
|
||||
var finalType = liftParamType(c, kind, genericParams, typ,
|
||||
arg.name.s, arg.info).skipIntLit
|
||||
arg.typ = finalType
|
||||
arg.position = counter
|
||||
inc(counter)
|
||||
@@ -703,6 +701,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
if skipTypes(r, {tyGenericInst}).kind != tyEmpty:
|
||||
if r.sym == nil or sfAnon notin r.sym.flags:
|
||||
r = liftParamType(c, kind, genericParams, r, "result", n.sons[0].info)
|
||||
r.flags.incl tfRetType
|
||||
result.sons[0] = skipIntLit(r)
|
||||
res.typ = result.sons[0]
|
||||
|
||||
|
||||
@@ -282,7 +282,12 @@ proc matchTypeClass(c: var TCandidate, typeClass, t: PType): TTypeRelation =
|
||||
# if the loop finished without returning, either all constraints matched
|
||||
# or none of them matched.
|
||||
result = if tfAny in typeClass.flags: isNone else: isGeneric
|
||||
|
||||
|
||||
proc matchTypeClass*(typeClass, typ: PType): bool =
|
||||
var c: TCandidate
|
||||
InitCandidate(c, typeClass)
|
||||
result = matchTypeClass(c, typeClass, typ) == isGeneric
|
||||
|
||||
proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||
proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
|
||||
result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)
|
||||
|
||||
@@ -626,9 +626,9 @@ proc SameTypeOrNil*(a, b: PType, flags: TTypeCmpFlags = {}): bool =
|
||||
var c = initSameTypeClosure()
|
||||
c.flags = flags
|
||||
result = SameTypeAux(a, b, c)
|
||||
|
||||
|
||||
proc equalParam(a, b: PSym): TParamsEquality =
|
||||
if SameTypeOrNil(a.typ, b.typ):
|
||||
if SameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}):
|
||||
if a.ast == b.ast:
|
||||
result = paramsEqual
|
||||
elif a.ast != nil and b.ast != nil:
|
||||
|
||||
@@ -2940,6 +2940,13 @@ from the proc body. This is usually used with the ``auto`` type class:
|
||||
.. code-block:: nimrod
|
||||
proc makePair(a, b): auto = (first: a, second: b)
|
||||
|
||||
The return type will be treated as additional generic param and can be
|
||||
explicitly specified at call sites as any other generic param.
|
||||
|
||||
Future versions of nimrod may also support overloading based on the return type
|
||||
of the overloads. In such settings, the expected result type at call sites may
|
||||
also influence the inferred return type.
|
||||
|
||||
Symbol lookup in generics
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -3431,7 +3438,7 @@ typedesc
|
||||
|
||||
`typedesc` is a special type allowing you to treat types as compile-time values
|
||||
(i.e. if types are compile-time values and all values have a type, then
|
||||
typedesc must be their type).
|
||||
typedesc must be their type).
|
||||
|
||||
When used as a regular proc param, typedesc acts as a type class. The proc
|
||||
will be instantiated for each unique type parameter and you can refer to the
|
||||
@@ -3642,15 +3649,20 @@ proc with no side effects:
|
||||
|
||||
destructor pragma
|
||||
-----------------
|
||||
`RAII`:idx:
|
||||
`automatic variables`:idx:
|
||||
`destructors`:idx:
|
||||
|
||||
The `destructor` pragma is used to mark a proc to act as a type destructor.
|
||||
The proc must have a single parameter, having a concrete type.
|
||||
The proc must have a single parameter with a concrete type (the name of a
|
||||
generic type is allowed too).
|
||||
|
||||
Destructors will be automatically invoked when a local stack variable goes
|
||||
out of scope. If a record type features a field with destructable type and
|
||||
out of scope.
|
||||
|
||||
If a record type features a field with destructable type and
|
||||
the user have not provided explicit implementation, Nimrod will automatically
|
||||
generate a destructor for the record type.
|
||||
generate a destructor for the record type. Nimrod will automatically insert
|
||||
calls to any base class destructors in both user-defined and generated
|
||||
destructors.
|
||||
|
||||
|
||||
procvar pragma
|
||||
--------------
|
||||
@@ -3658,7 +3670,6 @@ The `procvar`:idx: pragma is used to mark a proc that it can be passed to a
|
||||
procedural variable.
|
||||
|
||||
|
||||
|
||||
compileTime pragma
|
||||
------------------
|
||||
The `compileTime`:idx: pragma is used to mark a proc to be used at compile
|
||||
|
||||
@@ -51,10 +51,11 @@ type
|
||||
`nil` {.magic: "Nil".}
|
||||
expr* {.magic: Expr.} ## meta type to denote an expression (for templates)
|
||||
stmt* {.magic: Stmt.} ## meta type to denote a statement (for templates)
|
||||
typeDesc* {.magic: TypeDesc.} ## meta type to denote
|
||||
## a type description (for templates)
|
||||
void* {.magic: "VoidType".} ## meta type to denote the absense of any type
|
||||
|
||||
typeDesc* {.magic: TypeDesc.} ## meta type to denote a type description
|
||||
void* {.magic: "VoidType".} ## meta type to denote the absense of any type
|
||||
auto* = expr
|
||||
any* = distinct auto
|
||||
|
||||
TSignedInt* = int|int8|int16|int32|int64
|
||||
## type class matching all signed integer types
|
||||
|
||||
@@ -111,6 +112,11 @@ proc new*[T](a: var ref T) {.magic: "New", noSideEffect.}
|
||||
## creates a new object of type ``T`` and returns a safe (traced)
|
||||
## reference to it in ``a``.
|
||||
|
||||
proc new(T: typedesc): ref T =
|
||||
## creates a new object of type ``T`` and returns a safe (traced)
|
||||
## reference to it as result value
|
||||
new(result)
|
||||
|
||||
proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.}
|
||||
## leaked implementation detail. Do not use.
|
||||
|
||||
@@ -538,7 +544,7 @@ proc abs*(x: int64): int64 {.magic: "AbsI64", noSideEffect.}
|
||||
## checking is turned on).
|
||||
|
||||
type
|
||||
IntMax32 = distinct int|int8|int16|int32
|
||||
IntMax32 = int|int8|int16|int32
|
||||
|
||||
proc `+%` *(x, y: IntMax32): IntMax32 {.magic: "AddU", noSideEffect.}
|
||||
proc `+%` *(x, y: Int64): Int64 {.magic: "AddU", noSideEffect.}
|
||||
@@ -1315,11 +1321,10 @@ iterator items*(a: cstring): char {.inline.} =
|
||||
yield a[i]
|
||||
inc(i)
|
||||
|
||||
when not defined(booting):
|
||||
iterator items*(E: typedesc[enum]): E =
|
||||
## iterates over the values of the enum ``E``.
|
||||
for v in low(E)..high(E):
|
||||
yield v
|
||||
iterator items*(E: typedesc[enum]): E =
|
||||
## iterates over the values of the enum ``E``.
|
||||
for v in low(E)..high(E):
|
||||
yield v
|
||||
|
||||
iterator pairs*[T](a: openarray[T]): tuple[key: int, val: T] {.inline.} =
|
||||
## iterates over each item of `a`. Yields ``(index, a[index])`` pairs.
|
||||
|
||||
29
tests/run/trettypeinference.nim
Normal file
29
tests/run/trettypeinference.nim
Normal file
@@ -0,0 +1,29 @@
|
||||
discard """
|
||||
msg: "instantiated for string\ninstantiated for int\ninstantiated for bool"
|
||||
output: "int\nseq[string]\nA\nB\n100\ntrue"
|
||||
"""
|
||||
|
||||
import typetraits
|
||||
|
||||
proc plus(a, b): auto = a + b
|
||||
|
||||
proc `+`(a, b: string): seq[string] = @[a, b]
|
||||
|
||||
var i = plus(10, 20)
|
||||
var s = plus("A", "B")
|
||||
|
||||
echo i.type.name
|
||||
echo s.type.name
|
||||
|
||||
proc inst(a): auto =
|
||||
static: echo "instantiated for ", a.type.name
|
||||
result = a
|
||||
|
||||
echo inst("A")
|
||||
echo inst("B")
|
||||
echo inst(100)
|
||||
echo inst(true)
|
||||
|
||||
# XXX: [string, tyGenericParam] is cached instead of [string, string]
|
||||
# echo inst[string, string]("C")
|
||||
|
||||
Reference in New Issue
Block a user