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
parent b28fcdfa93
commit 7e44015491
9 changed files with 104 additions and 36 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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]

View File

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

View File

@@ -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:

View File

@@ -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

View File

@@ -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.

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