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:
Zahary Karadjov 2012-09-29 16:49:04 +03:00
commit 7e44015491
9 changed files with 104 additions and 36 deletions

View file

@ -350,6 +350,8 @@ type
# pass of semProcTypeNode performed after instantiation. # pass of semProcTypeNode performed after instantiation.
# this won't be needed if we don't perform this redundant # this won't be needed if we don't perform this redundant
# second pass (stay tuned). # 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) tfAll, # type class requires all constraints to be met (default)
tfAny, # type class requires any constraint to be met tfAny, # type class requires any constraint to be met
tfCapturesEnv, # whether proc really captures some environment tfCapturesEnv, # whether proc really captures some environment

View file

@ -1048,8 +1048,20 @@ proc semAsgn(c: PContext, n: PNode): PNode =
localError(a.info, errXCannotBeAssignedTo, localError(a.info, errXCannotBeAssignedTo,
renderTree(a, {renderNoComments})) renderTree(a, {renderNoComments}))
else: else:
n.sons[1] = semExprWithType(c, n.sons[1]) var
n.sons[1] = fitNode(c, le, n.sons[1]) 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) fixAbstractType(c, n)
asgnToResultVar(c, n, n.sons[0], n.sons[1]) asgnToResultVar(c, n, n.sons[0], n.sons[1])
result = n result = n

View file

@ -25,8 +25,13 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
s.flags = s.flags + {sfUsed, sfFromGeneric} s.flags = s.flags + {sfUsed, sfFromGeneric}
var t = PType(IdTableGet(pt, q.typ)) var t = PType(IdTableGet(pt, q.typ))
if t == nil: if t == nil:
LocalError(a.info, errCannotInstantiateX, s.name.s) if tfRetType in q.typ.flags:
t = errorType(c) # 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: elif t.kind == tyGenericParam:
InternalError(a.info, "instantiateGenericParamList: " & q.name.s) InternalError(a.info, "instantiateGenericParamList: " & q.name.s)
elif t.kind == tyGenericInvokation: elif t.kind == tyGenericInvokation:
@ -163,7 +168,6 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
result.typ = newTypeS(tyProc, c) result.typ = newTypeS(tyProc, c)
rawAddSon(result.typ, nil) rawAddSon(result.typ, nil)
result.typ.callConv = fn.typ.callConv result.typ.callConv = fn.typ.callConv
ParamsTypeCheck(c, result.typ)
var oldPrc = GenericCacheGet(c, entry) var oldPrc = GenericCacheGet(c, entry)
if oldPrc == nil: if oldPrc == nil:
c.generics.generics.add(entry) c.generics.generics.add(entry)
@ -174,6 +178,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
if fn.kind != skTemplate: if fn.kind != skTemplate:
instantiateBody(c, n, result) instantiateBody(c, n, result)
sideEffectsCheck(c, result) sideEffectsCheck(c, result)
ParamsTypeCheck(c, result.typ)
else: else:
result = oldPrc result = oldPrc
popInfoContext() popInfoContext()

View file

@ -584,22 +584,20 @@ proc paramTypeClass(c: PContext, paramType: PType, procKind: TSymKind):
result.typ = newTypeS(tyTypeDesc, c) result.typ = newTypeS(tyTypeDesc, c)
result.typ.sons = paramType.sons result.typ.sons = paramType.sons
of tyDistinct: 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 = paramTypeClass(c, paramType.lastSon, procKind)
result.id = paramType.sym.name # disable the bindOnce behavior for the type class
result.id = nil
return
of tyGenericBody: of tyGenericBody:
# type Foo[T] = object # type Foo[T] = object
# proc x(a: Foo, b: Foo) # proc x(a: Foo, b: Foo)
result.typ = newTypeS(tyTypeClass, c) result.typ = newTypeS(tyTypeClass, c)
result.typ.addSonSkipIntLit(paramType) result.typ.addSonSkipIntLit(paramType)
result.id = paramType.sym.name # bindOnce by default
of tyTypeClass: of tyTypeClass:
result.typ = copyType(paramType, getCurrOwner(), false) result.typ = copyType(paramType, getCurrOwner(), false)
else: nil else: nil
# bindOnce by default
if paramType.sym != nil: result.id = paramType.sym.name
proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
paramType: PType, paramName: string, paramType: PType, paramName: string,
@ -684,8 +682,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue
for j in countup(0, length-3): for j in countup(0, length-3):
var arg = newSymG(skParam, a.sons[j], c) var arg = newSymG(skParam, a.sons[j], c)
var finalType = liftParamType(c, kind, genericParams, typ, arg.name.s, var finalType = liftParamType(c, kind, genericParams, typ,
arg.info).skipIntLit arg.name.s, arg.info).skipIntLit
arg.typ = finalType arg.typ = finalType
arg.position = counter arg.position = counter
inc(counter) inc(counter)
@ -703,6 +701,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if skipTypes(r, {tyGenericInst}).kind != tyEmpty: if skipTypes(r, {tyGenericInst}).kind != tyEmpty:
if r.sym == nil or sfAnon notin r.sym.flags: if r.sym == nil or sfAnon notin r.sym.flags:
r = liftParamType(c, kind, genericParams, r, "result", n.sons[0].info) r = liftParamType(c, kind, genericParams, r, "result", n.sons[0].info)
r.flags.incl tfRetType
result.sons[0] = skipIntLit(r) result.sons[0] = skipIntLit(r)
res.typ = result.sons[0] res.typ = result.sons[0]

View file

@ -283,6 +283,11 @@ proc matchTypeClass(c: var TCandidate, typeClass, t: PType): TTypeRelation =
# or none of them matched. # or none of them matched.
result = if tfAny in typeClass.flags: isNone else: isGeneric 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 procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
proc inconsistentVarTypes(f, a: PType): bool {.inline.} = proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar) result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)

View file

@ -628,7 +628,7 @@ proc SameTypeOrNil*(a, b: PType, flags: TTypeCmpFlags = {}): bool =
result = SameTypeAux(a, b, c) result = SameTypeAux(a, b, c)
proc equalParam(a, b: PSym): TParamsEquality = proc equalParam(a, b: PSym): TParamsEquality =
if SameTypeOrNil(a.typ, b.typ): if SameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}):
if a.ast == b.ast: if a.ast == b.ast:
result = paramsEqual result = paramsEqual
elif a.ast != nil and b.ast != nil: elif a.ast != nil and b.ast != nil:

View file

@ -2940,6 +2940,13 @@ from the proc body. This is usually used with the ``auto`` type class:
.. code-block:: nimrod .. code-block:: nimrod
proc makePair(a, b): auto = (first: a, second: b) 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 Symbol lookup in generics
~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~
@ -3431,7 +3438,7 @@ typedesc
`typedesc` is a special type allowing you to treat types as compile-time values `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 (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 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 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 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 `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 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 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 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. procedural variable.
compileTime pragma compileTime pragma
------------------ ------------------
The `compileTime`:idx: pragma is used to mark a proc to be used at compile The `compileTime`:idx: pragma is used to mark a proc to be used at compile

View file

@ -51,9 +51,10 @@ type
`nil` {.magic: "Nil".} `nil` {.magic: "Nil".}
expr* {.magic: Expr.} ## meta type to denote an expression (for templates) expr* {.magic: Expr.} ## meta type to denote an expression (for templates)
stmt* {.magic: Stmt.} ## meta type to denote a statement (for templates) stmt* {.magic: Stmt.} ## meta type to denote a statement (for templates)
typeDesc* {.magic: TypeDesc.} ## meta type to denote typeDesc* {.magic: TypeDesc.} ## meta type to denote a type description
## a type description (for templates) void* {.magic: "VoidType".} ## meta type to denote the absense of any type
void* {.magic: "VoidType".} ## meta type to denote the absense of any type auto* = expr
any* = distinct auto
TSignedInt* = int|int8|int16|int32|int64 TSignedInt* = int|int8|int16|int32|int64
## type class matching all signed integer types ## 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) ## creates a new object of type ``T`` and returns a safe (traced)
## reference to it in ``a``. ## 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.} proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.}
## leaked implementation detail. Do not use. ## leaked implementation detail. Do not use.
@ -538,7 +544,7 @@ proc abs*(x: int64): int64 {.magic: "AbsI64", noSideEffect.}
## checking is turned on). ## checking is turned on).
type type
IntMax32 = distinct int|int8|int16|int32 IntMax32 = int|int8|int16|int32
proc `+%` *(x, y: IntMax32): IntMax32 {.magic: "AddU", noSideEffect.} proc `+%` *(x, y: IntMax32): IntMax32 {.magic: "AddU", noSideEffect.}
proc `+%` *(x, y: Int64): Int64 {.magic: "AddU", noSideEffect.} proc `+%` *(x, y: Int64): Int64 {.magic: "AddU", noSideEffect.}
@ -1315,11 +1321,10 @@ iterator items*(a: cstring): char {.inline.} =
yield a[i] yield a[i]
inc(i) inc(i)
when not defined(booting): iterator items*(E: typedesc[enum]): E =
iterator items*(E: typedesc[enum]): E = ## iterates over the values of the enum ``E``.
## iterates over the values of the enum ``E``. for v in low(E)..high(E):
for v in low(E)..high(E): yield v
yield v
iterator pairs*[T](a: openarray[T]): tuple[key: int, val: T] {.inline.} = iterator pairs*[T](a: openarray[T]): tuple[key: int, val: T] {.inline.} =
## iterates over each item of `a`. Yields ``(index, a[index])`` pairs. ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs.

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